From 6ce0df1e78b1554048cd4b08346aa3166b4c55b0 Mon Sep 17 00:00:00 2001 From: kgv Date: Sat, 10 Oct 2015 22:17:37 +0300 Subject: [PATCH] 0026765: Visualization - drop TKVoxel toolkit --- adm/MODULES | 2 +- adm/UDLIST | 2 - dox/FILES_HTML.txt | 1 - dox/FILES_PDF.txt | 1 - dox/dev_guides/tests/tests.md | 2 +- dox/overview/overview.md | 7 - dox/technical_overview/technical_overview.md | 4 - dox/user_guides/user_guides.md | 1 - .../voxels_wp/images/voxels_wp_image003.png | Bin 9472 -> 0 bytes .../voxels_wp/images/voxels_wp_image004.png | Bin 73490 -> 0 bytes .../voxels_wp/images/voxels_wp_image005.png | Bin 130773 -> 0 bytes .../voxels_wp/images/voxels_wp_image006.png | Bin 112583 -> 0 bytes .../voxels_wp/images/voxels_wp_image007.png | Bin 62429 -> 0 bytes .../voxels_wp/images/voxels_wp_image008.png | Bin 29261 -> 0 bytes .../voxels_wp/images/voxels_wp_image009.png | Bin 20132 -> 0 bytes .../voxels_wp/images/voxels_wp_image010.png | Bin 5257 -> 0 bytes dox/user_guides/voxels_wp/voxels_wp.md | 158 - samples/qt/VoxelDemo/VoxelDemo-vc10.sln | 26 - samples/qt/VoxelDemo/VoxelDemo-vc12.sln | 26 - samples/qt/VoxelDemo/VoxelDemo-vc9.sln | 20 - samples/qt/VoxelDemo/VoxelDemo.pro | 185 - samples/qt/VoxelDemo/genproj.bat | 43 - samples/qt/VoxelDemo/inc/Application.h | 116 - samples/qt/VoxelDemo/inc/ConversionThread.h | 40 - samples/qt/VoxelDemo/inc/Timer.h | 33 - samples/qt/VoxelDemo/inc/Viewer.h | 67 - .../qt/VoxelDemo/inc/VoxelClient_VisDrawer.h | 74 - samples/qt/VoxelDemo/msvc.bat | 38 - samples/qt/VoxelDemo/run.bat | 21 - samples/qt/VoxelDemo/src/Application.cpp | 2343 ------------ samples/qt/VoxelDemo/src/ConversionThread.cpp | 58 - samples/qt/VoxelDemo/src/Main.cpp | 13 - samples/qt/VoxelDemo/src/Timer.cpp | 67 - samples/qt/VoxelDemo/src/Viewer.cpp | 213 -- .../VoxelDemo/src/VoxelClient_VisDrawer.cxx | 3374 ----------------- src/OS/Visualization.tcl | 3 +- src/QABugs/QABugs_19.cxx | 77 - src/TKQADraw/EXTERNLIB | 1 - src/TKViewerTest/EXTERNLIB | 1 - src/TKVoxel/CMakeLists.txt | 7 - src/TKVoxel/EXTERNLIB | 10 - src/TKVoxel/FILES | 1 - src/TKVoxel/PACKAGES | 1 - src/ViewerTest/FILES | 1 - src/ViewerTest/ViewerTest.cxx | 1 - src/ViewerTest/ViewerTest.hxx | 2 - src/ViewerTest/ViewerTest_VoxelCommands.cxx | 1053 ----- src/Voxel/FILES | 32 - src/Voxel/Voxel_BoolDS.cxx | 141 - src/Voxel/Voxel_BoolDS.hxx | 96 - src/Voxel/Voxel_BooleanOperation.cxx | 230 -- src/Voxel/Voxel_BooleanOperation.hxx | 108 - src/Voxel/Voxel_CollisionDetection.cxx | 295 -- src/Voxel/Voxel_CollisionDetection.hxx | 151 - src/Voxel/Voxel_ColorDS.cxx | 173 - src/Voxel/Voxel_ColorDS.hxx | 96 - src/Voxel/Voxel_DS.cxx | 194 - src/Voxel/Voxel_DS.hxx | 134 - src/Voxel/Voxel_FastConverter.cxx | 1174 ------ src/Voxel/Voxel_FastConverter.hxx | 135 - src/Voxel/Voxel_FloatDS.cxx | 116 - src/Voxel/Voxel_FloatDS.hxx | 96 - src/Voxel/Voxel_OctBoolDS.cxx | 303 -- src/Voxel/Voxel_OctBoolDS.hxx | 123 - src/Voxel/Voxel_Prs.cxx | 307 -- src/Voxel/Voxel_Prs.hxx | 149 - src/Voxel/Voxel_ROctBoolDS.cxx | 727 ---- src/Voxel/Voxel_ROctBoolDS.hxx | 141 - src/Voxel/Voxel_Reader.cxx | 575 --- src/Voxel/Voxel_Reader.hxx | 105 - src/Voxel/Voxel_Selector.cxx | 581 --- src/Voxel/Voxel_Selector.hxx | 90 - src/Voxel/Voxel_SplitData.cxx | 32 - src/Voxel/Voxel_SplitData.hxx | 70 - src/Voxel/Voxel_TypeDef.hxx | 53 - src/Voxel/Voxel_VisData.h | 129 - src/Voxel/Voxel_VoxelDisplayMode.hxx | 28 - src/Voxel/Voxel_VoxelFileFormat.hxx | 26 - src/Voxel/Voxel_Writer.cxx | 442 --- src/Voxel/Voxel_Writer.hxx | 106 - tests/bugs/vis/bug19820 | 73 - tests/bugs/vis/bug24019 | 9 - tests/bugs/vis/bug24051 | 9 - tests/v3d/grids.list | 1 - tests/v3d/voxel/A1 | 1 - tests/v3d/voxel/A2 | 1 - tests/v3d/voxel/A3 | 1 - tests/v3d/voxel/A4 | 1 - tests/v3d/voxel/A5 | 1 - tests/v3d/voxel/A6 | 1 - tests/v3d/voxel/A7 | 1 - tests/v3d/voxel/A8 | 1 - tests/v3d/voxel/A9 | 1 - tests/v3d/voxel/B1 | 1 - tests/v3d/voxel/B2 | 1 - tests/v3d/voxel/B3 | 9 - tests/v3d/voxel/begin | 1 - 97 files changed, 3 insertions(+), 15361 deletions(-) delete mode 100644 dox/user_guides/voxels_wp/images/voxels_wp_image003.png delete mode 100644 dox/user_guides/voxels_wp/images/voxels_wp_image004.png delete mode 100644 dox/user_guides/voxels_wp/images/voxels_wp_image005.png delete mode 100644 dox/user_guides/voxels_wp/images/voxels_wp_image006.png delete mode 100644 dox/user_guides/voxels_wp/images/voxels_wp_image007.png delete mode 100644 dox/user_guides/voxels_wp/images/voxels_wp_image008.png delete mode 100644 dox/user_guides/voxels_wp/images/voxels_wp_image009.png delete mode 100644 dox/user_guides/voxels_wp/images/voxels_wp_image010.png delete mode 100644 dox/user_guides/voxels_wp/voxels_wp.md delete mode 100644 samples/qt/VoxelDemo/VoxelDemo-vc10.sln delete mode 100644 samples/qt/VoxelDemo/VoxelDemo-vc12.sln delete mode 100644 samples/qt/VoxelDemo/VoxelDemo-vc9.sln delete mode 100644 samples/qt/VoxelDemo/VoxelDemo.pro delete mode 100644 samples/qt/VoxelDemo/genproj.bat delete mode 100755 samples/qt/VoxelDemo/inc/Application.h delete mode 100755 samples/qt/VoxelDemo/inc/ConversionThread.h delete mode 100755 samples/qt/VoxelDemo/inc/Timer.h delete mode 100755 samples/qt/VoxelDemo/inc/Viewer.h delete mode 100644 samples/qt/VoxelDemo/inc/VoxelClient_VisDrawer.h delete mode 100644 samples/qt/VoxelDemo/msvc.bat delete mode 100644 samples/qt/VoxelDemo/run.bat delete mode 100644 samples/qt/VoxelDemo/src/Application.cpp delete mode 100644 samples/qt/VoxelDemo/src/ConversionThread.cpp delete mode 100644 samples/qt/VoxelDemo/src/Main.cpp delete mode 100644 samples/qt/VoxelDemo/src/Timer.cpp delete mode 100644 samples/qt/VoxelDemo/src/Viewer.cpp delete mode 100644 samples/qt/VoxelDemo/src/VoxelClient_VisDrawer.cxx delete mode 100644 src/TKVoxel/CMakeLists.txt delete mode 100644 src/TKVoxel/EXTERNLIB delete mode 100644 src/TKVoxel/FILES delete mode 100755 src/TKVoxel/PACKAGES delete mode 100644 src/ViewerTest/ViewerTest_VoxelCommands.cxx delete mode 100755 src/Voxel/FILES delete mode 100644 src/Voxel/Voxel_BoolDS.cxx delete mode 100644 src/Voxel/Voxel_BoolDS.hxx delete mode 100644 src/Voxel/Voxel_BooleanOperation.cxx delete mode 100644 src/Voxel/Voxel_BooleanOperation.hxx delete mode 100644 src/Voxel/Voxel_CollisionDetection.cxx delete mode 100644 src/Voxel/Voxel_CollisionDetection.hxx delete mode 100644 src/Voxel/Voxel_ColorDS.cxx delete mode 100644 src/Voxel/Voxel_ColorDS.hxx delete mode 100644 src/Voxel/Voxel_DS.cxx delete mode 100644 src/Voxel/Voxel_DS.hxx delete mode 100644 src/Voxel/Voxel_FastConverter.cxx delete mode 100644 src/Voxel/Voxel_FastConverter.hxx delete mode 100644 src/Voxel/Voxel_FloatDS.cxx delete mode 100644 src/Voxel/Voxel_FloatDS.hxx delete mode 100644 src/Voxel/Voxel_OctBoolDS.cxx delete mode 100644 src/Voxel/Voxel_OctBoolDS.hxx delete mode 100644 src/Voxel/Voxel_Prs.cxx delete mode 100644 src/Voxel/Voxel_Prs.hxx delete mode 100644 src/Voxel/Voxel_ROctBoolDS.cxx delete mode 100644 src/Voxel/Voxel_ROctBoolDS.hxx delete mode 100644 src/Voxel/Voxel_Reader.cxx delete mode 100644 src/Voxel/Voxel_Reader.hxx delete mode 100644 src/Voxel/Voxel_Selector.cxx delete mode 100644 src/Voxel/Voxel_Selector.hxx delete mode 100644 src/Voxel/Voxel_SplitData.cxx delete mode 100644 src/Voxel/Voxel_SplitData.hxx delete mode 100644 src/Voxel/Voxel_TypeDef.hxx delete mode 100644 src/Voxel/Voxel_VisData.h delete mode 100644 src/Voxel/Voxel_VoxelDisplayMode.hxx delete mode 100644 src/Voxel/Voxel_VoxelFileFormat.hxx delete mode 100644 src/Voxel/Voxel_Writer.cxx delete mode 100644 src/Voxel/Voxel_Writer.hxx delete mode 100755 tests/bugs/vis/bug19820 delete mode 100644 tests/bugs/vis/bug24019 delete mode 100644 tests/bugs/vis/bug24051 delete mode 100644 tests/v3d/voxel/A1 delete mode 100644 tests/v3d/voxel/A2 delete mode 100644 tests/v3d/voxel/A3 delete mode 100644 tests/v3d/voxel/A4 delete mode 100644 tests/v3d/voxel/A5 delete mode 100644 tests/v3d/voxel/A6 delete mode 100644 tests/v3d/voxel/A7 delete mode 100644 tests/v3d/voxel/A8 delete mode 100644 tests/v3d/voxel/A9 delete mode 100644 tests/v3d/voxel/B1 delete mode 100644 tests/v3d/voxel/B2 delete mode 100644 tests/v3d/voxel/B3 delete mode 100644 tests/v3d/voxel/begin diff --git a/adm/MODULES b/adm/MODULES index 566647483f..10b1b2c6b8 100644 --- a/adm/MODULES +++ b/adm/MODULES @@ -1,7 +1,7 @@ FoundationClasses TKernel TKMath ModelingData TKG2d TKG3d TKGeomBase TKBRep ModelingAlgorithms TKGeomAlgo TKTopAlgo TKPrim TKBO TKBool TKHLR TKFillet TKOffset TKFeat TKMesh TKXMesh TKShHealing -Visualization TKService TKV3d TKOpenGl TKMeshVS TKVoxel TKIVtk +Visualization TKService TKV3d TKOpenGl TKMeshVS TKIVtk ApplicationFramework TKCDF TKLCAF TKCAF TKBinL TKXmlL TKBin TKXml FWOSPlugin TKTObj TKBinTObj TKXmlTObj DataExchange TKXSBase TKSTEPBase TKSTEPAttr TKSTEP209 TKSTEP TKIGES TKXCAF TKXDEIGES TKXDESTEP TKSTL TKVRML TKXmlXCAF TKBinXCAF Draw TKDraw TKTopTest TKViewerTest TKXSDRAW TKDCAF TKXDEDRAW TKTObjDRAW TKQADraw TKIVtkDraw DRAWEXE \ No newline at end of file diff --git a/adm/UDLIST b/adm/UDLIST index 35248dbf5f..56446c378a 100644 --- a/adm/UDLIST +++ b/adm/UDLIST @@ -212,7 +212,6 @@ n StdPrs n StdSelect n TColQuantity n V3d -n Voxel n WNT n Xw n Cocoa @@ -223,7 +222,6 @@ t TKOpenGl t TKD3DHost t TKService t TKV3d -t TKVoxel n BinTObjDrivers n LDOM n TObj diff --git a/dox/FILES_HTML.txt b/dox/FILES_HTML.txt index 7db38e4504..a1ef0b24e6 100644 --- a/dox/FILES_HTML.txt +++ b/dox/FILES_HTML.txt @@ -35,7 +35,6 @@ user_guides/brep_wp/brep_wp.md user_guides/ocaf_functionmechanism_wp/ocaf_functionmechanism_wp.md user_guides/ocaf_tree_wp/ocaf_tree_wp.md user_guides/ocaf_wp/ocaf_wp.md -user_guides/voxels_wp/voxels_wp.md user_guides/vis/vis.md dev_guides/dev_guides.md diff --git a/dox/FILES_PDF.txt b/dox/FILES_PDF.txt index 3d1d797a1e..f4f7ffcdcb 100644 --- a/dox/FILES_PDF.txt +++ b/dox/FILES_PDF.txt @@ -19,7 +19,6 @@ user_guides/step/step.md user_guides/draw_test_harness/draw_test_harness.md user_guides/tobj/tobj.md user_guides/visualization/visualization.md -user_guides/voxels_wp/voxels_wp.md user_guides/xde/xde.md user_guides/vis/vis.md diff --git a/dox/dev_guides/tests/tests.md b/dox/dev_guides/tests/tests.md index fe36b4208c..2004d4850f 100644 --- a/dox/dev_guides/tests/tests.md +++ b/dox/dev_guides/tests/tests.md @@ -987,7 +987,7 @@ This group allows testing extended data exchange packages. | Foundation Classes | TKernel, TKMath | fclasses | | Modeling_algorithms | TKGeomAlgo, TKTopAlgo, TKPrim, TKBO, TKBool, TKHLR, TKFillet, TKOffset, TKFeat, TKXMesh | modalg | | Modeling Data | TKG2d, TKG3d, TKGeomBase, TKBRep | moddata | -| Visualization | TKService, TKV2d, TKV3d, TKOpenGl, TKMeshVS, TKNIS, TKVoxel | vis | +| Visualization | TKService, TKV2d, TKV3d, TKOpenGl, TKMeshVS, TKNIS | vis | @subsection testmanual_5_3 Recommended approaches to checking test results diff --git a/dox/overview/overview.md b/dox/overview/overview.md index 199ebcd4d6..21957069ad 100644 --- a/dox/overview/overview.md +++ b/dox/overview/overview.md @@ -556,13 +556,6 @@ The combination of these resources allows creating substantial applications. **See also:** @ref occt__tutorial "OCCT Tutorial" - Voxel ------- - -This is a demonstration application showing OCCT voxel models. It also includes a set of non-regression tests and other commands for testing this functionality (accessible only through TEST pre-processor definition). - -**See also:** Voxels User's guide - **Remarks:** * Qt samples are available on all supported platforms; diff --git a/dox/technical_overview/technical_overview.md b/dox/technical_overview/technical_overview.md index 85fbcbf678..f0077cee03 100644 --- a/dox/technical_overview/technical_overview.md +++ b/dox/technical_overview/technical_overview.md @@ -208,10 +208,6 @@ For more details see @ref occt_user_guides__visualization "Visualization User's The visualization of OCCT topological shapes by means of VTK library provided by VIS component is described in a separate @ref occt_user_guides__vis "VTK Integration Services" User's Guide. -Open CASCADE Technology also supports voxel representation providing basic data containers and visualization of voxels as colored or grayscale bricks. - -See @ref occt_user_guides__voxels_wp "Voxels User's Guide" for more information. - See also: our E-learning & Training offerings. diff --git a/dox/user_guides/user_guides.md b/dox/user_guides/user_guides.md index 70b1a20a72..289ed21f3c 100644 --- a/dox/user_guides/user_guides.md +++ b/dox/user_guides/user_guides.md @@ -10,7 +10,6 @@ OCCT User Guides are organized by OCCT modules: * @subpage occt_user_guides__boolean_operations "Boolean Operations" * @subpage occt_user_guides__shape_healing "Shape Healing" * @subpage occt_user_guides__visualization "Visualization" - * @subpage occt_user_guides__voxels_wp "Voxels" * @subpage occt_user_guides__vis "VTK Integration Services" * Data Exchange * @subpage occt_user_guides__iges "IGES translator" diff --git a/dox/user_guides/voxels_wp/images/voxels_wp_image003.png b/dox/user_guides/voxels_wp/images/voxels_wp_image003.png deleted file mode 100644 index 3e08944508dcb619b435a9739ba6deea89bc7387..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 9472 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 z zd|a>DC{hUi`$CGjFsz`!GdY=Df>d^%X8Sx-WqWTds8087xY(pwL`rCF-HQX@6vHw8 zAk5i=-rH01^q@P@L^D<)0<}p8?VxFRUeV`=M{G~B-4U#>;iyyWX||vG1D4*eYwuV) z?*)PrT$E0@k>@vYJuA6zx)_t*;NhjYW!Tha;5_OOB02zrlH1L Ib;qdx1vq^3_y7O^ diff --git a/dox/user_guides/voxels_wp/images/voxels_wp_image004.png b/dox/user_guides/voxels_wp/images/voxels_wp_image004.png deleted file mode 100644 index 666bca154e5d17e4b9b835dd65814da370dfddae..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 73490 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 z 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 YNHS{g6CF*_V4xp4DP_r8anq3h1$l;Af&c&j diff --git a/dox/user_guides/voxels_wp/images/voxels_wp_image006.png b/dox/user_guides/voxels_wp/images/voxels_wp_image006.png deleted file mode 100644 index 5d9222144ab912b75c185864a3ac944cd8180349..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 112583 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 Qv2$9UZocbItqo896Pt6;M*si- diff --git a/dox/user_guides/voxels_wp/images/voxels_wp_image010.png b/dox/user_guides/voxels_wp/images/voxels_wp_image010.png deleted file mode 100644 index b024013e005437bf8a6fbbed93c2dcbd6e347909..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 5257 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\::Fuse() summarizes the values of the corresponding voxels and limits the result by the upper limit (if succeeded). -* \::Cut() subtracts the values of the corresponding voxels and limits the result by zero. - -### Voxelization - -A class *Voxel_Convert* converts a *TopoDS_Shape* into one of the voxel data structures filling the solid shape by non-zero values. - -The algorithm of voxelization generates only 1-bit or 4-bit voxels. Other data types may be obtained by conversion of voxels from one type to another. - -Voxelization of a shape is performed by means of computation of intersection points between lines filling the volume and triangulation of the shape. The lines are parallel to main orthogonal axes and can intersect the shape from different sides: along +X, +Y and/or +Z axes. - -The algorithm can run in multi-threaded mode (the number of threads is unlimited). The user can see an integer value indicating the progress of computation. - -@section occt_voxels_wp_4 Visualization - - Visualization of voxels is not a simple task due to a great amount of data used for 3D analysis. - - Open CASCADE Technology allows visualization of a cube of voxels in two modes: - * Points – the centers of voxels as 3D points. - * Boxes – the voxels as 3D cubes of adjustable size. - - A degenerated mode displays only the points (boxes) visible - from the point of view of the user for transformation operations (zoom, pan and rotate). - - To focus on a particular part of the model non-relevant voxels can be erased. - The displayed region is defined by six co-ordinates along X, Y and Z axes . - - It is possible to display the voxels from a particular range of values (iso-volume): - -@image html voxels_wp_image006.png "Iso-volume of a shape" -@image latex voxels_wp_image006.png "Iso-volume of a shape" - -The voxels are displayed by means of "direct drawing in Open GL" technology or "user draw" technology. Therefore, some visualization files are compiled within Open CASCADE Technology, but the files of "direct drawing" are compiled  by the end-user application. - -It is necessary to include the files *Voxel_VisData.h*, *VoxelClient_VisDrawer.h* and *VoxelClient_VisDrawer.cxx* into the visualization library of the application (containing all files of *OpenGl* package) and call the method *Voxel_VisDrawer::Init()* from the application before the visualization of voxels. - -@section occt_voxels_wp_5 Demo-application - - A demonstration application has been created to show OCCT voxel models. - This is a test demo application because it includes a set of non-regression tests - and other commands for testing the functionality (accessible only through TEST pre-processor definition). - - The *File* menu allows creation of canonical shapes (box, cylinder, sphere, torus) or loading of shapes in BREP format: - -@image html voxels_wp_image007.png "Demo-application. Creation or loading of a shape" -@image latex voxels_wp_image007.png "Demo-application. Creation or loading of a shape" - -The menu *Converter* voxelizes the shape. Two types of voxels can be obtained: 1-bit or 4-bit voxels. - * 1-bit voxels are displayed in white color on black background. - * 4-bit voxels use 16 colors filling the model in a special way for demonstrative purposes: - -@image html voxels_wp_image008.png "Demo-application. Voxelization" -@image latex voxels_wp_image008.png "Demo-application. Voxelization" - - The converter uses two threads (two processors, if available) to perform voxelization. - - The menu *Visualization* offers two modes of visualization: Points and Boxes, - allows defining the size of points and boxes (quadrangles), - the minimum and the maximum displayed color, and the boundaries of the bounding box for displayed voxels: - -@image html voxels_wp_image009.png "Demo-application. Visualization" -@image latex voxels_wp_image009.png "Demo-application. Visualization" - - The last menu, *Demo* contains a demo-command for running waves of 4-bit voxels: - -@image html voxels_wp_image010.png "Demo-application. Running waves" -@image latex voxels_wp_image010.png "Demo-application. Running waves" - -@section occt_voxels_wp_6 Future development - -In the future OPEN CASCADE plans to develop the platform of voxels in the following directions: - * Data structure: - * Extension of the list of basic data types. - * Development of a deeper hierarchy of voxels (for example, octree – division of a voxel into 8 sub-voxels). - * Development of a doxel (4D voxels where the fourth co-ordinate is the time, for example). - - * Algorithms: - * Conversion of a voxel model into a geometrical model (a reversed operation to voxelization). - - * Visualization: - * Optimization of visualization (mainly, the speed of visualization). - * New shapes of voxel presentation in the 3D Viewer and new approaches to visualization. - * Selection of voxels. - diff --git a/samples/qt/VoxelDemo/VoxelDemo-vc10.sln b/samples/qt/VoxelDemo/VoxelDemo-vc10.sln deleted file mode 100644 index 00a81bed2a..0000000000 --- a/samples/qt/VoxelDemo/VoxelDemo-vc10.sln +++ /dev/null @@ -1,26 +0,0 @@ - -Microsoft Visual Studio Solution File, Format Version 11.00 -# Visual Studio 2010 -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "VoxelDemo", "VoxelDemo.vcxproj", "{EF9DFAE9-E10E-42D4-87D1-52431728AFDF}" -EndProject -Global - GlobalSection(SolutionConfigurationPlatforms) = preSolution - Debug|Win32 = Debug|Win32 - Debug|x64 = Debug|x64 - Release|Win32 = Release|Win32 - Release|x64 = Release|x64 - EndGlobalSection - GlobalSection(ProjectConfigurationPlatforms) = postSolution - {EF9DFAE9-E10E-42D4-87D1-52431728AFDF}.Debug|Win32.ActiveCfg = Debug|Win32 - {EF9DFAE9-E10E-42D4-87D1-52431728AFDF}.Debug|Win32.Build.0 = Debug|Win32 - {EF9DFAE9-E10E-42D4-87D1-52431728AFDF}.Debug|x64.ActiveCfg = Debug|x64 - {EF9DFAE9-E10E-42D4-87D1-52431728AFDF}.Debug|x64.Build.0 = Debug|x64 - {EF9DFAE9-E10E-42D4-87D1-52431728AFDF}.Release|Win32.ActiveCfg = Release|Win32 - {EF9DFAE9-E10E-42D4-87D1-52431728AFDF}.Release|Win32.Build.0 = Release|Win32 - {EF9DFAE9-E10E-42D4-87D1-52431728AFDF}.Release|x64.ActiveCfg = Release|x64 - {EF9DFAE9-E10E-42D4-87D1-52431728AFDF}.Release|x64.Build.0 = Release|x64 - EndGlobalSection - GlobalSection(SolutionProperties) = preSolution - HideSolutionNode = FALSE - EndGlobalSection -EndGlobal diff --git a/samples/qt/VoxelDemo/VoxelDemo-vc12.sln b/samples/qt/VoxelDemo/VoxelDemo-vc12.sln deleted file mode 100644 index 7bf6231f7d..0000000000 --- a/samples/qt/VoxelDemo/VoxelDemo-vc12.sln +++ /dev/null @@ -1,26 +0,0 @@ - -Microsoft Visual Studio Solution File, Format Version 12.00 -# Visual Studio 2013 -VisualStudioVersion = 12.0.21005.1 -MinimumVisualStudioVersion = 10.0.40219.1 -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "VoxelDemo", "VoxelDemo.vcxproj", "{59F93AA4-FBAD-3468-B4E7-2D6290D2D461}" -EndProject -Global - GlobalSection(SolutionConfigurationPlatforms) = preSolution - Debug|Win32 = Debug|Win32 - Debug|x64 = Debug|x64 - Release|Win32 = Release|Win32 - Release|x64 = Release|x64 - EndGlobalSection - GlobalSection(ProjectConfigurationPlatforms) = postSolution - {59F93AA4-FBAD-3468-B4E7-2D6290D2D461}.Debug|Win32.ActiveCfg = Debug|x64 - {59F93AA4-FBAD-3468-B4E7-2D6290D2D461}.Debug|x64.ActiveCfg = Debug|x64 - {59F93AA4-FBAD-3468-B4E7-2D6290D2D461}.Debug|x64.Build.0 = Debug|x64 - {59F93AA4-FBAD-3468-B4E7-2D6290D2D461}.Release|Win32.ActiveCfg = Release|x64 - {59F93AA4-FBAD-3468-B4E7-2D6290D2D461}.Release|x64.ActiveCfg = Release|x64 - {59F93AA4-FBAD-3468-B4E7-2D6290D2D461}.Release|x64.Build.0 = Release|x64 - EndGlobalSection - GlobalSection(SolutionProperties) = preSolution - HideSolutionNode = FALSE - EndGlobalSection -EndGlobal diff --git a/samples/qt/VoxelDemo/VoxelDemo-vc9.sln b/samples/qt/VoxelDemo/VoxelDemo-vc9.sln deleted file mode 100644 index fabee776e4..0000000000 --- a/samples/qt/VoxelDemo/VoxelDemo-vc9.sln +++ /dev/null @@ -1,20 +0,0 @@ - -Microsoft Visual Studio Solution File, Format Version 10.00 -# Visual Studio 2008 -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "VoxelDemo", "VoxelDemo.vcproj", "{EF9DFAE9-E10E-42D4-87D1-52431728AFDF}" -EndProject -Global - GlobalSection(SolutionConfigurationPlatforms) = preSolution - Debug|Win32 = Debug|Win32 - Release|Win32 = Release|Win32 - EndGlobalSection - GlobalSection(ProjectConfigurationPlatforms) = postSolution - {EF9DFAE9-E10E-42D4-87D1-52431728AFDF}.Debug|Win32.ActiveCfg = Debug|Win32 - {EF9DFAE9-E10E-42D4-87D1-52431728AFDF}.Debug|Win32.Build.0 = Debug|Win32 - {EF9DFAE9-E10E-42D4-87D1-52431728AFDF}.Release|Win32.ActiveCfg = Release|Win32 - {EF9DFAE9-E10E-42D4-87D1-52431728AFDF}.Release|Win32.Build.0 = Release|Win32 - EndGlobalSection - GlobalSection(SolutionProperties) = preSolution - HideSolutionNode = FALSE - EndGlobalSection -EndGlobal diff --git a/samples/qt/VoxelDemo/VoxelDemo.pro b/samples/qt/VoxelDemo/VoxelDemo.pro deleted file mode 100644 index 90a408003c..0000000000 --- a/samples/qt/VoxelDemo/VoxelDemo.pro +++ /dev/null @@ -1,185 +0,0 @@ -TEMPLATE = app -CONFIG += debug_and_release qt opengl - -TARGET = VoxelDemo - -SAMPLESROOT = $$(CASROOT)/samples/qt - -HEADERS = $${SAMPLESROOT}/voxeldemo/inc/*.h - -SOURCES = $${SAMPLESROOT}/voxeldemo/src/*.cxx \ - $${SAMPLESROOT}/voxeldemo/src/*.cpp - - -INCLUDEPATH += $$quote($${SAMPLESROOT}/voxeldemo/inc) - -DEFINES = - -unix { - UNAME = $$system(uname -s) - INCLUDES = $$(CSF_OPT_INC) - PATHS = $$split(INCLUDES,":") - for(path, PATHS):INCLUDEPATH += $${path} - LIBLIST = $$(LD_LIBRARY_PATH) - LIBPATHS = $$split(LIBLIST,":") - for(lib, LIBPATHS):LIBS += -L$${lib} - - CONFIG(debug, debug|release) { - DESTDIR = ./$$UNAME/bind - OBJECTS_DIR = ./$$UNAME/objd - MOC_DIR = ./$$UNAME/srcd - } else { - DESTDIR = ./$$UNAME/bin - OBJECTS_DIR = ./$$UNAME/obj - MOC_DIR = ./$$UNAME/src - } - - MACOSX_USE_GLX = $$(MACOSX_USE_GLX) - - !macx | equals(MACOSX_USE_GLX, true): INCLUDEPATH += $$QMAKE_INCDIR_X11 $$QMAKE_INCDIR_OPENGL $$QMAKE_INCDIR_THREAD - !macx | equals(MACOSX_USE_GLX, true): DEFINES += LIN LININTEL - equals(MACOSX_USE_GLX, true): DEFINES += MACOSX_USE_GLX - DEFINES += OCC_CONVERT_SIGNALS QT_NO_STL - !macx | equals(MACOSX_USE_GLX, true): LIBS += -L$$QMAKE_LIBDIR_X11 $$QMAKE_LIBS_X11 -L$$QMAKE_LIBDIR_OPENGL $$QMAKE_LIBS_OPENGL $$QMAKE_LIBS_THREAD - LIBS += -lfreeimageplus - LIBS += -ltbb -ltbbmalloc -} - -win32 { - INCLUDES = $$(CSF_OPT_INC) - PATHS = $$split(INCLUDES,";") - for(path, PATHS):INCLUDEPATH += $${path} - - CONFIG(debug, debug|release) { - DEFINES += _DEBUG - !contains(QMAKE_HOST.arch, x86_64) { - LIBS = -L$(CSF_OPT_LIB32D) - contains(QMAKE_COMPILER_DEFINES, _MSC_VER=1310) { - DESTDIR = ./win32/vc7/bind - OBJECTS_DIR = ./win32/vc7/objd - MOC_DIR = ./win32/vc7/srcd - } - contains(QMAKE_COMPILER_DEFINES, _MSC_VER=1400) { - DESTDIR = ./win32/vc8/bind - OBJECTS_DIR = ./win32/vc8/objd - MOC_DIR = ./win32/vc8/srcd - } - contains(QMAKE_COMPILER_DEFINES, _MSC_VER=1500) { - DESTDIR = ./win32/vc9/bind - OBJECTS_DIR = ./win32/vc9/objd - MOC_DIR = ./win32/vc9/srcd - } - contains(QMAKE_COMPILER_DEFINES, _MSC_VER=1600) { - DESTDIR = ./win32/vc10/bind - OBJECTS_DIR = ./win32/vc10/objd - MOC_DIR = ./win32/vc10/srcd - } - contains(QMAKE_COMPILER_DEFINES, _MSC_VER=1700) { - DESTDIR = ./win32/vc11/bind - OBJECTS_DIR = ./win32/vc11/objd - MOC_DIR = ./win32/vc11/srcd - } - contains(QMAKE_COMPILER_DEFINES, _MSC_VER=1800) { - DESTDIR = ./win32/vc12/bind - OBJECTS_DIR = ./win32/vc12/objd - MOC_DIR = ./win32/vc12/srcd - } - } else { - LIBS = -L$(CSF_OPT_LIB64D) - contains(QMAKE_COMPILER_DEFINES, _MSC_VER=1400) { - DESTDIR = ./win64/vc8/bind - OBJECTS_DIR = ./win64/vc8/objd - MOC_DIR = ./win64/vc8/srcd - } - contains(QMAKE_COMPILER_DEFINES, _MSC_VER=1500) { - DESTDIR = ./win64/vc9/bind - OBJECTS_DIR = ./win64/vc9/objd - MOC_DIR = ./win64/vc9/srcd - } - contains(QMAKE_COMPILER_DEFINES, _MSC_VER=1600) { - DESTDIR = ./win64/vc10/bind - OBJECTS_DIR = ./win64/vc10/objd - MOC_DIR = ./win64/vc10/srcd - } - contains(QMAKE_COMPILER_DEFINES, _MSC_VER=1700) { - DESTDIR = ./win64/vc11/bind - OBJECTS_DIR = ./win64/vc11/objd - MOC_DIR = ./win64/vc11/srcd - } - contains(QMAKE_COMPILER_DEFINES, _MSC_VER=1800) { - DESTDIR = ./win64/vc12/bind - OBJECTS_DIR = ./win64/vc12/objd - MOC_DIR = ./win64/vc12/srcd - } - } - } else { - DEFINES += NDEBUG - !contains(QMAKE_HOST.arch, x86_64) { - LIBS = -L$(CSF_OPT_LIB32) - contains(QMAKE_COMPILER_DEFINES, _MSC_VER=1310) { - DESTDIR = ./win32/vc7/bin - OBJECTS_DIR = ./win32/vc7/obj - MOC_DIR = ./win32/vc7/src - } - contains(QMAKE_COMPILER_DEFINES, _MSC_VER=1400) { - DESTDIR = ./win32/vc8/bin - OBJECTS_DIR = ./win32/vc8/obj - MOC_DIR = ./win32/vc8/src - } - contains(QMAKE_COMPILER_DEFINES, _MSC_VER=1500) { - DESTDIR = ./win32/vc9/bin - OBJECTS_DIR = ./win32/vc9/obj - MOC_DIR = ./win32/vc9/src - } - contains(QMAKE_COMPILER_DEFINES, _MSC_VER=1600) { - DESTDIR = ./win32/vc10/bin - OBJECTS_DIR = ./win32/vc10/obj - MOC_DIR = ./win32/vc10/src - } - contains(QMAKE_COMPILER_DEFINES, _MSC_VER=1700) { - DESTDIR = ./win32/vc11/bin - OBJECTS_DIR = ./win32/vc11/obj - MOC_DIR = ./win32/vc11/src - } - contains(QMAKE_COMPILER_DEFINES, _MSC_VER=1800) { - DESTDIR = ./win32/vc12/bin - OBJECTS_DIR = ./win32/vc12/obj - MOC_DIR = ./win32/vc12/src - } - } else { - LIBS = -L$(CSF_OPT_LIB64) - contains(QMAKE_COMPILER_DEFINES, _MSC_VER=1400) { - DESTDIR = ./win64/vc8/bin - OBJECTS_DIR = ./win64/vc8/obj - MOC_DIR = ./win64/vc8/src - } - contains(QMAKE_COMPILER_DEFINES, _MSC_VER=1500) { - DESTDIR = ./win64/vc9/bin - OBJECTS_DIR = ./win64/vc9/obj - MOC_DIR = ./win64/vc9/src - } - contains(QMAKE_COMPILER_DEFINES, _MSC_VER=1600) { - DESTDIR = ./win64/vc10/bin - OBJECTS_DIR = ./win64/vc10/obj - MOC_DIR = ./win64/vc10/src - } - contains(QMAKE_COMPILER_DEFINES, _MSC_VER=1700) { - DESTDIR = ./win64/vc11/bin - OBJECTS_DIR = ./win64/vc11/obj - MOC_DIR = ./win64/vc11/src - } - contains(QMAKE_COMPILER_DEFINES, _MSC_VER=1800) { - DESTDIR = ./win64/vc12/bin - OBJECTS_DIR = ./win64/vc12/obj - MOC_DIR = ./win64/vc12/src - } - } - } - DEFINES +=WNT WIN32 NO_COMMONSAMPLE_EXPORTS NO_IESAMPLE_EXPORTS -} - -LIBS += -lTKernel -lTKMath -lTKService -lTKV3d \ - -lTKBRep -lTKGeomBase -lTKTopAlgo -lTKPrim \ - -lTKBool -lTKVoxel -lTKOpenGl \ - -QMAKE_CXXFLAGS = -Zc:wchar_t diff --git a/samples/qt/VoxelDemo/genproj.bat b/samples/qt/VoxelDemo/genproj.bat deleted file mode 100644 index 997b1612b8..0000000000 --- a/samples/qt/VoxelDemo/genproj.bat +++ /dev/null @@ -1,43 +0,0 @@ -REM Generation of vcproj files with qmake utilite -REM Variable QTDIR and PATH to qmake executable must be defined without fail - -REM Use first argument to specify version of Visual Studio (vc8, vc9, or vc10), -REM second argument specifies architecture) (win32 or win64) -REM third argument specifies Debug or Release mode - -call "%~dp0../../../env.bat" %1 %2 %3 - -set EXT=vcxproj - -if not "%1" == "" ( - if /I "%1" == "vc8" ( - set VCVER=vc8 - set "VCVARS=%VS80COMNTOOLS%..\..\VC\vcvarsall.bat" - ) else if /I "%1" == "vc9" ( - set VCVER=vc9 - set "VCVARS=%VS90COMNTOOLS%..\..\VC\vcvarsall.bat" - ) else if /I "%1" == "vc10" ( - set VCVER=vc10 - set EXT=vcxproj - set "VCVARS=%VS100COMNTOOLS%..\..\VC\vcvarsall.bat" - ) else if /I "%1" == "vc11" ( - set VCVER=vc11 - set EXT=vcxproj - set "VCVARS=%VS110COMNTOOLS%..\..\VC\vcvarsall.bat" - ) else if /I "%1" == "vc12" ( - set VCVER=vc12 - set EXT=vcxproj - set "VCVARS=%VS120COMNTOOLS%..\..\VC\vcvarsall.bat" - ) else ( - echo Error: first argument ^(%1^) should specify supported version of Visual C++, - echo one of: vc8 ^(VS 2005 SP1^), vc9 ^(VS 2008 SP1^), vc10 ^(VS 2010^) or vc11 ^(VS 2012^) - exit - ) -) - -if ["%ARCH%"] == ["32"] set VCARCH=x86 -if ["%ARCH%"] == ["64"] set VCARCH=amd64 - -call "%VCVARS%" %VCARCH% - -qmake -tp vc -o VoxelDemo.%EXT% VoxelDemo.pro diff --git a/samples/qt/VoxelDemo/inc/Application.h b/samples/qt/VoxelDemo/inc/Application.h deleted file mode 100755 index 48116014fb..0000000000 --- a/samples/qt/VoxelDemo/inc/Application.h +++ /dev/null @@ -1,116 +0,0 @@ -#ifndef APPLICATION_H -#define APPLICATION_H - -#include -#include -#include -#include -#include -#include -#include "Viewer.h" - -class Application: public QMainWindow -{ - Q_OBJECT - -public: - Application(); - ~Application(); - -protected: - void closeEvent( QCloseEvent* ); - -private slots: - void box(); - void cylinder(); - void torus(); - void sphere(); - void choose(); - void load( const QString & ); - void load( const TopoDS_Shape & ); - void save(); - void open(); - - void testBoolDS(); - void testColorDS(); - void testFloatDS(); - void testOctBoolDS(); - void testROctBoolDS(); - - void testFuseBoolDS(); - void testFuseColorDS(); - void testFuseFloatDS(); - void testCutBoolDS(); - void testCutColorDS(); - void testCutFloatDS(); - - void convert2bool(); - void convert2color(); - void convert(const int ); - - void setNbX(); - void setNbY(); - void setNbZ(); - - void setScanSide(); - - void setVolumicBoolValue(); - void setVolumicColorValue(); - - void setQuadrangleSize(); - void setPointSize(); - - void setColorMinValue(); - void setColorMaxValue(); - - void setUsageOfGLlists(); - - void setDisplayedXMin(); - void setDisplayedXMax(); - void setDisplayedYMin(); - void setDisplayedYMax(); - void setDisplayedZMin(); - void setDisplayedZMax(); - - void displayPoints(); - void displayNearestPoints(); - void displayBoxes(); - void displayNearestBoxes(); - - void displayColorScale(); - - void displayWaves(); - void displayCut(); - void displayCollisions(); - - void about(); - -private: - void display(Voxel_VoxelDisplayMode ); - void initPrs(); - - Viewer* myViewer; - Handle(AIS_Shape) myShape; - Handle(Voxel_Prs) myVoxels; - Voxel_BoolDS* myBoolVoxels; - Voxel_ColorDS* myColorVoxels; - Handle(AIS_ColorScale) myColorScale; - int myNbX; - int myNbY; - int myNbZ; - unsigned char myScanSide; - bool myVolumicBoolValue; - unsigned char myVolumicColorValue; - int myQuadrangleSize; - int myPointSize; - unsigned char myColorMinValue; - unsigned char myColorMaxValue; - double myDisplayedXMin; - double myDisplayedXMax; - double myDisplayedYMin; - double myDisplayedYMax; - double myDisplayedZMin; - double myDisplayedZMax; -}; - -#endif // APPLICATION_H diff --git a/samples/qt/VoxelDemo/inc/ConversionThread.h b/samples/qt/VoxelDemo/inc/ConversionThread.h deleted file mode 100755 index 0b1b146dfd..0000000000 --- a/samples/qt/VoxelDemo/inc/ConversionThread.h +++ /dev/null @@ -1,40 +0,0 @@ -#ifndef _CONVERSIONTHREAD_H_ -#define _CONVERSIONTHREAD_H_ - -#include -//#include -#include - -class ConversionThread : public QThread -{ - -public: - - ConversionThread(); - ~ConversionThread(); - -// void setConverter(Voxel_Converter* converter); - void setConverter(Voxel_FastConverter* converter); - - void setVolumicValue(const int value); - void setScanSide(const int side); - void setThreadIndex(const int ithread); - - int* getProgress(); - -protected: - - void run(); - -private: - -// Voxel_Converter* myConverter; - Voxel_FastConverter* myFastConverter; - - int myVolumicValue; - int myScanSide; - int myThreadIndex; - int myProgress; -}; - -#endif // _CONVERSIONTHREAD_H_ diff --git a/samples/qt/VoxelDemo/inc/Timer.h b/samples/qt/VoxelDemo/inc/Timer.h deleted file mode 100755 index 338d7dc31b..0000000000 --- a/samples/qt/VoxelDemo/inc/Timer.h +++ /dev/null @@ -1,33 +0,0 @@ -// Timer.h: interface for the Timer class. -// -////////////////////////////////////////////////////////////////////// - -#if !defined(AFX_TIMER_H__528FB454_797E_11D7_9B1B_000103C0F1F9__INCLUDED_) -#define AFX_TIMER_H__528FB454_797E_11D7_9B1B_000103C0F1F9__INCLUDED_ - -#if _MSC_VER > 1000 -#pragma once -#endif // _MSC_VER > 1000 - -#include - -class Timer -{ -public: - Standard_EXPORT Timer(); - Standard_EXPORT Timer(const char* filename); - Standard_EXPORT ~Timer(); - Standard_EXPORT void Start(); - Standard_EXPORT void Stop(); - Standard_EXPORT void Continue(); - Standard_EXPORT void Reset(); - Standard_EXPORT float Seconds(); - Standard_EXPORT int Minutes(); - Standard_EXPORT void Print(char* label); - -private: - OSD_Timer myTimer; - FILE* myWriter; -}; - -#endif // !defined(AFX_TIMER_H__528FB454_797E_11D7_9B1B_000103C0F1F9__INCLUDED_) diff --git a/samples/qt/VoxelDemo/inc/Viewer.h b/samples/qt/VoxelDemo/inc/Viewer.h deleted file mode 100755 index 41c8f2dff5..0000000000 --- a/samples/qt/VoxelDemo/inc/Viewer.h +++ /dev/null @@ -1,67 +0,0 @@ -#ifndef _VIEWER_H_ -#define _VIEWER_H_ - -#if _MSC_VER > 1000 -#pragma once -#endif // _MSC_VER > 1000 - -#include - -#include -#include -#include -#include - -#include -#include - -class Viewer : public QWidget -{ - - Q_OBJECT - -public: - - Viewer(QWidget* parent); - virtual ~Viewer(); - - Handle(AIS_InteractiveContext) getIC() const { return myIC; } - Handle(V3d_View) getView() const { return myView; } - Handle(OpenGl_GraphicDriver) getGraphicDriver() const { return myGraphicDriver; } - Voxel_Selector& getSelector() { return mySelector; } - void setPrs(const Handle(Voxel_Prs)& prs) { myPrs = prs; } - virtual QPaintEngine* paintEngine() const; - -signals: - - void mousePressed(Qt::KeyboardModifiers,int,int); - void mouseMoved(Qt::KeyboardModifiers,int,int); - void mouseReleased(Qt::KeyboardModifiers,int,int); - void mouseDoubleClick(Qt::KeyboardModifiers,int,int); - -protected: - virtual void paintEvent(QPaintEvent* pEvent); - virtual void resizeEvent(QResizeEvent* rsEvent); - virtual void mousePressEvent(QMouseEvent* mpEvent); - virtual void mouseMoveEvent(QMouseEvent* mmEvent); - virtual void mouseReleaseEvent(QMouseEvent* mrEvent); - virtual void mouseDoubleClickEvent(QMouseEvent* mdcEvent); - -private: - Handle(AIS_InteractiveContext) myIC; - Handle(V3d_View) myView; - Handle(OpenGl_GraphicDriver) myGraphicDriver; - - bool myRotate; - bool myZoom; - bool myPan; - - QPoint myStartPnt; - - void setDegenerateMode(const bool ); - - Voxel_Selector mySelector; - Handle(Voxel_Prs) myPrs; -}; - -#endif // _VIEWER_H_ diff --git a/samples/qt/VoxelDemo/inc/VoxelClient_VisDrawer.h b/samples/qt/VoxelDemo/inc/VoxelClient_VisDrawer.h deleted file mode 100644 index d79dbd0985..0000000000 --- a/samples/qt/VoxelDemo/inc/VoxelClient_VisDrawer.h +++ /dev/null @@ -1,74 +0,0 @@ -/* - Copyright (c) 1999-2014 OPEN CASCADE SAS - - This file is part of Open CASCADE Technology software library. - - This library is free software; you can redistribute it and / or modify it - under the terms of the GNU Lesser General Public version 2.1 as published - by the Free Software Foundation, with special exception defined in the file - OCCT_LGPL_EXCEPTION.txt. Consult the file LICENSE_LGPL_21.txt included in OCCT - distribution for complete text of the license and disclaimer of any warranty. - - Alternatively, this file may be used under the terms of Open CASCADE - commercial license or contractual agreement. -*/ - -#ifndef _VOXEL_VISDRAWER_H_ -#define _VOXEL_VISDRAWER_H_ - -#include "Voxel_VisData.h" -#include - -#include - -//! Voxel presentation using UserDraw. -class VoxelClient_PrsGl : public Voxel_Prs -{ - -public: - - //! Empty constructor. - VoxelClient_PrsGl() {} - -protected: - - //! Override compute to create UserDraw element. - Standard_EXPORT virtual void Compute (const Handle(PrsMgr_PresentationManager3d)& thePrsMgr, - const Handle(Prs3d_Presentation)& thePrs, - const Standard_Integer theMode) Standard_OVERRIDE; - -public: - - DEFINE_STANDARD_RTTI(VoxelClient_PrsGl, Voxel_Prs) - -}; - -DEFINE_STANDARD_HANDLE(VoxelClient_PrsGl, Voxel_Prs) - -class VoxelClient_VisDrawer -{ -public: - - class VisElement; - -public: - - Standard_EXPORT VoxelClient_VisDrawer(Voxel_VisData* theData); - Standard_EXPORT virtual ~VoxelClient_VisDrawer(); - - Standard_EXPORT void EvalMinMax(Graphic3d_BndBox4f& theMinMax) const; - Standard_EXPORT void Display(const Standard_Boolean theHighlight); - -private: - - Standard_EXPORT void DisplayVoxels(const Standard_Boolean theHighlight); - Standard_EXPORT void DisplayPoints(const Standard_Boolean nearest); - Standard_EXPORT void DisplayBoxes(const Standard_Boolean nearest); - Standard_EXPORT void HighlightVoxel(); - - Standard_EXPORT void DisplayTriangulation(const Standard_Boolean theHighlight); - - Voxel_VisData* myData; -}; - -#endif // _VOXEL_VISDRAWER_H_ diff --git a/samples/qt/VoxelDemo/msvc.bat b/samples/qt/VoxelDemo/msvc.bat deleted file mode 100644 index cccdc4032d..0000000000 --- a/samples/qt/VoxelDemo/msvc.bat +++ /dev/null @@ -1,38 +0,0 @@ -@echo off - -rem Setup environment -call "%~dp0..\..\..\env.bat" %1 %2 %3 - -rem Define path to project file -set "PRJFILE=%~dp0VoxelDemo-%VCVER%.sln" - -set "VisualStudioExpressName=VCExpress" - -if "%VCVER%" == "vc8" ( - set "DevEnvDir=%VS80COMNTOOLS%..\IDE" -) else if "%VCVER%" == "vc9" ( - set "DevEnvDir=%VS90COMNTOOLS%..\IDE" -) else if "%VCVER%" == "vc10" ( - set "DevEnvDir=%VS100COMNTOOLS%..\IDE" -) else if "%VCVER%" == "vc11" ( - set "DevEnvDir=%VS110COMNTOOLS%..\IDE" - rem Visual Studio Express starting from VS 2012 is called "for Windows Desktop" - rem and has a new name for executable - WDExpress - set "VisualStudioExpressName=WDExpress" -) else if "%VCVER%" == "vc12" ( - set "DevEnvDir=%VS120COMNTOOLS%..\IDE" - set "VisualStudioExpressName=WDExpress" -) else ( - echo Error: wrong VS identifier - exit /B -) - -rem Launch Visual Studio - either professional (devenv) or Express, as available -if exist "%DevEnvDir%\devenv.exe" ( - start "%DevEnvDir%\devenv.exe" "%PRJFILE%" -) else if exist "%DevEnvDir%\%VisualStudioExpressName%.exe" ( - start "%DevEnvDir%\%VisualStudioExpressName%.exe" "%PRJFILE%" -) else ( - echo Error: Could not find MS Visual Studio ^(%VCVER%^) - echo Check relevant environment variable ^(e.g. VS80COMNTOOLS for vc8^) -) diff --git a/samples/qt/VoxelDemo/run.bat b/samples/qt/VoxelDemo/run.bat deleted file mode 100644 index 87a3009028..0000000000 --- a/samples/qt/VoxelDemo/run.bat +++ /dev/null @@ -1,21 +0,0 @@ -call "%~dp0..\..\..\env.bat" %1 %2 %3 - -set "BIN_DIR=win%ARCH%\%VCVER%\bind" -if ["%CASDEB%"] == [""] ( - set "BIN_DIR=win%ARCH%\%VCVER%\bin" -) - -if not exist "%~dp0%BIN_DIR%\VoxelDemo.exe" goto ERR_EXE - -echo Starting VoxelDemo ..... -"%~dp0%BIN_DIR%\VoxelDemo.exe" - -goto END - -:ERR_EXE -echo Executable %~dp0%BIN_DIR%\VoxelDemo.exe not found. -echo Probably you didn't compile the application. -pause -goto END - -:END \ No newline at end of file diff --git a/samples/qt/VoxelDemo/src/Application.cpp b/samples/qt/VoxelDemo/src/Application.cpp deleted file mode 100644 index c7d5896a7e..0000000000 --- a/samples/qt/VoxelDemo/src/Application.cpp +++ /dev/null @@ -1,2343 +0,0 @@ -#include "Application.h" -#include "ConversionThread.h" -#include "Timer.h" - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include -#include -#include -#include - -#include - -Application::Application() - : QMainWindow( 0 ) -{ - // File - QMenu * file = menuBar()->addMenu( "&File" ); - - QAction* a; - // Box - a = new QAction("Box", this); - connect(a, SIGNAL(triggered()), this, SLOT(box())); - file->addAction(a); - // Cylinder - a = new QAction("Cylinder", this); - connect(a, SIGNAL(triggered()), this, SLOT(cylinder())); - file->addAction(a); - // Torus - a = new QAction("Torus", this); - connect(a, SIGNAL(triggered()), this, SLOT(torus())); - file->addAction(a); - // Sphere - a = new QAction("Sphere", this); - connect(a, SIGNAL(triggered()), this, SLOT(sphere())); - file->addAction(a); - // Load shape... - a = new QAction("Load shape...", this); - a->setShortcut(tr("Ctrl+O")); - connect(a, SIGNAL(triggered()), this, SLOT(choose())); - file->addAction(a); - - file->addSeparator(); - - // Open - a = new QAction("Open", this); - connect(a, SIGNAL(triggered()), this, SLOT(open())); - file->addAction(a); - - // Save - a = new QAction("Save", this); - connect(a, SIGNAL(triggered()), this, SLOT(save())); - file->addAction(a); - - file->addSeparator(); - - // Quit - a = new QAction("&Quit", this); - a->setShortcut(tr("Ctrl+Q")); - connect(a, SIGNAL(triggered()), qApp, SLOT(closeAllWindows())); - file->addAction(a); - - menuBar()->addSeparator(); - - -#ifdef TEST - QMenu * test = menuBar()->addMenu( "Test" ); - - a = new QAction("Test boolean", this); - connect(a, SIGNAL(triggered()), this, SLOT(testBoolDS())); - test->addAction(a); - - a = new QAction("Test color", this); - connect(a, SIGNAL(triggered()), this, SLOT(testColorDS())); - test->addAction(a); - - a = new QAction("Test float", this); - connect(a, SIGNAL(triggered()), this, SLOT(testFloatDS())); - test->addAction(a); - - a = new QAction("Test boolean / 8", this); - connect(a, SIGNAL(triggered()), this, SLOT(testOctBoolDS())); - test->addAction(a); - - a = new QAction("Test boolean / 8 / 8..", this); - connect(a, SIGNAL(triggered()), this, SLOT(testROctBoolDS())); - test->addAction(a); - - test->addSeparator(); - - a = new QAction("Test fusion of booleans", this); - connect(a, SIGNAL(triggered()), this, SLOT(testFuseBoolDS())); - test->addAction(a); - - a = new QAction("Test fusion of colors", this); - connect(a, SIGNAL(triggered()), this, SLOT(testFuseColorDS())); - test->addAction(a); - - a = new QAction("Test fusion of floating-points", this); - connect(a, SIGNAL(triggered()), this, SLOT(testFuseFloatDS())); - test->addAction(a); - - a = new QAction("Test cutting of booleans", this); - connect(a, SIGNAL(triggered()), this, SLOT(testCutBoolDS())); - test->addAction(a); - - a = new QAction("Test cutting of booleans", this); - connect(a, SIGNAL(triggered()), this, SLOT(testCutColorDS())); - test->addAction(a); - - a = new QAction("Test cutting of floating-points", this); - connect(a, SIGNAL(triggered()), this, SLOT(testCutFloatDS())); - test->addAction(a); - -#endif // TEST - - QMenu * converter = menuBar()->addMenu( "Converter" ); - -#ifdef TEST - - a = new QAction("Number of splits along X", this); - connect(a, SIGNAL(triggered()), this, SLOT(setNbX())); - converter->addAction(a); - - a = new QAction("Number of splits along Y", this); - connect(a, SIGNAL(triggered()), this, SLOT(setNbY())); - converter->addAction(a); - - a = new QAction("Number of splits along Z", this); - connect(a, SIGNAL(triggered()), this, SLOT(setNbZ())); - converter->addAction(a); - - converter->addSeparator(); - - a = new QAction("Side of scanning", this); - connect(a, SIGNAL(triggered()), this, SLOT(setScanSide())); - converter->addAction(a); - - converter->addSeparator(); - - a = new QAction("Volumic value of 1bit voxels", this); - connect(a, SIGNAL(triggered()), this, SLOT(setVolumicBoolValue())); - converter->addAction(a); - - a = new QAction("Volumic value of 4bit voxels", this); - connect(a, SIGNAL(triggered()), this, SLOT(setVolumicColorValue())); - converter->addAction(a); - - converter->addSeparator(); - -#endif // TEST - - a = new QAction("Convert to 1bit voxels", this); - connect(a, SIGNAL(triggered()), this, SLOT(convert2bool())); - converter->addAction(a); - - a = new QAction("Convert to 4bit voxels", this); - connect(a, SIGNAL(triggered()), this, SLOT(convert2color())); - converter->addAction(a); - - QMenu * vis = menuBar()->addMenu( "&Visualization" ); - - a = new QAction("Points", this); - connect(a, SIGNAL(triggered()), this, SLOT(displayPoints())); - vis->addAction(a); - -#ifdef TEST - - a = new QAction("Nearest points", this); - connect(a, SIGNAL(triggered()), this, SLOT(displayNearestPoints())); - vis->addAction(a); - -#endif // TEST - - a = new QAction("Boxes", this); - connect(a, SIGNAL(triggered()), this, SLOT(displayBoxes())); - vis->addAction(a); - -#ifdef TEST - - a = new QAction("Nearest boxes", this); - connect(a, SIGNAL(triggered()), this, SLOT(displayNearestBoxes())); - vis->addAction(a); - -#endif // TEST - - vis->addSeparator(); - - a = new QAction("Point size", this); - connect(a, SIGNAL(triggered()), this, SLOT(setPointSize())); - vis->addAction(a); - - a = new QAction("Quadrangle size (%)", this); - connect(a, SIGNAL(triggered()), this, SLOT(setQuadrangleSize())); - vis->addAction(a); - - vis->addSeparator(); - - a = new QAction("Color min value", this); - connect(a, SIGNAL(triggered()), this, SLOT(setColorMinValue())); - vis->addAction(a); - - a = new QAction("Color max value", this); - connect(a, SIGNAL(triggered()), this, SLOT(setColorMaxValue())); - vis->addAction(a); - -#ifdef TEST - - vis->addSeparator(); - - a = new QAction("Use GL lists", this); - connect(a, SIGNAL(triggered()), this, SLOT(setUsageOfGLlists())); - vis->addAction(a); - -#endif // TEST - - vis->addSeparator(); - - a = new QAction("Displayed X min", this); - connect(a, SIGNAL(triggered()), this, SLOT(setDisplayedXMin())); - vis->addAction(a); - - a = new QAction("Displayed X max", this); - connect(a, SIGNAL(triggered()), this, SLOT(setDisplayedXMax())); - vis->addAction(a); - - a = new QAction("Displayed Y min", this); - connect(a, SIGNAL(triggered()), this, SLOT(setDisplayedYMin())); - vis->addAction(a); - - a = new QAction("Displayed Y max", this); - connect(a, SIGNAL(triggered()), this, SLOT(setDisplayedYMax())); - vis->addAction(a); - - a = new QAction("Displayed Z min", this); - connect(a, SIGNAL(triggered()), this, SLOT(setDisplayedZMin())); - vis->addAction(a); - - a = new QAction("Displayed Z max", this); - connect(a, SIGNAL(triggered()), this, SLOT(setDisplayedZMax())); - vis->addAction(a); - - - QMenu * demo = menuBar()->addMenu( "Demo" ); - - a = new QAction("Waves", this); - connect(a, SIGNAL(triggered()), this, SLOT(displayWaves())); - demo->addAction(a); - - a = new QAction("Cut", this); - connect(a, SIGNAL(triggered()), this, SLOT(displayCut())); - demo->addAction(a); - - a = new QAction("Collisions", this); - connect(a, SIGNAL(triggered()), this, SLOT(displayCollisions())); - demo->addAction(a); - - - QMenu * help = menuBar()->addMenu( "Help" ); - - a = new QAction("About", this); - a->setShortcut(tr("F1")); - connect(a, SIGNAL(triggered()), this, SLOT(about())); - help->addAction(a); - - - myViewer = new Viewer( this ); - myViewer->setFocus(); - setCentralWidget( myViewer ); - statusBar()->showMessage( "Ready", 2000 ); - - myNbX = 100; - myNbY = 100; - myNbZ = 100; - - myScanSide = 7; - - myVolumicBoolValue = false; - myVolumicColorValue = 0; - - myQuadrangleSize = 40; - - myColorMinValue = 1; - myColorMaxValue = 15; - - myBoolVoxels = 0; - myColorVoxels = 0; - myColorScale = new AIS_ColorScale; - myColorScale->SetZLayer (Graphic3d_ZLayerId_TopOSD); - myColorScale->SetTransformPersistence (Graphic3d_TMF_2d, gp_Pnt (-1,-1,0)); - - - myDisplayedXMin = -DBL_MAX; - myDisplayedXMax = DBL_MAX; - myDisplayedYMin = -DBL_MAX; - myDisplayedYMax = DBL_MAX; - myDisplayedZMin = -DBL_MAX; - myDisplayedZMax = DBL_MAX; - - resize( 450, 600 ); - - myViewer->getIC()->SetAutoActivateSelection (Standard_False); -} - -Application::~Application() -{ - if (myBoolVoxels) - delete myBoolVoxels; - if (myColorVoxels) - delete myColorVoxels; -} - -void Application::choose() -{ - QString fn = QFileDialog::getOpenFileName( this, QString::null, QString::null, "*.brep"); - if ( !fn.isEmpty() ) - load( fn ); - else - statusBar()->showMessage( "Loading aborted", 2000 ); -} - -void Application::load( const QString &fileName ) -{ - QFile f( fileName ); - if ( !f.open( QIODevice::ReadOnly ) ) - return; - - // Read shape - TopoDS_Shape S; - BRep_Builder B; - if (!BRepTools::Read(S, (char*) fileName.constData(), B)) - statusBar()->showMessage( "Loading failed", 2000 ); - - load(S); -} - -void Application::open() -{ - QString fn = QFileDialog::getOpenFileName( this, QString::null, QString::null, "*.vx"); - if ( fn.isEmpty() || !QFile::exists(fn) ) - { - statusBar()->showMessage( "Open aborted", 2000 ); - return; - } - - Timer timer; - timer.Start(); - - // Read the voxels - Voxel_Reader reader; - if (!reader.Read((char*)fn.constData())) - { - statusBar()->showMessage( "Open failed... sorry", 2000 ); - return; - } - - timer.Stop(); - timer.Print("Open"); - - // Release current voxels - if (myBoolVoxels) - { - delete myBoolVoxels; - myBoolVoxels = 0; - } - if (myColorVoxels) - { - delete myColorVoxels; - myColorVoxels = 0; - } - - // Take the voxels - if (reader.IsBoolVoxels()) - { - myBoolVoxels = (Voxel_BoolDS*) reader.GetBoolVoxels(); - myViewer->getSelector().SetVoxels(*myBoolVoxels); - } - else if (reader.IsColorVoxels()) - { - myColorVoxels = (Voxel_ColorDS*) reader.GetColorVoxels(); - myViewer->getSelector().SetVoxels(*myColorVoxels); - } - - // Display the voxels - myViewer->getIC()->EraseAll(false); - Voxel_DS* ds = myBoolVoxels; - if (!ds) - ds = myColorVoxels; - if (ds) - { - myDisplayedXMin = ds->GetX() - 10.0 * Precision::Confusion(); - myDisplayedXMax = ds->GetX() + ds->GetXLen() + 10.0 * Precision::Confusion(); - myDisplayedYMin = ds->GetY() - 10.0 * Precision::Confusion(); - myDisplayedYMax = ds->GetY() + ds->GetYLen() + 10.0 * Precision::Confusion(); - myDisplayedZMin = ds->GetZ() - 10.0 * Precision::Confusion(); - myDisplayedZMax = ds->GetZ() + ds->GetZLen() + 10.0 * Precision::Confusion(); - } - - // Init visual data - initPrs(); - - // Set voxels and display - Handle(Poly_Triangulation) empty; - myVoxels->SetBoolVoxels(myBoolVoxels); - myVoxels->SetColorVoxels(myColorVoxels); - myVoxels->SetTriangulation(empty); - if (myViewer->getIC()->IsDisplayed(myVoxels)) - myViewer->getIC()->Redisplay(myVoxels, false); - else - myViewer->getIC()->Display(myVoxels, false); - - // Color scale - if (myColorVoxels) - displayColorScale(); - else - myViewer->getIC()->Erase(myColorScale); - - myViewer->getView()->FitAll(); - - statusBar()->showMessage( "Ready.", 2000 ); -} - -void Application::save() -{ - QString fn = QFileDialog::getSaveFileName( this, QString::null, QString::null, "*.vx"); - if ( fn.isEmpty() ) - { - statusBar()->showMessage( "Storage aborted", 2000 ); - return; - } - if (fn.indexOf(".vx", -1, Qt::CaseInsensitive) == -1) - fn += ".vx"; - - Timer timer; - timer.Start(); - - // Write the voxels - Voxel_Writer writer; - writer.SetFormat(Voxel_VFF_BINARY); - if (myBoolVoxels) - writer.SetVoxels(*myBoolVoxels); - else if (myColorVoxels) - writer.SetVoxels(*myColorVoxels); - else - { - statusBar()->showMessage( "Nothing to store", 2000 ); - return; - } - if (!writer.Write((char*)fn.constData())) - { - statusBar()->showMessage( "Storage failed... sorry", 2000 ); - return; - } - - timer.Stop(); - timer.Print("Save"); - - statusBar()->showMessage( "Saved.", 2000 ); -} - -void Application::closeEvent( QCloseEvent* ce ) -{ - ce->accept(); -} - -void Application::about() -{ - QMessageBox::about( this, "Voxel demo-application", - "This example demonstrates simple usage of " - "voxel models of Open CASCADE."); -} - -void Application::testBoolDS() -{ - Timer timer; - int ix, iy, iz; - int nbx = 100, nby = 100, nbz = 100; - - - // 1. BoolDS: - - timer.Start(); - - Voxel_BoolDS ds(0, 0, 0, 1, 1, 1, nbx, nby, nbz); - - for (ix = 0; ix < nbx; ix++) - { - for (iy = 0; iy < nby; iy++) - { - for (iz = 0; iz < nbz; iz++) - { - if (ix & 0x01) - ds.Set(ix, iy, iz, false); - else - ds.Set(ix, iy, iz, true); - } - } - } - - for (ix = 0; ix < nbx; ix++) - { - for (iy = 0; iy < nby; iy++) - { - for (iz = 0; iz < nbz; iz++) - { - bool value = ds.Get(ix, iy, iz) == Standard_True; - if (ix & 0x01) - { - if (value != false) - cout<<"Wrong value!"<OptimizeMemory(); - - timer.Stop(); - timer.Print("ROctBoolDS::converter"); - - - // Display - myViewer->getIC()->EraseAll(false); - initPrs(); - myVoxels->SetBoolVoxels(0); - myVoxels->SetColorVoxels(0); - Handle(Poly_Triangulation) empty; - myVoxels->SetTriangulation(empty); - myVoxels->SetROctBoolVoxels(ds2); - myViewer->getIC()->Display(myVoxels, false); - myViewer->getIC()->Erase(myColorScale); - myViewer->getView()->FitAll(); - myViewer->getSelector().SetVoxels(*ds2); -} - -void Application::testFuseBoolDS() -{ - Timer timer; - int ix, iy, iz; - int nbx = 100, nby = 100, nbz = 100; - - - // 1. Set two BoolDS: - - timer.Start(); - - Voxel_BoolDS ds1(0, 0, 0, 1, 1, 1, nbx, nby, nbz); - Voxel_BoolDS ds2(0, 0, 0, 1, 1, 1, nbx, nby, nbz); - - for (ix = 0; ix < nbx; ix++) - { - for (iy = 0; iy < nby; iy++) - { - for (iz = 0; iz < nbz; iz++) - { - if (ix & 0x01) - ds2.Set(ix, iy, iz, false); - else - ds2.Set(ix, iy, iz, true); - } - } - } - - // 2. Fuse them - - Voxel_BooleanOperation fuser; - if (!fuser.Fuse(ds1, ds2)) - cout<<"The operation failed..."< 0.001) - cout<<"Wrong value!"< 0.001) - cout<<"Wrong value!"< 0.001) - cout<<"Wrong value!"< 0.001) - cout<<"Wrong value!"<Shape(); - if (S.IsNull()) - { - QMessageBox::warning( this, "Voxel demo-application", "No shape for conversion!"); - return; - } - - switch (ivoxel) - { - case 0: - { - if (!myBoolVoxels) - myBoolVoxels = new Voxel_BoolDS; - if (myColorVoxels) - { - delete myColorVoxels; - myColorVoxels = 0; - } - break; - } - case 1: - { - if (!myColorVoxels) - myColorVoxels = new Voxel_ColorDS; - if (myBoolVoxels) - { - delete myBoolVoxels; - myBoolVoxels = 0; - } - break; - } - } - - switch (ivoxel) - { - case 0: - { - Timer timer; - timer.Start(); - - /* - int progress; - Voxel_Converter converter(S, *myBoolVoxels, myNbX, myNbY, myNbZ); - if (!converter.Convert(progress, myVolumicBoolValue, myScanSide)) - { - QMessageBox::warning( this, "Voxel demo-application", "Conversion failed..."); - return; - } - */ - - /* - Voxel_Converter converter(S, *myBoolVoxels, myNbX, myNbY, myNbZ, 2); - ConversionThread thread1, thread2; - - thread1.setConverter(&converter); - thread2.setConverter(&converter); - - thread1.setVolumicValue(myVolumicBoolValue); - thread2.setVolumicValue(myVolumicBoolValue); - - thread1.setScanSide(myScanSide); - thread2.setScanSide(myScanSide); - - thread1.setThreadIndex(1); - thread2.setThreadIndex(2); - - thread1.start(); - thread2.start(); - - while (thread1.running() || thread2.running()) - { - ::Sleep(100); - } - */ - - /* - int progress; - Voxel_FastConverter converter(S, *myBoolVoxels, 0.1, myNbX, myNbY, myNbZ, 1); - converter.Convert(progress, 1); - //if (myVolumicBoolValue) - // converter.FillInVolume(myVolumicBoolValue); - */ - - Voxel_FastConverter converter(S, *myBoolVoxels, 0.1, myNbX, myNbY, myNbZ, 2); - ConversionThread thread1, thread2; - - thread1.setConverter(&converter); - thread2.setConverter(&converter); - - thread1.setThreadIndex(1); - thread2.setThreadIndex(2); - - thread1.start(); - thread2.start(); - - while (thread1.isRunning() || thread2.isRunning()) - { - ::Sleep(100); - } - - timer.Print("Converter"); - - myViewer->getSelector().SetVoxels(*myBoolVoxels); - break; - } - case 1: - { - - Timer timer; - timer.Start(); - - /* - int progress; - Voxel_Converter converter(S, *myColorVoxels, myNbX, myNbY, myNbZ); - if (!converter.Convert(progress, myVolumicColorValue, myScanSide)) - { - QMessageBox::warning( this, "Voxel demo-application", "Conversion failed..."); - return; - } - */ - - /* - Voxel_Converter converter(S, *myColorVoxels, myNbX, myNbY, myNbZ, 2); - ConversionThread thread1, thread2; - - thread1.setConverter(&converter); - thread2.setConverter(&converter); - - thread1.setVolumicValue(myVolumicColorValue); - thread2.setVolumicValue(myVolumicColorValue); - - thread1.setScanSide(myScanSide); - thread2.setScanSide(myScanSide); - - thread1.setThreadIndex(1); - thread2.setThreadIndex(2); - - thread1.start(); - thread2.start(); - - while (thread1.running() || thread2.running()) - { - ::Sleep(100); - } - */ - - /* - int progress; - Voxel_FastConverter converter(S, *myColorVoxels, myNbX, myNbY, myNbZ, 1); - converter.Convert(progress, 1); - if (myVolumicColorValue) - converter.FillInVolume(myVolumicColorValue); - */ - - Voxel_FastConverter converter(S, *myColorVoxels, 0.1, myNbX, myNbY, myNbZ, 2); - ConversionThread thread1, thread2; - - thread1.setConverter(&converter); - thread2.setConverter(&converter); - - thread1.setThreadIndex(1); - thread2.setThreadIndex(2); - - thread1.start(); - thread2.start(); - - while (thread1.isRunning() || thread2.isRunning()) - { - ::Sleep(100); - } - - timer.Print("Converter"); - - - // Set color for demonstration - double maxd = - fabs(myColorVoxels->GetX()) > fabs(myColorVoxels->GetY()) ? - fabs(myColorVoxels->GetX()) : fabs(myColorVoxels->GetY()); - maxd = maxd > fabs(myColorVoxels->GetZ()) ? maxd : fabs(myColorVoxels->GetZ()); - maxd = maxd > fabs(myColorVoxels->GetX() + myColorVoxels->GetXLen()) ? - maxd : fabs(myColorVoxels->GetX() + myColorVoxels->GetXLen()); - maxd = maxd > fabs(myColorVoxels->GetY() + myColorVoxels->GetYLen()) ? - maxd : fabs(myColorVoxels->GetY() + myColorVoxels->GetYLen()); - maxd = maxd > fabs(myColorVoxels->GetZ() + myColorVoxels->GetZLen()) ? - maxd : fabs(myColorVoxels->GetZ() + myColorVoxels->GetZLen()); - for (int ix = 0; ix < myNbX; ix++) - { - for (int iy = 0; iy < myNbY; iy++) - { - for (int iz = 0; iz < myNbZ; iz++) - { - unsigned char value = myColorVoxels->Get(ix, iy, iz); - if (value) - { - double xc, yc, zc, xd, yd, zd; - myColorVoxels->GetCenter(ix, iy, iz, xc, yc, zc); - xd = fabs(xc); - yd = fabs(yc); - zd = fabs(zc); - double mind = xd < yd ? xd : yd; - mind = zd < mind ? zd : mind; - value = unsigned char(15.0 * (maxd - mind) / maxd); - if (value <= 0) - value = 1; - myColorVoxels->Set(ix, iy, iz, value); - } - } - } - } - - myViewer->getSelector().SetVoxels(*myColorVoxels); - break; - } - } - - myViewer->getIC()->EraseAll(false); - - Voxel_DS* ds = myBoolVoxels; - if (!ds) - ds = myColorVoxels; - if (ds) - { - myDisplayedXMin = ds->GetX() - 10.0 * Precision::Confusion(); - myDisplayedXMax = ds->GetX() + ds->GetXLen() + 10.0 * Precision::Confusion(); - myDisplayedYMin = ds->GetY() - 10.0 * Precision::Confusion(); - myDisplayedYMax = ds->GetY() + ds->GetYLen() + 10.0 * Precision::Confusion(); - myDisplayedZMin = ds->GetZ() - 10.0 * Precision::Confusion(); - myDisplayedZMax = ds->GetZ() + ds->GetZLen() + 10.0 * Precision::Confusion(); - } - - // Init visual data - initPrs(); - - // Set voxels and display - Handle(Poly_Triangulation) empty; - myVoxels->SetBoolVoxels(myBoolVoxels); - myVoxels->SetColorVoxels(myColorVoxels); - myVoxels->SetTriangulation(empty); - if (myViewer->getIC()->IsDisplayed(myVoxels)) - myViewer->getIC()->Redisplay(myVoxels, false); - else - myViewer->getIC()->Display(myVoxels, false); - - // Color scale - if (myColorVoxels) - displayColorScale(); - else - myViewer->getIC()->Erase(myColorScale); - - myViewer->getView()->FitAll(); -} - -void Application::setNbX() -{ - bool ok; - myNbX = - QInputDialog::getInteger(this, "Voxel demo-application", "Number of splits in X-direction:", myNbX, - 1, 100000, 1, &ok); -} - -void Application::setNbY() -{ - bool ok; - myNbY = - QInputDialog::getInteger(this, "Voxel demo-application", "Number of splits in X-direction:", myNbY, - 1, 100000, 1, &ok); -} - -void Application::setNbZ() -{ - bool ok; - myNbZ = - QInputDialog::getInteger(this, "Voxel demo-application", "Number of splits in X-direction:", myNbZ, - 1, 100000, 1, &ok); -} - -void Application::setColorMinValue() -{ - bool ok; - myColorMinValue = - QInputDialog::getInteger(this, "Voxel demo-application", "Minimum value for color [0 .. 15]:", myColorMinValue, - 0, 15, 1, &ok); - if (!myVoxels.IsNull()) - myVoxels->SetColorRange(myColorMinValue, myColorMaxValue); -} - -void Application::setColorMaxValue() -{ - bool ok; - myColorMaxValue = - QInputDialog::getInteger(this, "Voxel demo-application", "Maximum value for color [0 .. 15]:", myColorMaxValue, - 0, 15, 1, &ok); - if (!myVoxels.IsNull()) - myVoxels->SetColorRange(myColorMinValue, myColorMaxValue); -} - -void Application::setUsageOfGLlists() -{ - int res = QMessageBox::question( this, "Voxel demo-application", "Press Yes to use GL lists and No not to use them.", QMessageBox::Yes, QMessageBox::No); - if (!myVoxels.IsNull()) - myVoxels->SetUsageOfGLlists(res == QMessageBox::Yes); -} - -void Application::setDisplayedXMin() -{ - myDisplayedXMin = QInputDialog::getDouble(this, "Voxel demo-application", "Minimum X value:", myDisplayedXMin); - if (!myVoxels.IsNull()) - { - myVoxels->SetSizeRange(myDisplayedXMin, myDisplayedXMax, - myDisplayedYMin, myDisplayedYMax, - myDisplayedZMin, myDisplayedZMax); - } -} - -void Application::setDisplayedXMax() -{ - myDisplayedXMax = QInputDialog::getDouble(this, "Voxel demo-application", "Maximum X value:", myDisplayedXMax); - if (!myVoxels.IsNull()) - { - myVoxels->SetSizeRange(myDisplayedXMin, myDisplayedXMax, - myDisplayedYMin, myDisplayedYMax, - myDisplayedZMin, myDisplayedZMax); - } -} - -void Application::setDisplayedYMin() -{ - myDisplayedYMin = QInputDialog::getDouble(this, "Voxel demo-application", "Minimum Y value:", myDisplayedYMin); - if (!myVoxels.IsNull()) - { - myVoxels->SetSizeRange(myDisplayedXMin, myDisplayedXMax, - myDisplayedYMin, myDisplayedYMax, - myDisplayedZMin, myDisplayedZMax); - } -} - -void Application::setDisplayedYMax() -{ - myDisplayedYMax = QInputDialog::getDouble(this, "Voxel demo-application", "Maximum Y value:", myDisplayedYMax); - if (!myVoxels.IsNull()) - { - myVoxels->SetSizeRange(myDisplayedXMin, myDisplayedXMax, - myDisplayedYMin, myDisplayedYMax, - myDisplayedZMin, myDisplayedZMax); - } -} - -void Application::setDisplayedZMin() -{ - myDisplayedZMin = QInputDialog::getDouble(this, "Voxel demo-application", "Minimum Z value:", myDisplayedZMin); - if (!myVoxels.IsNull()) - { - myVoxels->SetSizeRange(myDisplayedXMin, myDisplayedXMax, - myDisplayedYMin, myDisplayedYMax, - myDisplayedZMin, myDisplayedZMax); - } -} - -void Application::setDisplayedZMax() -{ - myDisplayedZMax = QInputDialog::getDouble(this, "Voxel demo-application", "Maximum Z value:", myDisplayedZMax); - if (!myVoxels.IsNull()) - { - myVoxels->SetSizeRange(myDisplayedXMin, myDisplayedXMax, - myDisplayedYMin, myDisplayedYMax, - myDisplayedZMin, myDisplayedZMax); - } -} - -void Application::setScanSide() -{ - myScanSide = - QInputDialog::getInteger(this, "Voxel demo-application", "Side of scanning (1: +X side, 2: +Y side, 3: +Z side, 4: +X & +Y sides, .. 7: +X, +Y,& +Z sides):", - myScanSide, 1, 7, 1); -} - -void Application::setVolumicBoolValue() -{ - myVolumicBoolValue = - QInputDialog::getInteger(this, "Voxel demo-application", "Volumic value on voxelization [0 .. 1]:", - myVolumicBoolValue, 0, 1, 1); -} - -void Application::setVolumicColorValue() -{ - myVolumicColorValue = - QInputDialog::getInteger(this, "Voxel demo-application", "Volumic value on voxelization [0 .. 15]:", - myVolumicColorValue, 0, 15, 1); -} - -void Application::setQuadrangleSize() -{ - myQuadrangleSize = - QInputDialog::getInteger(this, "Voxel demo-application", "Size of quadrangles (0% .. 100%):", - myQuadrangleSize, 1, 100, 10); - if (!myVoxels.IsNull()) - { - myVoxels->SetQuadrangleSize(myQuadrangleSize); - } -} - -void Application::setPointSize() -{ - myPointSize = - QInputDialog::getInteger(this, "Voxel demo-application", "Size of points (1 .. 10):", - myPointSize, 1, 10, 1); - if (!myVoxels.IsNull()) - { - myVoxels->SetPointSize(myPointSize); - } -} - -void Application::display(Voxel_VoxelDisplayMode mode) -{ - if (myVoxels.IsNull() || !myViewer->getIC()->IsDisplayed(myVoxels)) - { - QMessageBox::warning( this, "Voxel demo-application", "Voxels are not displayed"); - return; - } - - myVoxels->SetDisplayMode(mode); - - if (myColorVoxels) - displayColorScale(); - else - myViewer->getIC()->Erase(myColorScale); - - myViewer->getIC()->Redisplay(myVoxels, true); -} - -void Application::displayPoints() -{ - display(Voxel_VDM_POINTS); -} - -void Application::displayNearestPoints() -{ - display(Voxel_VDM_NEARESTPOINTS); -} - -void Application::displayBoxes() -{ - display(Voxel_VDM_BOXES); -} - -void Application::displayNearestBoxes() -{ - display(Voxel_VDM_NEARESTBOXES); -} - -void Application::displayColorScale() -{ - if (myColorScale.IsNull()) - { - myColorScale = new AIS_ColorScale; - } - if (!myColorScale.IsNull()) - { - int nb_colors = 1<<4 /* 4 bits */; - myColorScale->SetRange(0, nb_colors - 1); - myColorScale->SetNumberOfIntervals(nb_colors); - myColorScale->SetPosition(0.01, 0.5 - 0.01); - myColorScale->SetSize(0.5 - 0.01, 0.5 - 0.01); - } - myViewer->getIC()->Display(myColorScale); -} - -void Application::displayWaves() -{ - myViewer->getIC()->EraseAll(false); - - // Make voxels - if (myBoolVoxels) - { - delete myBoolVoxels; - myBoolVoxels = 0; - } - if (myColorVoxels) - delete myColorVoxels; - - int nbx = 500, nby = 50, nbz = 50; - double xlen = 100.0, ylen = 100.0, zlen = 20.0; - double dx = xlen / (double) nbx, dy = ylen / (double) nby, dz = zlen / (double) nbz; - myColorVoxels = new Voxel_ColorDS(0.0, 0.0, 0.0, xlen, ylen, zlen, nbx, nby, nbz); - - // Initial state - no colors - int ix, iy, iz; - for (ix = 0; ix < nbx; ix++) - { - for (iy = 0; iy < nby; iy++) - { - for (iz = 0; iz < nbz; iz++) - { - myColorVoxels->Set(ix, iy, iz, 0); - } - } - } - - // Init visual data - initPrs(); - myVoxels->SetDisplayMode(Voxel_VDM_POINTS); - myVoxels->SetUsageOfGLlists(false); - myVoxels->SetBoolVoxels(myBoolVoxels); - myVoxels->SetColorVoxels(myColorVoxels); - if (myViewer->getIC()->IsDisplayed(myVoxels)) - myViewer->getIC()->Redisplay(myVoxels, false); - else - myViewer->getIC()->Display(myVoxels, false); - myViewer->getView()->FitAll(); - - // Prepare arrays of values - // X&Z values - int i = 0, di = 5 /* nb waves */; - int* zvalues = new int[nbx]; - unsigned char* xvalues = new unsigned char[nbx]; - for (ix = 0; ix < nbx; ix++, i += di) - { - if (i > nbx || i < 0) - { - di *= -1; - i += di; - } - double rad = -M_PI / 2.0 + double(i) / (double) nbx * M_PI; - double c = cos(rad); - xvalues[ix] = 15.0 * c; - if (xvalues[ix] == 0) - xvalues[ix] = 1; - zvalues[ix] = (nbz - 2) * c; - } - - // Make waves - unsigned char value = 0; - for (i = 0; i <= 100; i++) - { - for (ix = 0; ix < nbx; ix++) - { - int ixi = ix + i; - if (ixi >= nbx) - ixi -= nbx; - for (iz = 0; iz < nbz; iz++) - { - value = 0; - if (iz < zvalues[ixi]) - value = xvalues[ixi]; - for (iy = 0; iy < nby; iy++) - { - myColorVoxels->Set(ix, iy, iz, value); - } - } - } - myViewer->getIC()->Redisplay(myVoxels, true); - qApp->processEvents(); - } - - delete[] xvalues; - delete[] zvalues; -} - -void Application::initPrs() -{ - if (myVoxels.IsNull()) - { - myVoxels = new VoxelClient_PrsGl(); - myVoxels->SetDisplayMode(Voxel_VDM_POINTS); - myVoxels->SetColor(Quantity_NOC_WHITE); - myVoxels->SetPointSize(1.0); - myVoxels->SetSmoothPoints(false); - myVoxels->SetQuadrangleSize(myQuadrangleSize); - myVoxels->SetColorRange(myColorMinValue, myColorMaxValue); - // Colors of ColorDS - int nb_colors = 16 /* 4 bits */; - Handle(Quantity_HArray1OfColor) colors = new Quantity_HArray1OfColor(0, nb_colors - 1); - for (int icolor = 0; icolor < nb_colors; icolor++) - { - Quantity_Color color; - AIS_ColorScale::FindColor(icolor, 0, nb_colors - 1, nb_colors, color); - colors->SetValue(icolor, color); - } - myVoxels->SetColors(colors); - myViewer->setPrs(myVoxels); - } - else - { - myViewer->getIC()->RecomputePrsOnly(myVoxels, false); - } -} - -void Application::box() -{ - gp_Ax2 axes(gp_Pnt(0, 0, 0), gp_Dir(0, 0, 1)); - TopoDS_Shape S = BRepPrimAPI_MakeBox(axes, 100, 100, 100); - load(S); -} - -void Application::cylinder() -{ - TopoDS_Shape S = BRepPrimAPI_MakeCylinder(50, 100); - load(S); -} - -void Application::torus() -{ - TopoDS_Shape S = BRepPrimAPI_MakeTorus(100, 20); - load(S); -} - -void Application::sphere() -{ - TopoDS_Shape S = BRepPrimAPI_MakeSphere(100); - load(S); -} - -void Application::load(const TopoDS_Shape& S) -{ - myViewer->getIC()->EraseAll(false); - - // Delete voxels of previous shape. - if (myBoolVoxels) - { - delete myBoolVoxels; - myBoolVoxels = 0; - } - if (myColorVoxels) - { - delete myColorVoxels; - myColorVoxels = 0; - } - - // Set view size - Bnd_Box box; - double xmin, ymin, zmin, xmax, ymax, zmax, length = 0; - BRepBndLib::Add(S, box); - box.Get(xmin, ymin, zmin, xmax, ymax, zmax); - length = xmax - xmin > ymax - ymin ? xmax - xmin : ymax - ymin; - length = length > zmax - zmin ? length : zmax - zmin; - length *= 2.0; - myViewer->getView()->SetSize(length); - myViewer->getView()->SetZSize(length); - - // Display shape - if (myShape.IsNull()) - { - myShape = new AIS_Shape(S); - myShape->SetDisplayMode(1); - } - else - { - myShape->Set(S); - myViewer->getIC()->RecomputePrsOnly(myShape, false); - } - if (myViewer->getIC()->IsDisplayed(myShape)) - myViewer->getIC()->Redisplay(myShape, false); - else - myViewer->getIC()->Display(myShape, false); - myViewer->getView()->FitAll(); -} - -void Application::displayCut() -{ - myViewer->getIC()->EraseAll(false); - - // Make a sphere with a lot of toruses, - // cut the toruses from the sphere. - TopoDS_Shape sphere = BRepPrimAPI_MakeSphere(100.0); - TopoDS_Shape torus1 = BRepPrimAPI_MakeTorus(gp_Ax2(gp_Pnt( 80, 0, 20), gp::DZ()), 30, 10); - TopoDS_Shape torus2 = BRepPrimAPI_MakeTorus(gp_Ax2(gp_Pnt( 0, 80, 20), gp::DZ()), 30, 10); - TopoDS_Shape torus3 = BRepPrimAPI_MakeTorus(gp_Ax2(gp_Pnt(-80, 0, 20), gp::DZ()), 30, 10); - TopoDS_Shape torus4 = BRepPrimAPI_MakeTorus(gp_Ax2(gp_Pnt( 0, -80, 20), gp::DZ()), 30, 10); - - // Compute bounding box of the shapes - Bnd_Box box; - BRepBndLib::Add(sphere, box); - BRepBndLib::Add(torus1, box); - BRepBndLib::Add(torus2, box); - BRepBndLib::Add(torus3, box); - BRepBndLib::Add(torus4, box); - - // Nullify voxels - if (myColorVoxels) - { - delete myColorVoxels; - myColorVoxels = 0; - } - if (myBoolVoxels) - { - delete myBoolVoxels; - myBoolVoxels = 0; - } - - Timer timer; - timer.Start(); - - // Create a cube of voxels - int nbx = 100, nby = 100, nbz = 100; - double xmin, ymin, zmin, xmax, ymax, zmax; - box.Get(xmin, ymin, zmin, xmax, ymax, zmax); - myColorVoxels = new Voxel_ColorDS(xmin, ymin, zmin, - xmax - xmin, ymax - ymin, zmax - zmin, - nbx, nby, nbz); - Voxel_ColorDS vtorus(xmin, ymin, zmin, - xmax - xmin, ymax - ymin, zmax - zmin, - nbx, nby, nbz); - - // Make a cube of voxels for the sphere. - int progress; - Voxel_FastConverter converter(sphere, *myColorVoxels, 0.1, nbx, nby, nbz); - converter.Convert(progress); - converter.FillInVolume(15); - - // Torus 1 - Voxel_FastConverter converter1(torus1, vtorus, 0.1, nbx, nby, nbz); - converter1.Convert(progress); - converter1.FillInVolume(3); - - // Torus 2 - Voxel_FastConverter converter2(torus2, vtorus, 0.1, nbx, nby, nbz); - converter2.Convert(progress); - converter2.FillInVolume(7); - - // Torus 3 - Voxel_FastConverter converter3(torus3, vtorus, 0.1, nbx, nby, nbz); - converter3.Convert(progress); - converter3.FillInVolume(10); - - // Torus 4 - Voxel_FastConverter converter4(torus4, vtorus, 0.1, nbx, nby, nbz); - converter4.Convert(progress); - converter4.FillInVolume(12); - - // Cut - Voxel_BooleanOperation cutter; - cutter.Cut(*myColorVoxels, vtorus); - - // Remove volumic voxels - converter.FillInVolume(0); - - timer.Stop(); - timer.Print("Cut"); - - // Display - initPrs(); - myVoxels->SetDisplayMode(Voxel_VDM_POINTS); - myVoxels->SetUsageOfGLlists(true); - myVoxels->SetBoolVoxels(myBoolVoxels); - myVoxels->SetColorVoxels(myColorVoxels); - if (myViewer->getIC()->IsDisplayed(myVoxels)) - myViewer->getIC()->Redisplay(myVoxels, false); - else - myViewer->getIC()->Display(myVoxels, false); - myViewer->getView()->FitAll(); -} - -void Application::displayCollisions() -{ - myViewer->getIC()->EraseAll(false); - - // Make a big box with a lot of small spheres inside. - double x = 0.0, y = 0.0, z = 0.0, xlen = 100.0, ylen = 100.0, zlen = 100.0, r = 10.0; - gp_Pnt P1(x, y, z); // center point of moving sphere (S1). - TopoDS_Shape B = BRepPrimAPI_MakeBox(gp_Pnt(x-r, y-r, z-r), gp_Pnt(xlen+r, ylen+r, zlen+r)); - TopoDS_Shape S1 = BRepPrimAPI_MakeSphere(gp_Ax2(gp_Pnt(x, y, z), gp::DZ()), r / 2.0); - TopoDS_Shape S2 = BRepPrimAPI_MakeSphere(gp_Ax2(gp_Pnt(xlen/2., y, z), gp::DZ()), r); - TopoDS_Shape S3 = BRepPrimAPI_MakeSphere(gp_Ax2(gp_Pnt(xlen, y, z), gp::DZ()), r); - TopoDS_Shape S4 = BRepPrimAPI_MakeSphere(gp_Ax2(gp_Pnt(x, ylen/2., z), gp::DZ()), r); - TopoDS_Shape S5 = BRepPrimAPI_MakeSphere(gp_Ax2(gp_Pnt(xlen/2., ylen/2., z), gp::DZ()), r); - TopoDS_Shape S6 = BRepPrimAPI_MakeSphere(gp_Ax2(gp_Pnt(xlen, ylen/2., z), gp::DZ()), r); - TopoDS_Shape S7 = BRepPrimAPI_MakeSphere(gp_Ax2(gp_Pnt(x, ylen, z), gp::DZ()), r); - TopoDS_Shape S8 = BRepPrimAPI_MakeSphere(gp_Ax2(gp_Pnt(xlen/2., ylen, z), gp::DZ()), r); - TopoDS_Shape S9 = BRepPrimAPI_MakeSphere(gp_Ax2(gp_Pnt(xlen, ylen, z), gp::DZ()), r); - TopoDS_Shape S10 = BRepPrimAPI_MakeSphere(gp_Ax2(gp_Pnt(x, y, zlen/2.), gp::DZ()), r); - TopoDS_Shape S11 = BRepPrimAPI_MakeSphere(gp_Ax2(gp_Pnt(xlen/2., y, zlen/2.), gp::DZ()), r); - TopoDS_Shape S12 = BRepPrimAPI_MakeSphere(gp_Ax2(gp_Pnt(xlen, y, zlen/2.), gp::DZ()), r); - TopoDS_Shape S13 = BRepPrimAPI_MakeSphere(gp_Ax2(gp_Pnt(x, ylen/2., zlen/2.), gp::DZ()), r); - TopoDS_Shape S14 = BRepPrimAPI_MakeSphere(gp_Ax2(gp_Pnt(xlen/2., ylen/2., zlen/2.), gp::DZ()), r); - TopoDS_Shape S15 = BRepPrimAPI_MakeSphere(gp_Ax2(gp_Pnt(xlen, ylen/2., zlen/2.), gp::DZ()), r); - TopoDS_Shape S16 = BRepPrimAPI_MakeSphere(gp_Ax2(gp_Pnt(x, ylen, zlen/2.), gp::DZ()), r); - TopoDS_Shape S17 = BRepPrimAPI_MakeSphere(gp_Ax2(gp_Pnt(xlen/2., ylen, zlen/2.), gp::DZ()), r); - TopoDS_Shape S18 = BRepPrimAPI_MakeSphere(gp_Ax2(gp_Pnt(xlen, ylen, zlen/2.), gp::DZ()), r); - TopoDS_Shape S19 = BRepPrimAPI_MakeSphere(gp_Ax2(gp_Pnt(x, y, zlen), gp::DZ()), r); - TopoDS_Shape S20 = BRepPrimAPI_MakeSphere(gp_Ax2(gp_Pnt(xlen/2., y, zlen), gp::DZ()), r); - TopoDS_Shape S21 = BRepPrimAPI_MakeSphere(gp_Ax2(gp_Pnt(xlen, y, zlen), gp::DZ()), r); - TopoDS_Shape S22 = BRepPrimAPI_MakeSphere(gp_Ax2(gp_Pnt(x, ylen/2., zlen), gp::DZ()), r); - TopoDS_Shape S23 = BRepPrimAPI_MakeSphere(gp_Ax2(gp_Pnt(xlen/2., ylen/2., zlen), gp::DZ()), r); - TopoDS_Shape S24 = BRepPrimAPI_MakeSphere(gp_Ax2(gp_Pnt(xlen, ylen/2., zlen), gp::DZ()), r); - TopoDS_Shape S25 = BRepPrimAPI_MakeSphere(gp_Ax2(gp_Pnt(x, ylen, zlen), gp::DZ()), r); - TopoDS_Shape S26 = BRepPrimAPI_MakeSphere(gp_Ax2(gp_Pnt(xlen/2., ylen, zlen), gp::DZ()), r); - TopoDS_Shape S27 = BRepPrimAPI_MakeSphere(gp_Ax2(gp_Pnt(xlen, ylen, zlen), gp::DZ()), r); - - // Planes of the big box - gp_Ax2 xminusPlane(gp_Pnt(x, y, z), gp::DX()); - gp_Ax2 xplusPlane (gp_Pnt(xlen, y, z), gp::DX()); - gp_Ax2 yminusPlane(gp_Pnt(x, y, z), gp::DY()); - gp_Ax2 yplusPlane (gp_Pnt(x, ylen, z), gp::DY()); - gp_Ax2 zminusPlane(gp_Pnt(x, y, z), gp::DZ()); - gp_Ax2 zplusPlane (gp_Pnt(x, y, zlen), gp::DZ()); - - // Nullify voxels - if (myColorVoxels) - { - delete myColorVoxels; - myColorVoxels = 0; - } - if (myBoolVoxels) - { - delete myBoolVoxels; - myBoolVoxels = 0; - } - - // Prepare visualization - initPrs(); - myVoxels->SetDisplayMode(Voxel_VDM_POINTS); - myVoxels->SetColor(Quantity_NOC_RED); - myVoxels->SetPointSize(4); - myVoxels->SetSmoothPoints(false); - myVoxels->SetUsageOfGLlists(false); - myVoxels->SetColorVoxels(myColorVoxels); - - // Display all shapes - double transparency = 0.9; - Handle(AIS_Shape) aisB = new AIS_Shape(B); - Handle(AIS_Shape) aisS1 = new AIS_Shape(S1); - Handle(AIS_Shape) aisS2 = new AIS_Shape(S2); - Handle(AIS_Shape) aisS3 = new AIS_Shape(S3); - Handle(AIS_Shape) aisS4 = new AIS_Shape(S4); - Handle(AIS_Shape) aisS5 = new AIS_Shape(S5); - Handle(AIS_Shape) aisS6 = new AIS_Shape(S6); - Handle(AIS_Shape) aisS7 = new AIS_Shape(S7); - Handle(AIS_Shape) aisS8 = new AIS_Shape(S8); - Handle(AIS_Shape) aisS9 = new AIS_Shape(S9); - Handle(AIS_Shape) aisS10 = new AIS_Shape(S10); - Handle(AIS_Shape) aisS11 = new AIS_Shape(S11); - Handle(AIS_Shape) aisS12 = new AIS_Shape(S12); - Handle(AIS_Shape) aisS13 = new AIS_Shape(S13); - Handle(AIS_Shape) aisS14 = new AIS_Shape(S14); - Handle(AIS_Shape) aisS15 = new AIS_Shape(S15); - Handle(AIS_Shape) aisS16 = new AIS_Shape(S16); - Handle(AIS_Shape) aisS17 = new AIS_Shape(S17); - Handle(AIS_Shape) aisS18 = new AIS_Shape(S18); - Handle(AIS_Shape) aisS19 = new AIS_Shape(S19); - Handle(AIS_Shape) aisS20 = new AIS_Shape(S20); - Handle(AIS_Shape) aisS21 = new AIS_Shape(S21); - Handle(AIS_Shape) aisS22 = new AIS_Shape(S22); - Handle(AIS_Shape) aisS23 = new AIS_Shape(S23); - Handle(AIS_Shape) aisS24 = new AIS_Shape(S24); - Handle(AIS_Shape) aisS25 = new AIS_Shape(S25); - Handle(AIS_Shape) aisS26 = new AIS_Shape(S26); - Handle(AIS_Shape) aisS27 = new AIS_Shape(S27); - aisS1-> SetDisplayMode(1); - aisS2-> SetDisplayMode(1); - aisS3-> SetDisplayMode(1); - aisS4-> SetDisplayMode(1); - aisS5-> SetDisplayMode(1); - aisS6-> SetDisplayMode(1); - aisS7-> SetDisplayMode(1); - aisS8-> SetDisplayMode(1); - aisS9-> SetDisplayMode(1); - aisS10->SetDisplayMode(1); - aisS11->SetDisplayMode(1); - aisS12->SetDisplayMode(1); - aisS13->SetDisplayMode(1); - aisS14->SetDisplayMode(1); - aisS15->SetDisplayMode(1); - aisS16->SetDisplayMode(1); - aisS17->SetDisplayMode(1); - aisS18->SetDisplayMode(1); - aisS19->SetDisplayMode(1); - aisS20->SetDisplayMode(1); - aisS21->SetDisplayMode(1); - aisS22->SetDisplayMode(1); - aisS23->SetDisplayMode(1); - aisS24->SetDisplayMode(1); - aisS25->SetDisplayMode(1); - aisS26->SetDisplayMode(1); - aisS27->SetDisplayMode(1); - aisS1-> SetTransparency(2.0 * transparency / 3.0); - aisS2-> SetTransparency(transparency); - aisS3-> SetTransparency(transparency); - aisS4-> SetTransparency(transparency); - aisS5-> SetTransparency(transparency); - aisS6-> SetTransparency(transparency); - aisS7-> SetTransparency(transparency); - aisS8-> SetTransparency(transparency); - aisS9-> SetTransparency(transparency); - aisS10->SetTransparency(transparency); - aisS11->SetTransparency(transparency); - aisS12->SetTransparency(transparency); - aisS13->SetTransparency(transparency); - aisS14->SetTransparency(transparency); - aisS15->SetTransparency(transparency); - aisS16->SetTransparency(transparency); - aisS17->SetTransparency(transparency); - aisS18->SetTransparency(transparency); - aisS19->SetTransparency(transparency); - aisS20->SetTransparency(transparency); - aisS21->SetTransparency(transparency); - aisS22->SetTransparency(transparency); - aisS23->SetTransparency(transparency); - aisS24->SetTransparency(transparency); - aisS25->SetTransparency(transparency); - aisS26->SetTransparency(transparency); - aisS27->SetTransparency(transparency); - myViewer->getIC()->Display(aisB, false); - myViewer->getIC()->Display(aisS1, false); - myViewer->getIC()->Display(aisS2, false); - myViewer->getIC()->Display(aisS3, false); - myViewer->getIC()->Display(aisS4, false); - myViewer->getIC()->Display(aisS5, false); - myViewer->getIC()->Display(aisS6, false); - myViewer->getIC()->Display(aisS7, false); - myViewer->getIC()->Display(aisS8, false); - myViewer->getIC()->Display(aisS9, false); - myViewer->getIC()->Display(aisS10, false); - myViewer->getIC()->Display(aisS11, false); - myViewer->getIC()->Display(aisS12, false); - myViewer->getIC()->Display(aisS13, false); - myViewer->getIC()->Display(aisS14, false); - myViewer->getIC()->Display(aisS15, false); - myViewer->getIC()->Display(aisS16, false); - myViewer->getIC()->Display(aisS17, false); - myViewer->getIC()->Display(aisS18, false); - myViewer->getIC()->Display(aisS19, false); - myViewer->getIC()->Display(aisS20, false); - myViewer->getIC()->Display(aisS21, false); - myViewer->getIC()->Display(aisS22, false); - myViewer->getIC()->Display(aisS23, false); - myViewer->getIC()->Display(aisS24, false); - myViewer->getIC()->Display(aisS25, false); - myViewer->getIC()->Display(aisS26, false); - myViewer->getIC()->Display(aisS27, false); - - // Prepare computer of collisions - double deflection = 0.1; - int nbx = 100, nby = 100, nbz = 100; - - Voxel_CollisionDetection coldet(deflection, nbx, nby, nbz); - coldet.SetUsageOfVolume(false); - coldet.KeepCollisions(false); - coldet.AddShape(S1); - coldet.AddShape(S2); - coldet.AddShape(S3); - coldet.AddShape(S4); - coldet.AddShape(S5); - coldet.AddShape(S6); - coldet.AddShape(S7); - coldet.AddShape(S8); - coldet.AddShape(S9); - coldet.AddShape(S10); - coldet.AddShape(S11); - coldet.AddShape(S12); - coldet.AddShape(S13); - coldet.AddShape(S14); - coldet.AddShape(S15); - coldet.AddShape(S16); - coldet.AddShape(S17); - coldet.AddShape(S18); - coldet.AddShape(S19); - coldet.AddShape(S20); - coldet.AddShape(S21); - coldet.AddShape(S22); - coldet.AddShape(S23); - coldet.AddShape(S24); - coldet.AddShape(S25); - coldet.AddShape(S26); - coldet.AddShape(S27); - //coldet.AddShape(BRepPrimAPI_MakeBox(gp_Pnt(x, y, z), gp_Pnt(xlen, ylen, zlen))); - - Bnd_Box box; - BRepBndLib::Add(B, box); - coldet.SetBoundaryBox(box); - - coldet.Voxelize(); - - // Move one of the spheres inside the box - // and compute collisions - gp_Trsf trsf; - gp_Vec vmove(1, 0.5, 0.25); - - int imove = 0, nb_moves = 900; - while (imove < nb_moves) - { - // Move - trsf.SetTranslation(vmove); - TopLoc_Location loc(trsf); - S1.Move(loc); - P1.Translate(vmove); - - // Check whether S1 is inside the big box - // Detect the plane S1 touches to. - if (P1.X() < x) - vmove.Mirror(xminusPlane); - else if (P1.X() > xlen) - vmove.Mirror(xplusPlane); - else if (P1.Y() < y) - vmove.Mirror(yminusPlane); - else if (P1.Y() > ylen) - vmove.Mirror(yplusPlane); - else if (P1.Z() < z) - vmove.Mirror(zminusPlane); - else if (P1.Z() > zlen) - vmove.Mirror(zplusPlane); - - // Compute collisions - coldet.ReplaceShape(1, S1); - coldet.Voxelize(1); // only the first sphere (S1) - coldet.Compute(); - myBoolVoxels = &((Voxel_BoolDS&) coldet.GetCollisions()); - - // Redisplay S1 - aisS1->Set(S1); - myViewer->getIC()->Redisplay(aisS1, false); - - // Display the collisions - myVoxels->SetBoolVoxels(myBoolVoxels); - if (myViewer->getIC()->IsDisplayed(myVoxels)) - myViewer->getIC()->Redisplay(myVoxels, true); - else - { - myViewer->getIC()->Display(myVoxels, false); - myViewer->getView()->FitAll(); - } - - imove++; - qApp->processEvents(); - } - - // Copy the result of collision detection - int ix, iy, iz; - myBoolVoxels = new Voxel_BoolDS(coldet.GetCollisions().GetX(), - coldet.GetCollisions().GetY(), - coldet.GetCollisions().GetZ(), - coldet.GetCollisions().GetXLen(), - coldet.GetCollisions().GetYLen(), - coldet.GetCollisions().GetZLen(), - nbx, nby, nbz); - for (ix = 0; ix < nbx; ix++) - { - for (iy = 0; iy < nby; iy++) - { - for (iz = 0; iz < nbz; iz++) - { - if (coldet.GetCollisions().Get(ix, iy, iz)) - myBoolVoxels->Set(ix, iy, iz, Standard_True); - } - } - } - myVoxels->SetBoolVoxels(myBoolVoxels); -} \ No newline at end of file diff --git a/samples/qt/VoxelDemo/src/ConversionThread.cpp b/samples/qt/VoxelDemo/src/ConversionThread.cpp deleted file mode 100644 index 6fee824a80..0000000000 --- a/samples/qt/VoxelDemo/src/ConversionThread.cpp +++ /dev/null @@ -1,58 +0,0 @@ -#include "ConversionThread.h" - -ConversionThread::ConversionThread():QThread(), -/*myConverter(0),*/myFastConverter(0), -myVolumicValue(0),myScanSide(1), -myThreadIndex(1) -{ - -} - -ConversionThread::~ConversionThread() -{ - -} - -/* -void ConversionThread::setConverter(Voxel_Converter* converter) -{ - myConverter = converter; - myFastConverter = 0; -} -*/ - -void ConversionThread::setConverter(Voxel_FastConverter* converter) -{ - myFastConverter = converter; - //myConverter = 0; -} - -void ConversionThread::setVolumicValue(const int value) -{ - myVolumicValue = value; -} - -void ConversionThread::setScanSide(const int side) -{ - myScanSide = side; -} - -void ConversionThread::setThreadIndex(const int ithread) -{ - myThreadIndex = ithread; -} - -int* ConversionThread::getProgress() -{ - return &myProgress; -} - -void ConversionThread::run() -{ - if (/*!myConverter && */!myFastConverter) - return; - //if (myConverter) - // myConverter->Convert(myProgress, myVolumicValue, myScanSide, myThreadIndex); - //else - myFastConverter->Convert(myProgress, myThreadIndex); -} diff --git a/samples/qt/VoxelDemo/src/Main.cpp b/samples/qt/VoxelDemo/src/Main.cpp deleted file mode 100644 index e5f91752ea..0000000000 --- a/samples/qt/VoxelDemo/src/Main.cpp +++ /dev/null @@ -1,13 +0,0 @@ -#include -#include "application.h" - -int main( int argc, char ** argv ) -{ - QApplication a( argc, argv ); - - Application *w = new Application(); - w->setWindowTitle( "Voxel demo-application" ); - w->show(); - a.connect( &a, SIGNAL(lastWindowClosed()), &a, SLOT(quit()) ); - return a.exec(); -} diff --git a/samples/qt/VoxelDemo/src/Timer.cpp b/samples/qt/VoxelDemo/src/Timer.cpp deleted file mode 100644 index 783b004290..0000000000 --- a/samples/qt/VoxelDemo/src/Timer.cpp +++ /dev/null @@ -1,67 +0,0 @@ -// Timer.cpp: implementation of the Timer class. -// -////////////////////////////////////////////////////////////////////// - -#include "Timer.h" - -////////////////////////////////////////////////////////////////////// -// Construction/Destruction -////////////////////////////////////////////////////////////////////// - -Timer::Timer():myWriter(0) {} - -Timer::Timer(const char* filename) -{ - fopen_s(&myWriter,filename, "a"); -} - -Timer::~Timer() -{ - if (myWriter) - fclose(myWriter); -} - -void Timer::Start() { - myTimer.Reset(); - myTimer.Start(); -} - -void Timer::Stop() { - myTimer.Stop(); -} - -void Timer::Continue() { - myTimer.Start(); -} - -void Timer::Reset() { - myTimer.Reset(); -} - -float Timer::Seconds() { - Standard_Real sec, cpu; - Standard_Integer minutes, hours; - myTimer.Show(sec, minutes, hours, cpu); - return (float) sec; -} - -int Timer::Minutes() { - Standard_Real sec, cpu; - Standard_Integer minutes, hours; - myTimer.Show(sec, minutes, hours, cpu); - return minutes; -} - -void Timer::Print(char* label) { - Standard_Real seconds, cpu; - Standard_Integer minutes, hours; - myTimer.Show(seconds, minutes, hours, cpu); - if (myWriter) - { - fprintf(myWriter, "%s took %d minutes %g seconds\n", label, minutes, seconds); - } - else - { - cout< -#include -#include - -#include - -#include - -#include -#include -#include -#include -#include -#include -#include - -static Handle(Graphic3d_GraphicDriver) Viewer_aGraphicDriver; - -Viewer::Viewer(QWidget* parent):QWidget(parent) -{ - if (myGraphicDriver.IsNull()) - { - if (Viewer_aGraphicDriver.IsNull()) - { - Handle(Aspect_DisplayConnection) aDisplayConnection; - Viewer_aGraphicDriver = new OpenGl_GraphicDriver (aDisplayConnection); - } - myGraphicDriver = Handle(OpenGl_GraphicDriver)::DownCast(Viewer_aGraphicDriver); - } - - Handle(V3d_Viewer) aViewer = new V3d_Viewer(myGraphicDriver, TCollection_ExtendedString("Visu3D").ToExtString(), "", - 1000, V3d_XposYnegZpos, - Quantity_NOC_GRAY30, V3d_ZBUFFER, V3d_GOURAUD, V3d_WAIT, - true, true, V3d_TEX_NONE); - - aViewer->SetDefaultBackgroundColor(Quantity_NOC_BLACK); - - myView = aViewer->CreateView(); - myIC = new AIS_InteractiveContext(aViewer); - myIC->SetDeviationCoefficient(1.e-3); - - Aspect_Handle aWindowHandle = (Aspect_Handle )winId(); - Handle(WNT_Window) hWnd = new WNT_Window (aWindowHandle); - - myView->SetWindow(hWnd); - if(!hWnd->IsMapped()) - hWnd->Map(); - - myView->MustBeResized(); - myView->SetSurfaceDetail(V3d_TEX_NONE); - myView->SetSize(10000.0); - myView->SetZSize(10000.0); - myView->SetViewMappingDefault(); - - myZoom = false; - myPan = false; - myRotate = false; - setMouseTracking(true); - - setMinimumSize(400, 200); - - myView->ZBufferTriedronSetup(); - myView->TriedronDisplay(Aspect_TOTP_LEFT_LOWER, Quantity_NOC_BLACK, 0.1, V3d_ZBUFFER); - - mySelector.Init(myView); - - setBackgroundRole( QPalette::NoRole );//NoBackground ); - // set focus policy to threat QContextMenuEvent from keyboard - setFocusPolicy( Qt::StrongFocus ); - setAttribute( Qt::WA_PaintOnScreen ); - setAttribute( Qt::WA_NoSystemBackground ); - - aViewer->SetLightOn(new V3d_DirectionalLight(aViewer, V3d_XnegYnegZneg, Quantity_NOC_WHITE, Standard_True)); - aViewer->SetLightOn(new V3d_AmbientLight(aViewer, Quantity_NOC_WHITE)); -} - -Viewer::~Viewer() -{ - -} - -void Viewer::paintEvent(QPaintEvent * pEvent) -{ - if (!myView.IsNull()) - myView->Redraw(); -} - - -/*! - Get paint engine for the OpenGL viewer. [ virtual public ] -*/ -QPaintEngine* Viewer::paintEngine() const -{ - return 0; -} - -void Viewer::resizeEvent(QResizeEvent * e) -{ - if (!myView.IsNull()) - { - myView->MustBeResized(); - } -} - -void Viewer::mousePressEvent(QMouseEvent * mpEvent) -{ - // Memorize start point - myStartPnt.setX(mpEvent->x()); - myStartPnt.setY(mpEvent->y()); - - // Inform IC that the mouse cursor is at the point - myIC->MoveTo(myStartPnt.x(), myStartPnt.y(), myView); - - // In case of rotation, define the start rotation point - if ((mpEvent->modifiers() & Qt::ControlModifier) && (mpEvent->buttons() & Qt::RightButton)) - { - myView->StartRotation(myStartPnt.x(), myStartPnt.y()); - } - - // Start degenerate mode - setDegenerateMode(true); - - emit mousePressed(mpEvent->modifiers(), mpEvent->x(), mpEvent->y()); -} - -void Viewer::mouseMoveEvent(QMouseEvent * mmEvent) -{ - QPoint currentPnt(mmEvent->x(), mmEvent->y()); - - if (mmEvent->modifiers() & Qt::ControlModifier) - { - if (mmEvent->buttons() & Qt::LeftButton) - { - myView->Zoom(myStartPnt.x(), myStartPnt.y(), currentPnt.x(), currentPnt.y()); - myStartPnt = currentPnt; - } - else if (mmEvent->buttons() & Qt::MidButton) - { - myView->Pan(currentPnt.x() - myStartPnt.x(), myStartPnt.y() - currentPnt.y()); - myStartPnt = currentPnt; - } - else if (mmEvent->buttons() & Qt::RightButton) - { - myView->Rotation(currentPnt.x(), currentPnt.y()); - } - } - else - { - myIC->MoveTo(currentPnt.x(), currentPnt.y(), myView); - } - - emit mouseMoved(mmEvent->modifiers(), currentPnt.x(), currentPnt.y()); -} - -void Viewer::mouseReleaseEvent(QMouseEvent * mrEvent) -{ - if(mrEvent->button() == Qt::LeftButton) - { - if(!myZoom && !myPan && !myRotate) - { - if(mrEvent->modifiers() & Qt::ShiftModifier) - myIC->ShiftSelect(); - else - myIC->Select(); - - // Select a voxel - int ix = -1, iy = -1, iz = -1; - bool detected = mySelector.Detect(mrEvent->x(), mrEvent->y(), ix, iy, iz); - if (detected) - { - cout<<"("<Highlight(ix, iy, iz); - } - } - else if(mrEvent->button() == Qt::RightButton) - { - // Popup menu: - - - } - - // Finish degenerate mode - setDegenerateMode(false); - - emit mouseReleased(mrEvent->modifiers(), mrEvent->x(), mrEvent->y()); -} - -void Viewer::mouseDoubleClickEvent(QMouseEvent * mdcEvent) -{ - emit mouseDoubleClick(mdcEvent->modifiers(), mdcEvent->x(), mdcEvent->y()); -} - -void Viewer::setDegenerateMode(const bool on) -{ - AIS_ListOfInteractive displayed; - myIC->DisplayedObjects(displayed); - AIS_ListIteratorOfListOfInteractive itri(displayed); - for (; itri.More(); itri.Next()) - { - Handle(Voxel_Prs) prs = Handle(Voxel_Prs)::DownCast(itri.Value()); - if (!prs.IsNull()) - { - prs->SetDegenerateMode(on); - myView->Redraw(); - break; - } - } -} diff --git a/samples/qt/VoxelDemo/src/VoxelClient_VisDrawer.cxx b/samples/qt/VoxelDemo/src/VoxelClient_VisDrawer.cxx deleted file mode 100644 index f23e525e0d..0000000000 --- a/samples/qt/VoxelDemo/src/VoxelClient_VisDrawer.cxx +++ /dev/null @@ -1,3374 +0,0 @@ -// Copyright (c) 1999-2014 OPEN CASCADE SAS -// -// This file is part of Open CASCADE Technology software library. -// -// This library is free software; you can redistribute it and / or modify it -// under the terms of the GNU Lesser General Public version 2.1 as published -// by the Free Software Foundation, with special exception defined in the file -// OCCT_LGPL_EXCEPTION.txt. Consult the file LICENSE_LGPL_21.txt included in OCCT -// distribution for complete text of the license and disclaimer of any warranty. -// -// Alternatively, this file may be used under the terms of Open CASCADE -// commercial license or contractual agreement. - -// required for correct APIENTRY definition -#if defined(_WIN32) && !defined(APIENTRY) && !defined(__CYGWIN__) && !defined(__SCITECH_SNAP__) - #define WIN32_LEAN_AND_MEAN - #include -#endif - -#if defined(__APPLE__) - #include -#else - #include -#endif - -#include "VoxelClient_VisDrawer.h" - -#include - -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include -#include - -/**************************************************************************/ - -class VoxelClient_VisDrawer::VisElement : public OpenGl_Element -{ -public: - - VisElement (Voxel_VisData*); - virtual ~VisElement(); - - void EvaluateBounds (Graphic3d_BndBox4f& theMinMax); - - void Render (const Handle(OpenGl_Workspace) &theWorkspace) const; - - virtual void Release (OpenGl_Context* theContext) - { - // - } - -private: - - VoxelClient_VisDrawer* myHandler; - -public: - - DEFINE_STANDARD_ALLOC - -}; - -//======================================================================= -//function : VisElement -//purpose : Constructor -//======================================================================= - -VoxelClient_VisDrawer::VisElement::VisElement (Voxel_VisData* theData) -{ - myHandler = new VoxelClient_VisDrawer (theData); -} - -//======================================================================= -//function : ~VisElement -//purpose : Destructor -//======================================================================= - -VoxelClient_VisDrawer::VisElement::~VisElement () -{ - delete myHandler; -} - -//======================================================================= -//function : EvaluateBounds -//purpose : -//======================================================================= - -void VoxelClient_VisDrawer::VisElement::EvaluateBounds - (Graphic3d_BndBox4f& theMinMax) -{ - myHandler->EvalMinMax (theMinMax); -} - -//======================================================================= -//function : Render -//purpose : display element -//======================================================================= - -void VoxelClient_VisDrawer::VisElement::Render - (const Handle (OpenGl_Workspace) &theWorkspace) const -{ - const Standard_Boolean aHl = (theWorkspace->NamedStatus & OPENGL_NS_HIGHLIGHT); - myHandler->Display (aHl); -} - -//======================================================================= -//function : VisDrawerCallBack -//purpose : visdrawer element create callback, adds an element to graphic -// driver's structure -//======================================================================= -void VoxelClient_PrsGl::Compute (const Handle(PrsMgr_PresentationManager3d)& thePrsMgr, - const Handle(Prs3d_Presentation)& thePrs, - const Standard_Integer theMode) -{ - if (myVisData == NULL) - { - return; - } - Voxel_Prs::Compute (thePrsMgr, thePrs, theMode); - - Handle(OpenGl_Group) aGroup = Handle(OpenGl_Group)::DownCast (Prs3d_Root::CurrentGroup (thePrs)); - VoxelClient_VisDrawer::VisElement* anElem = new VoxelClient_VisDrawer::VisElement ((Voxel_VisData* )myVisData); - aGroup->AddElement (anElem); - - Graphic3d_BndBox4f aMinMax; - anElem->EvaluateBounds (aMinMax); - aGroup->SetMinMaxValues (aMinMax.CornerMin().x(), aMinMax.CornerMin().y(), aMinMax.CornerMin().z(), - aMinMax.CornerMax().x(), aMinMax.CornerMax().y(), aMinMax.CornerMax().z()); - thePrsMgr->StructureManager()->Update (thePrsMgr->StructureManager()->UpdateMode()); -} - -/**************************************************************************/ -VoxelClient_VisDrawer::VoxelClient_VisDrawer(Voxel_VisData * theData):myData(theData) -{ - -} - -/**************************************************************************/ -VoxelClient_VisDrawer::~VoxelClient_VisDrawer() -{ - if (myData) - { - // Because a pointer to the data is copied, - // it is possible to make an attempt to delete GL lists for - // a structure, which is already deleted. - // Such a situation may happen on close of the application. - // Therefore, this try / catch is used. - - try - { - Standard_Integer idir; - - // Points - - // BoolDS - if (myData->myDisplay.myBoolPointsList > 0) - { - glDeleteLists(myData->myDisplay.myBoolPointsList, 1); - myData->myDisplay.myBoolPointsList = -1; - } - for (idir = Xminus; idir <= Zplus; idir++) - { - if (myData->myDisplay.myBoolNearestPointsList[idir] > 0) - { - glDeleteLists(myData->myDisplay.myBoolNearestPointsList[idir], 1); - myData->myDisplay.myBoolNearestPointsList[idir] = -1; - } - } - - // ColorDS - if (myData->myDisplay.myColorPointsList > 0) - { - glDeleteLists(myData->myDisplay.myColorPointsList, 1); - myData->myDisplay.myColorPointsList = -1; - } - for (idir = Xminus; idir <= Zplus; idir++) - { - if (myData->myDisplay.myColorNearestPointsList[idir] > 0) - { - glDeleteLists(myData->myDisplay.myColorNearestPointsList[idir], 1); - myData->myDisplay.myColorNearestPointsList[idir] = -1; - } - } - - // ROctBoolDS - if (myData->myDisplay.myROctBoolPointsList > 0) - { - glDeleteLists(myData->myDisplay.myROctBoolPointsList, 1); - myData->myDisplay.myROctBoolPointsList = -1; - } - for (idir = Xminus; idir <= Zplus; idir++) - { - if (myData->myDisplay.myROctBoolNearestPointsList[idir] > 0) - { - glDeleteLists(myData->myDisplay.myROctBoolNearestPointsList[idir], 1); - myData->myDisplay.myROctBoolNearestPointsList[idir] = -1; - } - } - - // Triangulation - if (myData->myDisplay.myTriangulationList > 0) - { - glDeleteLists(myData->myDisplay.myTriangulationList, 1); - myData->myDisplay.myTriangulationList = -1; - } - } - catch (...) - { - - } - } -} - -/**************************************************************************/ -void VoxelClient_VisDrawer::EvalMinMax(Graphic3d_BndBox4f& theMinMax) const -{ - Graphic3d_Vec4 aMinPt (FLT_MAX, FLT_MAX, FLT_MAX, 1.0f); - Graphic3d_Vec4 aMaxPt (-FLT_MAX, -FLT_MAX, -FLT_MAX, 1.0f); - - if(!myData) - return; - if(myData->myBoolVoxels) - { - Graphic3d_Vec4 aBoolVoxelsMin (RealToShortReal (myData->myBoolVoxels->GetX()), - RealToShortReal (myData->myBoolVoxels->GetY()), - RealToShortReal (myData->myBoolVoxels->GetZ()), - 1.0f); - Graphic3d_Vec4 aBoolVoxelsMax ( - RealToShortReal (myData->myBoolVoxels->GetX() + myData->myBoolVoxels->GetXLen()), - RealToShortReal (myData->myBoolVoxels->GetY() + myData->myBoolVoxels->GetYLen()), - RealToShortReal (myData->myBoolVoxels->GetZ() + myData->myBoolVoxels->GetZLen()), - 1.0f); - - aMinPt = aMinPt.cwiseMin (aBoolVoxelsMin); - aMaxPt = aMaxPt.cwiseMax (aBoolVoxelsMax); - } - if(myData->myColorVoxels) - { - Graphic3d_Vec4 aColorVoxelsMin (RealToShortReal (myData->myColorVoxels->GetX()), - RealToShortReal (myData->myColorVoxels->GetY()), - RealToShortReal (myData->myColorVoxels->GetZ()), - 1.0f); - Graphic3d_Vec4 aColorVoxelsMax ( - RealToShortReal (myData->myColorVoxels->GetX() + myData->myColorVoxels->GetXLen()), - RealToShortReal (myData->myColorVoxels->GetY() + myData->myColorVoxels->GetYLen()), - RealToShortReal (myData->myColorVoxels->GetZ() + myData->myColorVoxels->GetZLen()), - 1.0f); - - aMinPt = aMinPt.cwiseMin (aColorVoxelsMin); - aMaxPt = aMaxPt.cwiseMax (aColorVoxelsMax); - } - if(myData->myROctBoolVoxels) - { - Graphic3d_Vec4 aROctBoolVoxelsMin (RealToShortReal (myData->myROctBoolVoxels->GetX()), - RealToShortReal (myData->myROctBoolVoxels->GetY()), - RealToShortReal (myData->myROctBoolVoxels->GetZ()), - 1.0f); - Graphic3d_Vec4 aROctBoolVoxelsMax ( - RealToShortReal (myData->myROctBoolVoxels->GetX() + myData->myROctBoolVoxels->GetXLen()), - RealToShortReal (myData->myROctBoolVoxels->GetY() + myData->myROctBoolVoxels->GetYLen()), - RealToShortReal (myData->myROctBoolVoxels->GetZ() + myData->myROctBoolVoxels->GetZLen()), - 1.0f); - - aMinPt = aMinPt.cwiseMin (aROctBoolVoxelsMin); - aMaxPt = aMaxPt.cwiseMax (aROctBoolVoxelsMax); - } - if (!myData->myTriangulation.IsNull()) - { - Standard_Real x, y, z; - const TColgp_Array1OfPnt& nodes = myData->myTriangulation->Nodes(); - Standard_Integer inode = nodes.Lower(), nb_nodes = nodes.Upper(); - for (; inode <= nb_nodes; inode++) - { - nodes.Value(inode).Coord(x, y, z); - Graphic3d_Vec4 aNodeCoord (RealToShortReal (x), - RealToShortReal (y), - RealToShortReal (z), - 1.0f); - aMinPt = aMinPt.cwiseMin (aNodeCoord); - aMaxPt = aMaxPt.cwiseMax (aNodeCoord); - } - } - - if (theMinMax.IsValid()) - { - theMinMax.CornerMin() = aMinPt; - theMinMax.CornerMax() = aMaxPt; - } - else - { - theMinMax.Add (aMinPt); - theMinMax.Add (aMaxPt); - } -} - -/**************************************************************************/ -void VoxelClient_VisDrawer::Display(const Standard_Boolean theHighlight) -{ - if (!myData) - return; - if (myData->myBoolVoxels) - DisplayVoxels(theHighlight); - if (myData->myColorVoxels) - DisplayVoxels(theHighlight); - if (myData->myROctBoolVoxels) - DisplayVoxels(theHighlight); - if (!myData->myTriangulation.IsNull()) - DisplayTriangulation(theHighlight); -} - -// Some static method to define Open GL visual attributes -// COlor -static void setColor(const Quantity_Color& color, const Standard_Boolean highlight) -{ - static Quantity_Color highlight_color(Quantity_NOC_BLUE1); - if(highlight) - glColor3f((GLfloat)highlight_color.Red(), (GLfloat)highlight_color.Green(), (GLfloat)highlight_color.Blue()); - else - glColor3d(color.Red(), color.Green(), color.Blue()); -} - -// Type of Line -static void setTypeOfLine(const Aspect_TypeOfLine type) -{ - if(type == Aspect_TOL_SOLID) - { - glDisable(GL_LINE_STIPPLE); - } - else - { - glEnable(GL_LINE_STIPPLE); - if(type == Aspect_TOL_DOT) - glLineStipple(1, 0xCCCC); - else if(type == Aspect_TOL_DASH) - glLineStipple(1, 0xFFC0); - else if(type == Aspect_TOL_DOTDASH) - glLineStipple(1, 0xFF18); - } -} - -// Width of Line -static void setWidthOfLine(const Standard_Integer width) -{ - glLineWidth((Standard_ShortReal) width); -} - -// Normal of the view -static void getNormal(gp_Dir& normal) -{ - Standard_Real x, y, z; - GLint viewport[4]; - GLdouble model_matrix[16], proj_matrix[16]; - - glGetDoublev(GL_MODELVIEW_MATRIX, model_matrix); - glGetDoublev(GL_PROJECTION_MATRIX, proj_matrix); - glGetIntegerv(GL_VIEWPORT, viewport); - - gluUnProject(viewport[0], viewport[1], 0., model_matrix, proj_matrix, viewport, &x, &y, &z); - gp_Pnt p1(x, y, z); - gluUnProject(viewport[0] + viewport[2], viewport[1], 0., model_matrix, proj_matrix, viewport, &x, &y, &z); - gp_Pnt p2(x, y, z); - gluUnProject(viewport[0], viewport[1] + viewport[3], 0., model_matrix, proj_matrix, viewport, &x, &y, &z); - gp_Pnt p3(x, y, z); - - gce_MakePln mkNormal(p1, p2, p3); - if (mkNormal.IsDone()) - { - const gp_Pln& normal_plane = mkNormal.Value(); - normal = normal_plane.Axis().Direction(); - } - else - { - normal = gp::DZ(); - } -} - -// Normal 2 VoxelDirection converter -static VoxelDirection getVoxelDirection(const gp_Dir& viewnormal) -{ - VoxelDirection vdir; - Standard_Real fabsviewnormalx = fabs(viewnormal.X()); - Standard_Real fabsviewnormaly = fabs(viewnormal.Y()); - Standard_Real fabsviewnormalz = fabs(viewnormal.Z()); - if (fabsviewnormalx >= fabsviewnormaly && - fabsviewnormalx >= fabsviewnormalz) - { - if (viewnormal.X() > 0) - vdir = Xminus; - else - vdir = Xplus; - } - else if (fabsviewnormaly >= fabsviewnormalx && - fabsviewnormaly >= fabsviewnormalz) - { - if (viewnormal.Y() > 0) - vdir = Yminus; - else - vdir = Yplus; - } - else if (fabsviewnormalz >= fabsviewnormalx && - fabsviewnormalz >= fabsviewnormaly) - { - if (viewnormal.Z() > 0) - vdir = Zminus; - else - vdir = Zplus; - } - return vdir; -} - -// Normal 2 VoxelDirection 3 converter -static void getVoxel3Directions(const gp_Dir& viewnormal, - VoxelDirection& vdir1, - VoxelDirection& vdir2, - VoxelDirection& vdir3) -{ - Standard_Boolean vdir1_set = Standard_False, vdir2_set = Standard_False, vdir3_set = Standard_False; - - // Test X minus - Standard_Real dot = viewnormal.Dot(-gp::DX()); - if (dot >= 0.0) - { - if (!vdir1_set) - { - vdir1 = Xminus; - vdir1_set = Standard_True; - } - else if (!vdir2_set) - { - vdir2 = Xminus; - vdir2_set = Standard_True; - } - else if (!vdir3_set) - { - vdir3 = Xminus; - vdir3_set = Standard_True; - } - } - - // Test X plus - dot = viewnormal.Dot(gp::DX()); - if (dot >= 0.0) - { - if (!vdir1_set) - { - vdir1 = Xplus; - vdir1_set = Standard_True; - } - else if (!vdir2_set) - { - vdir2 = Xplus; - vdir2_set = Standard_True; - } - else if (!vdir3_set) - { - vdir3 = Xplus; - vdir3_set = Standard_True; - } - } - - // Test Y minus - dot = viewnormal.Dot(-gp::DY()); - if (dot >= 0.0) - { - if (!vdir1_set) - { - vdir1 = Yminus; - vdir1_set = Standard_True; - } - else if (!vdir2_set) - { - vdir2 = Yminus; - vdir2_set = Standard_True; - } - else if (!vdir3_set) - { - vdir3 = Yminus; - vdir3_set = Standard_True; - } - } - - // Test Y plus - dot = viewnormal.Dot(gp::DY()); - if (dot >= 0.0) - { - if (!vdir1_set) - { - vdir1 = Yplus; - vdir1_set = Standard_True; - } - else if (!vdir2_set) - { - vdir2 = Yplus; - vdir2_set = Standard_True; - } - else if (!vdir3_set) - { - vdir3 = Yplus; - vdir3_set = Standard_True; - } - } - - // Test Z minus - dot = viewnormal.Dot(-gp::DZ()); - if (dot >= 0.0) - { - if (!vdir1_set) - { - vdir1 = Zminus; - vdir1_set = Standard_True; - } - else if (!vdir2_set) - { - vdir2 = Zminus; - vdir2_set = Standard_True; - } - else if (!vdir3_set) - { - vdir3 = Zminus; - vdir3_set = Standard_True; - } - } - - // Test Y plus - dot = viewnormal.Dot(gp::DZ()); - if (dot >= 0.0) - { - if (!vdir1_set) - { - vdir1 = Zplus; - vdir1_set = Standard_True; - } - else if (!vdir2_set) - { - vdir2 = Zplus; - vdir2_set = Standard_True; - } - else if (!vdir3_set) - { - vdir3 = Zplus; - vdir3_set = Standard_True; - } - } -} - -static Standard_Boolean CheckSize(Voxel_DS* voxels, - const Standard_Integer ix, const Standard_Integer iy, const Standard_Integer iz, - const Standard_Real xmin, const Standard_Real xmax, - const Standard_Real ymin, const Standard_Real ymax, - const Standard_Real zmin, const Standard_Real zmax, - Standard_Real& xc, Standard_Real& yc, Standard_Real& zc) -{ - voxels->GetCenter(ix, iy, iz, xc, yc, zc); - if (xc < xmin || xc > xmax) - return Standard_False; - if (yc < ymin || yc > ymax) - return Standard_False; - if (zc < zmin || zc > zmax) - return Standard_False; - return Standard_True; -} - -static Standard_Boolean CheckSize(Voxel_ROctBoolDS* voxels, - const Standard_Integer ix, const Standard_Integer iy, const Standard_Integer iz, - const Standard_Integer i, const Standard_Integer j, - const Standard_Real xmin, const Standard_Real xmax, - const Standard_Real ymin, const Standard_Real ymax, - const Standard_Real zmin, const Standard_Real zmax, - Standard_Real& xc, Standard_Real& yc, Standard_Real& zc) -{ - if (j == -1) - voxels->GetCenter(ix, iy, iz, i, xc, yc, zc); - else - voxels->GetCenter(ix, iy, iz, i, j, xc, yc, zc); - if (xc < xmin || xc > xmax) - return Standard_False; - if (yc < ymin || yc > ymax) - return Standard_False; - if (zc < zmin || zc > zmax) - return Standard_False; - return Standard_True; -} - -static void drawBoolPoints(const VoxelDirection vdir, const Standard_Boolean nearest, - Voxel_BoolDS* voxels, - const Standard_Real xmin, const Standard_Real xmax, - const Standard_Real ymin, const Standard_Real ymax, - const Standard_Real zmin, const Standard_Real zmax) -{ - Standard_Real xc, yc, zc; - Standard_Integer ix = 0, nbx = voxels->GetNbX(); - Standard_Integer iy = 0, nby = voxels->GetNbY(); - Standard_Integer iz = 0, nbz = voxels->GetNbZ(); - - Standard_Boolean check_size = (xmin <= DBL_MAX && xmax >= DBL_MAX && - ymin <= DBL_MAX && ymax >= DBL_MAX && - zmin <= DBL_MAX && zmax >= DBL_MAX); - check_size = !check_size; - - glBegin(GL_POINTS); - switch (vdir) - { - case Xminus: - { - for (iy = 0; iy < nby; iy++) - { - for (iz = 0; iz < nbz; iz++) - { - for (ix = 0; ix < nbx; ix++) - { - if (check_size && !CheckSize(voxels, ix, iy, iz, xmin, xmax, ymin, ymax, zmin, zmax, xc, yc, zc)) - continue; - Standard_Boolean value = voxels->Get(ix, iy, iz) == Standard_True; - if (value) - { - if (!check_size) - voxels->GetCenter(ix, iy, iz, xc, yc, zc); - glVertex3d(xc, yc, zc); - if (nearest) - break; - } - } - } - } - break; - } - case Xplus: - { - for (iy = 0; iy < nby; iy++) - { - for (iz = 0; iz < nbz; iz++) - { - for (ix = nbx - 1; ix >= 0; ix--) - { - if (check_size && !CheckSize(voxels, ix, iy, iz, xmin, xmax, ymin, ymax, zmin, zmax, xc, yc, zc)) - continue; - Standard_Boolean value = voxels->Get(ix, iy, iz) == Standard_True; - if (value) - { - if (!check_size) - voxels->GetCenter(ix, iy, iz, xc, yc, zc); - glVertex3d(xc, yc, zc); - if (nearest) - break; - } - } - } - } - break; - } - case Yminus: - { - for (ix = 0; ix < nbx; ix++) - { - for (iz = 0; iz < nbz; iz++) - { - for (iy = 0; iy < nby; iy++) - { - if (check_size && !CheckSize(voxels, ix, iy, iz, xmin, xmax, ymin, ymax, zmin, zmax, xc, yc, zc)) - continue; - Standard_Boolean value = voxels->Get(ix, iy, iz) == Standard_True; - if (value) - { - if (!check_size) - voxels->GetCenter(ix, iy, iz, xc, yc, zc); - glVertex3d(xc, yc, zc); - if (nearest) - break; - } - } - } - } - break; - } - case Yplus: - { - for (ix = 0; ix < nbx; ix++) - { - for (iz = 0; iz < nbz; iz++) - { - for (iy = nby - 1; iy >= 0; iy--) - { - if (check_size && !CheckSize(voxels, ix, iy, iz, xmin, xmax, ymin, ymax, zmin, zmax, xc, yc, zc)) - continue; - Standard_Boolean value = voxels->Get(ix, iy, iz) == Standard_True; - if (value) - { - if (!check_size) - voxels->GetCenter(ix, iy, iz, xc, yc, zc); - glVertex3d(xc, yc, zc); - if (nearest) - break; - } - } - } - } - break; - } - case Zminus: - { - for (ix = 0; ix < nbx; ix++) - { - for (iy = 0; iy < nby; iy++) - { - for (iz = 0; iz < nbz; iz++) - { - if (check_size && !CheckSize(voxels, ix, iy, iz, xmin, xmax, ymin, ymax, zmin, zmax, xc, yc, zc)) - continue; - Standard_Boolean value = voxels->Get(ix, iy, iz) == Standard_True; - if (value) - { - if (!check_size) - voxels->GetCenter(ix, iy, iz, xc, yc, zc); - glVertex3d(xc, yc, zc); - if (nearest) - break; - } - } - } - } - break; - } - case Zplus: - { - for (ix = 0; ix < nbx; ix++) - { - for (iy = 0; iy < nby; iy++) - { - for (iz = nbz - 1; iz >= 0; iz--) - { - if (check_size && !CheckSize(voxels, ix, iy, iz, xmin, xmax, ymin, ymax, zmin, zmax, xc, yc, zc)) - continue; - Standard_Boolean value = voxels->Get(ix, iy, iz) == Standard_True; - if (value) - { - if (!check_size) - voxels->GetCenter(ix, iy, iz, xc, yc, zc); - glVertex3d(xc, yc, zc); - if (nearest) - break; - } - } - } - } - break; - } - } - glEnd(); -} - -static void drawROctBoolPoints(const VoxelDirection vdir, const Standard_Boolean nearest, - Voxel_ROctBoolDS* voxels, - const Standard_Real xmin, const Standard_Real xmax, - const Standard_Real ymin, const Standard_Real ymax, - const Standard_Real zmin, const Standard_Real zmax) -{ - Standard_Real xc, yc, zc; - Standard_Integer ix = 0, nbx = voxels->GetNbX(); - Standard_Integer iy = 0, nby = voxels->GetNbY(); - Standard_Integer iz = 0, nbz = voxels->GetNbZ(); - Standard_Integer i, j; - - Standard_Boolean check_size = (xmin <= DBL_MAX && xmax >= DBL_MAX && - ymin <= DBL_MAX && ymax >= DBL_MAX && - zmin <= DBL_MAX && zmax >= DBL_MAX); - check_size = !check_size; - - glBegin(GL_POINTS); - switch (vdir) - { - case Xminus: - { - for (iy = 0; iy < nby; iy++) - { - for (iz = 0; iz < nbz; iz++) - { - for (ix = 0; ix < nbx; ix++) - { - switch (voxels->Deepness(ix, iy, iz)) - { - case 0: - { - if (check_size && !CheckSize(voxels, ix, iy, iz, xmin, xmax, ymin, ymax, zmin, zmax, xc, yc, zc)) - continue; - Standard_Boolean value = voxels->Get(ix, iy, iz) == Standard_True; - if (value) - { - if (!check_size) - ((Voxel_DS*)voxels)->GetCenter(ix, iy, iz, xc, yc, zc); - glVertex3d(xc, yc, zc); - if (nearest) - break; - } - break; - } - case 1: - { - for (i = 0; i < 8; i++) - { - if (check_size && !CheckSize(voxels, ix, iy, iz, i, -1, xmin, xmax, ymin, ymax, zmin, zmax, xc, yc, zc)) - continue; - Standard_Boolean value = voxels->Get(ix, iy, iz, i) == Standard_True; - if (value) - { - if (!check_size) - voxels->GetCenter(ix, iy, iz, i, xc, yc, zc); - glVertex3d(xc, yc, zc); - if (nearest) - break; - } - } - break; - } - case 2: - { - for (i = 0; i < 8; i++) - { - for (j = 0; j < 8; j++) - { - if (check_size && !CheckSize(voxels, ix, iy, iz, i, j, xmin, xmax, ymin, ymax, zmin, zmax, xc, yc, zc)) - continue; - Standard_Boolean value = voxels->Get(ix, iy, iz, i, j) == Standard_True; - if (value) - { - if (!check_size) - voxels->GetCenter(ix, iy, iz, i, j, xc, yc, zc); - glVertex3d(xc, yc, zc); - if (nearest) - break; - } - } - } - break; - } - } - } - } - } - break; - } - case Xplus: - { - for (iy = 0; iy < nby; iy++) - { - for (iz = 0; iz < nbz; iz++) - { - for (ix = nbx - 1; ix >= 0; ix--) - { - switch (voxels->Deepness(ix, iy, iz)) - { - case 0: - { - if (check_size && !CheckSize(voxels, ix, iy, iz, xmin, xmax, ymin, ymax, zmin, zmax, xc, yc, zc)) - continue; - Standard_Boolean value = voxels->Get(ix, iy, iz) == Standard_True; - if (value) - { - if (!check_size) - ((Voxel_DS*)voxels)->GetCenter(ix, iy, iz, xc, yc, zc); - glVertex3d(xc, yc, zc); - if (nearest) - break; - } - break; - } - case 1: - { - for (i = 0; i < 8; i++) - { - if (check_size && !CheckSize(voxels, ix, iy, iz, i, -1, xmin, xmax, ymin, ymax, zmin, zmax, xc, yc, zc)) - continue; - Standard_Boolean value = voxels->Get(ix, iy, iz, i) == Standard_True; - if (value) - { - if (!check_size) - voxels->GetCenter(ix, iy, iz, i, xc, yc, zc); - glVertex3d(xc, yc, zc); - if (nearest) - break; - } - } - break; - } - case 2: - { - for (i = 0; i < 8; i++) - { - for (j = 0; j < 8; j++) - { - if (check_size && !CheckSize(voxels, ix, iy, iz, i, j, xmin, xmax, ymin, ymax, zmin, zmax, xc, yc, zc)) - continue; - Standard_Boolean value = voxels->Get(ix, iy, iz, i, j) == Standard_True; - if (value) - { - if (!check_size) - voxels->GetCenter(ix, iy, iz, i, j, xc, yc, zc); - glVertex3d(xc, yc, zc); - if (nearest) - break; - } - } - } - break; - } - } - } - } - } - break; - } - case Yminus: - { - for (ix = 0; ix < nbx; ix++) - { - for (iz = 0; iz < nbz; iz++) - { - for (iy = 0; iy < nby; iy++) - { - switch (voxels->Deepness(ix, iy, iz)) - { - case 0: - { - if (check_size && !CheckSize(voxels, ix, iy, iz, xmin, xmax, ymin, ymax, zmin, zmax, xc, yc, zc)) - continue; - Standard_Boolean value = voxels->Get(ix, iy, iz) == Standard_True; - if (value) - { - if (!check_size) - ((Voxel_DS*)voxels)->GetCenter(ix, iy, iz, xc, yc, zc); - glVertex3d(xc, yc, zc); - if (nearest) - break; - } - break; - } - case 1: - { - for (i = 0; i < 8; i++) - { - if (check_size && !CheckSize(voxels, ix, iy, iz, i, -1, xmin, xmax, ymin, ymax, zmin, zmax, xc, yc, zc)) - continue; - Standard_Boolean value = voxels->Get(ix, iy, iz, i) == Standard_True; - if (value) - { - if (!check_size) - voxels->GetCenter(ix, iy, iz, i, xc, yc, zc); - glVertex3d(xc, yc, zc); - if (nearest) - break; - } - } - break; - } - case 2: - { - for (i = 0; i < 8; i++) - { - for (j = 0; j < 8; j++) - { - if (check_size && !CheckSize(voxels, ix, iy, iz, i, j, xmin, xmax, ymin, ymax, zmin, zmax, xc, yc, zc)) - continue; - Standard_Boolean value = voxels->Get(ix, iy, iz, i, j) == Standard_True; - if (value) - { - if (!check_size) - voxels->GetCenter(ix, iy, iz, i, j, xc, yc, zc); - glVertex3d(xc, yc, zc); - if (nearest) - break; - } - } - } - break; - } - } - } - } - } - break; - } - case Yplus: - { - for (ix = 0; ix < nbx; ix++) - { - for (iz = 0; iz < nbz; iz++) - { - for (iy = nby - 1; iy >= 0; iy--) - { - switch (voxels->Deepness(ix, iy, iz)) - { - case 0: - { - if (check_size && !CheckSize(voxels, ix, iy, iz, xmin, xmax, ymin, ymax, zmin, zmax, xc, yc, zc)) - continue; - Standard_Boolean value = voxels->Get(ix, iy, iz) == Standard_True; - if (value) - { - if (!check_size) - ((Voxel_DS*)voxels)->GetCenter(ix, iy, iz, xc, yc, zc); - glVertex3d(xc, yc, zc); - if (nearest) - break; - } - break; - } - case 1: - { - for (i = 0; i < 8; i++) - { - if (check_size && !CheckSize(voxels, ix, iy, iz, i, -1, xmin, xmax, ymin, ymax, zmin, zmax, xc, yc, zc)) - continue; - Standard_Boolean value = voxels->Get(ix, iy, iz, i) == Standard_True; - if (value) - { - if (!check_size) - voxels->GetCenter(ix, iy, iz, i, xc, yc, zc); - glVertex3d(xc, yc, zc); - if (nearest) - break; - } - } - break; - } - case 2: - { - for (i = 0; i < 8; i++) - { - for (j = 0; j < 8; j++) - { - if (check_size && !CheckSize(voxels, ix, iy, iz, i, j, xmin, xmax, ymin, ymax, zmin, zmax, xc, yc, zc)) - continue; - Standard_Boolean value = voxels->Get(ix, iy, iz, i, j) == Standard_True; - if (value) - { - if (!check_size) - voxels->GetCenter(ix, iy, iz, i, j, xc, yc, zc); - glVertex3d(xc, yc, zc); - if (nearest) - break; - } - } - } - break; - } - } - } - } - } - break; - } - case Zminus: - { - for (ix = 0; ix < nbx; ix++) - { - for (iy = 0; iy < nby; iy++) - { - for (iz = 0; iz < nbz; iz++) - { - switch (voxels->Deepness(ix, iy, iz)) - { - case 0: - { - if (check_size && !CheckSize(voxels, ix, iy, iz, xmin, xmax, ymin, ymax, zmin, zmax, xc, yc, zc)) - continue; - Standard_Boolean value = voxels->Get(ix, iy, iz) == Standard_True; - if (value) - { - if (!check_size) - ((Voxel_DS*)voxels)->GetCenter(ix, iy, iz, xc, yc, zc); - glVertex3d(xc, yc, zc); - if (nearest) - break; - } - break; - } - case 1: - { - for (i = 0; i < 8; i++) - { - if (check_size && !CheckSize(voxels, ix, iy, iz, i, -1, xmin, xmax, ymin, ymax, zmin, zmax, xc, yc, zc)) - continue; - Standard_Boolean value = voxels->Get(ix, iy, iz, i) == Standard_True; - if (value) - { - if (!check_size) - voxels->GetCenter(ix, iy, iz, i, xc, yc, zc); - glVertex3d(xc, yc, zc); - if (nearest) - break; - } - } - break; - } - case 2: - { - for (i = 0; i < 8; i++) - { - for (j = 0; j < 8; j++) - { - if (check_size && !CheckSize(voxels, ix, iy, iz, i, j, xmin, xmax, ymin, ymax, zmin, zmax, xc, yc, zc)) - continue; - Standard_Boolean value = voxels->Get(ix, iy, iz, i, j) == Standard_True; - if (value) - { - if (!check_size) - voxels->GetCenter(ix, iy, iz, i, j, xc, yc, zc); - glVertex3d(xc, yc, zc); - if (nearest) - break; - } - } - } - break; - } - } - } - } - } - break; - } - case Zplus: - { - for (ix = 0; ix < nbx; ix++) - { - for (iy = 0; iy < nby; iy++) - { - for (iz = nbz - 1; iz >= 0; iz--) - { - switch (voxels->Deepness(ix, iy, iz)) - { - case 0: - { - if (check_size && !CheckSize(voxels, ix, iy, iz, xmin, xmax, ymin, ymax, zmin, zmax, xc, yc, zc)) - continue; - Standard_Boolean value = voxels->Get(ix, iy, iz) == Standard_True; - if (value) - { - if (!check_size) - ((Voxel_DS*)voxels)->GetCenter(ix, iy, iz, xc, yc, zc); - glVertex3d(xc, yc, zc); - if (nearest) - break; - } - break; - } - case 1: - { - for (i = 0; i < 8; i++) - { - if (check_size && !CheckSize(voxels, ix, iy, iz, i, -1, xmin, xmax, ymin, ymax, zmin, zmax, xc, yc, zc)) - continue; - Standard_Boolean value = voxels->Get(ix, iy, iz, i) == Standard_True; - if (value) - { - if (!check_size) - voxels->GetCenter(ix, iy, iz, i, xc, yc, zc); - glVertex3d(xc, yc, zc); - if (nearest) - break; - } - } - break; - } - case 2: - { - for (i = 0; i < 8; i++) - { - for (j = 0; j < 8; j++) - { - if (check_size && !CheckSize(voxels, ix, iy, iz, i, j, xmin, xmax, ymin, ymax, zmin, zmax, xc, yc, zc)) - continue; - Standard_Boolean value = voxels->Get(ix, iy, iz, i, j) == Standard_True; - if (value) - { - if (!check_size) - voxels->GetCenter(ix, iy, iz, i, j, xc, yc, zc); - glVertex3d(xc, yc, zc); - if (nearest) - break; - } - } - } - break; - } - } - } - } - } - break; - } - } - glEnd(); -} - - - -static void drawColorPoints(const VoxelDirection vdir, const Standard_Boolean nearest, - Voxel_ColorDS* voxels, const Handle(Quantity_HArray1OfColor)& hcolors, - const Standard_Byte minvalue, const Standard_Byte maxvalue, - const Standard_Real xmin, const Standard_Real xmax, - const Standard_Real ymin, const Standard_Real ymax, - const Standard_Real zmin, const Standard_Real zmax) -{ - Standard_Real xc, yc, zc; - Standard_Integer ix = 0, nbx = voxels->GetNbX(); - Standard_Integer iy = 0, nby = voxels->GetNbY(); - Standard_Integer iz = 0, nbz = voxels->GetNbZ(); - Standard_Byte value; - - // Colors - const Quantity_Array1OfColor& colors = hcolors->Array1(); - - Standard_Boolean check_size = (xmin <= DBL_MAX && xmax >= DBL_MAX && - ymin <= DBL_MAX && ymax >= DBL_MAX && - zmin <= DBL_MAX && zmax >= DBL_MAX); - check_size = !check_size; - - glBegin(GL_POINTS); - switch (vdir) - { - case Xminus: - { - for (iy = 0; iy < nby; iy++) - { - for (iz = 0; iz < nbz; iz++) - { - for (ix = 0; ix < nbx; ix++) - { - if (check_size && !CheckSize(voxels, ix, iy, iz, xmin, xmax, ymin, ymax, zmin, zmax, xc, yc, zc)) - continue; - value = voxels->Get(ix, iy, iz); - if (value >= minvalue && value <= maxvalue) - { - if (!check_size) - voxels->GetCenter(ix, iy, iz, xc, yc, zc); - setColor(colors.Value(value), Standard_False); - glVertex3d(xc, yc, zc); - if (nearest) - break; - } - } - } - } - break; - } - case Xplus: - { - for (iy = 0; iy < nby; iy++) - { - for (iz = 0; iz < nbz; iz++) - { - for (ix = nbx - 1; ix >= 0; ix--) - { - if (check_size && !CheckSize(voxels, ix, iy, iz, xmin, xmax, ymin, ymax, zmin, zmax, xc, yc, zc)) - continue; - value = voxels->Get(ix, iy, iz); - if (value >= minvalue && value <= maxvalue) - { - if (!check_size) - voxels->GetCenter(ix, iy, iz, xc, yc, zc); - setColor(colors.Value(value), Standard_False); - glVertex3d(xc, yc, zc); - if (nearest) - break; - } - } - } - } - break; - } - case Yminus: - { - for (ix = 0; ix < nbx; ix++) - { - for (iz = 0; iz < nbz; iz++) - { - for (iy = 0; iy < nby; iy++) - { - if (check_size && !CheckSize(voxels, ix, iy, iz, xmin, xmax, ymin, ymax, zmin, zmax, xc, yc, zc)) - continue; - value = voxels->Get(ix, iy, iz); - if (value >= minvalue && value <= maxvalue) - { - if (!check_size) - voxels->GetCenter(ix, iy, iz, xc, yc, zc); - setColor(colors.Value(value), Standard_False); - glVertex3d(xc, yc, zc); - if (nearest) - break; - } - } - } - } - break; - } - case Yplus: - { - for (ix = 0; ix < nbx; ix++) - { - for (iz = 0; iz < nbz; iz++) - { - for (iy = nby - 1; iy >= 0; iy--) - { - if (check_size && !CheckSize(voxels, ix, iy, iz, xmin, xmax, ymin, ymax, zmin, zmax, xc, yc, zc)) - continue; - value = voxels->Get(ix, iy, iz); - if (value >= minvalue && value <= maxvalue) - { - if (!check_size) - voxels->GetCenter(ix, iy, iz, xc, yc, zc); - setColor(colors.Value(value), Standard_False); - glVertex3d(xc, yc, zc); - if (nearest) - break; - } - } - } - } - break; - } - case Zminus: - { - for (ix = 0; ix < nbx; ix++) - { - for (iy = 0; iy < nby; iy++) - { - for (iz = 0; iz < nbz; iz++) - { - if (check_size && !CheckSize(voxels, ix, iy, iz, xmin, xmax, ymin, ymax, zmin, zmax, xc, yc, zc)) - continue; - value = voxels->Get(ix, iy, iz); - if (value >= minvalue && value <= maxvalue) - { - if (!check_size) - voxels->GetCenter(ix, iy, iz, xc, yc, zc); - setColor(colors.Value(value), Standard_False); - glVertex3d(xc, yc, zc); - if (nearest) - break; - } - } - } - } - break; - } - case Zplus: - { - for (ix = 0; ix < nbx; ix++) - { - for (iy = 0; iy < nby; iy++) - { - for (iz = nbz - 1; iz >= 0; iz--) - { - if (check_size && !CheckSize(voxels, ix, iy, iz, xmin, xmax, ymin, ymax, zmin, zmax, xc, yc, zc)) - continue; - value = voxels->Get(ix, iy, iz); - if (value >= minvalue && value <= maxvalue) - { - if (!check_size) - voxels->GetCenter(ix, iy, iz, xc, yc, zc); - setColor(colors.Value(value), Standard_False); - glVertex3d(xc, yc, zc); - if (nearest) - break; - } - } - } - } - break; - } - } - glEnd(); -} - -static void drawBoolQuadrangles(Voxel_BoolDS* voxels, const VoxelDirection vdir, - const gp_Dir& viewnormal, const Standard_Boolean nearest, - const gp_Pnt& p1, const gp_Pnt& p2, const gp_Pnt& p3, const gp_Pnt& p4, - const Standard_Real xmin, const Standard_Real xmax, - const Standard_Real ymin, const Standard_Real ymax, - const Standard_Real zmin, const Standard_Real zmax) -{ - gp_Vec vc; - gp_Pnt pc1, pc2, pc3, pc4; - Standard_Real xc, yc, zc, xn = 0.0, yn = 0.0, zn = 1.0; - Standard_Integer ix, iy, iz, nbx = voxels->GetNbX(), nby = voxels->GetNbY(), nbz = voxels->GetNbZ(); - - // Normal - viewnormal.Coord(xn, yn, zn); - - Standard_Boolean check_size = (xmin <= DBL_MAX && xmax >= DBL_MAX && - ymin <= DBL_MAX && ymax >= DBL_MAX && - zmin <= DBL_MAX && zmax >= DBL_MAX); - check_size = !check_size; - - glBegin(GL_QUADS); - switch (vdir) - { - case Xminus: - { - for (iy = 0; iy < nby; iy++) - { - for (iz = 0; iz < nbz; iz++) - { - for (ix = 0; ix < nbx; ix++) - { - if (check_size && !CheckSize(voxels, ix, iy, iz, xmin, xmax, ymin, ymax, zmin, zmax, xc, yc, zc)) - continue; - Standard_Boolean value = voxels->Get(ix, iy, iz) == Standard_True; - if (value) - { - // Define translation vector - if (!check_size) - voxels->GetCenter(ix, iy, iz, xc, yc, zc); - vc.SetCoord(xc, yc, zc); - - // Translate - pc1 = p1.Translated(vc); - pc2 = p2.Translated(vc); - pc3 = p3.Translated(vc); - pc4 = p4.Translated(vc); - - // Display - glNormal3d(xn, yn, zn); - pc1.Coord(xc, yc, zc); - glVertex3d(xc, yc, zc); - pc2.Coord(xc, yc, zc); - glVertex3d(xc, yc, zc); - pc3.Coord(xc, yc, zc); - glVertex3d(xc, yc, zc); - pc4.Coord(xc, yc, zc); - glVertex3d(xc, yc, zc); - - if (nearest) - break; - } - } - } - } - break; - } - case Xplus: - { - for (iy = 0; iy < nby; iy++) - { - for (iz = 0; iz < nbz; iz++) - { - for (ix = nbx - 1; ix >= 0; ix--) - { - if (check_size && !CheckSize(voxels, ix, iy, iz, xmin, xmax, ymin, ymax, zmin, zmax, xc, yc, zc)) - continue; - Standard_Boolean value = voxels->Get(ix, iy, iz) == Standard_True; - if (value) - { - // Define translation vector - if (!check_size) - voxels->GetCenter(ix, iy, iz, xc, yc, zc); - vc.SetCoord(xc, yc, zc); - - // Translate - pc1 = p1.Translated(vc); - pc2 = p2.Translated(vc); - pc3 = p3.Translated(vc); - pc4 = p4.Translated(vc); - - // Display - glNormal3d(xn, yn, zn); - pc1.Coord(xc, yc, zc); - glVertex3d(xc, yc, zc); - pc2.Coord(xc, yc, zc); - glVertex3d(xc, yc, zc); - pc3.Coord(xc, yc, zc); - glVertex3d(xc, yc, zc); - pc4.Coord(xc, yc, zc); - glVertex3d(xc, yc, zc); - - if (nearest) - break; - } - } - } - } - break; - } - case Yminus: - { - for (ix = 0; ix < nbx; ix++) - { - for (iz = 0; iz < nbz; iz++) - { - for (iy = 0; iy < nby; iy++) - { - if (check_size && !CheckSize(voxels, ix, iy, iz, xmin, xmax, ymin, ymax, zmin, zmax, xc, yc, zc)) - continue; - Standard_Boolean value = voxels->Get(ix, iy, iz) == Standard_True; - if (value) - { - // Define translation vector - if (!check_size) - voxels->GetCenter(ix, iy, iz, xc, yc, zc); - vc.SetCoord(xc, yc, zc); - - // Translate - pc1 = p1.Translated(vc); - pc2 = p2.Translated(vc); - pc3 = p3.Translated(vc); - pc4 = p4.Translated(vc); - - // Display - glNormal3d(xn, yn, zn); - pc1.Coord(xc, yc, zc); - glVertex3d(xc, yc, zc); - pc2.Coord(xc, yc, zc); - glVertex3d(xc, yc, zc); - pc3.Coord(xc, yc, zc); - glVertex3d(xc, yc, zc); - pc4.Coord(xc, yc, zc); - glVertex3d(xc, yc, zc); - - if (nearest) - break; - } - } - } - } - break; - } - case Yplus: - { - for (ix = 0; ix < nbx; ix++) - { - for (iz = 0; iz < nbz; iz++) - { - for (iy = nby - 1; iy >= 0; iy--) - { - if (check_size && !CheckSize(voxels, ix, iy, iz, xmin, xmax, ymin, ymax, zmin, zmax, xc, yc, zc)) - continue; - Standard_Boolean value = voxels->Get(ix, iy, iz) == Standard_True; - if (value) - { - // Define translation vector - if (!check_size) - voxels->GetCenter(ix, iy, iz, xc, yc, zc); - vc.SetCoord(xc, yc, zc); - - // Translate - pc1 = p1.Translated(vc); - pc2 = p2.Translated(vc); - pc3 = p3.Translated(vc); - pc4 = p4.Translated(vc); - - // Display - glNormal3d(xn, yn, zn); - pc1.Coord(xc, yc, zc); - glVertex3d(xc, yc, zc); - pc2.Coord(xc, yc, zc); - glVertex3d(xc, yc, zc); - pc3.Coord(xc, yc, zc); - glVertex3d(xc, yc, zc); - pc4.Coord(xc, yc, zc); - glVertex3d(xc, yc, zc); - - if (nearest) - break; - } - } - } - } - break; - } - case Zminus: - { - for (ix = 0; ix < nbx; ix++) - { - for (iy = 0; iy < nby; iy++) - { - for (iz = 0; iz < nbz; iz++) - { - if (check_size && !CheckSize(voxels, ix, iy, iz, xmin, xmax, ymin, ymax, zmin, zmax, xc, yc, zc)) - continue; - Standard_Boolean value = voxels->Get(ix, iy, iz) == Standard_True; - if (value) - { - // Define translation vector - if (!check_size) - voxels->GetCenter(ix, iy, iz, xc, yc, zc); - vc.SetCoord(xc, yc, zc); - - // Translate - pc1 = p1.Translated(vc); - pc2 = p2.Translated(vc); - pc3 = p3.Translated(vc); - pc4 = p4.Translated(vc); - - // Display - glNormal3d(xn, yn, zn); - pc1.Coord(xc, yc, zc); - glVertex3d(xc, yc, zc); - pc2.Coord(xc, yc, zc); - glVertex3d(xc, yc, zc); - pc3.Coord(xc, yc, zc); - glVertex3d(xc, yc, zc); - pc4.Coord(xc, yc, zc); - glVertex3d(xc, yc, zc); - - if (nearest) - break; - } - } - } - } - break; - } - case Zplus: - { - for (ix = 0; ix < nbx; ix++) - { - for (iy = 0; iy < nby; iy++) - { - for (iz = nbz - 1; iz >= 0; iz--) - { - if (check_size && !CheckSize(voxels, ix, iy, iz, xmin, xmax, ymin, ymax, zmin, zmax, xc, yc, zc)) - continue; - Standard_Boolean value = voxels->Get(ix, iy, iz) == Standard_True; - if (value) - { - // Define translation vector - if (!check_size) - voxels->GetCenter(ix, iy, iz, xc, yc, zc); - vc.SetCoord(xc, yc, zc); - - // Translate - pc1 = p1.Translated(vc); - pc2 = p2.Translated(vc); - pc3 = p3.Translated(vc); - pc4 = p4.Translated(vc); - - // Display - glNormal3d(xn, yn, zn); - pc1.Coord(xc, yc, zc); - glVertex3d(xc, yc, zc); - pc2.Coord(xc, yc, zc); - glVertex3d(xc, yc, zc); - pc3.Coord(xc, yc, zc); - glVertex3d(xc, yc, zc); - pc4.Coord(xc, yc, zc); - glVertex3d(xc, yc, zc); - - if (nearest) - break; - } - } - } - } - break; - } - } - glEnd(); -} - -static void drawROctBoolQuadrangles(Voxel_ROctBoolDS* voxels, const VoxelDirection vdir, - const gp_Dir& viewnormal, const Standard_Boolean nearest, - const gp_Pnt& p1, const gp_Pnt& p2, const gp_Pnt& p3, const gp_Pnt& p4, - const Standard_Real xmin, const Standard_Real xmax, - const Standard_Real ymin, const Standard_Real ymax, - const Standard_Real zmin, const Standard_Real zmax) -{ - gp_Vec vc; - gp_Pnt pc1, pc2, pc3, pc4; - Standard_Real xc, yc, zc, xn = 0.0, yn = 0.0, zn = 1.0; - Standard_Integer ix, iy, iz, nbx = voxels->GetNbX(), nby = voxels->GetNbY(), nbz = voxels->GetNbZ(), i, j, deepness; - - // Normal - viewnormal.Coord(xn, yn, zn); - - Standard_Boolean check_size = (xmin <= DBL_MAX && xmax >= DBL_MAX && - ymin <= DBL_MAX && ymax >= DBL_MAX && - zmin <= DBL_MAX && zmax >= DBL_MAX); - check_size = !check_size; - - glBegin(GL_QUADS); - switch (vdir) - { - case Xminus: - { - for (iy = 0; iy < nby; iy++) - { - for (iz = 0; iz < nbz; iz++) - { - for (ix = 0; ix < nbx; ix++) - { - deepness = voxels->Deepness(ix, iy, iz); - for (i = 0; i < 8; i++) - { - for (j = 0; j < 8; j++) - { - if (deepness == 0 && j) - { - i = 8; - break; - } - if (deepness == 1 && j) - break; - if (deepness == 0) - { - if (check_size && !CheckSize(voxels, ix, iy, iz, xmin, xmax, ymin, ymax, zmin, zmax, xc, yc, zc)) - continue; - } - else if (deepness == 1) - { - if (check_size && !CheckSize(voxels, ix, iy, iz, i, -1, xmin, xmax, ymin, ymax, zmin, zmax, xc, yc, zc)) - continue; - } - else if (deepness == 2) - { - if (check_size && !CheckSize(voxels, ix, iy, iz, i, j, xmin, xmax, ymin, ymax, zmin, zmax, xc, yc, zc)) - continue; - } - - Standard_Boolean value; - switch (deepness) - { - case 0: - value = voxels->Get(ix, iy, iz) == Standard_True; - break; - case 1: - value = voxels->Get(ix, iy, iz, i) == Standard_True; - break; - case 2: - value = voxels->Get(ix, iy, iz, i, j) == Standard_True; - break; - } - - if (value) - { - // Define translation vector - if (!check_size) - { - switch (deepness) - { - case 0: - ((Voxel_DS*)voxels)->GetCenter(ix, iy, iz, xc, yc, zc); - break; - case 1: - voxels->GetCenter(ix, iy, iz, i, xc, yc, zc); - break; - case 2: - voxels->GetCenter(ix, iy, iz, i, j, xc, yc, zc); - break; - } - } - vc.SetCoord(xc, yc, zc); - - // Translate - pc1 = p1.Translated(vc); - pc2 = p2.Translated(vc); - pc3 = p3.Translated(vc); - pc4 = p4.Translated(vc); - - // Display - glNormal3d(xn, yn, zn); - pc1.Coord(xc, yc, zc); - glVertex3d(xc, yc, zc); - pc2.Coord(xc, yc, zc); - glVertex3d(xc, yc, zc); - pc3.Coord(xc, yc, zc); - glVertex3d(xc, yc, zc); - pc4.Coord(xc, yc, zc); - glVertex3d(xc, yc, zc); - - if (nearest) - break; - } - } - } - } - } - } - break; - } - case Xplus: - { - for (iy = 0; iy < nby; iy++) - { - for (iz = 0; iz < nbz; iz++) - { - for (ix = nbx - 1; ix >= 0; ix--) - { - deepness = voxels->Deepness(ix, iy, iz); - for (i = 0; i < 8; i++) - { - for (j = 0; j < 8; j++) - { - if (deepness == 0 && j) - { - i = 8; - break; - } - if (deepness == 1 && j) - break; - if (deepness == 0) - { - if (check_size && !CheckSize(voxels, ix, iy, iz, xmin, xmax, ymin, ymax, zmin, zmax, xc, yc, zc)) - continue; - } - else if (deepness == 1) - { - if (check_size && !CheckSize(voxels, ix, iy, iz, i, -1, xmin, xmax, ymin, ymax, zmin, zmax, xc, yc, zc)) - continue; - } - else if (deepness == 2) - { - if (check_size && !CheckSize(voxels, ix, iy, iz, i, j, xmin, xmax, ymin, ymax, zmin, zmax, xc, yc, zc)) - continue; - } - - Standard_Boolean value; - switch (deepness) - { - case 0: - value = voxels->Get(ix, iy, iz) == Standard_True; - break; - case 1: - value = voxels->Get(ix, iy, iz, i) == Standard_True; - break; - case 2: - value = voxels->Get(ix, iy, iz, i, j) == Standard_True; - break; - } - - if (value) - { - // Define translation vector - if (!check_size) - { - switch (deepness) - { - case 0: - ((Voxel_DS*)voxels)->GetCenter(ix, iy, iz, xc, yc, zc); - break; - case 1: - voxels->GetCenter(ix, iy, iz, i, xc, yc, zc); - break; - case 2: - voxels->GetCenter(ix, iy, iz, i, j, xc, yc, zc); - break; - } - } - vc.SetCoord(xc, yc, zc); - - // Translate - pc1 = p1.Translated(vc); - pc2 = p2.Translated(vc); - pc3 = p3.Translated(vc); - pc4 = p4.Translated(vc); - - // Display - glNormal3d(xn, yn, zn); - pc1.Coord(xc, yc, zc); - glVertex3d(xc, yc, zc); - pc2.Coord(xc, yc, zc); - glVertex3d(xc, yc, zc); - pc3.Coord(xc, yc, zc); - glVertex3d(xc, yc, zc); - pc4.Coord(xc, yc, zc); - glVertex3d(xc, yc, zc); - - if (nearest) - break; - } - } - } - } - } - } - break; - } - case Yminus: - { - for (ix = 0; ix < nbx; ix++) - { - for (iz = 0; iz < nbz; iz++) - { - for (iy = 0; iy < nby; iy++) - { - deepness = voxels->Deepness(ix, iy, iz); - for (i = 0; i < 8; i++) - { - for (j = 0; j < 8; j++) - { - if (deepness == 0 && j) - { - i = 8; - break; - } - if (deepness == 1 && j) - break; - if (deepness == 0) - { - if (check_size && !CheckSize(voxels, ix, iy, iz, xmin, xmax, ymin, ymax, zmin, zmax, xc, yc, zc)) - continue; - } - else if (deepness == 1) - { - if (check_size && !CheckSize(voxels, ix, iy, iz, i, -1, xmin, xmax, ymin, ymax, zmin, zmax, xc, yc, zc)) - continue; - } - else if (deepness == 2) - { - if (check_size && !CheckSize(voxels, ix, iy, iz, i, j, xmin, xmax, ymin, ymax, zmin, zmax, xc, yc, zc)) - continue; - } - - Standard_Boolean value; - switch (deepness) - { - case 0: - value = voxels->Get(ix, iy, iz) == Standard_True; - break; - case 1: - value = voxels->Get(ix, iy, iz, i) == Standard_True; - break; - case 2: - value = voxels->Get(ix, iy, iz, i, j) == Standard_True; - break; - } - - if (value) - { - // Define translation vector - if (!check_size) - { - switch (deepness) - { - case 0: - ((Voxel_DS*)voxels)->GetCenter(ix, iy, iz, xc, yc, zc); - break; - case 1: - voxels->GetCenter(ix, iy, iz, i, xc, yc, zc); - break; - case 2: - voxels->GetCenter(ix, iy, iz, i, j, xc, yc, zc); - break; - } - } - vc.SetCoord(xc, yc, zc); - - // Translate - pc1 = p1.Translated(vc); - pc2 = p2.Translated(vc); - pc3 = p3.Translated(vc); - pc4 = p4.Translated(vc); - - // Display - glNormal3d(xn, yn, zn); - pc1.Coord(xc, yc, zc); - glVertex3d(xc, yc, zc); - pc2.Coord(xc, yc, zc); - glVertex3d(xc, yc, zc); - pc3.Coord(xc, yc, zc); - glVertex3d(xc, yc, zc); - pc4.Coord(xc, yc, zc); - glVertex3d(xc, yc, zc); - - if (nearest) - break; - } - } - } - } - } - } - break; - } - case Yplus: - { - for (ix = 0; ix < nbx; ix++) - { - for (iz = 0; iz < nbz; iz++) - { - for (iy = nby - 1; iy >= 0; iy--) - { - deepness = voxels->Deepness(ix, iy, iz); - for (i = 0; i < 8; i++) - { - for (j = 0; j < 8; j++) - { - if (deepness == 0 && j) - { - i = 8; - break; - } - if (deepness == 1 && j) - break; - if (deepness == 0) - { - if (check_size && !CheckSize(voxels, ix, iy, iz, xmin, xmax, ymin, ymax, zmin, zmax, xc, yc, zc)) - continue; - } - else if (deepness == 1) - { - if (check_size && !CheckSize(voxels, ix, iy, iz, i, -1, xmin, xmax, ymin, ymax, zmin, zmax, xc, yc, zc)) - continue; - } - else if (deepness == 2) - { - if (check_size && !CheckSize(voxels, ix, iy, iz, i, j, xmin, xmax, ymin, ymax, zmin, zmax, xc, yc, zc)) - continue; - } - - Standard_Boolean value; - switch (deepness) - { - case 0: - value = voxels->Get(ix, iy, iz) == Standard_True; - break; - case 1: - value = voxels->Get(ix, iy, iz, i) == Standard_True; - break; - case 2: - value = voxels->Get(ix, iy, iz, i, j) == Standard_True; - break; - } - - if (value) - { - // Define translation vector - if (!check_size) - { - switch (deepness) - { - case 0: - ((Voxel_DS*)voxels)->GetCenter(ix, iy, iz, xc, yc, zc); - break; - case 1: - voxels->GetCenter(ix, iy, iz, i, xc, yc, zc); - break; - case 2: - voxels->GetCenter(ix, iy, iz, i, j, xc, yc, zc); - break; - } - } - vc.SetCoord(xc, yc, zc); - - // Translate - pc1 = p1.Translated(vc); - pc2 = p2.Translated(vc); - pc3 = p3.Translated(vc); - pc4 = p4.Translated(vc); - - // Display - glNormal3d(xn, yn, zn); - pc1.Coord(xc, yc, zc); - glVertex3d(xc, yc, zc); - pc2.Coord(xc, yc, zc); - glVertex3d(xc, yc, zc); - pc3.Coord(xc, yc, zc); - glVertex3d(xc, yc, zc); - pc4.Coord(xc, yc, zc); - glVertex3d(xc, yc, zc); - - if (nearest) - break; - } - } - } - } - } - } - break; - } - case Zminus: - { - for (ix = 0; ix < nbx; ix++) - { - for (iy = 0; iy < nby; iy++) - { - for (iz = 0; iz < nbz; iz++) - { - deepness = voxels->Deepness(ix, iy, iz); - for (i = 0; i < 8; i++) - { - for (j = 0; j < 8; j++) - { - if (deepness == 0 && j) - { - i = 8; - break; - } - if (deepness == 1 && j) - break; - if (deepness == 0) - { - if (check_size && !CheckSize(voxels, ix, iy, iz, xmin, xmax, ymin, ymax, zmin, zmax, xc, yc, zc)) - continue; - } - else if (deepness == 1) - { - if (check_size && !CheckSize(voxels, ix, iy, iz, i, -1, xmin, xmax, ymin, ymax, zmin, zmax, xc, yc, zc)) - continue; - } - else if (deepness == 2) - { - if (check_size && !CheckSize(voxels, ix, iy, iz, i, j, xmin, xmax, ymin, ymax, zmin, zmax, xc, yc, zc)) - continue; - } - - Standard_Boolean value; - switch (deepness) - { - case 0: - value = voxels->Get(ix, iy, iz) == Standard_True; - break; - case 1: - value = voxels->Get(ix, iy, iz, i) == Standard_True; - break; - case 2: - value = voxels->Get(ix, iy, iz, i, j) == Standard_True; - break; - } - - if (value) - { - // Define translation vector - if (!check_size) - { - switch (deepness) - { - case 0: - ((Voxel_DS*)voxels)->GetCenter(ix, iy, iz, xc, yc, zc); - break; - case 1: - voxels->GetCenter(ix, iy, iz, i, xc, yc, zc); - break; - case 2: - voxels->GetCenter(ix, iy, iz, i, j, xc, yc, zc); - break; - } - } - vc.SetCoord(xc, yc, zc); - - // Translate - pc1 = p1.Translated(vc); - pc2 = p2.Translated(vc); - pc3 = p3.Translated(vc); - pc4 = p4.Translated(vc); - - // Display - glNormal3d(xn, yn, zn); - pc1.Coord(xc, yc, zc); - glVertex3d(xc, yc, zc); - pc2.Coord(xc, yc, zc); - glVertex3d(xc, yc, zc); - pc3.Coord(xc, yc, zc); - glVertex3d(xc, yc, zc); - pc4.Coord(xc, yc, zc); - glVertex3d(xc, yc, zc); - - if (nearest) - break; - } - } - } - } - } - } - break; - } - case Zplus: - { - for (ix = 0; ix < nbx; ix++) - { - for (iy = 0; iy < nby; iy++) - { - for (iz = nbz - 1; iz >= 0; iz--) - { - deepness = voxels->Deepness(ix, iy, iz); - for (i = 0; i < 8; i++) - { - for (j = 0; j < 8; j++) - { - if (deepness == 0 && j) - { - i = 8; - break; - } - if (deepness == 1 && j) - break; - if (deepness == 0) - { - if (check_size && !CheckSize(voxels, ix, iy, iz, xmin, xmax, ymin, ymax, zmin, zmax, xc, yc, zc)) - continue; - } - else if (deepness == 1) - { - if (check_size && !CheckSize(voxels, ix, iy, iz, i, -1, xmin, xmax, ymin, ymax, zmin, zmax, xc, yc, zc)) - continue; - } - else if (deepness == 2) - { - if (check_size && !CheckSize(voxels, ix, iy, iz, i, j, xmin, xmax, ymin, ymax, zmin, zmax, xc, yc, zc)) - continue; - } - - Standard_Boolean value; - switch (deepness) - { - case 0: - value = voxels->Get(ix, iy, iz) == Standard_True; - break; - case 1: - value = voxels->Get(ix, iy, iz, i) == Standard_True; - break; - case 2: - value = voxels->Get(ix, iy, iz, i, j) == Standard_True; - break; - } - - if (value) - { - // Define translation vector - if (!check_size) - { - switch (deepness) - { - case 0: - ((Voxel_DS*)voxels)->GetCenter(ix, iy, iz, xc, yc, zc); - break; - case 1: - voxels->GetCenter(ix, iy, iz, i, xc, yc, zc); - break; - case 2: - voxels->GetCenter(ix, iy, iz, i, j, xc, yc, zc); - break; - } - } - vc.SetCoord(xc, yc, zc); - - // Translate - pc1 = p1.Translated(vc); - pc2 = p2.Translated(vc); - pc3 = p3.Translated(vc); - pc4 = p4.Translated(vc); - - // Display - glNormal3d(xn, yn, zn); - pc1.Coord(xc, yc, zc); - glVertex3d(xc, yc, zc); - pc2.Coord(xc, yc, zc); - glVertex3d(xc, yc, zc); - pc3.Coord(xc, yc, zc); - glVertex3d(xc, yc, zc); - pc4.Coord(xc, yc, zc); - glVertex3d(xc, yc, zc); - - if (nearest) - break; - } - } - } - } - } - } - break; - } - } - glEnd(); -} - - - -static void drawColorQuadrangles(Voxel_ColorDS* voxels, const VoxelDirection vdir, - const gp_Dir& viewnormal, const Standard_Boolean nearest, - const Handle(Quantity_HArray1OfColor)& hcolors, - const gp_Pnt& p1, const gp_Pnt& p2, const gp_Pnt& p3, const gp_Pnt& p4, - const Standard_Byte minvalue, const Standard_Byte maxvalue, - const Standard_Real xmin, const Standard_Real xmax, - const Standard_Real ymin, const Standard_Real ymax, - const Standard_Real zmin, const Standard_Real zmax) -{ - gp_Vec vc; - gp_Pnt pc1, pc2, pc3, pc4; - Standard_Real xc, yc, zc, xn = 0.0, yn = 0.0, zn = 0.0; - Standard_Integer ix, iy, iz, nbx = voxels->GetNbX(), nby = voxels->GetNbY(), nbz = voxels->GetNbZ(); - Standard_Byte value; - - // Normal - //viewnormal.Coord(xn, yn, zn); - glNormal3d(xn, yn, zn); - - // Colors - const Quantity_Array1OfColor& colors = hcolors->Array1(); - - Standard_Boolean check_size = (xmin <= DBL_MAX && xmax >= DBL_MAX && - ymin <= DBL_MAX && ymax >= DBL_MAX && - zmin <= DBL_MAX && zmax >= DBL_MAX); - check_size = !check_size; - - glBegin(GL_QUADS); - switch (vdir) - { - case Xminus: - { - for (iy = 0; iy < nby; iy++) - { - for (iz = 0; iz < nbz; iz++) - { - for (ix = 0; ix < nbx; ix++) - { - if (check_size && !CheckSize(voxels, ix, iy, iz, xmin, xmax, ymin, ymax, zmin, zmax, xc, yc, zc)) - continue; - value = voxels->Get(ix, iy, iz); - if (value >= minvalue && value <= maxvalue) - { - // Define translation vector - if (!check_size) - voxels->GetCenter(ix, iy, iz, xc, yc, zc); - vc.SetCoord(xc, yc, zc); - - // Translate - pc1 = p1.Translated(vc); - pc2 = p2.Translated(vc); - pc3 = p3.Translated(vc); - pc4 = p4.Translated(vc); - - // Color - setColor(colors.Value(value), Standard_False); - - // Display - //glNormal3d(xn, yn, zn); - pc1.Coord(xc, yc, zc); - glVertex3d(xc, yc, zc); - pc2.Coord(xc, yc, zc); - glVertex3d(xc, yc, zc); - pc3.Coord(xc, yc, zc); - glVertex3d(xc, yc, zc); - pc4.Coord(xc, yc, zc); - glVertex3d(xc, yc, zc); - - if (nearest) - break; - } - } - } - } - break; - } - case Xplus: - { - for (iy = 0; iy < nby; iy++) - { - for (iz = 0; iz < nbz; iz++) - { - for (ix = nbx - 1; ix >= 0; ix--) - { - if (check_size && !CheckSize(voxels, ix, iy, iz, xmin, xmax, ymin, ymax, zmin, zmax, xc, yc, zc)) - continue; - value = voxels->Get(ix, iy, iz); - if (value >= minvalue && value <= maxvalue) - { - // Define translation vector - if (!check_size) - voxels->GetCenter(ix, iy, iz, xc, yc, zc); - vc.SetCoord(xc, yc, zc); - - // Translate - pc1 = p1.Translated(vc); - pc2 = p2.Translated(vc); - pc3 = p3.Translated(vc); - pc4 = p4.Translated(vc); - - // Color - setColor(colors.Value(value), Standard_False); - - // Display - //glNormal3d(xn, yn, zn); - pc1.Coord(xc, yc, zc); - glVertex3d(xc, yc, zc); - pc2.Coord(xc, yc, zc); - glVertex3d(xc, yc, zc); - pc3.Coord(xc, yc, zc); - glVertex3d(xc, yc, zc); - pc4.Coord(xc, yc, zc); - glVertex3d(xc, yc, zc); - - if (nearest) - break; - } - } - } - } - break; - } - case Yminus: - { - for (ix = 0; ix < nbx; ix++) - { - for (iz = 0; iz < nbz; iz++) - { - for (iy = 0; iy < nby; iy++) - { - if (check_size && !CheckSize(voxels, ix, iy, iz, xmin, xmax, ymin, ymax, zmin, zmax, xc, yc, zc)) - continue; - value = voxels->Get(ix, iy, iz); - if (value >= minvalue && value <= maxvalue) - { - // Define translation vector - if (!check_size) - voxels->GetCenter(ix, iy, iz, xc, yc, zc); - vc.SetCoord(xc, yc, zc); - - // Translate - pc1 = p1.Translated(vc); - pc2 = p2.Translated(vc); - pc3 = p3.Translated(vc); - pc4 = p4.Translated(vc); - - // Color - setColor(colors.Value(value), Standard_False); - - // Display - //glNormal3d(xn, yn, zn); - pc1.Coord(xc, yc, zc); - glVertex3d(xc, yc, zc); - pc2.Coord(xc, yc, zc); - glVertex3d(xc, yc, zc); - pc3.Coord(xc, yc, zc); - glVertex3d(xc, yc, zc); - pc4.Coord(xc, yc, zc); - glVertex3d(xc, yc, zc); - - if (nearest) - break; - } - } - } - } - break; - } - case Yplus: - { - for (ix = 0; ix < nbx; ix++) - { - for (iz = 0; iz < nbz; iz++) - { - for (iy = nby - 1; iy >= 0; iy--) - { - if (check_size && !CheckSize(voxels, ix, iy, iz, xmin, xmax, ymin, ymax, zmin, zmax, xc, yc, zc)) - continue; - value = voxels->Get(ix, iy, iz); - if (value >= minvalue && value <= maxvalue) - { - // Define translation vector - if (!check_size) - voxels->GetCenter(ix, iy, iz, xc, yc, zc); - vc.SetCoord(xc, yc, zc); - - // Translate - pc1 = p1.Translated(vc); - pc2 = p2.Translated(vc); - pc3 = p3.Translated(vc); - pc4 = p4.Translated(vc); - - // Color - setColor(colors.Value(value), Standard_False); - - // Display - //glNormal3d(xn, yn, zn); - pc1.Coord(xc, yc, zc); - glVertex3d(xc, yc, zc); - pc2.Coord(xc, yc, zc); - glVertex3d(xc, yc, zc); - pc3.Coord(xc, yc, zc); - glVertex3d(xc, yc, zc); - pc4.Coord(xc, yc, zc); - glVertex3d(xc, yc, zc); - - if (nearest) - break; - } - } - } - } - break; - } - case Zminus: - { - for (ix = 0; ix < nbx; ix++) - { - for (iy = 0; iy < nby; iy++) - { - for (iz = 0; iz < nbz; iz++) - { - if (check_size && !CheckSize(voxels, ix, iy, iz, xmin, xmax, ymin, ymax, zmin, zmax, xc, yc, zc)) - continue; - value = voxels->Get(ix, iy, iz); - if (value >= minvalue && value <= maxvalue) - { - // Define translation vector - if (!check_size) - voxels->GetCenter(ix, iy, iz, xc, yc, zc); - vc.SetCoord(xc, yc, zc); - - // Translate - pc1 = p1.Translated(vc); - pc2 = p2.Translated(vc); - pc3 = p3.Translated(vc); - pc4 = p4.Translated(vc); - - // Color - setColor(colors.Value(value), Standard_False); - - // Display - //glNormal3d(xn, yn, zn); - pc1.Coord(xc, yc, zc); - glVertex3d(xc, yc, zc); - pc2.Coord(xc, yc, zc); - glVertex3d(xc, yc, zc); - pc3.Coord(xc, yc, zc); - glVertex3d(xc, yc, zc); - pc4.Coord(xc, yc, zc); - glVertex3d(xc, yc, zc); - - if (nearest) - break; - } - } - } - } - break; - } - case Zplus: - { - for (ix = 0; ix < nbx; ix++) - { - for (iy = 0; iy < nby; iy++) - { - for (iz = nbz - 1; iz >= 0; iz--) - { - if (check_size && !CheckSize(voxels, ix, iy, iz, xmin, xmax, ymin, ymax, zmin, zmax, xc, yc, zc)) - continue; - value = voxels->Get(ix, iy, iz); - if (value >= minvalue && value <= maxvalue) - { - // Define translation vector - if (!check_size) - voxels->GetCenter(ix, iy, iz, xc, yc, zc); - vc.SetCoord(xc, yc, zc); - - // Translate - pc1 = p1.Translated(vc); - pc2 = p2.Translated(vc); - pc3 = p3.Translated(vc); - pc4 = p4.Translated(vc); - - // Color - setColor(colors.Value(value), Standard_False); - - // Display - //glNormal3d(xn, yn, zn); - pc1.Coord(xc, yc, zc); - glVertex3d(xc, yc, zc); - pc2.Coord(xc, yc, zc); - glVertex3d(xc, yc, zc); - pc3.Coord(xc, yc, zc); - glVertex3d(xc, yc, zc); - pc4.Coord(xc, yc, zc); - glVertex3d(xc, yc, zc); - - if (nearest) - break; - } - } - } - } - break; - } - } - glEnd(); -} - -static void genListIndex(GLint& index) -{ - GLint i = 0; - while (++i <= INT_MAX) - { - if (!glIsList(i)) - { - index = i; - break; - } - } -} - -static void setPlaneNormal(const VoxelDirection& dir, - const Standard_Real dx, const Standard_Real dy, const Standard_Real dz, - gp_Pln& plane, gp_Pnt& p1, gp_Pnt& p2, gp_Pnt& p3, gp_Pnt& p4) -{ - gp_Ax3 axes = plane.Position(); - Standard_Real dx2 = 0.5 * dx, dy2 = 0.5 * dy, dz2 = 0.5 * dz; - switch (dir) - { - case Xminus: - p1.SetCoord(-dx2, -dy2, dz2); - p2.SetCoord(-dx2, -dy2, -dz2); - p3.SetCoord(-dx2, dy2, -dz2); - p4.SetCoord(-dx2, dy2, dz2); - axes.SetDirection(-gp::DX()); - break; - case Xplus: - p1.SetCoord(dx2, -dy2, dz2); - p2.SetCoord(dx2, -dy2, -dz2); - p3.SetCoord(dx2, dy2, -dz2); - p4.SetCoord(dx2, dy2, dz2); - axes.SetDirection(gp::DX()); - break; - case Yminus: - p1.SetCoord(dx2, -dy2, dz2); - p2.SetCoord(dx2, -dy2, -dz2); - p3.SetCoord(-dx2, -dy2, -dz2); - p4.SetCoord(-dx2, -dy2, dz2); - axes.SetDirection(-gp::DY()); - break; - case Yplus: - p1.SetCoord(dx2, dy2, dz2); - p2.SetCoord(dx2, dy2, -dz2); - p3.SetCoord(-dx2, dy2, -dz2); - p4.SetCoord(-dx2, dy2, dz2); - axes.SetDirection(gp::DY()); - break; - case Zminus: - p1.SetCoord(dx2, dy2, -dz2); - p2.SetCoord(-dx2, dy2, -dz2); - p3.SetCoord(-dx2, -dy2, -dz2); - p4.SetCoord(dx2, -dy2, -dz2); - axes.SetDirection(-gp::DZ()); - break; - case Zplus: - p1.SetCoord(dx2, dy2, dz2); - p2.SetCoord(-dx2, dy2, dz2); - p3.SetCoord(-dx2, -dy2, dz2); - p4.SetCoord(dx2, -dy2, dz2); - axes.SetDirection(gp::DZ()); - break; - } -} - -/**************************************************************************/ -void VoxelClient_VisDrawer::DisplayVoxels(const Standard_Boolean theHighlight) -{ - if(!myData) - return; - - glEnable(GL_DEPTH_TEST); - - // Boolean voxels - if (myData->myBoolVoxels) - { - // Points - if (myData->myDisplay.myDisplayMode == Voxel_VDM_POINTS || - myData->myDisplay.myDisplayMode == Voxel_VDM_NEARESTPOINTS) - { - glDisable(GL_LIGHTING); - if (myData->myDisplay.mySmoothPoints) - glEnable(GL_POINT_SMOOTH); - else - glDisable(GL_POINT_SMOOTH); - - // Draw the points of voxels (center points of the voxels) - // starting visualization from the side looking out from the user. - setColor(myData->myDisplay.myColor, theHighlight); - glPointSize((Standard_ShortReal) myData->myDisplay.myPointSize); - - // Display - DisplayPoints(myData->myDisplay.myDisplayMode == Voxel_VDM_NEARESTPOINTS); - } - } - - // Color values - if (myData->myColorVoxels) - { - // Points - if (myData->myDisplay.myDisplayMode == Voxel_VDM_POINTS || - myData->myDisplay.myDisplayMode == Voxel_VDM_NEARESTPOINTS) - { - glDisable(GL_LIGHTING); - if (myData->myDisplay.mySmoothPoints) - glEnable(GL_POINT_SMOOTH); - else - glDisable(GL_POINT_SMOOTH); - - // Draw the points of voxels (center points of the voxels) - // starting visualization from the side looking out from the user. - glPointSize((Standard_ShortReal) myData->myDisplay.myPointSize); - - // Display - DisplayPoints(myData->myDisplay.myDisplayMode == Voxel_VDM_NEARESTPOINTS); - } - } - - // Recursive Octree Boolean voxels - if (myData->myROctBoolVoxels) - { - // Points - if (myData->myDisplay.myDisplayMode == Voxel_VDM_POINTS || - myData->myDisplay.myDisplayMode == Voxel_VDM_NEARESTPOINTS) - { - glDisable(GL_LIGHTING); - if (myData->myDisplay.mySmoothPoints) - glEnable(GL_POINT_SMOOTH); - else - glDisable(GL_POINT_SMOOTH); - - // Draw the points of voxels (center points of the voxels) - // starting visualization from the side looking out from the user. - setColor(myData->myDisplay.myColor, theHighlight); - glPointSize((Standard_ShortReal) myData->myDisplay.myPointSize); - - // Display - DisplayPoints(myData->myDisplay.myDisplayMode == Voxel_VDM_NEARESTPOINTS); - } - } - - // Shading drawn by boxes - if (myData->myDisplay.myDisplayMode == Voxel_VDM_BOXES || - myData->myDisplay.myDisplayMode == Voxel_VDM_NEARESTBOXES) - { - glEnable(GL_LIGHTING); - glEnable(GL_COLOR_MATERIAL); - - // Draw quadrangles of voxels looking to the user. - setColor(myData->myDisplay.myColor, theHighlight); - - // Display - DisplayBoxes(myData->myDisplay.myDisplayMode == Voxel_VDM_NEARESTBOXES); - - glDisable(GL_COLOR_MATERIAL); - } - - // Highlighted voxel - HighlightVoxel(); -} - -/**************************************************************************/ -void VoxelClient_VisDrawer::DisplayPoints(const Standard_Boolean nearest) -{ - //OSD_Timer timer; - //timer.Start(); - - // Find the side of the cube which normal looks to (or out) the user's eye. - gp_Dir viewnormal; - getNormal(viewnormal); - - // Range of displayed data - Standard_Real xmin = myData->myDisplay.myDisplayedXMin; - Standard_Real xmax = myData->myDisplay.myDisplayedXMax; - Standard_Real ymin = myData->myDisplay.myDisplayedYMin; - Standard_Real ymax = myData->myDisplay.myDisplayedYMax; - Standard_Real zmin = myData->myDisplay.myDisplayedZMin; - Standard_Real zmax = myData->myDisplay.myDisplayedZMax; - - // Boolean points - if (myData->myBoolVoxels) - { - if (nearest || myData->myDisplay.myDegenerateMode) - { - VoxelDirection vdir1, vdir2, vdir3; - getVoxel3Directions(viewnormal, vdir1, vdir2, vdir3); - - if (myData->myDisplay.myUsageOfGLlists) - { - // Clean all allocated GL lists for the case of first call. - if (myData->myDisplay.myBoolNearestPointsFirst) - { - for (Standard_Integer idir = Xminus; idir <= Zplus; idir++) - { - if (myData->myDisplay.myBoolNearestPointsList[idir] > 0) - { - glDeleteLists(myData->myDisplay.myBoolNearestPointsList[idir], 1); - myData->myDisplay.myBoolNearestPointsList[idir] = -1; - } - } - myData->myDisplay.myBoolNearestPointsFirst = Standard_False; - } - - // Generate GL lists if needed. - if (myData->myDisplay.myBoolNearestPointsList[(Standard_Integer) vdir1] < 0) - { - genListIndex(myData->myDisplay.myBoolNearestPointsList[(Standard_Integer) vdir1]); - glNewList(myData->myDisplay.myBoolNearestPointsList[(Standard_Integer) vdir1], GL_COMPILE); - drawBoolPoints(vdir1, Standard_True, myData->myBoolVoxels, - xmin, xmax, ymin, ymax, zmin, zmax); - glEndList(); - } - if (myData->myDisplay.myBoolNearestPointsList[(Standard_Integer) vdir2] < 0) - { - genListIndex(myData->myDisplay.myBoolNearestPointsList[(Standard_Integer) vdir2]); - glNewList(myData->myDisplay.myBoolNearestPointsList[(Standard_Integer) vdir2], GL_COMPILE); - drawBoolPoints(vdir2, Standard_True, myData->myBoolVoxels, - xmin, xmax, ymin, ymax, zmin, zmax); - glEndList(); - } - if (myData->myDisplay.myBoolNearestPointsList[(Standard_Integer) vdir3] < 0) - { - genListIndex(myData->myDisplay.myBoolNearestPointsList[(Standard_Integer) vdir3]); - glNewList(myData->myDisplay.myBoolNearestPointsList[(Standard_Integer) vdir3], GL_COMPILE); - drawBoolPoints(vdir3, Standard_True, myData->myBoolVoxels, - xmin, xmax, ymin, ymax, zmin, zmax); - glEndList(); - } - glCallList(myData->myDisplay.myBoolNearestPointsList[(Standard_Integer) vdir1]); - glCallList(myData->myDisplay.myBoolNearestPointsList[(Standard_Integer) vdir2]); - glCallList(myData->myDisplay.myBoolNearestPointsList[(Standard_Integer) vdir3]); - } - else - { - drawBoolPoints(vdir1, Standard_True, myData->myBoolVoxels, - xmin, xmax, ymin, ymax, zmin, zmax); - drawBoolPoints(vdir2, Standard_True, myData->myBoolVoxels, - xmin, xmax, ymin, ymax, zmin, zmax); - drawBoolPoints(vdir3, Standard_True, myData->myBoolVoxels, - xmin, xmax, ymin, ymax, zmin, zmax); - } - } - else - { - if (myData->myDisplay.myUsageOfGLlists) - { - if (myData->myDisplay.myBoolPointsFirst) - { - // Delete previous GL list. - if (myData->myDisplay.myBoolPointsList > 0) - { - glDeleteLists(myData->myDisplay.myBoolPointsList, 1); - myData->myDisplay.myBoolPointsList = -1; - } - - // Generate a new GL list - genListIndex(myData->myDisplay.myBoolPointsList); - glNewList(myData->myDisplay.myBoolPointsList, GL_COMPILE); - VoxelDirection vdir = getVoxelDirection(viewnormal); - drawBoolPoints(vdir, Standard_False, myData->myBoolVoxels, - xmin, xmax, ymin, ymax, zmin, zmax); - glEndList(); - - // The first call has just been passed... - myData->myDisplay.myBoolPointsFirst = Standard_False; - } - glCallList(myData->myDisplay.myBoolPointsList); - } - else - { - VoxelDirection vdir = getVoxelDirection(viewnormal); - drawBoolPoints(vdir, Standard_False, myData->myBoolVoxels, - xmin, xmax, ymin, ymax, zmin, zmax); - } - } - } - - // Color points - if (myData->myColorVoxels) - { - if (nearest || myData->myDisplay.myDegenerateMode) - { - VoxelDirection vdir1, vdir2, vdir3; - getVoxel3Directions(viewnormal, vdir1, vdir2, vdir3); - - if (myData->myDisplay.myUsageOfGLlists) - { - // Clean all allocated GL lists for the case of first call. - if (myData->myDisplay.myColorNearestPointsFirst) - { - for (Standard_Integer idir = Xminus; idir <= Zplus; idir++) - { - if (myData->myDisplay.myColorNearestPointsList[idir] > 0) - { - glDeleteLists(myData->myDisplay.myColorNearestPointsList[idir], 1); - myData->myDisplay.myColorNearestPointsList[idir] = -1; - } - } - myData->myDisplay.myColorNearestPointsFirst = Standard_False; - } - - // Generate GL lists if needed. - if (myData->myDisplay.myColorNearestPointsList[(Standard_Integer) vdir1] < 0) - { - genListIndex(myData->myDisplay.myColorNearestPointsList[(Standard_Integer) vdir1]); - glNewList(myData->myDisplay.myColorNearestPointsList[(Standard_Integer) vdir1], GL_COMPILE); - drawColorPoints(vdir1, Standard_True, myData->myColorVoxels, myData->myDisplay.myColors, - myData->myDisplay.myColorMinValue, myData->myDisplay.myColorMaxValue, - xmin, xmax, ymin, ymax, zmin, zmax); - glEndList(); - } - if (myData->myDisplay.myColorNearestPointsList[(Standard_Integer) vdir2] < 0) - { - genListIndex(myData->myDisplay.myColorNearestPointsList[(Standard_Integer) vdir2]); - glNewList(myData->myDisplay.myColorNearestPointsList[(Standard_Integer) vdir2], GL_COMPILE); - drawColorPoints(vdir2, Standard_True, myData->myColorVoxels, myData->myDisplay.myColors, - myData->myDisplay.myColorMinValue, myData->myDisplay.myColorMaxValue, - xmin, xmax, ymin, ymax, zmin, zmax); - glEndList(); - } - if (myData->myDisplay.myColorNearestPointsList[(Standard_Integer) vdir3] < 0) - { - genListIndex(myData->myDisplay.myColorNearestPointsList[(Standard_Integer) vdir3]); - glNewList(myData->myDisplay.myColorNearestPointsList[(Standard_Integer) vdir3], GL_COMPILE); - drawColorPoints(vdir3, Standard_True, myData->myColorVoxels, myData->myDisplay.myColors, - myData->myDisplay.myColorMinValue, myData->myDisplay.myColorMaxValue, - xmin, xmax, ymin, ymax, zmin, zmax); - glEndList(); - } - - glCallList(myData->myDisplay.myColorNearestPointsList[(Standard_Integer) vdir1]); - glCallList(myData->myDisplay.myColorNearestPointsList[(Standard_Integer) vdir2]); - glCallList(myData->myDisplay.myColorNearestPointsList[(Standard_Integer) vdir3]); - } - else - { - drawColorPoints(vdir1, Standard_True, myData->myColorVoxels, myData->myDisplay.myColors, - myData->myDisplay.myColorMinValue, myData->myDisplay.myColorMaxValue, - xmin, xmax, ymin, ymax, zmin, zmax); - drawColorPoints(vdir2, Standard_True, myData->myColorVoxels, myData->myDisplay.myColors, - myData->myDisplay.myColorMinValue, myData->myDisplay.myColorMaxValue, - xmin, xmax, ymin, ymax, zmin, zmax); - drawColorPoints(vdir3, Standard_True, myData->myColorVoxels, myData->myDisplay.myColors, - myData->myDisplay.myColorMinValue, myData->myDisplay.myColorMaxValue, - xmin, xmax, ymin, ymax, zmin, zmax); - } - } - else - { - if (myData->myDisplay.myUsageOfGLlists) - { - if (myData->myDisplay.myColorPointsFirst) - { - // Delete previous GL list. - if (myData->myDisplay.myColorPointsList > 0) - { - glDeleteLists(myData->myDisplay.myColorPointsList, 1); - myData->myDisplay.myColorPointsList = -1; - } - - // Generate a new GL list - genListIndex(myData->myDisplay.myColorPointsList); - glNewList(myData->myDisplay.myColorPointsList, GL_COMPILE); - VoxelDirection vdir = getVoxelDirection(viewnormal); - drawColorPoints(vdir, Standard_False, myData->myColorVoxels, myData->myDisplay.myColors, - myData->myDisplay.myColorMinValue, myData->myDisplay.myColorMaxValue, - xmin, xmax, ymin, ymax, zmin, zmax); - glEndList(); - - // The first call has just been passed... - myData->myDisplay.myColorPointsFirst = Standard_False; - } - glCallList(myData->myDisplay.myColorPointsList); - } - else - { - VoxelDirection vdir = getVoxelDirection(viewnormal); - drawColorPoints(vdir, Standard_False, myData->myColorVoxels, myData->myDisplay.myColors, - myData->myDisplay.myColorMinValue, myData->myDisplay.myColorMaxValue, - xmin, xmax, ymin, ymax, zmin, zmax); - } - } - } - - // Recursive Octree Boolean points - if (myData->myROctBoolVoxels) - { - if (nearest || myData->myDisplay.myDegenerateMode) - { - VoxelDirection vdir1, vdir2, vdir3; - getVoxel3Directions(viewnormal, vdir1, vdir2, vdir3); - - if (myData->myDisplay.myUsageOfGLlists) - { - // Clean all allocated GL lists for the case of first call. - if (myData->myDisplay.myROctBoolNearestPointsFirst) - { - for (Standard_Integer idir = Xminus; idir <= Zplus; idir++) - { - if (myData->myDisplay.myROctBoolNearestPointsList[idir] > 0) - { - glDeleteLists(myData->myDisplay.myROctBoolNearestPointsList[idir], 1); - myData->myDisplay.myROctBoolNearestPointsList[idir] = -1; - } - } - myData->myDisplay.myROctBoolNearestPointsFirst = Standard_False; - } - - // Generate GL lists if needed. - if (myData->myDisplay.myROctBoolNearestPointsList[(Standard_Integer) vdir1] < 0) - { - genListIndex(myData->myDisplay.myROctBoolNearestPointsList[(Standard_Integer) vdir1]); - glNewList(myData->myDisplay.myROctBoolNearestPointsList[(Standard_Integer) vdir1], GL_COMPILE); - drawROctBoolPoints(vdir1, Standard_True, myData->myROctBoolVoxels, - xmin, xmax, ymin, ymax, zmin, zmax); - glEndList(); - } - if (myData->myDisplay.myROctBoolNearestPointsList[(Standard_Integer) vdir2] < 0) - { - genListIndex(myData->myDisplay.myROctBoolNearestPointsList[(Standard_Integer) vdir2]); - glNewList(myData->myDisplay.myROctBoolNearestPointsList[(Standard_Integer) vdir2], GL_COMPILE); - drawROctBoolPoints(vdir2, Standard_True, myData->myROctBoolVoxels, - xmin, xmax, ymin, ymax, zmin, zmax); - glEndList(); - } - if (myData->myDisplay.myROctBoolNearestPointsList[(Standard_Integer) vdir3] < 0) - { - genListIndex(myData->myDisplay.myROctBoolNearestPointsList[(Standard_Integer) vdir3]); - glNewList(myData->myDisplay.myROctBoolNearestPointsList[(Standard_Integer) vdir3], GL_COMPILE); - drawROctBoolPoints(vdir3, Standard_True, myData->myROctBoolVoxels, - xmin, xmax, ymin, ymax, zmin, zmax); - glEndList(); - } - glCallList(myData->myDisplay.myROctBoolNearestPointsList[(Standard_Integer) vdir1]); - glCallList(myData->myDisplay.myROctBoolNearestPointsList[(Standard_Integer) vdir2]); - glCallList(myData->myDisplay.myROctBoolNearestPointsList[(Standard_Integer) vdir3]); - } - else - { - drawROctBoolPoints(vdir1, Standard_True, myData->myROctBoolVoxels, - xmin, xmax, ymin, ymax, zmin, zmax); - drawROctBoolPoints(vdir2, Standard_True, myData->myROctBoolVoxels, - xmin, xmax, ymin, ymax, zmin, zmax); - drawROctBoolPoints(vdir3, Standard_True, myData->myROctBoolVoxels, - xmin, xmax, ymin, ymax, zmin, zmax); - } - } - else - { - if (myData->myDisplay.myUsageOfGLlists) - { - if (myData->myDisplay.myROctBoolPointsFirst) - { - // Delete previous GL list. - if (myData->myDisplay.myROctBoolPointsList > 0) - { - glDeleteLists(myData->myDisplay.myROctBoolPointsList, 1); - myData->myDisplay.myROctBoolPointsList = -1; - } - - // Generate a new GL list - genListIndex(myData->myDisplay.myROctBoolPointsList); - glNewList(myData->myDisplay.myROctBoolPointsList, GL_COMPILE); - VoxelDirection vdir = getVoxelDirection(viewnormal); - drawROctBoolPoints(vdir, Standard_False, myData->myROctBoolVoxels, - xmin, xmax, ymin, ymax, zmin, zmax); - glEndList(); - - // The first call has just been passed... - myData->myDisplay.myROctBoolPointsFirst = Standard_False; - } - glCallList(myData->myDisplay.myROctBoolPointsList); - } - else - { - VoxelDirection vdir = getVoxelDirection(viewnormal); - drawROctBoolPoints(vdir, Standard_False, myData->myROctBoolVoxels, - xmin, xmax, ymin, ymax, zmin, zmax); - } - } - } - - //timer.Stop(); - //Standard_Real seconds, cpu; - //Standard_Integer minutes, hours; - //timer.Show(seconds, minutes, hours, cpu); - //cout<<"DisplayPoints()"<<" took "<myDisplay.myDisplayedXMin; - Standard_Real xmax = myData->myDisplay.myDisplayedXMax; - Standard_Real ymin = myData->myDisplay.myDisplayedYMin; - Standard_Real ymax = myData->myDisplay.myDisplayedYMax; - Standard_Real zmin = myData->myDisplay.myDisplayedZMin; - Standard_Real zmax = myData->myDisplay.myDisplayedZMax; - - // Find the side of the cube which normal looks to (or out) the user's eye. - gp_Dir viewnormal; - getNormal(viewnormal); - - // Get three sides of the box looking to the user. - VoxelDirection vdir1, vdir2, vdir3; - getVoxel3Directions(viewnormal, vdir1, vdir2, vdir3); - - // Three quadrangles with normals looking to the user - gp_Pln plane1(gp::Origin(), viewnormal); - gp_Pln plane2(plane1), plane3(plane1); - - // Boolean boxes - if (myData->myBoolVoxels && - myData->myBoolVoxels->GetNbX() && - myData->myBoolVoxels->GetNbY() && - myData->myBoolVoxels->GetNbZ()) - { - // Compute size - Standard_Real dx = myData->myBoolVoxels->GetXLen() / (Standard_Real) myData->myBoolVoxels->GetNbX(); - Standard_Real dy = myData->myBoolVoxels->GetYLen() / (Standard_Real) myData->myBoolVoxels->GetNbY(); - Standard_Real dz = myData->myBoolVoxels->GetZLen() / (Standard_Real) myData->myBoolVoxels->GetNbZ(); - Standard_Real d = 0.01 * (Standard_Real) myData->myDisplay.myQuadrangleSize; - dx *= d; - dy *= d; - dz *= d; - - // Translatethe quadrangles to the side of the voxel - gp_Pnt p11, p12, p13, p14, p21, p22, p23, p24, p31, p32, p33, p34; - setPlaneNormal(vdir1, dx, dy, dz, plane1, p11, p12, p13, p14); - setPlaneNormal(vdir2, dx, dy, dz, plane2, p21, p22, p23, p24); - setPlaneNormal(vdir3, dx, dy, dz, plane3, p31, p32, p33, p34); - - // Display - Standard_Boolean skin = nearest || myData->myDisplay.myDegenerateMode; - drawBoolQuadrangles(myData->myBoolVoxels, vdir1, plane1.Axis().Direction(), - skin, p11, p12, p13, p14, - xmin, xmax, ymin, ymax, zmin, zmax); - drawBoolQuadrangles(myData->myBoolVoxels, vdir2, plane2.Axis().Direction(), - skin, p21, p22, p23, p24, - xmin, xmax, ymin, ymax, zmin, zmax); - drawBoolQuadrangles(myData->myBoolVoxels, vdir3, plane3.Axis().Direction(), - skin, p31, p32, p33, p34, - xmin, xmax, ymin, ymax, zmin, zmax); - } - // Color quadrangles - else if (myData->myColorVoxels && - myData->myColorVoxels->GetNbX() && - myData->myColorVoxels->GetNbY() && - myData->myColorVoxels->GetNbZ()) - { - // Compute size - Standard_Real dx = myData->myColorVoxels->GetXLen() / (Standard_Real) myData->myColorVoxels->GetNbX(); - Standard_Real dy = myData->myColorVoxels->GetYLen() / (Standard_Real) myData->myColorVoxels->GetNbY(); - Standard_Real dz = myData->myColorVoxels->GetZLen() / (Standard_Real) myData->myColorVoxels->GetNbZ(); - Standard_Real d = 0.01 * (Standard_Real) myData->myDisplay.myQuadrangleSize; - dx *= d; - dy *= d; - dz *= d; - - // Translatethe quadrangles to the side of the voxel - gp_Pnt p11, p12, p13, p14, p21, p22, p23, p24, p31, p32, p33, p34; - setPlaneNormal(vdir1, dx, dy, dz, plane1, p11, p12, p13, p14); - setPlaneNormal(vdir2, dx, dy, dz, plane2, p21, p22, p23, p24); - setPlaneNormal(vdir3, dx, dy, dz, plane3, p31, p32, p33, p34); - - // Display - Standard_Boolean skin = nearest || myData->myDisplay.myDegenerateMode; - drawColorQuadrangles(myData->myColorVoxels, vdir1, plane1.Axis().Direction(), skin, - myData->myDisplay.myColors, p11, p12, p13, p14, - myData->myDisplay.myColorMinValue, myData->myDisplay.myColorMaxValue, - xmin, xmax, ymin, ymax, zmin, zmax); - drawColorQuadrangles(myData->myColorVoxels, vdir2, plane2.Axis().Direction(), skin, - myData->myDisplay.myColors, p21, p22, p23, p24, - myData->myDisplay.myColorMinValue, myData->myDisplay.myColorMaxValue, - xmin, xmax, ymin, ymax, zmin, zmax); - drawColorQuadrangles(myData->myColorVoxels, vdir3, plane3.Axis().Direction(), skin, - myData->myDisplay.myColors, p31, p32, p33, p34, - myData->myDisplay.myColorMinValue, myData->myDisplay.myColorMaxValue, - xmin, xmax, ymin, ymax, zmin, zmax); - } - // Recursive Octree Boolean boxes - else if (myData->myROctBoolVoxels && - myData->myROctBoolVoxels->GetNbX() && - myData->myROctBoolVoxels->GetNbY() && - myData->myROctBoolVoxels->GetNbZ()) - { - // Compute size - Standard_Real dx = myData->myROctBoolVoxels->GetXLen() / (Standard_Real) myData->myROctBoolVoxels->GetNbX(); - Standard_Real dy = myData->myROctBoolVoxels->GetYLen() / (Standard_Real) myData->myROctBoolVoxels->GetNbY(); - Standard_Real dz = myData->myROctBoolVoxels->GetZLen() / (Standard_Real) myData->myROctBoolVoxels->GetNbZ(); - Standard_Real d = 0.01 * (Standard_Real) myData->myDisplay.myQuadrangleSize; - dx *= d; - dy *= d; - dz *= d; - - // Translatethe quadrangles to the side of the voxel - gp_Pnt p11, p12, p13, p14, p21, p22, p23, p24, p31, p32, p33, p34; - setPlaneNormal(vdir1, dx, dy, dz, plane1, p11, p12, p13, p14); - setPlaneNormal(vdir2, dx, dy, dz, plane2, p21, p22, p23, p24); - setPlaneNormal(vdir3, dx, dy, dz, plane3, p31, p32, p33, p34); - - // Display - Standard_Boolean skin = nearest || myData->myDisplay.myDegenerateMode; - drawROctBoolQuadrangles(myData->myROctBoolVoxels, vdir1, plane1.Axis().Direction(), - skin, p11, p12, p13, p14, - xmin, xmax, ymin, ymax, zmin, zmax); - drawROctBoolQuadrangles(myData->myROctBoolVoxels, vdir2, plane2.Axis().Direction(), - skin, p21, p22, p23, p24, - xmin, xmax, ymin, ymax, zmin, zmax); - drawROctBoolQuadrangles(myData->myROctBoolVoxels, vdir3, plane3.Axis().Direction(), - skin, p31, p32, p33, p34, - xmin, xmax, ymin, ymax, zmin, zmax); - } -} - -/**************************************************************************/ -void VoxelClient_VisDrawer::DisplayTriangulation(const Standard_Boolean theHighlight) -{ - if(!myData || myData->myTriangulation.IsNull()) - return; - - glEnable(GL_DEPTH_TEST); - glEnable(GL_LIGHTING); - - const TColgp_Array1OfPnt& nodes = myData->myTriangulation->Nodes(); - const Poly_Array1OfTriangle& triangles = myData->myTriangulation->Triangles(); - Standard_Integer itriangle = triangles.Lower(), nb_triangles = triangles.Upper(); - - Standard_Boolean compute_normals = Standard_False; - if (myData->myNormalsOfNodes.IsNull()) - { - compute_normals = Standard_True; - myData->myNormalsOfNodes = new TColgp_HArray1OfDir(itriangle, nb_triangles); - - // Release the GL list - if (myData->myDisplay.myTriangulationList > 0) - { - glDeleteLists(myData->myDisplay.myTriangulationList, 1); - myData->myDisplay.myTriangulationList = -1; - } - - // Generate a new GL list - if (myData->myDisplay.myUsageOfGLlists) - { - genListIndex(myData->myDisplay.myTriangulationList); - glNewList(myData->myDisplay.myTriangulationList, GL_COMPILE); - } - } - TColgp_Array1OfDir& normals = myData->myNormalsOfNodes->ChangeArray1(); - - if (!myData->myDisplay.myUsageOfGLlists || compute_normals) - { - - glBegin(GL_TRIANGLES); - - Standard_Integer n1, n2, n3; - Standard_Real x, y, z; - for (; itriangle <= nb_triangles; itriangle++) - { - const Poly_Triangle& t = triangles.Value(itriangle); - t.Get(n1, n2, n3); - - const gp_Pnt& p1 = nodes.Value(n1); - const gp_Pnt& p2 = nodes.Value(n2); - const gp_Pnt& p3 = nodes.Value(n3); - - // Make the normal: - if (compute_normals) - { - gp_Vec v1(p1, p2), v2(p1, p3); - v1.Cross(v2); - if (v1.SquareMagnitude() > 1.e-14) - v1.Normalize(); - else - v1.SetCoord(0.0, 0.0, 1.0); - normals.SetValue(itriangle, v1); - v1.Coord(x, y, z); - } - else - { - normals.Value(itriangle).Coord(x, y, z); - } - glNormal3d(x, y, z); - - // P1 - p1.Coord(x, y, z); - glVertex3d(x, y, z); - - // P2 - p2.Coord(x, y, z); - glVertex3d(x, y, z); - - // P3 - p3.Coord(x, y, z); - glVertex3d(x, y, z); - } - - glEnd(); - - if (myData->myDisplay.myUsageOfGLlists) - glEndList(); - } - - if (myData->myDisplay.myUsageOfGLlists) - glCallList(myData->myDisplay.myTriangulationList); -} - -void VoxelClient_VisDrawer::HighlightVoxel() -{ - if (myData && - myData->myDisplay.myHighlightx >= 0 && - myData->myDisplay.myHighlighty >= 0 && - myData->myDisplay.myHighlightz >= 0) - { - Standard_Integer nbx, nby, nbz; - Standard_Real xlen, ylen, zlen, xc, yc, zc; - Voxel_DS* ds = (Voxel_DS*) myData->myBoolVoxels; - if (myData->myColorVoxels) - ds = (Voxel_DS*) myData->myColorVoxels; - if (myData->myROctBoolVoxels) - ds = (Voxel_DS*) myData->myROctBoolVoxels; - nbx = ds->GetNbX(); - nby = ds->GetNbY(); - nbz = ds->GetNbZ(); - xlen = ds->GetXLen(); - ylen = ds->GetYLen(); - zlen = ds->GetZLen(); - ds->GetCenter(myData->myDisplay.myHighlightx, - myData->myDisplay.myHighlighty, - myData->myDisplay.myHighlightz, - xc, yc, zc); - - Standard_Real half_voxelx = xlen / Standard_Real(nbx) / 2.0; - Standard_Real half_voxely = ylen / Standard_Real(nby) / 2.0; - Standard_Real half_voxelz = zlen / Standard_Real(nbz) / 2.0; - Standard_Real x1 = xc - half_voxelx, y1 = yc - half_voxely, z1 = zc - half_voxelz; - Standard_Real x2 = xc + half_voxelx, y2 = yc + half_voxely, z2 = zc + half_voxelz; - - setColor(Quantity_NOC_BLUE1, Standard_True); - setTypeOfLine(Aspect_TOL_SOLID); - setWidthOfLine(3); - - glBegin(GL_LINES); - - glVertex3d(x1, y1, z1); - glVertex3d(x1, y2, z1); - - glVertex3d(x1, y1, z1); - glVertex3d(x2, y1, z1); - - glVertex3d(x1, y1, z1); - glVertex3d(x1, y1, z2); - - glVertex3d(x1, y2, z1); - glVertex3d(x2, y2, z1); - - glVertex3d(x2, y1, z1); - glVertex3d(x2, y2, z1); - - glVertex3d(x2, y2, z1); - glVertex3d(x2, y2, z2); - - glVertex3d(x1, y1, z2); - glVertex3d(x1, y2, z2); - - glVertex3d(x1, y1, z2); - glVertex3d(x2, y1, z2); - - glVertex3d(x2, y1, z2); - glVertex3d(x2, y2, z2); - - glVertex3d(x2, y2, z2); - glVertex3d(x1, y2, z2); - - glVertex3d(x1, y2, z2); - glVertex3d(x1, y2, z1); - - glVertex3d(x2, y1, z1); - glVertex3d(x2, y1, z2); - - glEnd(); - } -} diff --git a/src/OS/Visualization.tcl b/src/OS/Visualization.tcl index 85bdfe9d27..a817f4ce3e 100644 --- a/src/OS/Visualization.tcl +++ b/src/OS/Visualization.tcl @@ -18,8 +18,7 @@ proc Visualization:toolkits { } { set aResult [list TKService \ TKV3d \ TKOpenGl \ - TKMeshVS \ - TKVoxel] + TKMeshVS] if { [info exists ::env(HAVE_VTK)] && "$::env(HAVE_VTK)" == "true" } { lappend aResult "TKIVtk" diff --git a/src/QABugs/QABugs_19.cxx b/src/QABugs/QABugs_19.cxx index f72eb665fb..3189c31ebb 100644 --- a/src/QABugs/QABugs_19.cxx +++ b/src/QABugs/QABugs_19.cxx @@ -691,53 +691,6 @@ static Standard_Integer OCC23945 (Draw_Interpretor& /*di*/,Standard_Integer n, c return 0; } -#include -#include -#include -static Standard_Integer OCC24019 (Draw_Interpretor& di, Standard_Integer argc, const char** argv) -{ - if ( argc != 2 ) { - di << "Error: " << argv[0] << " - invalid number of arguments" << "\n"; - return 1; - } - - TCollection_AsciiString aFileName = argv[1]; - TopoDS_Shape aShape; - BRep_Builder aBuilder; - - if (!BRepTools::Read(aShape, aFileName.ToCString(), aBuilder)) { - di << "Error: Could not read a shape!" << "\n"; - return 1; - } - - TopoDS_Solid aShape1 = BRepPrimAPI_MakeSphere(gp_Pnt(20,25,35), 7); - - Standard_Real deflection = 0.005; - Standard_Integer nbThreads = 1; - Standard_Integer nbx = 200, nby = 200, nbz = 200; - Voxel_BoolDS theVoxels(0,0,0, 50, 50, 50, nbx, nby, nbz); - Voxel_BoolDS theVoxels1(0,0,0, 50, 50, 50, nbx, nby, nbz); - - Standard_Integer progress = 0; - Voxel_FastConverter fcp(aShape, theVoxels, deflection, nbx, nby, nbz, nbThreads); - fcp.ConvertUsingSAT(progress, 1); - fcp.FillInVolume(1); - - Voxel_FastConverter fcp1(aShape1, theVoxels1, deflection, nbx, nby, nbz, nbThreads); - fcp1.ConvertUsingSAT(progress, 1); - fcp1.FillInVolume(1); - - Voxel_BooleanOperation op; - Standard_Boolean result = op.Cut(theVoxels1, theVoxels); - if ( result != 1 ) { - di << "Error: invalid boolean operation" << "\n"; - } else { - di << "OK: boolean operation is ok" << "\n"; - } - - return 0; -} - //======================================================================= //function : OCC11758 //purpose : @@ -1331,34 +1284,6 @@ static Standard_Integer OCC24012 (Draw_Interpretor& di, Standard_Integer argc, c return 0; } -#include -static Standard_Integer OCC24051 (Draw_Interpretor& di, Standard_Integer argc, const char ** argv) -{ - if (argc != 1) { - di << "Usage : " << argv[0] << " should be one argument (command name only)"; - return 1; - } - - TopoDS_Shape shape = BRepPrimAPI_MakeBox(gp_Pnt(5, 10, 10), 10, 20, 30).Shape(); - Standard_Integer progress = 0; - Standard_Real deflection = 0.005; - Standard_Integer nbx = 200, nby = 200, nbz = 200; - Voxel_BoolDS theVoxels(-50,-50,-30, 100, 100, 100, nbx, nby, nbz); - Voxel_BoolDS theVoxels1(-50,-50,-30, 100, 100, 100, nbx, nby, nbz); - Standard_Integer nbThreads = 5; - Voxel_FastConverter fcp(shape, theVoxels, deflection, nbx, nby, nbz, nbThreads, Standard_True); - - #ifdef _MSC_VER - #pragma omp parallel for - for(int i = 0; i < nbThreads; i++) - fcp.ConvertUsingSAT(progress, i+1); - #endif - - fcp.ConvertUsingSAT(progress); - - return 0; -} - #include #include #include @@ -4635,7 +4560,6 @@ void QABugs::Commands_19(Draw_Interpretor& theCommands) { theCommands.Add ("test_offset", "test_offset", __FILE__, test_offset, group); theCommands.Add ("OCC23945", "OCC23945 surfname U V X Y Z [DUX DUY DUZ DVX DVY DVZ [D2UX D2UY D2UZ D2VX D2VY D2VZ D2UVX D2UVY D2UVZ]]", __FILE__, OCC23945,group); theCommands.Add ("OCC24008", "OCC24008 curve surface", __FILE__, OCC24008, group); - theCommands.Add ("OCC24019", "OCC24019 aShape", __FILE__, OCC24019, group); theCommands.Add ("OCC11758", "OCC11758", __FILE__, OCC11758, group); theCommands.Add ("OCC24005", "OCC24005 result", __FILE__, OCC24005, group); theCommands.Add ("OCC24137", "OCC24137 face vertex U V [N]", __FILE__, OCC24137, group); @@ -4644,7 +4568,6 @@ void QABugs::Commands_19(Draw_Interpretor& theCommands) { theCommands.Add ("OCC24370", "OCC24370 edge pcurve surface prec", __FILE__, OCC24370, group); theCommands.Add ("OCC24533", "OCC24533", __FILE__, OCC24533, group); theCommands.Add ("OCC24012", "OCC24012 face edge", __FILE__, OCC24012, group); - theCommands.Add ("OCC24051", "OCC24051", __FILE__, OCC24051, group); theCommands.Add ("OCC24086", "OCC24086 face wire", __FILE__, OCC24086, group); theCommands.Add ("OCC24622", "OCC24622 texture={1D|2D}\n Tests sourcing of 1D/2D pixmaps for AIS_TexturedShape", __FILE__, OCC24622, group); theCommands.Add ("OCC24667", "OCC24667 result Wire_spine Profile [Mode [Approx]], no args to get help", __FILE__, OCC24667, group); diff --git a/src/TKQADraw/EXTERNLIB b/src/TKQADraw/EXTERNLIB index 8bbf06a049..122b92fca4 100755 --- a/src/TKQADraw/EXTERNLIB +++ b/src/TKQADraw/EXTERNLIB @@ -31,7 +31,6 @@ TKSTEPBase TKXDESTEP TKXSDRAW TKSTL -TKVoxel CSF_gdi32 CSF_advapi32 CSF_user32 diff --git a/src/TKViewerTest/EXTERNLIB b/src/TKViewerTest/EXTERNLIB index 329892d6ad..dab82959d3 100755 --- a/src/TKViewerTest/EXTERNLIB +++ b/src/TKViewerTest/EXTERNLIB @@ -14,7 +14,6 @@ TKG2d TKTopTest TKG3d TKOffset -TKVoxel TKMesh TKV3d TKDraw diff --git a/src/TKVoxel/CMakeLists.txt b/src/TKVoxel/CMakeLists.txt deleted file mode 100644 index 2f65e0a898..0000000000 --- a/src/TKVoxel/CMakeLists.txt +++ /dev/null @@ -1,7 +0,0 @@ -project(TKVoxel) - -set (TOOLKIT_MODULES - Voxel -) - -OCCT_INCLUDE_CMAKE_FILE (adm/cmake/occt_toolkit) diff --git a/src/TKVoxel/EXTERNLIB b/src/TKVoxel/EXTERNLIB deleted file mode 100644 index 0a06c3417c..0000000000 --- a/src/TKVoxel/EXTERNLIB +++ /dev/null @@ -1,10 +0,0 @@ -TKBRep -TKernel -TKV3d -TKMath -TKService -TKGeomBase -TKG2d -TKTopAlgo -TKG3d -TKMesh diff --git a/src/TKVoxel/FILES b/src/TKVoxel/FILES deleted file mode 100644 index ee6f7057f7..0000000000 --- a/src/TKVoxel/FILES +++ /dev/null @@ -1 +0,0 @@ -EXTERNLIB diff --git a/src/TKVoxel/PACKAGES b/src/TKVoxel/PACKAGES deleted file mode 100755 index a04bc5d863..0000000000 --- a/src/TKVoxel/PACKAGES +++ /dev/null @@ -1 +0,0 @@ -Voxel diff --git a/src/ViewerTest/FILES b/src/ViewerTest/FILES index a60d157500..6706686d80 100755 --- a/src/ViewerTest/FILES +++ b/src/ViewerTest/FILES @@ -16,4 +16,3 @@ ViewerTest_OpenGlCommands.cxx ViewerTest_RelationCommands.cxx ViewerTest_ViewerCommands.cxx ViewerTest_ViewerCommands_1.mm -ViewerTest_VoxelCommands.cxx diff --git a/src/ViewerTest/ViewerTest.cxx b/src/ViewerTest/ViewerTest.cxx index 69e3e017d7..d59dd06ac4 100644 --- a/src/ViewerTest/ViewerTest.cxx +++ b/src/ViewerTest/ViewerTest.cxx @@ -5467,7 +5467,6 @@ void ViewerTest::Commands(Draw_Interpretor& theCommands) ViewerTest::RelationCommands(theCommands); ViewerTest::ObjectCommands(theCommands); ViewerTest::FilletCommands(theCommands); - ViewerTest::VoxelCommands(theCommands); ViewerTest::OpenGlCommands(theCommands); const char *group = "AIS_Display"; diff --git a/src/ViewerTest/ViewerTest.hxx b/src/ViewerTest/ViewerTest.hxx index 266e68a252..0f26ac11d9 100644 --- a/src/ViewerTest/ViewerTest.hxx +++ b/src/ViewerTest/ViewerTest.hxx @@ -130,8 +130,6 @@ public: Standard_EXPORT static void FilletCommands (Draw_Interpretor& theCommands); - Standard_EXPORT static void VoxelCommands (Draw_Interpretor& theCommands); - Standard_EXPORT static void OpenGlCommands (Draw_Interpretor& theCommands); Standard_EXPORT static void GetMousePosition (Standard_Integer& xpix, Standard_Integer& ypix); diff --git a/src/ViewerTest/ViewerTest_VoxelCommands.cxx b/src/ViewerTest/ViewerTest_VoxelCommands.cxx deleted file mode 100644 index 41c3280473..0000000000 --- a/src/ViewerTest/ViewerTest_VoxelCommands.cxx +++ /dev/null @@ -1,1053 +0,0 @@ -// Created on: 2008-09-17 -// Created by: Vlad Romashko -// Copyright (c) 2008-2014 OPEN CASCADE SAS -// -// This file is part of Open CASCADE Technology software library. -// -// This library is free software; you can redistribute it and/or modify it under -// the terms of the GNU Lesser General Public License version 2.1 as published -// by the Free Software Foundation, with special exception defined in the file -// OCCT_LGPL_EXCEPTION.txt. Consult the file LICENSE_LGPL_21.txt included in OCCT -// distribution for complete text of the license and disclaimer of any warranty. -// -// Alternatively, this file may be used under the terms of Open CASCADE -// commercial license or contractual agreement. - -#include - -#include -#include -#include - -#include -#include -#include -#include -#include -#include -#include - -// A method parses the input parameters. -static Standard_Boolean GetInputParameters(Standard_Integer nbargs, const char** args, - Standard_Real& x, Standard_Real& y, Standard_Real& z, - Standard_Real& dx, Standard_Real& dy, Standard_Real& dz, - Standard_Integer& nbx, Standard_Integer& nby, Standard_Integer& nbz) -{ - // Default values. - nbx = 10; nby = 10; nbz = 10; - x = 0.0; y = 0.0; z = 0.0; - dx = 1.0; dy = 1.0; dz = 1.0; - - // "voxelboolds 0 0 0 1 1 1 100 100 100" - if (nbargs == 10) - { - nbx = Draw::Atoi(args[7]); - nby = Draw::Atoi(args[8]); - nbz = Draw::Atoi(args[9]); - } - // "voxelboolds 0 0 0 1 1 1" - if (nbargs == 7 || nbargs == 10) - { - dx = Draw::Atof(args[4]); - dy = Draw::Atof(args[5]); - dz = Draw::Atof(args[6]); - } - // "voxelboolds 0 0 0" - if (nbargs == 4 || nbargs == 7 || nbargs == 10) - { - x = Draw::Atof(args[1]); - y = Draw::Atof(args[2]); - z = Draw::Atof(args[3]); - } - // "voxelboolds" - if (nbargs == 1) - { - // use default parameters - } - - return nbargs == 1 || nbargs == 4 || nbargs == 7 || nbargs == 10; -} - -static Standard_Integer VOXELBOOLDS(Draw_Interpretor& di, Standard_Integer nbargs, const char** args) -{ - // Parse input parameters. - Standard_Real x, y, z, dx, dy, dz; - Standard_Integer ix, iy, iz, nbx, nby, nbz; - if (!GetInputParameters(nbargs, args, x, y, z, dx, dy, dz, nbx, nby, nbz)) - { - di << "Usage: voxelboolds [x, y, z, dx, dy, dz nbx, nby, nbz]"; - return 1; - } - - // 1. BoolDS: - Voxel_BoolDS ds(x, y, z, dx, dy, dz, nbx, nby, nbz); - - for (ix = 0; ix < nbx; ix++) - { - for (iy = 0; iy < nby; iy++) - { - for (iz = 0; iz < nbz; iz++) - { - if (ix & 0x01) - ds.Set(ix, iy, iz, Standard_False); - else - ds.Set(ix, iy, iz, Standard_True); - } - } - } - - for (ix = 0; ix < nbx; ix++) - { - for (iy = 0; iy < nby; iy++) - { - for (iz = 0; iz < nbz; iz++) - { - Standard_Boolean value = ds.Get(ix, iy, iz) == Standard_True; - if (ix & 0x01) - { - if (value != Standard_False) - { - di<<"error : voxelboolds"; - return 1; // error - } - } - else - { - if (value != Standard_True) - { - di<<"error : voxelboolds"; - return 1; // error - } - } - } - } - } - - return 0; -} - -static Standard_Integer VOXELCOLORDS(Draw_Interpretor& di, Standard_Integer nbargs, const char** args) -{ - // Parse input parameters. - Standard_Real x, y, z, dx, dy, dz; - Standard_Integer ix, iy, iz, nbx, nby, nbz; - if (!GetInputParameters(nbargs, args, x, y, z, dx, dy, dz, nbx, nby, nbz)) - { - di << "Usage: voxelcolords [x, y, z, dx, dy, dz nbx, nby, nbz]"; - return 1; - } - - // 1. ColorDS: - Voxel_ColorDS ds(x, y, z, dx, dy, dz, nbx, nby, nbz); - - for (ix = 0; ix < nbx; ix++) - { - for (iy = 0; iy < nby; iy++) - { - for (iz = 0; iz < nbz; iz++) - { - if (ix & 0x01) - ds.Set(ix, iy, iz, 8); - else - ds.Set(ix, iy, iz, 7); - } - } - } - - for (ix = 0; ix < nbx; ix++) - { - for (iy = 0; iy < nby; iy++) - { - for (iz = 0; iz < nbz; iz++) - { - Standard_Byte value = ds.Get(ix, iy, iz); - if (ix & 0x01) - { - if (value != 8) - { - di<<"error : voxelcolords"; - return 1; - } - } - else - { - if (value != 7) - { - di<<"error : voxelcolords"; - return 1; - } - } - } - } - } - - return 0; -} - -static Standard_Integer VOXELFLOATDS(Draw_Interpretor& di, Standard_Integer nbargs, const char** args) -{ - // Parse input parameters. - Standard_Real x, y, z, dx, dy, dz; - Standard_Integer ix, iy, iz, nbx, nby, nbz; - if (!GetInputParameters(nbargs, args, x, y, z, dx, dy, dz, nbx, nby, nbz)) - { - di << "Usage: voxelfloatds [x, y, z, dx, dy, dz nbx, nby, nbz]"; - return 1; - } - - // 1. FloatDS: - Voxel_FloatDS ds(x, y, z, dx, dy, dz, nbx, nby, nbz); - - for (ix = 0; ix < nbx; ix++) - { - for (iy = 0; iy < nby; iy++) - { - for (iz = 0; iz < nbz; iz++) - { - if (ix & 0x01) - ds.Set(ix, iy, iz, 8.8f); - else - ds.Set(ix, iy, iz, 7.7f); - } - } - } - - /* - // Write the voxels - Voxel_Writer writer; - writer.SetFormat(Voxel_VFF_BINARY); - writer.SetVoxels(ds); - if (!writer.Write("W:\\OCC621\\samples\\standard\\voxeldemo\\f.vx")) - { - statusBar()->message( "Storage failed... sorry", 2000 ); - return; - } - - // Read the voxels - Voxel_Reader reader; - if (!reader.Read("W:\\OCC621\\samples\\standard\\voxeldemo\\f.vx")) - { - statusBar()->message( "Open failed... sorry", 2000 ); - return; - } - */ - - for (ix = 0; ix < nbx; ix++) - { - for (iy = 0; iy < nby; iy++) - { - for (iz = 0; iz < nbz; iz++) - { - float value = ds.Get(ix, iy, iz); - if (ix & 0x01) - { - if (value != 8.8f) - { - di<<"error : voxelfloatds"; - return 1; - } - } - else - { - if (value != 7.7f) - { - di<<"error : voxelfloatds"; - return 1; - } - } - } - } - } - - return 0; -} - -static Standard_Integer VOXELOCTBOOLDS(Draw_Interpretor& di, Standard_Integer nbargs, const char** args) -{ - // Parse input parameters. - Standard_Real x, y, z, dx, dy, dz; - Standard_Integer ix, iy, iz, nbx, nby, nbz; - if (!GetInputParameters(nbargs, args, x, y, z, dx, dy, dz, nbx, nby, nbz)) - { - di << "Usage: voxeloctboolds [x, y, z, dx, dy, dz nbx, nby, nbz]"; - return 1; - } - - // 1. OctBoolDS: - Voxel_OctBoolDS ds(x, y, z, dx, dy, dz, nbx, nby, nbz); - - for (ix = 0; ix < nbx; ix++) - { - for (iy = 0; iy < nby; iy++) - { - for (iz = 0; iz < nbz; iz++) - { - if (ix & 0x01) - { - ds.Set(ix, iy, iz, Standard_True); - } - else - { - for (Standard_Integer i = 0; i < 8; i++) - { - if (i & 0x01) - ds.Set(ix, iy, iz, i, Standard_True); - else - ds.Set(ix, iy, iz, i, Standard_False); - } - } - } - } - } - - for (ix = 0; ix < nbx; ix++) - { - for (iy = 0; iy < nby; iy++) - { - for (iz = 0; iz < nbz; iz++) - { - if (ix & 0x01) - { - Standard_Boolean value = ds.Get(ix, iy, iz) == Standard_True; - if (value != Standard_True) - cout<<"Wrong value!"< 0.001) - { - di<<"error : voxelfusefloatds"; - return 1; - } - } - else - { - if (fabs(value - 16.6f) > 0.001) - { - di<<"error : voxelfusefloatds"; - return 1; - } - } - } - } - } - - return 0; -} - -static Standard_Integer VOXELCUTBOOLDS(Draw_Interpretor& di, Standard_Integer nbargs, const char** args) -{ - // Parse input parameters. - Standard_Real x, y, z, dx, dy, dz; - Standard_Integer ix, iy, iz, nbx, nby, nbz; - if (!GetInputParameters(nbargs, args, x, y, z, dx, dy, dz, nbx, nby, nbz)) - { - di << "Usage: voxelcutboolds [x, y, z, dx, dy, dz nbx, nby, nbz]"; - return 1; - } - - // 1. Set two BoolDS: - Voxel_BoolDS ds1(x, y, z, dx, dy, dz, nbx, nby, nbz); - Voxel_BoolDS ds2(x, y, z, dx, dy, dz, nbx, nby, nbz); - - for (ix = 0; ix < nbx; ix++) - { - for (iy = 0; iy < nby; iy++) - { - for (iz = 0; iz < nbz; iz++) - { - ds1.Set(ix, iy, iz, Standard_True); - } - } - } - for (ix = 0; ix < nbx; ix++) - { - for (iy = 0; iy < nby; iy++) - { - for (iz = 0; iz < nbz; iz++) - { - if (ix & 0x01) - ds2.Set(ix, iy, iz, Standard_False); - else - ds2.Set(ix, iy, iz, Standard_True); - } - } - } - - // 2. Cut them - - Voxel_BooleanOperation cutter; - if (!cutter.Cut(ds1, ds2)) - { - di<<"error : voxelcutboolds"; - return 1; - } - - // 3. Check result - - for (ix = 0; ix < nbx; ix++) - { - for (iy = 0; iy < nby; iy++) - { - for (iz = 0; iz < nbz; iz++) - { - Standard_Boolean value = ds1.Get(ix, iy, iz) == Standard_True; - if (ix & 0x01) - { - if (value != Standard_True) - { - di<<"error : voxelcutboolds"; - return 1; - } - } - else - { - if (value != Standard_False) - { - di<<"error : voxelcutboolds"; - return 1; - } - } - } - } - } - - return 0; -} - -static Standard_Integer VOXELCUTCOLORDS(Draw_Interpretor& di, Standard_Integer nbargs, const char** args) -{ - // Parse input parameters. - Standard_Real x, y, z, dx, dy, dz; - Standard_Integer ix, iy, iz, nbx, nby, nbz; - if (!GetInputParameters(nbargs, args, x, y, z, dx, dy, dz, nbx, nby, nbz)) - { - di << "Usage: voxelcutcolords [x, y, z, dx, dy, dz nbx, nby, nbz]"; - return 1; - } - - // 1. Set two ColorDS: - Voxel_ColorDS ds1(x, y, z, dx, dy, dz, nbx, nby, nbz); - Voxel_ColorDS ds2(x, y, z, dx, dy, dz, nbx, nby, nbz); - - for (ix = 0; ix < nbx; ix++) - { - for (iy = 0; iy < nby; iy++) - { - for (iz = 0; iz < nbz; iz++) - { - ds1.Set(ix, iy, iz, 11); - } - } - } - for (ix = 0; ix < nbx; ix++) - { - for (iy = 0; iy < nby; iy++) - { - for (iz = 0; iz < nbz; iz++) - { - if (ix & 0x01) - ds2.Set(ix, iy, iz, 3); - else - ds2.Set(ix, iy, iz, 5); - } - } - } - - // 2. Cut them - - Voxel_BooleanOperation cutter; - if (!cutter.Cut(ds1, ds2)) - { - di<<"error : voxelcutcolords"; - return 1; - } - - // 3. Check result - - for (ix = 0; ix < nbx; ix++) - { - for (iy = 0; iy < nby; iy++) - { - for (iz = 0; iz < nbz; iz++) - { - Standard_Byte value = ds1.Get(ix, iy, iz); - if (ix & 0x01) - { - if (value != 8) - { - di<<"error : voxelcutcolords"; - return 1; - } - } - else - { - if (value != 6) - { - di<<"error : voxelcutcolords"; - return 1; - } - } - } - } - } - - return 0; -} - -static Standard_Integer VOXELCUTFLOATDS(Draw_Interpretor& di, Standard_Integer nbargs, const char** args) -{ - // Parse input parameters. - Standard_Real x, y, z, dx, dy, dz; - Standard_Integer ix, iy, iz, nbx, nby, nbz; - if (!GetInputParameters(nbargs, args, x, y, z, dx, dy, dz, nbx, nby, nbz)) - { - di << "Usage: voxelcutfloatds [x, y, z, dx, dy, dz nbx, nby, nbz]"; - return 1; - } - - // 1. Set two FloatDS: - Voxel_FloatDS ds1(x, y, z, dx, dy, dz, nbx, nby, nbz); - Voxel_FloatDS ds2(x, y, z, dx, dy, dz, nbx, nby, nbz); - - for (ix = 0; ix < nbx; ix++) - { - for (iy = 0; iy < nby; iy++) - { - for (iz = 0; iz < nbz; iz++) - { - ds1.Set(ix, iy, iz, 11.1f); - } - } - } - for (ix = 0; ix < nbx; ix++) - { - for (iy = 0; iy < nby; iy++) - { - for (iz = 0; iz < nbz; iz++) - { - if (ix & 0x01) - ds2.Set(ix, iy, iz, 3.3f); - else - ds2.Set(ix, iy, iz, 5.5f); - } - } - } - - // 2. Cut them - - Voxel_BooleanOperation cutter; - if (!cutter.Cut(ds1, ds2)) - { - di<<"error : voxelcutcolords"; - return 1; - } - - // 3. Check result - - for (ix = 0; ix < nbx; ix++) - { - for (iy = 0; iy < nby; iy++) - { - for (iz = 0; iz < nbz; iz++) - { - float value = ds1.Get(ix, iy, iz); - if (ix & 0x01) - { - if (fabs(value - 7.8f) > 0.001) - { - di<<"error : voxelcutcolords"; - return 1; - } - } - else - { - if (fabs(value - 5.6f) > 0.001) - { - di<<"error : voxelcutcolords"; - return 1; - } - } - } - } - } - - return 0; -} - -static Standard_Integer VOXELBOOLDSCONVERT(Draw_Interpretor& di, Standard_Integer nbargs, const char** args) -{ - TopoDS_Shape S; - Standard_Integer nbx = 100, nby = 100, nbz = 100; - Standard_Real deflection = 0.1; - Standard_Boolean fast = Standard_True; - Standard_Integer nbthreads = 1; - Standard_Boolean usetriangulation = Standard_False; - Standard_Integer fillInVolume = 0; - - if (nbargs < 2) - { - di << "Usage: voxelbooldsconvert shape [nbx nby nbz deflection 0|1(fast or accurate) nbthreads usetriangulation \ - 0|1|2(fill-in volume: no|yes|yes using shape)]"; - return 1; - } - - // Get shape for conversion. - S = DBRep::Get(args[1]); - - if (nbargs > 2) - { - if (nbargs >= 5) - { - nbx = Draw::Atoi(args[2]); - nby = Draw::Atoi(args[3]); - nbz = Draw::Atoi(args[4]); - } - if (nbargs >= 6) - { - deflection = Draw::Atof(args[5]); - } - if (nbargs >= 7) - { - // 0 means fast, - // 1 means accurate. - fast = Draw::Atoi(args[6]) == 0 ? Standard_True : Standard_False; - } - if (nbargs >= 8) - { - nbthreads = Draw::Atoi(args[7]); - if (nbthreads < 1) - { - di << "Wrong number of threads: 1 .. nb cores"; - return 1; - } - else if (nbthreads > 100) // seems too much... - { - di << "Too many threads..."; - } - } - if (nbargs >= 9) - { - usetriangulation = Draw::Atoi(args[8]) == 0 ? Standard_False : Standard_True; - } - if (nbargs >= 10) - { - fillInVolume = Draw::Atoi(args[9]); - if (fillInVolume < 0 || fillInVolume > 2) - { - di << "Fill-in volume parameter accepts the values: 0 - no, 1 - yes, 2 - yes wusing shape"; - return 1; - } - } - } - - // Call converter. - Voxel_BoolDS ds; - Standard_Boolean ret; - Standard_Integer progress = 0; - Voxel_FastConverter converter(S, ds, deflection, nbx, nby, nbz, nbthreads, usetriangulation); - - // Convert. - if (fast) - ret = converter.Convert(progress); - else - ret = converter.ConvertUsingSAT(progress); - - // Fill-in volume. - if (ret) - { - switch (fillInVolume) - { - case 0: - break; - case 1: - converter.FillInVolume(1, nbthreads); - break; - case 2: - converter.FillInVolume(1, S, nbthreads); - break; - } - } - - return ret == Standard_True ? 0 : 1; -} - -void ViewerTest::VoxelCommands(Draw_Interpretor& theCommands) -{ - const char* g = "Voxel draw-commands"; - // Basic commands. - theCommands.Add("voxelboolds" ,"voxelboolds [x y z dx dy dz nbx nby nbz]" ,__FILE__,VOXELBOOLDS,g); - theCommands.Add("voxelcolords" ,"voxelcolords [x y z dx dy dz nbx nby nbz]" ,__FILE__,VOXELCOLORDS,g); - theCommands.Add("voxelfloatds" ,"voxelfloatds [x y z dx dy dz nbx nby nbz]" ,__FILE__,VOXELFLOATDS,g); - theCommands.Add("voxeloctboolds" ,"voxeloctboolds [x y z dx dy dz nbx nby nbz]" ,__FILE__,VOXELOCTBOOLDS,g); - theCommands.Add("voxelroctboolds" ,"voxelroctboolds [x y z dx dy dz nbx nby nbz]" ,__FILE__,VOXELROCTBOOLDS,g); - // Boolean operations. - theCommands.Add("voxelfuseboolds" ,"voxelfuseboolds [x y z dx dy dz nbx nby nbz]" ,__FILE__,VOXELFUSEBOOLDS,g); - theCommands.Add("voxelfusecolords","voxelfusecolords [x y z dx dy dz nbx nby nbz]",__FILE__,VOXELFUSECOLORDS,g); - theCommands.Add("voxelfusefloatds","voxelfusefloatds [x y z dx dy dz nbx nby nbz]",__FILE__,VOXELFUSEFLOATDS,g); - theCommands.Add("voxelcutboolds" ,"voxelcutboolds [x y z dx dy dz nbx nby nbz]" ,__FILE__,VOXELCUTBOOLDS,g); - theCommands.Add("voxelcutcolords" ,"voxelcutcolords [x y z dx dy dz nbx nby nbz]" ,__FILE__,VOXELCUTCOLORDS,g); - theCommands.Add("voxelcutfloatds" ,"voxelcutfloatds [x y z dx dy dz nbx nby nbz]" ,__FILE__,VOXELCUTFLOATDS,g); - // Conversion of a shape into voxels. - theCommands.Add("voxelbooldsconvert", - "voxelbooldsconvert shape [nbx nby nbz deflection 0|1(fast or accurate) nbthreads usetriangulation \ - 0|1|2(fill-in volume: no|yes|yes using shape)]", - __FILE__,VOXELBOOLDSCONVERT,g); -} diff --git a/src/Voxel/FILES b/src/Voxel/FILES deleted file mode 100755 index 1271ed4fa5..0000000000 --- a/src/Voxel/FILES +++ /dev/null @@ -1,32 +0,0 @@ -Voxel_BoolDS.cxx -Voxel_BoolDS.hxx -Voxel_BooleanOperation.cxx -Voxel_BooleanOperation.hxx -Voxel_CollisionDetection.cxx -Voxel_CollisionDetection.hxx -Voxel_ColorDS.cxx -Voxel_ColorDS.hxx -Voxel_DS.cxx -Voxel_DS.hxx -Voxel_FastConverter.cxx -Voxel_FastConverter.hxx -Voxel_FloatDS.cxx -Voxel_FloatDS.hxx -Voxel_OctBoolDS.cxx -Voxel_OctBoolDS.hxx -Voxel_Prs.cxx -Voxel_Prs.hxx -Voxel_Reader.cxx -Voxel_Reader.hxx -Voxel_ROctBoolDS.cxx -Voxel_ROctBoolDS.hxx -Voxel_Selector.cxx -Voxel_Selector.hxx -Voxel_SplitData.cxx -Voxel_SplitData.hxx -Voxel_TypeDef.hxx -Voxel_VisData.h -Voxel_VoxelDisplayMode.hxx -Voxel_VoxelFileFormat.hxx -Voxel_Writer.cxx -Voxel_Writer.hxx diff --git a/src/Voxel/Voxel_BoolDS.cxx b/src/Voxel/Voxel_BoolDS.cxx deleted file mode 100644 index 20adea5b74..0000000000 --- a/src/Voxel/Voxel_BoolDS.cxx +++ /dev/null @@ -1,141 +0,0 @@ -// Created on: 2008-06-21 -// Created by: Vladislav ROMASHKO -// Copyright (c) 2008-2014 OPEN CASCADE SAS -// -// This file is part of Open CASCADE Technology software library. -// -// This library is free software; you can redistribute it and/or modify it under -// the terms of the GNU Lesser General Public License version 2.1 as published -// by the Free Software Foundation, with special exception defined in the file -// OCCT_LGPL_EXCEPTION.txt. Consult the file LICENSE_LGPL_21.txt included in OCCT -// distribution for complete text of the license and disclaimer of any warranty. -// -// Alternatively, this file may be used under the terms of Open CASCADE -// commercial license or contractual agreement. - - -#include - -#include -static Standard_Byte gbits[8] = {1, 2, 4, 8, 16, 32, 64, 128}; -static Standard_Byte gnbits[8] = {255-1, 255-2, 255-4, 255-8, 255-16, 255-32, 255-64, 255-128}; - -// Empty constructor -Voxel_BoolDS::Voxel_BoolDS():Voxel_DS() -{ - -} - -// Constructor with intialization. -Voxel_BoolDS::Voxel_BoolDS(const Standard_Real x, const Standard_Real y, const Standard_Real z, - const Standard_Real xlen, const Standard_Real ylen, const Standard_Real zlen, - const Standard_Integer nbx, const Standard_Integer nby, const Standard_Integer nbz) -:Voxel_DS() -{ - Init(x, y, z, xlen, ylen, zlen, nbx, nby, nbz); -} - -// Initialization. -void Voxel_BoolDS::Init(const Standard_Real x, const Standard_Real y, const Standard_Real z, - const Standard_Real xlen, const Standard_Real ylen, const Standard_Real zlen, - const Standard_Integer nbx, const Standard_Integer nby, const Standard_Integer nbz) -{ - Destroy(); - - Voxel_DS::Init(x, y, z, xlen, ylen, zlen, nbx, nby, nbz); - - if (!myNbX || !myNbY || !myNbZ) - return; - - Standard_Integer nb_bytes = RealToInt(ceil(myNbXY * myNbZ / 8.0)); - Standard_Integer nb_slices = RealToInt(ceil(nb_bytes / 8.0)); - myData = (Standard_Address) calloc(nb_slices, sizeof(Standard_Byte*)); -} - -// Destructor -void Voxel_BoolDS::Destroy() -{ - if (myData) - { - SetZero(); - free((Standard_Byte**)myData); - myData = 0; - } -} - -void Voxel_BoolDS::SetZero() -{ - if (myData) - { - Standard_Integer nb_bytes = RealToInt(ceil(myNbXY * myNbZ / 8.0)); - Standard_Integer ix = 0, nb_slices = RealToInt(ceil(nb_bytes / 8.0)); - for (; ix < nb_slices; ix++) - { - if (((Standard_Byte**)myData)[ix]) - { - free(((Standard_Byte**)myData)[ix]); - ((Standard_Byte**)myData)[ix] = 0; - } - } - } -} - -// Access to the boolean information attached to a particular voxel: -// Info: (ix >= 0 && ix < theNb_x), etc. -void Voxel_BoolDS::Set(const Standard_Integer ix, const Standard_Integer iy, const Standard_Integer iz, - const Standard_Boolean data) -{ - Standard_Integer ibit = ix + myNbX * iy + myNbXY * iz; - Standard_Integer islice = ibit >> 6; - - if (!data && !((Standard_Byte**)myData)[islice]) - return; // don't allocate a slice of data for setting a 0 value - - // Allocate the slice if it is not done yet. - if (!((Standard_Byte**)myData)[islice]) - { - ((Standard_Byte**)myData)[islice] = (Standard_Byte*) calloc(8/*number of bytes in slice*/, sizeof(Standard_Byte)); - } - - // Index within 8 bytes of the slice. - Standard_Integer ibit_in_current_slice = ibit - (islice << 6); - Standard_Integer ibyte = ibit_in_current_slice >> 3; - - // Value (byte) - Standard_Byte value = ((Standard_Byte*)((Standard_Byte**)myData)[islice])[ibyte]; - - // Position of data in the 8 bit-"value". - Standard_Integer shift = ibit_in_current_slice - (ibyte << 3); - - // Set data - if (data != ((value & gbits[shift]) ? Standard_True : Standard_False)) - { - if (data) - value |= gbits[shift]; - else - value &= gnbits[shift]; - ((Standard_Byte*)((Standard_Byte**)myData)[islice])[ibyte] = value; - } -} - -Standard_Boolean Voxel_BoolDS::Get(const Standard_Integer ix, const Standard_Integer iy, const Standard_Integer iz) const -{ - Standard_Integer ibit = ix + myNbX * iy + myNbXY * iz; - Standard_Integer islice = ibit >> 6; - - // If the slice of data is not allocated, it means that its values are 0. - if (!((Standard_Byte**)myData)[islice]) - return Standard_False; - - // Index within 8 bytes of the slice. - Standard_Integer ibit_in_current_slice = ibit - (islice << 6); - Standard_Integer ibyte = ibit_in_current_slice >> 3; - - // Value (byte) - Standard_Byte value = ((Standard_Byte*)((Standard_Byte**)myData)[islice])[ibyte]; - - // Position of data in the 8 bit-"value". - Standard_Integer shift = ibit_in_current_slice - (ibyte << 3); - - return ((value & gbits[shift]) ? Standard_True : Standard_False); -} diff --git a/src/Voxel/Voxel_BoolDS.hxx b/src/Voxel/Voxel_BoolDS.hxx deleted file mode 100644 index e672e6dcaa..0000000000 --- a/src/Voxel/Voxel_BoolDS.hxx +++ /dev/null @@ -1,96 +0,0 @@ -// Created on: 2008-05-04 -// Created by: Vladislav ROMASHKO -// Copyright (c) 2008-2014 OPEN CASCADE SAS -// -// This file is part of Open CASCADE Technology software library. -// -// This library is free software; you can redistribute it and/or modify it under -// the terms of the GNU Lesser General Public License version 2.1 as published -// by the Free Software Foundation, with special exception defined in the file -// OCCT_LGPL_EXCEPTION.txt. Consult the file LICENSE_LGPL_21.txt included in OCCT -// distribution for complete text of the license and disclaimer of any warranty. -// -// Alternatively, this file may be used under the terms of Open CASCADE -// commercial license or contractual agreement. - -#ifndef _Voxel_BoolDS_HeaderFile -#define _Voxel_BoolDS_HeaderFile - -#include -#include -#include - -#include -#include -#include -#include - - -//! A 3D voxel model keeping a bool flag (1 or 0) -//! value for each voxel. -class Voxel_BoolDS : public Voxel_DS -{ -public: - - DEFINE_STANDARD_ALLOC - - - //! An empty constructor. - Standard_EXPORT Voxel_BoolDS(); - - //! A constructor initializing the voxel model. - //! (x, y, z) - the start point of the box. - //! (x_len, y_len, z_len) - lengths in x, y and z directions along axes of a co-ordinate system. - //! (nb_x, nb_y, nb_z) - number of splits (voxels) along x, y and z directions. - Standard_EXPORT Voxel_BoolDS(const Standard_Real x, const Standard_Real y, const Standard_Real z, const Standard_Real x_len, const Standard_Real y_len, const Standard_Real z_len, const Standard_Integer nb_x, const Standard_Integer nb_y, const Standard_Integer nb_z); - - //! Initialization of the voxel model. - //! (x, y, z) - the start point of the box. - //! (x_len, y_len, z_len) - lengths in x, y and z directions along axes of a co-ordinate system. - //! (nb_x, nb_y, nb_z) - number of splits (voxels) along x, y and z directions. - Standard_EXPORT virtual void Init (const Standard_Real x, const Standard_Real y, const Standard_Real z, const Standard_Real x_len, const Standard_Real y_len, const Standard_Real z_len, const Standard_Integer nb_x, const Standard_Integer nb_y, const Standard_Integer nb_z) Standard_OVERRIDE; - - //! A destructor of the voxel model. - Standard_EXPORT void Destroy(); -~Voxel_BoolDS() -{ - Destroy(); -} - - //! The method sets all values equal to 0 (false) and - //! releases the memory. - Standard_EXPORT void SetZero(); - - //! Defines a value for voxel with co-ordinates (ix, iy, iz). - //! Initial state of the model is so that all voxels have value 0 (false), - //! and this data doesn't occupy memory. - //! Memory for data is allocating during setting non-zero values (true). - Standard_EXPORT void Set (const Standard_Integer ix, const Standard_Integer iy, const Standard_Integer iz, const Standard_Boolean data = Standard_True); - - //! Returns the value of voxel with co-ordinates (ix, iy, iz). - Standard_EXPORT Standard_Boolean Get (const Standard_Integer ix, const Standard_Integer iy, const Standard_Integer iz) const; - - - - -protected: - - - - - -private: - - - - - -}; - - - - - - - -#endif // _Voxel_BoolDS_HeaderFile diff --git a/src/Voxel/Voxel_BooleanOperation.cxx b/src/Voxel/Voxel_BooleanOperation.cxx deleted file mode 100644 index 127120059b..0000000000 --- a/src/Voxel/Voxel_BooleanOperation.cxx +++ /dev/null @@ -1,230 +0,0 @@ -// Created on: 2008-05-21 -// Created by: Vladislav ROMASHKO -// Copyright (c) 2008-2014 OPEN CASCADE SAS -// -// This file is part of Open CASCADE Technology software library. -// -// This library is free software; you can redistribute it and/or modify it under -// the terms of the GNU Lesser General Public License version 2.1 as published -// by the Free Software Foundation, with special exception defined in the file -// OCCT_LGPL_EXCEPTION.txt. Consult the file LICENSE_LGPL_21.txt included in OCCT -// distribution for complete text of the license and disclaimer of any warranty. -// -// Alternatively, this file may be used under the terms of Open CASCADE -// commercial license or contractual agreement. - - -#include -#include -#include -#include -#include -#include - -Voxel_BooleanOperation::Voxel_BooleanOperation() -{ - -} - -Standard_Boolean Voxel_BooleanOperation::Fuse( Voxel_BoolDS& theVoxels1, - const Voxel_BoolDS& theVoxels2) const -{ - // Check the voxels - if (!Check(theVoxels1, theVoxels2)) - return Standard_False; - - // Take the values of the second cube and put them to the first one. - Standard_Integer ix, iy, iz; - for (iz = 0; iz < theVoxels2.GetNbZ(); iz++) - { - for (iy = 0; iy < theVoxels2.GetNbY(); iy++) - { - for (ix = 0; ix < theVoxels2.GetNbX(); ix++) - { - Standard_Boolean value2 = theVoxels2.Get(ix, iy, iz); - if (value2) - theVoxels1.Set(ix, iy, iz, value2); - } - } - } - - return Standard_True; -} - -Standard_Boolean Voxel_BooleanOperation::Fuse( Voxel_ColorDS& theVoxels1, - const Voxel_ColorDS& theVoxels2) const -{ - // Check the voxels - if (!Check(theVoxels1, theVoxels2)) - return Standard_False; - - // Take the values of the second cube and put them to the first one. - Standard_Integer ix, iy, iz; - for (iz = 0; iz < theVoxels2.GetNbZ(); iz++) - { - for (iy = 0; iy < theVoxels2.GetNbY(); iy++) - { - for (ix = 0; ix < theVoxels2.GetNbX(); ix++) - { - Standard_Byte value2 = theVoxels2.Get(ix, iy, iz); - if (value2) - { - Standard_Byte value1 = theVoxels1.Get(ix, iy, iz); - Standard_Byte value = value1 + value2; - if (value > 15) - value = 15; - theVoxels1.Set(ix, iy, iz, value); - } - } - } - } - - return Standard_True; -} - -Standard_Boolean Voxel_BooleanOperation::Fuse( Voxel_FloatDS& theVoxels1, - const Voxel_FloatDS& theVoxels2) const -{ - // Check the voxels - if (!Check(theVoxels1, theVoxels2)) - return Standard_False; - - // Take the values of the second cube and put them to the first one. - Standard_Integer ix, iy, iz; - for (iz = 0; iz < theVoxels2.GetNbZ(); iz++) - { - for (iy = 0; iy < theVoxels2.GetNbY(); iy++) - { - for (ix = 0; ix < theVoxels2.GetNbX(); ix++) - { - Standard_ShortReal value2 = theVoxels2.Get(ix, iy, iz); - if (value2) - { - Standard_ShortReal value1 = theVoxels1.Get(ix, iy, iz); - theVoxels1.Set(ix, iy, iz, value1 + value2); - } - } - } - } - - return Standard_True; -} - -Standard_Boolean Voxel_BooleanOperation::Cut( Voxel_BoolDS& theVoxels1, - const Voxel_BoolDS& theVoxels2) const -{ - // Check the voxels - if (!Check(theVoxels1, theVoxels2)) - return Standard_False; - - // Subtract the values. - Standard_Integer ix, iy, iz; - for (iz = 0; iz < theVoxels2.GetNbZ(); iz++) - { - for (iy = 0; iy < theVoxels2.GetNbY(); iy++) - { - for (ix = 0; ix < theVoxels2.GetNbX(); ix++) - { - Standard_Boolean value1 = theVoxels1.Get(ix, iy, iz); - if (value1) - { - Standard_Boolean value2 = theVoxels2.Get(ix, iy, iz); - if (value2) - theVoxels1.Set(ix, iy, iz, Standard_False); - } - } - } - } - - return Standard_True; -} - -Standard_Boolean Voxel_BooleanOperation::Cut( Voxel_ColorDS& theVoxels1, - const Voxel_ColorDS& theVoxels2) const -{ - // Check the voxels - if (!Check(theVoxels1, theVoxels2)) - return Standard_False; - - // Subtract the values. - Standard_Integer ix, iy, iz; - for (iz = 0; iz < theVoxels2.GetNbZ(); iz++) - { - for (iy = 0; iy < theVoxels2.GetNbY(); iy++) - { - for (ix = 0; ix < theVoxels2.GetNbX(); ix++) - { - Standard_Byte value2 = theVoxels2.Get(ix, iy, iz); - if (value2) - { - Standard_Byte value1 = theVoxels1.Get(ix, iy, iz); - if (value1) - { - Standard_Integer value = value1 - value2; - if (value < 0) - value = 0; - theVoxels1.Set(ix, iy, iz, (Standard_Byte)value); - } - } - } - } - } - - return Standard_True; -} - -Standard_Boolean Voxel_BooleanOperation::Cut( Voxel_FloatDS& theVoxels1, - const Voxel_FloatDS& theVoxels2) const -{ - // Check the voxels - if (!Check(theVoxels1, theVoxels2)) - return Standard_False; - - // Subtract the values. - Standard_Integer ix, iy, iz; - for (iz = 0; iz < theVoxels2.GetNbZ(); iz++) - { - for (iy = 0; iy < theVoxels2.GetNbY(); iy++) - { - for (ix = 0; ix < theVoxels2.GetNbX(); ix++) - { - Standard_ShortReal value2 = theVoxels2.Get(ix, iy, iz); - if (value2) - { - Standard_ShortReal value1 = theVoxels1.Get(ix, iy, iz); - theVoxels1.Set(ix, iy, iz, value1 - value2); - } - } - } - } - - return Standard_True; -} - -Standard_Boolean Voxel_BooleanOperation::Check(const Voxel_DS& theVoxels1, - const Voxel_DS& theVoxels2) const -{ - // Check the voxels - // Number of splits along X, Y and Z axes. - if (!theVoxels1.GetNbX() && theVoxels1.GetNbX() != theVoxels2.GetNbX()) - return Standard_False; - if (!theVoxels1.GetNbY() && theVoxels1.GetNbY() != theVoxels2.GetNbY()) - return Standard_False; - if (!theVoxels1.GetNbZ() && theVoxels1.GetNbZ() != theVoxels2.GetNbZ()) - return Standard_False; - // Start point - if (fabs(theVoxels1.GetX() - theVoxels2.GetX()) > Precision::Confusion() || - fabs(theVoxels1.GetY() - theVoxels2.GetY()) > Precision::Confusion() || - fabs(theVoxels1.GetZ() - theVoxels2.GetZ()) > Precision::Confusion()) - { - return Standard_False; - } - // Length along X, Y and Z axes. - if (fabs(theVoxels1.GetXLen() - theVoxels2.GetXLen()) > Precision::Confusion() || - fabs(theVoxels1.GetYLen() - theVoxels2.GetYLen()) > Precision::Confusion() || - fabs(theVoxels1.GetZLen() - theVoxels2.GetZLen()) > Precision::Confusion()) - { - return Standard_False; - } - return Standard_True; -} diff --git a/src/Voxel/Voxel_BooleanOperation.hxx b/src/Voxel/Voxel_BooleanOperation.hxx deleted file mode 100644 index f2ec647679..0000000000 --- a/src/Voxel/Voxel_BooleanOperation.hxx +++ /dev/null @@ -1,108 +0,0 @@ -// Created on: 2008-05-21 -// Created by: Vladislav ROMASHKO -// Copyright (c) 2008-2014 OPEN CASCADE SAS -// -// This file is part of Open CASCADE Technology software library. -// -// This library is free software; you can redistribute it and/or modify it under -// the terms of the GNU Lesser General Public License version 2.1 as published -// by the Free Software Foundation, with special exception defined in the file -// OCCT_LGPL_EXCEPTION.txt. Consult the file LICENSE_LGPL_21.txt included in OCCT -// distribution for complete text of the license and disclaimer of any warranty. -// -// Alternatively, this file may be used under the terms of Open CASCADE -// commercial license or contractual agreement. - -#ifndef _Voxel_BooleanOperation_HeaderFile -#define _Voxel_BooleanOperation_HeaderFile - -#include -#include -#include - -#include -class Voxel_BoolDS; -class Voxel_ColorDS; -class Voxel_FloatDS; -class Voxel_DS; - - -//! Boolean operations (fuse, cut) -//! for voxels of the same dimension. -class Voxel_BooleanOperation -{ -public: - - DEFINE_STANDARD_ALLOC - - - //! An empty constructor. - Standard_EXPORT Voxel_BooleanOperation(); - - //! Fuses two cubes of voxels. - //! It modifies the first cube of voxels. - //! It returns false in case of different dimension of the cube, - //! different number of voxels. - Standard_EXPORT Standard_Boolean Fuse (Voxel_BoolDS& theVoxels1, const Voxel_BoolDS& theVoxels2) const; - - //! Fuses two cubes of voxels. - //! It modifies the first cube of voxels. - //! It returns false in case of different dimension of the cube, - //! different number of voxels. - //! It summerizes the value of corresponding voxels and puts the result to theVoxels1. - //! If the result exceeds 15 or becomes greater, it keeps 15. - Standard_EXPORT Standard_Boolean Fuse (Voxel_ColorDS& theVoxels1, const Voxel_ColorDS& theVoxels2) const; - - //! Fuses two cubes of voxels. - //! It modifies the first cube of voxels. - //! It returns false in case of different dimension of the cube, - //! different number of voxels. - //! It summerizes the value of corresponding voxels and puts the result to theVoxels1. - Standard_EXPORT Standard_Boolean Fuse (Voxel_FloatDS& theVoxels1, const Voxel_FloatDS& theVoxels2) const; - - //! Cuts two cubes of voxels. - //! It modifies the first cube of voxels. - //! It returns false in case of different dimension of the cube, - //! different number of voxels. - Standard_EXPORT Standard_Boolean Cut (Voxel_BoolDS& theVoxels1, const Voxel_BoolDS& theVoxels2) const; - - //! Cuts two cubes of voxels. - //! It modifies the first cube of voxels. - //! It returns false in case of different dimension of the cube, - //! different number of voxels. - //! It subtracts the value of corresponding voxels and puts the result to theVoxels1. - Standard_EXPORT Standard_Boolean Cut (Voxel_ColorDS& theVoxels1, const Voxel_ColorDS& theVoxels2) const; - - //! Cuts two cubes of voxels. - //! It modifies the first cube of voxels. - //! It returns false in case of different dimension of the cube, - //! different number of voxels. - //! It subtracts the value of corresponding voxels and puts the result to theVoxels1. - Standard_EXPORT Standard_Boolean Cut (Voxel_FloatDS& theVoxels1, const Voxel_FloatDS& theVoxels2) const; - - - - -protected: - - - - - -private: - - - Standard_EXPORT Standard_Boolean Check (const Voxel_DS& theVoxels1, const Voxel_DS& theVoxels2) const; - - - - -}; - - - - - - - -#endif // _Voxel_BooleanOperation_HeaderFile diff --git a/src/Voxel/Voxel_CollisionDetection.cxx b/src/Voxel/Voxel_CollisionDetection.cxx deleted file mode 100644 index 5fd8b5f4dc..0000000000 --- a/src/Voxel/Voxel_CollisionDetection.cxx +++ /dev/null @@ -1,295 +0,0 @@ -// Created on: 2008-07-16 -// Created by: Vladislav ROMASHKO -// Copyright (c) 2008-2014 OPEN CASCADE SAS -// -// This file is part of Open CASCADE Technology software library. -// -// This library is free software; you can redistribute it and/or modify it under -// the terms of the GNU Lesser General Public License version 2.1 as published -// by the Free Software Foundation, with special exception defined in the file -// OCCT_LGPL_EXCEPTION.txt. Consult the file LICENSE_LGPL_21.txt included in OCCT -// distribution for complete text of the license and disclaimer of any warranty. -// -// Alternatively, this file may be used under the terms of Open CASCADE -// commercial license or contractual agreement. - - -#include -#include -#include -#include -#include -#include -#include -#include - -Voxel_CollisionDetection::Voxel_CollisionDetection() -:myDeflection(0.1), - myNbX(100), - myNbY(100), - myNbZ(100), - myUsageOfVolume(Standard_False), - myKeepCollisions(Standard_False), - myXLen(-1.0), - myYLen(-1.0), - myZLen(-1.0), - myVoxels(0), - myHasCollisions(Standard_False) -{ - -} - -Voxel_CollisionDetection::Voxel_CollisionDetection(const Standard_Real deflection, - const Standard_Integer nbx, - const Standard_Integer nby, - const Standard_Integer nbz) -:myDeflection(deflection), - myNbX(nbx), - myNbY(nby), - myNbZ(nbz), - myUsageOfVolume(Standard_False), - myKeepCollisions(Standard_False), - myXLen(-1.0), - myYLen(-1.0), - myZLen(-1.0), - myVoxels(0), - myHasCollisions(Standard_False) -{ - -} - -// Destructor -void Voxel_CollisionDetection::Destroy() -{ - Clear(); -} - -Standard_Integer Voxel_CollisionDetection::AddShape(const TopoDS_Shape& shape) -{ - Clear(); - myShapes.Append(shape); - return myShapes.Extent(); -} - -Standard_Boolean Voxel_CollisionDetection::ReplaceShape(const Standard_Integer ishape, - const TopoDS_Shape& shape) -{ - if (ishape == 1) - { - myShapes.RemoveFirst(); - myShapes.Prepend(shape); - return Standard_True; - } - - Standard_Integer i = 1; - Standard_Boolean is_replaced = Standard_False; - TopTools_ListIteratorOfListOfShape itr(myShapes); - for (; itr.More(); itr.Next(), i++) - { - if (i == ishape) - { - myShapes.Remove(itr); - myShapes.InsertBefore(shape, itr); - is_replaced = Standard_True; - break; - } - } - return is_replaced; -} - -void Voxel_CollisionDetection::SetDeflection(const Standard_Real deflection) -{ - myDeflection = deflection; -} - -void Voxel_CollisionDetection::SetNbVoxels(const Standard_Integer nbx, - const Standard_Integer nby, - const Standard_Integer nbz) -{ - myNbX = nbx; - myNbY = nby; - myNbZ = nbz; -} - -void Voxel_CollisionDetection::SetBoundaryBox(const Bnd_Box& box) -{ - if (box.IsVoid()) - return; - - Standard_Real xmin, ymin, zmin, xmax, ymax, zmax; - box.Get(xmin, ymin, zmin, xmax, ymax, zmax); - - myX = xmin; - myY = ymin; - myZ = zmin; - - myXLen = xmax - xmin; - myYLen = ymax - ymin; - myZLen = zmax - zmin; -} - -void Voxel_CollisionDetection::SetUsageOfVolume(const Standard_Boolean usage) -{ - myUsageOfVolume = usage; -} - -void Voxel_CollisionDetection::KeepCollisions(const Standard_Boolean keep) -{ - myKeepCollisions = keep; -} - -Standard_Boolean Voxel_CollisionDetection::Voxelize(const Standard_Integer ishape) -{ - // Check the arguments - if (myNbX <= 0 || myNbY <= 0 || myNbZ <= 0) - return Standard_False; - - // Calculate the boundary box of the shapes to define the size of voxels. - // This code is called only if the user didn't define the boundary box himself. - if (myXLen < 0.0) - { - Bnd_Box B, b; - TopTools_ListIteratorOfListOfShape itrs(myShapes); - for (; itrs.More(); itrs.Next()) - { - TopoDS_Shape S = itrs.Value(); - BRepBndLib::Add(S, b); - B.Add(b); - } - SetBoundaryBox(B); - } - - // Voxelize the shapes - Standard_Integer progress, ithread = 1, i = 1; - TopTools_ListIteratorOfListOfShape itrs(myShapes); - for (; itrs.More(); itrs.Next(), i++) - { - if (ishape != -1 && i != ishape) - continue; - - if (!myVoxels) - myVoxels = (Standard_Address) new Voxel_BoolDS[myShapes.Extent()]; - Voxel_BoolDS& voxels = ((Voxel_BoolDS*)myVoxels)[i - 1]; - if (!CheckVoxels(voxels)) - { - voxels.Init(myX, myY, myZ, myXLen, myYLen, myZLen, myNbX, myNbY, myNbZ); - } - else - { - voxels.SetZero(); - } - - TopoDS_Shape S = itrs.Value(); - Voxel_FastConverter voxelizer(S, voxels, myDeflection, myNbX, myNbY, myNbZ, 1 /*number of threads */); - if (!voxelizer.Convert(progress, ithread)) - return Standard_False; - if (myUsageOfVolume && !voxelizer.FillInVolume(1, ithread)) - return Standard_False; - } - - return Standard_True; -} - -Standard_Boolean Voxel_CollisionDetection::Compute() -{ - myHasCollisions = Standard_False; - - // Check voxels of shapes - if (!myVoxels) - return Standard_False; - Standard_Integer ishape = 0, nb_shapes = myShapes.Extent(); - for (; ishape < nb_shapes; ishape++) - { - Voxel_BoolDS& voxels = ((Voxel_BoolDS*)myVoxels)[ishape]; - if (!CheckVoxels(voxels)) - { - return Standard_False; - } - } - - // Check the resulting voxels - Standard_Boolean created = Standard_False; - if (!CheckVoxels(myCollisions)) - { - // Create 0-voxels for the result, if it is needed. - created = Standard_True; - myCollisions.Init(myX, myY, myZ, myXLen, myYLen, myZLen, myNbX, myNbY, myNbZ); - } - - // Nullify the voxels of the result (it corresponds to the state of no collisions). - if (!myKeepCollisions && !created) - { - myCollisions.SetZero(); - } - - // Check collisions - if (nb_shapes) - { - Standard_Integer ix, iy, iz; - Voxel_BoolDS& voxels = ((Voxel_BoolDS*)myVoxels)[0]; // 1st shape - for (ix = 0; ix < myNbX; ix++) - { - for (iy = 0; iy < myNbY; iy++) - { - for (iz = 0; iz < myNbZ; iz++) - { - if (voxels.Get(ix, iy, iz)) - { - for (ishape = 1; ishape < nb_shapes; ishape++) // start with second shape - { - Voxel_BoolDS& anOtherVoxels = ((Voxel_BoolDS*)myVoxels)[ishape]; - if (anOtherVoxels.Get(ix, iy, iz)) - { - myCollisions.Set(ix, iy, iz, Standard_True); - if (!myHasCollisions) - { - myHasCollisions = Standard_True; - } - break; - } - } - } - } - } - } - } - - - return Standard_True; -} - -Standard_Boolean Voxel_CollisionDetection::HasCollisions() const -{ - return myHasCollisions; -} - -const Voxel_BoolDS& Voxel_CollisionDetection::GetCollisions() const -{ - return myCollisions; -} - -void Voxel_CollisionDetection::Clear() -{ - if (myVoxels) - { - delete[] ((Voxel_BoolDS*)myVoxels); - myVoxels = 0; - } -} - -Standard_Boolean Voxel_CollisionDetection::CheckVoxels(const Voxel_BoolDS& voxels) const -{ - if (fabs(voxels.GetX() - myX) > Precision::Confusion() || - fabs(voxels.GetY() - myY) > Precision::Confusion() || - fabs(voxels.GetZ() - myZ) > Precision::Confusion() || - fabs(voxels.GetXLen() - myXLen) > Precision::Confusion() || - fabs(voxels.GetYLen() - myYLen) > Precision::Confusion() || - fabs(voxels.GetZLen() - myZLen) > Precision::Confusion() || - voxels.GetNbX() != myNbX || - voxels.GetNbY() != myNbY || - voxels.GetNbZ() != myNbZ) - { - return Standard_False; - } - return Standard_True; -} diff --git a/src/Voxel/Voxel_CollisionDetection.hxx b/src/Voxel/Voxel_CollisionDetection.hxx deleted file mode 100644 index 2733984d10..0000000000 --- a/src/Voxel/Voxel_CollisionDetection.hxx +++ /dev/null @@ -1,151 +0,0 @@ -// Created on: 2008-07-14 -// Created by: Vladislav ROMASHKO -// Copyright (c) 2008-2014 OPEN CASCADE SAS -// -// This file is part of Open CASCADE Technology software library. -// -// This library is free software; you can redistribute it and/or modify it under -// the terms of the GNU Lesser General Public License version 2.1 as published -// by the Free Software Foundation, with special exception defined in the file -// OCCT_LGPL_EXCEPTION.txt. Consult the file LICENSE_LGPL_21.txt included in OCCT -// distribution for complete text of the license and disclaimer of any warranty. -// -// Alternatively, this file may be used under the terms of Open CASCADE -// commercial license or contractual agreement. - -#ifndef _Voxel_CollisionDetection_HeaderFile -#define _Voxel_CollisionDetection_HeaderFile - -#include -#include -#include - -#include -#include -#include -#include -#include -#include -class TopoDS_Shape; -class Bnd_Box; -class Voxel_BoolDS; - - -//! Detects collisions between shapes. -class Voxel_CollisionDetection -{ -public: - - DEFINE_STANDARD_ALLOC - - - //! An empty constructor. - Standard_EXPORT Voxel_CollisionDetection(); - - //! A constructor. - //! It defines deflection of triangulation for the shapes. - //! As lower the deflection is, as proper the triangulation is generated. - //! Also, it defines number of splits along X, Y and Z axes for generation of voxels. - //! As greater the numbers are, as greater number of voxels is used for detection of collision. - Standard_EXPORT Voxel_CollisionDetection(const Standard_Real deflection, const Standard_Integer nbx, const Standard_Integer nby, const Standard_Integer nbz); - - //! Adds a shape. - //! Returns an index of the shape. - Standard_EXPORT Standard_Integer AddShape (const TopoDS_Shape& shape); - - //! Replaces a shape by another one. - //! is an index of the shape. - //! This method is useful for moving shape, for example. - Standard_EXPORT Standard_Boolean ReplaceShape (const Standard_Integer ishape, const TopoDS_Shape& shape); - - //! Defines the deflection of triangulation of shapes. - Standard_EXPORT void SetDeflection (const Standard_Real deflection); - - //! Defines the number of voxels along X, Y and Z axes. - Standard_EXPORT void SetNbVoxels (const Standard_Integer nbx, const Standard_Integer nby, const Standard_Integer nbz); - - //! Defines a user-defined boundary box for generation of voxels. - //! If this method is not called, the algorithm calculates the boundary box itself. - Standard_EXPORT void SetBoundaryBox (const Bnd_Box& box); - - //! Defines usage of volume of shapes in collision detection algorithm. - //! Beware, usage of volume a little bit decreases the speed of algorithm. - Standard_EXPORT void SetUsageOfVolume (const Standard_Boolean usage); - - //! Doesn't clean the collision points on new call to the method Compute(). - //! It allows to see the collisions for a moving shape. - Standard_EXPORT void KeepCollisions (const Standard_Boolean keep); - - //! Prepares data for computation of collisions. - //! It checks the inner parameters (number of voxels along X, Y and Z axes) and - //! voxelizes the shapes. - //! If the shape is not changed since the last call to this method, - //! this method may be not called for this shape. - //! - is the index of the shape for processing by this method. - //! If it is equal to -1, all shapes will be processed. - Standard_EXPORT Standard_Boolean Voxelize (const Standard_Integer ishape = -1); - - //! Computes the collisions. - //! This method may be called many times if, for example, the shapes are being moved. - Standard_EXPORT Standard_Boolean Compute(); - - //! Returns true if a collision is detected. - Standard_EXPORT Standard_Boolean HasCollisions() const; - - //! Returns the collided voxels. - Standard_EXPORT const Voxel_BoolDS& GetCollisions() const; - - //! A destructor. - Standard_EXPORT void Destroy(); -~Voxel_CollisionDetection() -{ - Destroy(); -} - - - - -protected: - - - - - -private: - - - //! An internal method for cleaning the intermediate data. - Standard_EXPORT void Clear(); - - //! An internal method, which checks correspondance - //! of voxels to the parameters defined by user. - Standard_EXPORT Standard_Boolean CheckVoxels (const Voxel_BoolDS& voxels) const; - - - TopTools_ListOfShape myShapes; - Standard_Real myDeflection; - Standard_Integer myNbX; - Standard_Integer myNbY; - Standard_Integer myNbZ; - Standard_Boolean myUsageOfVolume; - Standard_Boolean myKeepCollisions; - Standard_Real myX; - Standard_Real myY; - Standard_Real myZ; - Standard_Real myXLen; - Standard_Real myYLen; - Standard_Real myZLen; - Standard_Address myVoxels; - Voxel_BoolDS myCollisions; - Standard_Boolean myHasCollisions; - - -}; - - - - - - - -#endif // _Voxel_CollisionDetection_HeaderFile diff --git a/src/Voxel/Voxel_ColorDS.cxx b/src/Voxel/Voxel_ColorDS.cxx deleted file mode 100644 index 1ec26750f1..0000000000 --- a/src/Voxel/Voxel_ColorDS.cxx +++ /dev/null @@ -1,173 +0,0 @@ -// Created on: 2008-05-08 -// Created by: Vladislav ROMASHKO -// Copyright (c) 2008-2014 OPEN CASCADE SAS -// -// This file is part of Open CASCADE Technology software library. -// -// This library is free software; you can redistribute it and/or modify it under -// the terms of the GNU Lesser General Public License version 2.1 as published -// by the Free Software Foundation, with special exception defined in the file -// OCCT_LGPL_EXCEPTION.txt. Consult the file LICENSE_LGPL_21.txt included in OCCT -// distribution for complete text of the license and disclaimer of any warranty. -// -// Alternatively, this file may be used under the terms of Open CASCADE -// commercial license or contractual agreement. - - -#include - -#include -static Standard_Byte gbits[8] = {1, 2, 4, 8, 16, 32, 64, 128}; -static Standard_Byte gnbits[8] = {255-1, 255-2, 255-4, 255-8, 255-16, 255-32, 255-64, 255-128}; - -// Empty constructor -Voxel_ColorDS::Voxel_ColorDS():Voxel_DS() -{ - -} - -// Constructor with intialization. -Voxel_ColorDS::Voxel_ColorDS(const Standard_Real x, const Standard_Real y, const Standard_Real z, - const Standard_Real xlen, const Standard_Real ylen, const Standard_Real zlen, - const Standard_Integer nbx, const Standard_Integer nby, const Standard_Integer nbz) -:Voxel_DS() -{ - Init(x, y, z, xlen, ylen, zlen, nbx, nby, nbz); -} - -// Initialization. -void Voxel_ColorDS::Init(const Standard_Real x, const Standard_Real y, const Standard_Real z, - const Standard_Real xlen, const Standard_Real ylen, const Standard_Real zlen, - const Standard_Integer nbx, const Standard_Integer nby, const Standard_Integer nbz) -{ - Destroy(); - - Voxel_DS::Init(x, y, z, xlen, ylen, zlen, nbx, nby, nbz); - - if (!myNbX || !myNbY || !myNbZ) - return; - - Standard_Integer nb_bytes = RealToInt(ceil(myNbXY * myNbZ / 2.0)); - Standard_Integer nb_slices = RealToInt(ceil(nb_bytes / 32.0)); - myData = (Standard_Address) calloc(nb_slices, sizeof(Standard_Byte*)); -} - -// Destructor -void Voxel_ColorDS::Destroy() -{ - if (myData) - { - SetZero(); - free((Standard_Byte**)myData); - myData = 0; - } -} - -void Voxel_ColorDS::SetZero() -{ - if (myData) - { - Standard_Integer nb_bytes = RealToInt(ceil(myNbXY * myNbZ / 2.0)); - Standard_Integer ix = 0, nb_slices = RealToInt(ceil(nb_bytes / 32.0)); - for (; ix < nb_slices; ix++) - { - if (((Standard_Byte**)myData)[ix]) - { - free(((Standard_Byte**)myData)[ix]); - ((Standard_Byte**)myData)[ix] = 0; - } - } - } -} - -// Access to the boolean information attached to a particular voxel: -// Info: (ix >= 0 && ix < theNb_x), etc. -void Voxel_ColorDS::Set(const Standard_Integer ix, const Standard_Integer iy, const Standard_Integer iz, - const Standard_Byte data) -{ - Standard_Integer ibit = 4 * (ix + myNbX * iy + myNbXY * iz); - Standard_Integer islice = ibit >> 8; - - if (!data && !((Standard_Byte**)myData)[islice]) - return; // don't allocate a slice of data for setting a 0 value - - // Allocate the slice if it is not done yet. - if (!((Standard_Byte**)myData)[islice]) - { - ((Standard_Byte**)myData)[islice] = (Standard_Byte*) calloc(32/*number of bytes in slice*/, sizeof(Standard_Byte)); - } - - // Index within 32 bytes of the slice. - Standard_Integer ibit_in_current_slice = ibit - (islice << 8); - Standard_Integer ibyte = ibit_in_current_slice >> 3; - - // Value (byte) - Standard_Byte value = ((Standard_Byte*)((Standard_Byte**)myData)[islice])[ibyte]; - - // Position of data in the 8 bit-"value": - // It is either 0 (first part of the byte) or 2 (second part of the byte) - Standard_Integer shift = ibit_in_current_slice - (ibyte << 3); - - // Set data - Standard_Integer i = 0, j = 0, nb = 4; // first part of byte - if (shift == 4) - { - // second part of byte - i = 4; - nb = 8; - } - - for (; i < nb; i++, j++) - { - if (data & gbits[j]) // if j-th bit is 1 - { - value |= gbits[i]; // set 1 to the i-th bit - } - else - { - value &= gnbits[i]; // set 0 to the i-th bit - } - } - - ((Standard_Byte*)((Standard_Byte**)myData)[islice])[ibyte] = value; -} - -Standard_Byte Voxel_ColorDS::Get(const Standard_Integer ix, const Standard_Integer iy, const Standard_Integer iz) const -{ - Standard_Byte ret = 0; - Standard_Integer ibit = 4 * (ix + myNbX * iy + myNbXY * iz); - Standard_Integer islice = ibit >> 8; - - // If the slice of data is not allocated, it means that its values are 0. - if (!((Standard_Byte**)myData)[islice]) - return ret; - - // Index within 8 bytes of the slice. - Standard_Integer ibit_in_current_slice = ibit - (islice << 8); - Standard_Integer ibyte = ibit_in_current_slice >> 3; - - // Value (byte) - Standard_Byte value = ((Standard_Byte*)((Standard_Byte**)myData)[islice])[ibyte]; - - // Position of data in the 8 bit-"value". - Standard_Integer shift = ibit_in_current_slice - (ibyte << 3); - - // Return a byte with 4 first bits filled-in by the value. - Standard_Integer i = 0, j = 0, nb = 4; // first part of byte - if (shift == 4) - { - // second part of byte - i = 4; - nb = 8; - } - - for (; i < nb; i++, j++) - { - if (value & gbits[i]) // if i-th bit is 1 - { - ret |= gbits[j]; // set 1 to the j-th bit - } - } - - return ret; -} diff --git a/src/Voxel/Voxel_ColorDS.hxx b/src/Voxel/Voxel_ColorDS.hxx deleted file mode 100644 index b58963319c..0000000000 --- a/src/Voxel/Voxel_ColorDS.hxx +++ /dev/null @@ -1,96 +0,0 @@ -// Created on: 2008-05-08 -// Created by: Vladislav ROMASHKO -// Copyright (c) 2008-2014 OPEN CASCADE SAS -// -// This file is part of Open CASCADE Technology software library. -// -// This library is free software; you can redistribute it and/or modify it under -// the terms of the GNU Lesser General Public License version 2.1 as published -// by the Free Software Foundation, with special exception defined in the file -// OCCT_LGPL_EXCEPTION.txt. Consult the file LICENSE_LGPL_21.txt included in OCCT -// distribution for complete text of the license and disclaimer of any warranty. -// -// Alternatively, this file may be used under the terms of Open CASCADE -// commercial license or contractual agreement. - -#ifndef _Voxel_ColorDS_HeaderFile -#define _Voxel_ColorDS_HeaderFile - -#include -#include -#include - -#include -#include -#include -#include - - -//! A 3D voxel model keeping 4 bits for each voxel (one of 16 colors). -class Voxel_ColorDS : public Voxel_DS -{ -public: - - DEFINE_STANDARD_ALLOC - - - //! An empty constructor. - Standard_EXPORT Voxel_ColorDS(); - - //! A constructor initializing the voxel model. - //! (x, y, z) - the start point of the box. - //! (x_len, y_len, z_len) - lengths in x, y and z directions along axes of a co-ordinate system. - //! (nb_x, nb_y, nb_z) - number of splits (voxels) along x, y and z directions. - Standard_EXPORT Voxel_ColorDS(const Standard_Real x, const Standard_Real y, const Standard_Real z, const Standard_Real x_len, const Standard_Real y_len, const Standard_Real z_len, const Standard_Integer nb_x, const Standard_Integer nb_y, const Standard_Integer nb_z); - - //! Initialization of the voxel model. - //! (x, y, z) - the start point of the box. - //! (x_len, y_len, z_len) - lengths in x, y and z directions along axes of a co-ordinate system. - //! (nb_x, nb_y, nb_z) - number of splits (voxels) along x, y and z directions. - Standard_EXPORT virtual void Init (const Standard_Real x, const Standard_Real y, const Standard_Real z, const Standard_Real x_len, const Standard_Real y_len, const Standard_Real z_len, const Standard_Integer nb_x, const Standard_Integer nb_y, const Standard_Integer nb_z) Standard_OVERRIDE; - - //! A destructor of the voxel model. - Standard_EXPORT void Destroy(); -~Voxel_ColorDS() -{ - Destroy(); -} - - //! The method sets all values equal to 0 (false) and - //! releases the memory. - Standard_EXPORT void SetZero(); - - //! Defines a value for voxel with co-ordinates (ix, iy, iz). - //! Only the first four bits are used! - //! Initial state of the model is so that all voxels have value 0x0000, - //! and this data doesn't occupy memory. - //! Memory for data is allocating during setting non-zero values (0x0101, for example). - Standard_EXPORT void Set (const Standard_Integer ix, const Standard_Integer iy, const Standard_Integer iz, const Standard_Byte data); - - //! Returns the value of voxel with co-ordinates (ix, iy, iz). - Standard_EXPORT Standard_Byte Get (const Standard_Integer ix, const Standard_Integer iy, const Standard_Integer iz) const; - - - - -protected: - - - - - -private: - - - - - -}; - - - - - - - -#endif // _Voxel_ColorDS_HeaderFile diff --git a/src/Voxel/Voxel_DS.cxx b/src/Voxel/Voxel_DS.cxx deleted file mode 100644 index e7d84111e2..0000000000 --- a/src/Voxel/Voxel_DS.cxx +++ /dev/null @@ -1,194 +0,0 @@ -// Created on: 2008-05-11 -// Created by: Vladislav ROMASHKO -// Copyright (c) 2008-2014 OPEN CASCADE SAS -// -// This file is part of Open CASCADE Technology software library. -// -// This library is free software; you can redistribute it and/or modify it under -// the terms of the GNU Lesser General Public License version 2.1 as published -// by the Free Software Foundation, with special exception defined in the file -// OCCT_LGPL_EXCEPTION.txt. Consult the file LICENSE_LGPL_21.txt included in OCCT -// distribution for complete text of the license and disclaimer of any warranty. -// -// Alternatively, this file may be used under the terms of Open CASCADE -// commercial license or contractual agreement. - - -#include -#include -#include - -// Empty constructor -Voxel_DS::Voxel_DS() -:myData(0), - myX(0.0),myY(0.0),myZ(0.0), - myXLen(0.0),myYLen(0.0),myZLen(0.0), - myNbX(0),myNbY(0),myNbZ(0), - myNbXY(0),myDX(0.0),myDY(0.0),myDZ(0.0), - myHalfDX(0.0),myHalfDY(0.0),myHalfDZ(0.0) -{ - -} - -// Constructor with intialization. -Voxel_DS::Voxel_DS(const Standard_Real x, const Standard_Real y, const Standard_Real z, - const Standard_Real xlen, const Standard_Real ylen, const Standard_Real zlen, - const Standard_Integer nbx, const Standard_Integer nby, const Standard_Integer nbz) -{ - Init(x, y, z, xlen, ylen, zlen, nbx, nby, nbz); -} - -// Initialization. -void Voxel_DS::Init(const Standard_Real x, const Standard_Real y, const Standard_Real z, - const Standard_Real xlen, const Standard_Real ylen, const Standard_Real zlen, - const Standard_Integer nbx, const Standard_Integer nby, const Standard_Integer nbz) -{ - myX = x; - myY = y; - myZ = z; - myXLen = xlen; - myYLen = ylen; - myZLen = zlen; - myNbX = nbx; - myNbY = nby; - myNbZ = nbz; - myNbXY = myNbX * myNbY; - myDX = myXLen / (Standard_Real) myNbX; - myDY = myYLen / (Standard_Real) myNbY; - myDZ = myZLen / (Standard_Real) myNbZ; - myHalfDX = myDX / 2.0; - myHalfDY = myDY / 2.0; - myHalfDZ = myDZ / 2.0; -} - -// Get the initial information on voxels -Standard_Real Voxel_DS::GetX() const -{ - return myX; -} - -Standard_Real Voxel_DS::GetY() const -{ - return myY; -} - -Standard_Real Voxel_DS::GetZ() const -{ - return myZ; -} - -Standard_Real Voxel_DS::GetXLen() const -{ - return myXLen; -} - -Standard_Real Voxel_DS::GetYLen() const -{ - return myYLen; -} - -Standard_Real Voxel_DS::GetZLen() const -{ - return myZLen; -} - -Standard_Integer Voxel_DS::GetNbX() const -{ - return myNbX; -} - -Standard_Integer Voxel_DS::GetNbY() const -{ - return myNbY; -} - -Standard_Integer Voxel_DS::GetNbZ() const -{ - return myNbZ; -} - -void Voxel_DS::GetCenter(const Standard_Integer ix, const Standard_Integer iy, const Standard_Integer iz, - Standard_Real& xc, Standard_Real& yc, Standard_Real& zc) const -{ - GetOrigin(ix, iy, iz, xc, yc, zc); - xc += myHalfDX; - yc += myHalfDY; - zc += myHalfDZ; -} - -void Voxel_DS::GetOrigin(const Standard_Integer ix, const Standard_Integer iy, const Standard_Integer iz, - Standard_Real& x0, Standard_Real& y0, Standard_Real& z0) const -{ - x0 = myX + ix * myDX; - y0 = myY + iy * myDY; - z0 = myZ + iz * myDZ; -} - -// The method uses a chordial approach to find the index of voxel by co-ordinate. -static Standard_Integer findIndex(const Standard_Real xstart, const Standard_Real dx, - const Standard_Integer ix1, const Standard_Integer ix2, - const Standard_Real x) -{ - if (ix2 - ix1 < 2) - { - if (x < xstart + ix2 * dx) - return ix1; - return ix2; - } - - // Middle index - const Standard_Integer ixm = (ix1 + ix2) / 2; - - // Check if it is in the first half: - if (x >= xstart + ix1 * dx && x < xstart + ixm * dx) - { - return findIndex(xstart, dx, ix1, ixm, x); - } - - return findIndex(xstart, dx, ixm, ix2, x); -} - -Standard_Boolean Voxel_DS::GetVoxel(const Standard_Real x, const Standard_Real y, const Standard_Real z, - Standard_Integer& ix, Standard_Integer& iy, Standard_Integer& iz) const -{ - // X - if (!GetVoxelX(x, ix)) - return Standard_False; - - // Y - if (!GetVoxelY(y, iy)) - return Standard_False; - - // Z - return GetVoxelZ(z, iz); -} - -Standard_Boolean Voxel_DS::GetVoxelX(const Standard_Real x, - Standard_Integer& ix) const -{ - // X - if (x < myX || x > myX + myXLen) - return Standard_False; - ix = findIndex(myX, myXLen / (Standard_Real) myNbX, 0, myNbX - 1, x); - return Standard_True; -} - -Standard_Boolean Voxel_DS::GetVoxelY(const Standard_Real y, - Standard_Integer& iy) const -{ - // Y - if (y < myY || y > myY + myYLen) - return Standard_False; - iy = findIndex(myY, myYLen / (Standard_Real) myNbY, 0, myNbY - 1, y); - return Standard_True; -} - -Standard_Boolean Voxel_DS::GetVoxelZ(const Standard_Real z, - Standard_Integer& iz) const -{ - // Z - if (z < myZ || z > myZ + myZLen) - return Standard_False; - iz = findIndex(myZ, myZLen / (Standard_Real) myNbZ, 0, myNbZ - 1, z); - return Standard_True; -} diff --git a/src/Voxel/Voxel_DS.hxx b/src/Voxel/Voxel_DS.hxx deleted file mode 100644 index a6969bd2b4..0000000000 --- a/src/Voxel/Voxel_DS.hxx +++ /dev/null @@ -1,134 +0,0 @@ -// Created on: 2008-05-07 -// Created by: Vladislav ROMASHKO -// Copyright (c) 2008-2014 OPEN CASCADE SAS -// -// This file is part of Open CASCADE Technology software library. -// -// This library is free software; you can redistribute it and/or modify it under -// the terms of the GNU Lesser General Public License version 2.1 as published -// by the Free Software Foundation, with special exception defined in the file -// OCCT_LGPL_EXCEPTION.txt. Consult the file LICENSE_LGPL_21.txt included in OCCT -// distribution for complete text of the license and disclaimer of any warranty. -// -// Alternatively, this file may be used under the terms of Open CASCADE -// commercial license or contractual agreement. - -#ifndef _Voxel_DS_HeaderFile -#define _Voxel_DS_HeaderFile - -#include -#include -#include - -#include -#include -#include -#include -class Voxel_Writer; -class Voxel_Reader; - - -//! A base class for all voxel data structures. -class Voxel_DS -{ -public: - - DEFINE_STANDARD_ALLOC - - - //! An empty constructor. - Standard_EXPORT Voxel_DS(); - - //! A constructor initializing the voxel model. - //! (x, y, z) - the start point of the box. - //! (x_len, y_len, z_len) - lengths in x, y and z directions along axes of a co-ordinate system. - //! (nb_x, nb_y, nb_z) - number of splits (voxels) along x, y and z directions. - Standard_EXPORT Voxel_DS(const Standard_Real x, const Standard_Real y, const Standard_Real z, const Standard_Real x_len, const Standard_Real y_len, const Standard_Real z_len, const Standard_Integer nb_x, const Standard_Integer nb_y, const Standard_Integer nb_z); - - //! Initialization of the voxel model. - //! (x, y, z) - the start point of the box. - //! (x_len, y_len, z_len) - lengths in x, y and z directions along axes of a co-ordinate system. - //! (nb_x, nb_y, nb_z) - number of splits (voxels) along x, y and z directions. - //! The methods below return initial data of the voxel model. - Standard_EXPORT virtual void Init (const Standard_Real x, const Standard_Real y, const Standard_Real z, const Standard_Real x_len, const Standard_Real y_len, const Standard_Real z_len, const Standard_Integer nb_x, const Standard_Integer nb_y, const Standard_Integer nb_z); - - Standard_EXPORT Standard_Real GetX() const; - - Standard_EXPORT Standard_Real GetY() const; - - Standard_EXPORT Standard_Real GetZ() const; - - Standard_EXPORT Standard_Real GetXLen() const; - - Standard_EXPORT Standard_Real GetYLen() const; - - Standard_EXPORT Standard_Real GetZLen() const; - - Standard_EXPORT Standard_Integer GetNbX() const; - - Standard_EXPORT Standard_Integer GetNbY() const; - - Standard_EXPORT Standard_Integer GetNbZ() const; - - //! Returns the center point of a voxel with co-ordinates (ix, iy, iz). - Standard_EXPORT void GetCenter (const Standard_Integer ix, const Standard_Integer iy, const Standard_Integer iz, Standard_Real& xc, Standard_Real& yc, Standard_Real& zc) const; - - //! Returns the origin point of a voxel with co-ordinates (ix, iy, iz). - Standard_EXPORT void GetOrigin (const Standard_Integer ix, const Standard_Integer iy, const Standard_Integer iz, Standard_Real& x0, Standard_Real& y0, Standard_Real& z0) const; - - //! Finds a voxel corresponding to a 3D point. - //! Returns true if it is found. - Standard_EXPORT Standard_Boolean GetVoxel (const Standard_Real x, const Standard_Real y, const Standard_Real z, Standard_Integer& ix, Standard_Integer& iy, Standard_Integer& iz) const; - - //! Returns x-index of a voxel corresponding to x-coordinate. - Standard_EXPORT Standard_Boolean GetVoxelX (const Standard_Real x, Standard_Integer& ix) const; - - //! Returns y-index of a voxel corresponding to y-coordinate. - Standard_EXPORT Standard_Boolean GetVoxelY (const Standard_Real y, Standard_Integer& iy) const; - - //! Returns z-index of a voxel corresponding to z-coordinate. - Standard_EXPORT Standard_Boolean GetVoxelZ (const Standard_Real z, Standard_Integer& iz) const; - - -friend class Voxel_Writer; -friend class Voxel_Reader; - - -protected: - - - - Standard_Address myData; - Standard_Real myX; - Standard_Real myY; - Standard_Real myZ; - Standard_Real myXLen; - Standard_Real myYLen; - Standard_Real myZLen; - Standard_Integer myNbX; - Standard_Integer myNbY; - Standard_Integer myNbZ; - Standard_Integer myNbXY; - Standard_Real myDX; - Standard_Real myDY; - Standard_Real myDZ; - Standard_Real myHalfDX; - Standard_Real myHalfDY; - Standard_Real myHalfDZ; - - -private: - - - - - -}; - - - - - - - -#endif // _Voxel_DS_HeaderFile diff --git a/src/Voxel/Voxel_FastConverter.cxx b/src/Voxel/Voxel_FastConverter.cxx deleted file mode 100644 index 249b94bb20..0000000000 --- a/src/Voxel/Voxel_FastConverter.cxx +++ /dev/null @@ -1,1174 +0,0 @@ -// Created on: 2008-05-30 -// Created by: Vladislav ROMASHKO -// Copyright (c) 2008-2014 OPEN CASCADE SAS -// -// This file is part of Open CASCADE Technology software library. -// -// This library is free software; you can redistribute it and/or modify it under -// the terms of the GNU Lesser General Public License version 2.1 as published -// by the Free Software Foundation, with special exception defined in the file -// OCCT_LGPL_EXCEPTION.txt. Consult the file LICENSE_LGPL_21.txt included in OCCT -// distribution for complete text of the license and disclaimer of any warranty. -// -// Alternatively, this file may be used under the terms of Open CASCADE -// commercial license or contractual agreement. - - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -// Printing the progress in stdout. -//#define CONV_DUMP -Voxel_FastConverter::Voxel_FastConverter(const TopoDS_Shape& shape, - Voxel_ROctBoolDS& voxels, - const Standard_Real deflection, - const Standard_Integer nbx, - const Standard_Integer nby, - const Standard_Integer nbz, - const Standard_Integer nbthreads, - const Standard_Boolean useExistingTriangulation) -:myShape(shape),myVoxels(&voxels), - myDeflection(deflection), - myIsBool(2), - myNbX(nbx),myNbY(nby),myNbZ(nbz), - myNbThreads(nbthreads), - myNbTriangles(0), - myUseExistingTriangulation(useExistingTriangulation) -{ - Init(); -} - -Voxel_FastConverter::Voxel_FastConverter(const TopoDS_Shape& shape, - Voxel_BoolDS& voxels, - const Standard_Real deflection, - const Standard_Integer nbx, - const Standard_Integer nby, - const Standard_Integer nbz, - const Standard_Integer nbthreads, - const Standard_Boolean useExistingTriangulation) -:myShape(shape),myVoxels(&voxels), - myDeflection(deflection), - myIsBool(1), - myNbX(nbx),myNbY(nby),myNbZ(nbz), - myNbThreads(nbthreads), - myNbTriangles(0), - myUseExistingTriangulation(useExistingTriangulation) -{ - Init(); -} - -Voxel_FastConverter::Voxel_FastConverter(const TopoDS_Shape& shape, - Voxel_ColorDS& voxels, - const Standard_Real deflection, - const Standard_Integer nbx, - const Standard_Integer nby, - const Standard_Integer nbz, - const Standard_Integer nbthreads, - const Standard_Boolean useExistingTriangulation) -:myShape(shape),myVoxels(&voxels), - myDeflection(deflection), - myIsBool(0), - myNbX(nbx),myNbY(nby),myNbZ(nbz), - myNbThreads(nbthreads), - myNbTriangles(0), - myUseExistingTriangulation(useExistingTriangulation) -{ - Init(); -} - -void Voxel_FastConverter::Init() -{ - if (myShape.IsNull()) - return; - if (myNbThreads < 1) - return; - - // Check number of splits. - Voxel_DS* voxels = (Voxel_DS*) myVoxels; - if (voxels->GetNbX() != myNbX || voxels->GetNbY() != myNbY || voxels->GetNbZ() != myNbZ) - { - // Compute boundary box of the shape - Bnd_Box box; - BRepBndLib::Add(myShape, box); - - // Define the voxel model by means of the boundary box of shape - Standard_Real xmin, ymin, zmin, xmax, ymax, zmax; - box.Get(xmin, ymin, zmin, xmax, ymax, zmax); - - // Initialize the voxels. - if (myIsBool == 2) - ((Voxel_ROctBoolDS*) voxels)->Init(xmin, ymin, zmin, xmax - xmin, ymax - ymin, zmax - zmin, myNbX, myNbY, myNbZ); - else if (myIsBool == 1) - ((Voxel_BoolDS*) voxels)->Init(xmin, ymin, zmin, xmax - xmin, ymax - ymin, zmax - zmin, myNbX, myNbY, myNbZ); - else if (myIsBool == 0) - ((Voxel_ColorDS*) voxels)->Init(xmin, ymin, zmin, xmax - xmin, ymax - ymin, zmax - zmin, myNbX, myNbY, myNbZ); - } - - // Check presence of triangulation. - TopLoc_Location L; - Standard_Boolean triangulate = Standard_False; - TopExp_Explorer expl(myShape, TopAbs_FACE); - if(myUseExistingTriangulation == Standard_False) - { - for (; expl.More(); expl.Next()) - { - const TopoDS_Face & F = TopoDS::Face(expl.Current()); - Handle(Poly_Triangulation) T = BRep_Tool::Triangulation(F, L); - if (T.IsNull() || (T->Deflection() > myDeflection)) - { - triangulate = Standard_True; - break; - } - } - } - - // Re-create the triangulation. - if (triangulate) - { - BRepMesh_IncrementalMesh(myShape, myDeflection); - } - - // Compute the number of triangles. - myNbTriangles = 0; - expl.Init(myShape, TopAbs_FACE); - for (; expl.More(); expl.Next()) - { - const TopoDS_Face & F = TopoDS::Face(expl.Current()); - Handle(Poly_Triangulation) T = BRep_Tool::Triangulation(F, L); - if (T.IsNull() == Standard_False) - myNbTriangles += T->NbTriangles(); - } -} - -// Destructor -void Voxel_FastConverter::Destroy() -{ - -} - -Standard_Boolean Voxel_FastConverter::Convert(Standard_Integer& progress, - const Standard_Integer ithread) -{ - if (ithread == 1) - progress = 0; -#ifdef CONV_DUMP - if (ithread == 1) - printf("Progress = %d \r", progress); -#endif - - if (myNbX <= 0 || myNbY <= 0 || myNbZ <= 0) - return Standard_False; - - if(myNbTriangles == 0) - return Standard_False; - - // Half of diagonal of a voxel - Voxel_DS* ds = (Voxel_DS*) myVoxels; - Standard_Real dx = ds->GetXLen() / (Standard_Real) ds->GetNbX(), - dy = ds->GetYLen() / (Standard_Real) ds->GetNbY(), - dz = ds->GetZLen() / (Standard_Real) ds->GetNbZ(); - Standard_Real hdiagonal = sqrt(dx * dx + dy * dy + dz * dz); - hdiagonal /= 2.0; - - // Compute the scope of triangles for current thread - Standard_Integer start_thread_triangle = 1, end_thread_triangle = myNbTriangles, ithread_triangle = 0; - if(myNbTriangles < myNbThreads) - { - if(ithread != 1) - return Standard_False; - //in case we're in thread one process all triangles - } - else - { - div_t division = div(myNbTriangles, myNbThreads); - start_thread_triangle = (ithread - 1) * division.quot + 1; - end_thread_triangle = (ithread - 0) * division.quot; - - if(ithread == myNbThreads) - end_thread_triangle += division.rem; - } - - // Convert - TopLoc_Location L; - Standard_Integer iprogress = 0; - Standard_Integer n1, n2, n3; - Standard_Integer ixmin, iymin, izmin, ixmax, iymax, izmax; - Standard_Real xmin, ymin, zmin, xmax, ymax, zmax; - TopExp_Explorer expl(myShape, TopAbs_FACE); - for (; expl.More(); expl.Next()) - { - const TopoDS_Face & F = TopoDS::Face(expl.Current()); - Handle(Poly_Triangulation) T = BRep_Tool::Triangulation(F, L); - if (T.IsNull()) - continue; - - gp_Trsf trsf; - Standard_Boolean transform = !L.IsIdentity(); - if (transform) - trsf = L.Transformation(); - - const TColgp_Array1OfPnt& nodes = T->Nodes(); - const Poly_Array1OfTriangle& triangles = T->Triangles(); - Standard_Integer itriangle = triangles.Lower(), nb_triangles = triangles.Upper(); - for (; itriangle <= nb_triangles; itriangle++) - { - ithread_triangle++; - if (ithread_triangle < start_thread_triangle ) - continue; - if (ithread_triangle > end_thread_triangle) - { - if (ithread == 1) - progress = 100; -#ifdef CONV_DUMP - if (ithread == 1) - printf("Progress = %d \r", progress); -#endif - return Standard_True; - } - - const Poly_Triangle& t = triangles.Value(itriangle); - t.Get(n1, n2, n3); - gp_Pnt p1 = nodes.Value(n1); - gp_Pnt p2 = nodes.Value(n2); - gp_Pnt p3 = nodes.Value(n3); - if (transform) - { - p1.Transform(trsf); - p2.Transform(trsf); - p3.Transform(trsf); - } - - // Get boundary box of the triangle - GetBndBox(p1, p2, p3, xmin, ymin, zmin, xmax, ymax, zmax); - - // Find the range of voxels inside the boudary box of the triangle. - if (!ds->GetVoxel(xmin, ymin, zmin, ixmin, iymin, izmin)) - continue; - if (!ds->GetVoxel(xmax, ymax, zmax, ixmax, iymax, izmax)) - continue; - - // Refuse voxels for whom distance from their center to plane of triangle is greater than half of diagonal. - // Make a line from center of each voxel to the center of triangle and - // compute intersection of the line with sides of triangle. - // Refuse the voxel in case of intersection. - gce_MakePln mkPlane(p1, p2, p3); - if (!mkPlane.IsDone()) - continue; - gp_Pln plane = mkPlane.Value(); - ComputeVoxelsNearTriangle(plane, p1, p2, p3, hdiagonal, ixmin, iymin, izmin, ixmax, iymax, izmax); - - // Progress - if (ithread == 1) - { - iprogress++; - progress = (Standard_Integer) ( (Standard_Real) iprogress / (Standard_Real) myNbTriangles * 100.0 ); - } -#ifdef CONV_DUMP - if (ithread == 1 && prev_progress != progress) - { - printf("Progress = %d \r", progress); - prev_progress = progress; - } -#endif - - } // iteration of triangles - } // iteration of faces - - if (ithread == 1) - progress = 100; -#ifdef CONV_DUMP - if (ithread == 1) - printf("Progress = %d \r", progress); -#endif - return Standard_True; -} - -Standard_Boolean Voxel_FastConverter::ConvertUsingSAT(Standard_Integer& progress, - const Standard_Integer ithread) -{ - if (ithread == 1) - progress = 0; -#ifdef CONV_DUMP - if (ithread == 1) - printf("Progress = %d \r", progress); -#endif - - if (myNbX <= 0 || myNbY <= 0 || myNbZ <= 0) - return Standard_False; - - if(myNbTriangles == 0) - return Standard_False; - - // Half of size of a voxel (also for Voxel_ROctBoolDS) - Voxel_DS* ds = (Voxel_DS*) myVoxels; - Standard_Real dx = ds->GetXLen() / (Standard_Real) ds->GetNbX(), - dy = ds->GetYLen() / (Standard_Real) ds->GetNbY(), - dz = ds->GetZLen() / (Standard_Real) ds->GetNbZ(); - gp_Pnt extents(dx/2.0, dy/2.0, dz/2.0); - gp_Pnt extents2(dx/4.0, dy/4.0, dz/4.0); - gp_Pnt extents4(dx/8.0, dy/8.0, dz/8.0); - - // Compute the scope of triangles for current thread - Standard_Integer start_thread_triangle = 1, end_thread_triangle = myNbTriangles, ithread_triangle = 0; - if(myNbTriangles < myNbThreads) - { - if(ithread != 1) - return Standard_False; - //in case we're in thread one process all triangles - } - else - { - div_t division = div(myNbTriangles, myNbThreads); - start_thread_triangle = (ithread - 1) * division.quot + 1; - end_thread_triangle = (ithread - 0) * division.quot; - - if(ithread == myNbThreads) - end_thread_triangle += division.rem; - } - - // Convert - TopLoc_Location L; - Standard_Integer iprogress = 0; - Standard_Integer n1, n2, n3; - Standard_Integer ixmin, iymin, izmin, ixmax, iymax, izmax; - Standard_Real xmin, ymin, zmin, xmax, ymax, zmax; - TopExp_Explorer expl(myShape, TopAbs_FACE); - for (; expl.More(); expl.Next()) - { - const TopoDS_Face & F = TopoDS::Face(expl.Current()); - Handle(Poly_Triangulation) T = BRep_Tool::Triangulation(F, L); - if (T.IsNull()) - continue; - - gp_Trsf trsf; - Standard_Boolean transform = !L.IsIdentity(); - if (transform) - trsf = L.Transformation(); - - const TColgp_Array1OfPnt& nodes = T->Nodes(); - const Poly_Array1OfTriangle& triangles = T->Triangles(); - Standard_Integer itriangle = triangles.Lower(), nb_triangles = triangles.Upper(); - for (; itriangle <= nb_triangles; itriangle++) - { - ithread_triangle++; - if (ithread_triangle < start_thread_triangle ) - continue; - if (ithread_triangle > end_thread_triangle) - { - if (ithread == 1) - progress = 100; -#ifdef CONV_DUMP - if (ithread == 1) - printf("Progress = %d \r", progress); -#endif - return Standard_True; - } - - const Poly_Triangle& t = triangles.Value(itriangle); - - t.Get(n1, n2, n3); - gp_Pnt p1 = nodes.Value(n1); - gp_Pnt p2 = nodes.Value(n2); - gp_Pnt p3 = nodes.Value(n3); - if (transform) - { - p1.Transform(trsf); - p2.Transform(trsf); - p3.Transform(trsf); - } - - // Get boundary box of the triangle - GetBndBox(p1, p2, p3, xmin, ymin, zmin, xmax, ymax, zmax); - - // Find the range of voxels inside the boudary box of the triangle. - if (!ds->GetVoxel(xmin, ymin, zmin, ixmin, iymin, izmin)) - continue; - if (!ds->GetVoxel(xmax, ymax, zmax, ixmax, iymax, izmax)) - continue; - - // Perform triangle-box intersection to find the voxels resulting from the processed triangle.; - // Using SAT theorem to quickly find the intersection. - ComputeVoxelsNearTriangle(p1, p2, p3, - extents, extents2, extents4, - ixmin, iymin, izmin, ixmax, iymax, izmax); - - // Progress - if (ithread == 1) - { - iprogress++; - progress = (Standard_Integer) ( (Standard_Real) iprogress / (Standard_Real) myNbTriangles * 100.0 ); - } -#ifdef CONV_DUMP - if (ithread == 1 && prev_progress != progress) - { - printf("Progress = %d \r", progress); - prev_progress = progress; - } -#endif - - } // iteration of triangles - } // iteration of faces - - if (ithread == 1) - progress = 100; -#ifdef CONV_DUMP - if (ithread == 1) - printf("Progress = %d \r", progress); -#endif - return Standard_True; -} - -Standard_Boolean Voxel_FastConverter::FillInVolume(const Standard_Byte inner, - const Standard_Integer /*ithread*/) -{ - Voxel_DS* ds = (Voxel_DS*) myVoxels; - Standard_Integer ix, iy, iz, nbx = ds->GetNbX(), nby = ds->GetNbY(), nbz = ds->GetNbZ(); - Standard_Boolean prev_surface, surface, volume; - - if (inner) - { - // Fill-in internal voxels by the value "inner" - for (ix = 0; ix < nbx; ix++) - { - for (iy = 0; iy < nby; iy++) - { - // Check existence of volume. - volume = Standard_False; - surface = Standard_False; - prev_surface = Standard_False; - for (iz = 0; iz < nbz; iz++) - { - surface = (myIsBool == 1) ? - ((Voxel_BoolDS*)myVoxels)->Get(ix, iy, iz) == Standard_True : - ((Voxel_ColorDS*)myVoxels)->Get(ix, iy, iz) > 0; - if (prev_surface && !surface) - { - volume = !volume; - } - prev_surface = surface; - } - if (volume) - continue; - - // Fill-in the volume. - volume = Standard_False; - surface = Standard_False; - prev_surface = Standard_False; - for (iz = 0; iz < nbz; iz++) - { - surface = (myIsBool == 1) ? - ((Voxel_BoolDS*)myVoxels)->Get(ix, iy, iz) == Standard_True : - ((Voxel_ColorDS*)myVoxels)->Get(ix, iy, iz) > 0; - if (prev_surface && !surface) - { - volume = !volume; - } - if (volume && !surface) - { - (myIsBool == 1) ? ((Voxel_BoolDS*)myVoxels)->Set(ix, iy, iz, inner) : - ((Voxel_ColorDS*)myVoxels)->Set(ix, iy, iz, inner); - } - prev_surface = surface; - } - } - } - } - else - { - // Set value of interbal voxels to 0 ("inner" = 0) - Standard_Boolean next_surface; - for (ix = 0; ix < nbx; ix++) - { - for (iy = 0; iy < nby; iy++) - { - volume = Standard_False; - surface = Standard_False; - prev_surface = Standard_False; - next_surface = Standard_False; - for (iz = 0; iz < nbz; iz++) - { - surface = (myIsBool == 1) ? - ((Voxel_BoolDS*)myVoxels)->Get(ix, iy, iz) == Standard_True : - ((Voxel_ColorDS*)myVoxels)->Get(ix, iy, iz) > 0; - if (prev_surface != surface) - { - volume = !volume; - } - if (volume && iz + 1 < nbz) - { - next_surface = (myIsBool == 1) ? - ((Voxel_BoolDS*)myVoxels)->Get(ix, iy, iz + 1) == Standard_True : - ((Voxel_ColorDS*)myVoxels)->Get(ix, iy, iz + 1) > 0; - } - if (volume && prev_surface == surface && next_surface) - { - (myIsBool == 1) ? ((Voxel_BoolDS*)myVoxels)->Set(ix, iy, iz, inner) : - ((Voxel_ColorDS*)myVoxels)->Set(ix, iy, iz, inner); - } - prev_surface = surface; - } - } - } - } - - return Standard_True; -} - -Standard_Boolean Voxel_FastConverter::FillInVolume(const Standard_Byte inner, const TopoDS_Shape & shape, const Standard_Integer /*ithread*/) -{ - Voxel_DS* ds = (Voxel_DS*) myVoxels; - Standard_Integer ix, iy, iz, nbx = ds->GetNbX(), nby = ds->GetNbY(), nbz = ds->GetNbZ(); - Standard_Boolean prev_surface, surface, volume, isOnVerticalSurface; - - BRepClass3d_SolidClassifier solidClassifier(shape); - Standard_Real xc, yc, zc; - - if (inner) - { - // Fill-in internal voxels by the value "inner" - for (ix = 0; ix < nbx; ix++) - { - for (iy = 0; iy < nby; iy++) - { - // Check existence of volume. - volume = Standard_False; - surface = Standard_False; - prev_surface = Standard_False; - isOnVerticalSurface = Standard_False; - for (iz = 0; iz < nbz; iz++) - { - surface = (myIsBool == 1) ? - ((Voxel_BoolDS*)myVoxels)->Get(ix, iy, iz) == Standard_True : - ((Voxel_ColorDS*)myVoxels)->Get(ix, iy, iz) > 0; - if (prev_surface && !surface) - { - if(isOnVerticalSurface) - { - isOnVerticalSurface = Standard_False; - ((Voxel_BoolDS*)myVoxels)->GetCenter(ix, iy, iz, xc, yc, zc); - gp_Pnt P(xc, yc, zc); - solidClassifier.Perform(P, Precision::Confusion()); - - if(solidClassifier.State() == TopAbs_IN) - volume = Standard_True; - else - volume = Standard_False; - } - else - volume = !volume; - } - if(prev_surface && surface) - isOnVerticalSurface = Standard_True; - else - isOnVerticalSurface = Standard_False; - prev_surface = surface; - } - if (volume) - continue; - - // Fill-in the volume. - volume = Standard_False; - surface = Standard_False; - prev_surface = Standard_False; - isOnVerticalSurface = Standard_False; - for (iz = 0; iz < nbz; iz++) - { - surface = (myIsBool == 1) ? - ((Voxel_BoolDS*)myVoxels)->Get(ix, iy, iz) == Standard_True : - ((Voxel_ColorDS*)myVoxels)->Get(ix, iy, iz) > 0; - if (prev_surface && !surface) - { - if(isOnVerticalSurface) - { - isOnVerticalSurface = Standard_False; - ((Voxel_BoolDS*)myVoxels)->GetCenter(ix, iy, iz, xc, yc, zc); - gp_Pnt P(xc, yc, zc); - solidClassifier.Perform(P, Precision::Confusion()); - - if(solidClassifier.State() == TopAbs_IN) - volume = Standard_True; - else - volume = Standard_False; - } - else - volume = !volume; - } - if (volume && !surface) - { - (myIsBool == 1) ? ((Voxel_BoolDS*)myVoxels)->Set(ix, iy, iz, inner) : - ((Voxel_ColorDS*)myVoxels)->Set(ix, iy, iz, inner); - } - if(prev_surface && surface) - isOnVerticalSurface = Standard_True; - else - isOnVerticalSurface = Standard_False; - prev_surface = surface; - } - } - } - } - - return Standard_True; -} - -void Voxel_FastConverter::GetBndBox(const gp_Pnt& p1, - const gp_Pnt& p2, - const gp_Pnt& p3, - Standard_Real& xmin, - Standard_Real& ymin, - Standard_Real& zmin, - Standard_Real& xmax, - Standard_Real& ymax, - Standard_Real& zmax) const -{ - // P1: - xmin = p1.X(); - ymin = p1.Y(); - zmin = p1.Z(); - xmax = p1.X(); - ymax = p1.Y(); - zmax = p1.Z(); - // P2: - if (xmin > p2.X()) - xmin = p2.X(); - if (ymin > p2.Y()) - ymin = p2.Y(); - if (zmin > p2.Z()) - zmin = p2.Z(); - if (xmax < p2.X()) - xmax = p2.X(); - if (ymax < p2.Y()) - ymax = p2.Y(); - if (zmax < p2.Z()) - zmax = p2.Z(); - // P3: - if (xmin > p3.X()) - xmin = p3.X(); - if (ymin > p3.Y()) - ymin = p3.Y(); - if (zmin > p3.Z()) - zmin = p3.Z(); - if (xmax < p3.X()) - xmax = p3.X(); - if (ymax < p3.Y()) - ymax = p3.Y(); - if (zmax < p3.Z()) - zmax = p3.Z(); -} - -// This method is copied from Voxel_ShapeIntersector.cxx -static Standard_Boolean mayIntersect(const gp_Pnt2d& p11, const gp_Pnt2d& p12, - const gp_Pnt2d& p21, const gp_Pnt2d& p22) -{ - if (p11.X() > p21.X() && p11.X() > p22.X() && p12.X() > p21.X() && p12.X() > p22.X()) - return Standard_False; - if (p11.X() < p21.X() && p11.X() < p22.X() && p12.X() < p21.X() && p12.X() < p22.X()) - return Standard_False; - if (p11.Y() > p21.Y() && p11.Y() > p22.Y() && p12.Y() > p21.Y() && p12.Y() > p22.Y()) - return Standard_False; - if (p11.Y() < p21.Y() && p11.Y() < p22.Y() && p12.Y() < p21.Y() && p12.Y() < p22.Y()) - return Standard_False; - return Standard_True; -} - -void Voxel_FastConverter::ComputeVoxelsNearTriangle(const gp_Pln& plane, - const gp_Pnt& p1, - const gp_Pnt& p2, - const gp_Pnt& p3, - const Standard_Real hdiagonal, - const Standard_Integer ixmin, - const Standard_Integer iymin, - const Standard_Integer izmin, - const Standard_Integer ixmax, - const Standard_Integer iymax, - const Standard_Integer izmax) const -{ - gp_Pnt pc; - Standard_Real xc, yc, zc, uc, vc, u1, v1, u2, v2, u3, v3; - Standard_Integer ix, iy, iz; - IntAna2d_AnaIntersection intersector2d; - - // Project points of triangle onto the plane - ElSLib::Parameters(plane, p1, u1, v1); - ElSLib::Parameters(plane, p2, u2, v2); - ElSLib::Parameters(plane, p3, u3, v3); - - // Make lines of triangle - gp_Pnt2d p2d1(u1, v1), p2d2(u2, v2), p2d3(u3, v3), p2dt((u1+u2+u3)/3.0,(v1+v2+v3)/3.0), p2dc; - gp_Vec2d v2d12(p2d1, p2d2), v2d23(p2d2, p2d3), v2d31(p2d3, p2d1); - gp_Lin2d L1(p2d1, v2d12), L2(p2d2, v2d23), L3(p2d3, v2d31), Lv; - Standard_Real d1 = p2d1.Distance(p2d2) - Precision::Confusion(), - d2 = p2d2.Distance(p2d3) - Precision::Confusion(), - d3 = p2d3.Distance(p2d1) - Precision::Confusion(), dv; - - Voxel_DS* ds = (Voxel_DS*) myVoxels; - for (ix = ixmin; ix <= ixmax; ix++) - { - for (iy = iymin; iy <= iymax; iy++) - { - for (iz = izmin; iz <= izmax; iz++) - { - ds->GetCenter(ix, iy, iz, xc, yc, zc); - pc.SetCoord(xc, yc, zc); - if (plane.Distance(pc) < hdiagonal) - { - ElSLib::Parameters(plane, pc, uc, vc); - p2dc.SetCoord(uc, vc); - - gp_Vec2d v2dct(p2dc, p2dt); - dv = v2dct.Magnitude() - Precision::Confusion(); - Lv.SetLocation(p2dc); - Lv.SetDirection(v2dct); - - // Side 1: - if (mayIntersect(p2d1, p2d2, p2dc, p2dt)) - { - intersector2d.Perform(Lv, L1); - if (intersector2d.IsDone() && !intersector2d.ParallelElements() && intersector2d.NbPoints()) - { - const IntAna2d_IntPoint& i2d = intersector2d.Point(1); - Standard_Real param1 = i2d.ParamOnFirst(); - Standard_Real param2 = i2d.ParamOnSecond(); - if (param1 > Precision::Confusion() && param1 < dv && - param2 > Precision::Confusion() && param2 < d1) - { - continue; - } - } - } - - // Side 2: - if (mayIntersect(p2d2, p2d3, p2dc, p2dt)) - { - intersector2d.Perform(Lv, L2); - if (intersector2d.IsDone() && !intersector2d.ParallelElements() && intersector2d.NbPoints()) - { - const IntAna2d_IntPoint& i2d = intersector2d.Point(1); - Standard_Real param1 = i2d.ParamOnFirst(); - Standard_Real param2 = i2d.ParamOnSecond(); - if (param1 > Precision::Confusion() && param1 < dv && - param2 > Precision::Confusion() && param2 < d2) - { - continue; - } - } - } - - // Side 3: - if (mayIntersect(p2d3, p2d1, p2dc, p2dt)) - { - intersector2d.Perform(Lv, L3); - if (intersector2d.IsDone() && !intersector2d.ParallelElements() && intersector2d.NbPoints()) - { - const IntAna2d_IntPoint& i2d = intersector2d.Point(1); - Standard_Real param1 = i2d.ParamOnFirst(); - Standard_Real param2 = i2d.ParamOnSecond(); - if (param1 > Precision::Confusion() && param1 < dv && - param2 > Precision::Confusion() && param2 < d3) - { - continue; - } - } - } - - // Set positive value to this voxel: - switch (myIsBool) - { - case 0: - ((Voxel_ColorDS*) myVoxels)->Set(ix, iy, iz, 15); - break; - case 1: - ((Voxel_BoolDS*) myVoxels)->Set(ix, iy, iz, Standard_True); - break; - case 2: - { - //((Voxel_ROctBoolDS*) myVoxels)->Set(ix, iy, iz, Standard_True); - - // Check intersection between the triangle & sub-voxels of the voxel. - Standard_Real hdiagonal2 = hdiagonal / 2.0, hdiagonal4 = hdiagonal / 4.0; - for (Standard_Integer i = 0; i < 8; i++) - { - ((Voxel_ROctBoolDS*) myVoxels)->GetCenter(ix, iy, iz, i, xc, yc, zc); - pc.SetCoord(xc, yc, zc); - if (plane.Distance(pc) < hdiagonal2) - { - ElSLib::Parameters(plane, pc, uc, vc); - p2dc.SetCoord(uc, vc); - - gp_Vec2d aVec2dct1(p2dc, p2dt); - dv = aVec2dct1.Magnitude() - Precision::Confusion(); - Lv.SetLocation(p2dc); - Lv.SetDirection(aVec2dct1); - - // Side 1: - if (mayIntersect(p2d1, p2d2, p2dc, p2dt)) - { - intersector2d.Perform(Lv, L1); - if (intersector2d.IsDone() && !intersector2d.ParallelElements() && intersector2d.NbPoints()) - { - const IntAna2d_IntPoint& i2d = intersector2d.Point(1); - Standard_Real param1 = i2d.ParamOnFirst(); - Standard_Real param2 = i2d.ParamOnSecond(); - if (param1 > Precision::Confusion() && param1 < dv && - param2 > Precision::Confusion() && param2 < d1) - { - continue; - } - } - } - - // Side 2: - if (mayIntersect(p2d2, p2d3, p2dc, p2dt)) - { - intersector2d.Perform(Lv, L2); - if (intersector2d.IsDone() && !intersector2d.ParallelElements() && intersector2d.NbPoints()) - { - const IntAna2d_IntPoint& i2d = intersector2d.Point(1); - Standard_Real param1 = i2d.ParamOnFirst(); - Standard_Real param2 = i2d.ParamOnSecond(); - if (param1 > Precision::Confusion() && param1 < dv && - param2 > Precision::Confusion() && param2 < d2) - { - continue; - } - } - } - - // Side 3: - if (mayIntersect(p2d3, p2d1, p2dc, p2dt)) - { - intersector2d.Perform(Lv, L3); - if (intersector2d.IsDone() && !intersector2d.ParallelElements() && intersector2d.NbPoints()) - { - const IntAna2d_IntPoint& i2d = intersector2d.Point(1); - Standard_Real param1 = i2d.ParamOnFirst(); - Standard_Real param2 = i2d.ParamOnSecond(); - if (param1 > Precision::Confusion() && param1 < dv && - param2 > Precision::Confusion() && param2 < d3) - { - continue; - } - } - } - - //((Voxel_ROctBoolDS*) myVoxels)->Set(ix, iy, iz, i, Standard_True); - - // Check intersection between the triangle & sub-voxels of the sub-voxel. - for (Standard_Integer j = 0; j < 8; j++) - { - ((Voxel_ROctBoolDS*) myVoxels)->GetCenter(ix, iy, iz, i, j, xc, yc, zc); - pc.SetCoord(xc, yc, zc); - if (plane.Distance(pc) < hdiagonal4) - { - ElSLib::Parameters(plane, pc, uc, vc); - p2dc.SetCoord(uc, vc); - - gp_Vec2d aVec2dct2(p2dc, p2dt); - dv = aVec2dct2.Magnitude() - Precision::Confusion(); - Lv.SetLocation(p2dc); - Lv.SetDirection(aVec2dct2); - - // Side 1: - if (mayIntersect(p2d1, p2d2, p2dc, p2dt)) - { - intersector2d.Perform(Lv, L1); - if (intersector2d.IsDone() && !intersector2d.ParallelElements() && intersector2d.NbPoints()) - { - const IntAna2d_IntPoint& i2d = intersector2d.Point(1); - Standard_Real param1 = i2d.ParamOnFirst(); - Standard_Real param2 = i2d.ParamOnSecond(); - if (param1 > Precision::Confusion() && param1 < dv && - param2 > Precision::Confusion() && param2 < d1) - { - continue; - } - } - } - - // Side 2: - if (mayIntersect(p2d2, p2d3, p2dc, p2dt)) - { - intersector2d.Perform(Lv, L2); - if (intersector2d.IsDone() && !intersector2d.ParallelElements() && intersector2d.NbPoints()) - { - const IntAna2d_IntPoint& i2d = intersector2d.Point(1); - Standard_Real param1 = i2d.ParamOnFirst(); - Standard_Real param2 = i2d.ParamOnSecond(); - if (param1 > Precision::Confusion() && param1 < dv && - param2 > Precision::Confusion() && param2 < d2) - { - continue; - } - } - } - - // Side 3: - if (mayIntersect(p2d3, p2d1, p2dc, p2dt)) - { - intersector2d.Perform(Lv, L3); - if (intersector2d.IsDone() && !intersector2d.ParallelElements() && intersector2d.NbPoints()) - { - const IntAna2d_IntPoint& i2d = intersector2d.Point(1); - Standard_Real param1 = i2d.ParamOnFirst(); - Standard_Real param2 = i2d.ParamOnSecond(); - if (param1 > Precision::Confusion() && param1 < dv && - param2 > Precision::Confusion() && param2 < d3) - { - continue; - } - } - } - - ((Voxel_ROctBoolDS*) myVoxels)->Set(ix, iy, iz, i, j, Standard_True); - } - } // End of "Check level 2". - - } - } // End of "Check level 1". - - break; - } - } - } - } - } - } -} - -//! This macro quickly finds the min & max values among 3 variables -#define FINDMINMAX(x0, x1, x2, min, max) \ - min = max = x0; \ - if(x1max) max=x1; \ - if(x2max) max=x2; - -static bool planeBoxOverlap(const gp_Vec & normal, const double d, const gp_Pnt & maxbox) -{ - gp_Vec vmin, vmax; - if(normal.X() > 0.0) { vmin.SetX(-maxbox.X()); vmax.SetX(maxbox.X()); } - else { vmin.SetX(maxbox.X()); vmax.SetX(-maxbox.X()); } - - if(normal.Y() > 0.0) { vmin.SetY(-maxbox.Y()); vmax.SetY(maxbox.Y()); } - else { vmin.SetY(maxbox.Y()); vmax.SetY(-maxbox.Y()); } - - if(normal.Z() > 0.0) { vmin.SetZ(-maxbox.Z()); vmax.SetZ(maxbox.Z()); } - else { vmin.SetZ(maxbox.Z()); vmax.SetZ(-maxbox.Z()); } - - if((normal.Dot(vmin)) + d > 0.0) return false; - if((normal.Dot(vmax)) + d>= 0.0) return true; - - return false; -} - -#define AXISTEST_X01(a, b, fa, fb) \ - min = a*v0.Y() - b*v0.Z(); \ - max = a*v2.Y() - b*v2.Z(); \ - if(min>max) {const double tmp=max; max=min; min=tmp; } \ - rad = fa * extents.Y() + fb * extents.Z(); \ - if(min>rad || max<-rad) return false; - -#define AXISTEST_X2(a, b, fa, fb) \ - min = a*v0.Y() - b*v0.Z(); \ - max = a*v1.Y() - b*v1.Z(); \ - if(min>max) {const double tmp=max; max=min; min=tmp; } \ - rad = fa * extents.Y() + fb * extents.Z(); \ - if(min>rad || max<-rad) return false; - -#define AXISTEST_Y02(a, b, fa, fb) \ - min = b*v0.Z() - a*v0.X(); \ - max = b*v2.Z() - a*v2.X(); \ - if(min>max) {const double tmp=max; max=min; min=tmp; } \ - rad = fa * extents.X() + fb * extents.Z(); \ - if(min>rad || max<-rad) return false; - -#define AXISTEST_Y1(a, b, fa, fb) \ - min = b*v0.Z() - a*v0.X(); \ - max = b*v1.Z() - a*v1.X(); \ - if(min>max) {const double tmp=max; max=min; min=tmp; } \ - rad = fa * extents.X() + fb * extents.Z(); \ - if(min>rad || max<-rad) return false; - -#define AXISTEST_Z12(a, b, fa, fb) \ - min = a*v1.X() - b*v1.Y(); \ - max = a*v2.X() - b*v2.Y(); \ - if(min>max) {const double tmp=max; max=min; min=tmp; } \ - rad = fa * extents.X() + fb * extents.Y(); \ - if(min>rad || max<-rad) return false; - -#define AXISTEST_Z0(a, b, fa, fb) \ - min = a*v0.X() - b*v0.Y(); \ - max = a*v1.X() - b*v1.Y(); \ - if(min>max) {const double tmp=max; max=min; min=tmp; } \ - rad = fa * extents.X() + fb * extents.Y(); \ - if(min>rad || max<-rad) return false; - -// compute triangle edges -// - edges lazy evaluated to take advantage of early exits -// - fabs precomputed (half less work, possible since extents are always >0) -// - customized macros to take advantage of the null component -// - axis vector discarded, possibly saves useless movs -#define IMPLEMENT_CLASS3_TESTS \ - double rad; \ - double min, max; \ - \ - const double fey0 = fabs(e0.Y()); \ - const double fez0 = fabs(e0.Z()); \ - AXISTEST_X01(e0.Z(), e0.Y(), fez0, fey0); \ - const double fex0 = fabs(e0.X()); \ - AXISTEST_Y02(e0.Z(), e0.X(), fez0, fex0); \ - AXISTEST_Z12(e0.Y(), e0.X(), fey0, fex0); \ - \ - const double fey1 = fabs(e1.Y()); \ - const double fez1 = fabs(e1.Z()); \ - AXISTEST_X01(e1.Z(), e1.Y(), fez1, fey1); \ - const double fex1 = fabs(e1.X()); \ - AXISTEST_Y02(e1.Z(), e1.X(), fez1, fex1); \ - AXISTEST_Z0(e1.Y(), e1.X(), fey1, fex1); \ - \ - const gp_Vec e2 = v2 - v0; \ - const double fey2 = fabs(e2.Y()); \ - const double fez2 = fabs(e2.Z()); \ - AXISTEST_X2(e2.Z(), e2.Y(), fez2, fey2); \ - const double fex2 = fabs(e2.X()); \ - AXISTEST_Y1(e2.Z(), e2.X(), fez2, fex2); \ - AXISTEST_Z12(e2.Y(), e2.X(), fey2, fex2); - -static bool TriBoxOverlap(const gp_Pnt & p1, const gp_Pnt & p2, const gp_Pnt & p3, - const gp_Pnt & center, const gp_Pnt & extents) -{ - // use separating axis theorem to test overlap between triangle and box - // need to test for overlap in these directions: - // 1) the {x,y,z}-directions (actually, since we use the AABB of the triangle - // we do not even need to test these) - // 2) normal of the triangle - // 3) crossproduct(edge from tri, {x,y,z}-directin) - // this gives 3x3=9 more tests - - // move everything so that the boxcenter is in (0,0,0) - gp_Vec v0(center, p1); - gp_Vec v1(center, p2); - gp_Vec v2(center, p3); - - // First, test overlap in the {x,y,z}-directions - double aMin,aMax; - // Find min, max of the triangle in x-direction, and test for overlap in X - FINDMINMAX(v0.X(), v1.X(), v2.X(), aMin, aMax); - if(aMin>extents.X() || aMax<-extents.X()) return false; - - // Same for Y - FINDMINMAX(v0.Y(), v1.Y(), v2.Y(), aMin, aMax); - if(aMin>extents.Y() || aMax<-extents.Y()) return false; - - // Same for Z - FINDMINMAX(v0.Z(), v1.Z(), v2.Z(), aMin, aMax); - if(aMin>extents.Z() || aMax<-extents.Z()) return false; - - // 2) Test if the box intersects the plane of the triangle - // compute plane equation of triangle: normal*x+d=0 - // ### could be precomputed since we use the same leaf triangle several times - const gp_Vec e0 = v1 - v0; - const gp_Vec e1 = v2 - v1; - const gp_Vec normal = e0.Crossed(e1); - const double d = -normal.Dot(v0); - if(!planeBoxOverlap(normal, d, extents)) return false; - - // 3) "Class III" tests - //if(mFullPrimBoxTest) - { - IMPLEMENT_CLASS3_TESTS - } - - return true; -} - -void Voxel_FastConverter::ComputeVoxelsNearTriangle(const gp_Pnt& p1, - const gp_Pnt& p2, - const gp_Pnt& p3, - const gp_Pnt& extents, - const gp_Pnt& extents2, - const gp_Pnt& extents4, - const Standard_Integer ixmin, - const Standard_Integer iymin, - const Standard_Integer izmin, - const Standard_Integer ixmax, - const Standard_Integer iymax, - const Standard_Integer izmax) const -{ - gp_Pnt pc; - Standard_Real xc, yc, zc; - Standard_Integer ix, iy, iz; - - Voxel_DS* ds = (Voxel_DS*) myVoxels; - for (ix = ixmin; ix <= ixmax; ix++) - { - for (iy = iymin; iy <= iymax; iy++) - { - for (iz = izmin; iz <= izmax; iz++) - { - ds->GetCenter(ix, iy, iz, xc, yc, zc); - pc.SetCoord(xc, yc, zc); - - if(TriBoxOverlap(p1, p2, p3, pc, extents)) - { - // Set positive value to this voxel: - switch (myIsBool) - { - case 0: - ((Voxel_ColorDS*) myVoxels)->Set(ix, iy, iz, 15); - break; - case 1: - ((Voxel_BoolDS*) myVoxels)->Set(ix, iy, iz, Standard_True); - break; - case 2: - { - //((Voxel_ROctBoolDS*) myVoxels)->Set(ix, iy, iz, Standard_True); - - // Check intersection between the triangle & sub-voxels of the voxel. - for (Standard_Integer i = 0; i < 8; i++) - { - ((Voxel_ROctBoolDS*) myVoxels)->GetCenter(ix, iy, iz, i, xc, yc, zc); - pc.SetCoord(xc, yc, zc); - if(TriBoxOverlap(p1, p2, p3, pc, extents2)) - { - //((Voxel_ROctBoolDS*) myVoxels)->Set(ix, iy, iz, i, Standard_True); - - // Check intersection between the triangle & sub-voxels of the sub-voxel. - for (Standard_Integer j = 0; j < 8; j++) - { - ((Voxel_ROctBoolDS*) myVoxels)->GetCenter(ix, iy, iz, i, j, xc, yc, zc); - pc.SetCoord(xc, yc, zc); - if(TriBoxOverlap(p1, p2, p3, pc, extents4)) - { - ((Voxel_ROctBoolDS*) myVoxels)->Set(ix, iy, iz, i, j, Standard_True); - } - } // End of "Check level 2". - - } - } // End of "Check level 1". - break; - } - } - } - } - } - } -} diff --git a/src/Voxel/Voxel_FastConverter.hxx b/src/Voxel/Voxel_FastConverter.hxx deleted file mode 100644 index 9e6b533b2b..0000000000 --- a/src/Voxel/Voxel_FastConverter.hxx +++ /dev/null @@ -1,135 +0,0 @@ -// Created on: 2008-05-30 -// Created by: Vladislav ROMASHKO -// Copyright (c) 2008-2014 OPEN CASCADE SAS -// -// This file is part of Open CASCADE Technology software library. -// -// This library is free software; you can redistribute it and/or modify it under -// the terms of the GNU Lesser General Public License version 2.1 as published -// by the Free Software Foundation, with special exception defined in the file -// OCCT_LGPL_EXCEPTION.txt. Consult the file LICENSE_LGPL_21.txt included in OCCT -// distribution for complete text of the license and disclaimer of any warranty. -// -// Alternatively, this file may be used under the terms of Open CASCADE -// commercial license or contractual agreement. - -#ifndef _Voxel_FastConverter_HeaderFile -#define _Voxel_FastConverter_HeaderFile - -#include -#include -#include - -#include -#include -#include -#include -#include -#include -class TopoDS_Shape; -class Voxel_BoolDS; -class Voxel_ColorDS; -class Voxel_ROctBoolDS; -class gp_Pnt; -class gp_Pln; - - -//! Converts a shape to voxel representation. -//! It does it fast, but with less precision. -//! Also, it doesn't fill-in volumic part of the shape. -class Voxel_FastConverter -{ -public: - - DEFINE_STANDARD_ALLOC - - - //! A constructor for conversion of a shape into a cube of boolean voxels. - //! It allocates the voxels in memory. - //! "nbthreads" defines the number of threads used to convert the shape. - Standard_EXPORT Voxel_FastConverter(const TopoDS_Shape& shape, Voxel_BoolDS& voxels, const Standard_Real deflection = 0.1, const Standard_Integer nbx = 10, const Standard_Integer nby = 10, const Standard_Integer nbz = 10, const Standard_Integer nbthreads = 1, const Standard_Boolean useExistingTriangulation = Standard_False); - - //! A constructor for conversion of a shape into a cube of colored voxels. - //! It allocates the voxels in memory. - //! "nbthreads" defines the number of threads used to convert the shape. - Standard_EXPORT Voxel_FastConverter(const TopoDS_Shape& shape, Voxel_ColorDS& voxels, const Standard_Real deflection = 0.1, const Standard_Integer nbx = 10, const Standard_Integer nby = 10, const Standard_Integer nbz = 10, const Standard_Integer nbthreads = 1, const Standard_Boolean useExistingTriangulation = Standard_False); - - //! A constructor for conversion of a shape into a cube of boolean voxels - //! split into 8 sub-voxels recursively. - //! It allocates the voxels in memory. - //! "nbthreads" defines the number of threads used to convert the shape. - Standard_EXPORT Voxel_FastConverter(const TopoDS_Shape& shape, Voxel_ROctBoolDS& voxels, const Standard_Real deflection = 0.1, const Standard_Integer nbx = 10, const Standard_Integer nby = 10, const Standard_Integer nbz = 10, const Standard_Integer nbthreads = 1, const Standard_Boolean useExistingTriangulation = Standard_False); - - //! Converts a shape into a voxel representation. - //! It sets to 0 the outside volume of the shape and - //! 1 for surfacic part of the shape. - //! "ithread" is the index of the thread for current call of ::Convert(). - //! Start numeration of "ithread" with 1, please. - Standard_EXPORT Standard_Boolean Convert (Standard_Integer& progress, const Standard_Integer ithread = 1); - - //! Converts a shape into a voxel representation using separating axis theorem. - //! It sets to 0 the outside volume of the shape and - //! 1 for surfacic part of the shape. - //! "ithread" is the index of the thread for current call of ::Convert(). - //! Start numeration of "ithread" with 1, please. - Standard_EXPORT Standard_Boolean ConvertUsingSAT (Standard_Integer& progress, const Standard_Integer ithread = 1); - - //! Fills-in volume of the shape by a value. - Standard_EXPORT Standard_Boolean FillInVolume (const Standard_Byte inner, const Standard_Integer ithread = 1); - - //! Fills-in volume of the shape by a value. - //! Uses the topological information from the provided shape - //! to judge whether points are inside the shape or not - //! (only when processing vertical faces). - //! The inner value has to be positive. - Standard_EXPORT Standard_Boolean FillInVolume (const Standard_Byte inner, const TopoDS_Shape& shape, const Standard_Integer ithread = 1); - - //! A destructor. - Standard_EXPORT void Destroy(); -~Voxel_FastConverter() -{ - Destroy(); -} - - - - -protected: - - - - - -private: - - - Standard_EXPORT void Init(); - - Standard_EXPORT void GetBndBox (const gp_Pnt& p1, const gp_Pnt& p2, const gp_Pnt& p3, Standard_Real& xmin, Standard_Real& ymin, Standard_Real& zmin, Standard_Real& xmax, Standard_Real& ymax, Standard_Real& zmax) const; - - Standard_EXPORT void ComputeVoxelsNearTriangle (const gp_Pln& plane, const gp_Pnt& p1, const gp_Pnt& p2, const gp_Pnt& p3, const Standard_Real hdiag, const Standard_Integer ixmin, const Standard_Integer iymin, const Standard_Integer izmin, const Standard_Integer ixmax, const Standard_Integer iymax, const Standard_Integer izmax) const; - - Standard_EXPORT void ComputeVoxelsNearTriangle (const gp_Pnt& p1, const gp_Pnt& p2, const gp_Pnt& p3, const gp_Pnt& extents, const gp_Pnt& extents2, const gp_Pnt& extents4, const Standard_Integer ixmin, const Standard_Integer iymin, const Standard_Integer izmin, const Standard_Integer ixmax, const Standard_Integer iymax, const Standard_Integer izmax) const; - - - TopoDS_Shape myShape; - Standard_Address myVoxels; - Standard_Real myDeflection; - Standard_Integer myIsBool; - Standard_Integer myNbX; - Standard_Integer myNbY; - Standard_Integer myNbZ; - Standard_Integer myNbThreads; - Standard_Integer myNbTriangles; - Standard_Boolean myUseExistingTriangulation; - - -}; - - - - - - - -#endif // _Voxel_FastConverter_HeaderFile diff --git a/src/Voxel/Voxel_FloatDS.cxx b/src/Voxel/Voxel_FloatDS.cxx deleted file mode 100644 index 294f4fe0fc..0000000000 --- a/src/Voxel/Voxel_FloatDS.cxx +++ /dev/null @@ -1,116 +0,0 @@ -// Created on: 2008-05-15 -// Created by: Vladislav ROMASHKO -// Copyright (c) 2008-2014 OPEN CASCADE SAS -// -// This file is part of Open CASCADE Technology software library. -// -// This library is free software; you can redistribute it and/or modify it under -// the terms of the GNU Lesser General Public License version 2.1 as published -// by the Free Software Foundation, with special exception defined in the file -// OCCT_LGPL_EXCEPTION.txt. Consult the file LICENSE_LGPL_21.txt included in OCCT -// distribution for complete text of the license and disclaimer of any warranty. -// -// Alternatively, this file may be used under the terms of Open CASCADE -// commercial license or contractual agreement. - - -#include - -#include -// Empty constructor -Voxel_FloatDS::Voxel_FloatDS():Voxel_DS() -{ - -} - -// Constructor with intialization. -Voxel_FloatDS::Voxel_FloatDS(const Standard_Real x, const Standard_Real y, const Standard_Real z, - const Standard_Real xlen, const Standard_Real ylen, const Standard_Real zlen, - const Standard_Integer nbx, const Standard_Integer nby, const Standard_Integer nbz) -:Voxel_DS() -{ - Init(x, y, z, xlen, ylen, zlen, nbx, nby, nbz); -} - -// Initialization. -void Voxel_FloatDS::Init(const Standard_Real x, const Standard_Real y, const Standard_Real z, - const Standard_Real xlen, const Standard_Real ylen, const Standard_Real zlen, - const Standard_Integer nbx, const Standard_Integer nby, const Standard_Integer nbz) -{ - Destroy(); - - Voxel_DS::Init(x, y, z, xlen, ylen, zlen, nbx, nby, nbz); - - if (!myNbX || !myNbY || !myNbZ) - return; - - Standard_Integer nb_floats = myNbXY * myNbZ; - Standard_Integer nb_slices = RealToInt(ceil(nb_floats / 32.0)); // 32 values in 1 slice - myData = (Standard_Address) calloc(nb_slices, sizeof(Standard_ShortReal*)); -} - -// Destructor -void Voxel_FloatDS::Destroy() -{ - if (myData) - { - SetZero(); - free((Standard_ShortReal**)myData); - myData = 0; - } -} - -void Voxel_FloatDS::SetZero() -{ - if (myData) - { - Standard_Integer nb_bytes = myNbXY * myNbZ; - Standard_Integer ix = 0, nb_slices = RealToInt(ceil(nb_bytes / 32.0)); - for (; ix < nb_slices; ix++) - { - if (((Standard_ShortReal**)myData)[ix]) - { - free(((Standard_ShortReal**)myData)[ix]); - ((Standard_ShortReal**)myData)[ix] = 0; - } - } - } -} - -// Access to the floating-point information attached to a particular voxel: -// Info: (ix >= 0 && ix < theNb_x), etc. -void Voxel_FloatDS::Set(const Standard_Integer ix, const Standard_Integer iy, const Standard_Integer iz, - const Standard_ShortReal data) -{ - Standard_Integer ifloat = ix + myNbX * iy + myNbXY * iz; - Standard_Integer islice = ifloat >> 5; - - // Allocate the slice if it is not done yet. - if (!((Standard_ShortReal**)myData)[islice]) - { - ((Standard_ShortReal**)myData)[islice] = - (Standard_ShortReal*) calloc(32/*number of floating values in slice*/, sizeof(Standard_ShortReal)); - } - - // Index of start-byte of the value within the slice. - Standard_Integer ivalue = ifloat - (islice << 5); - - // Value (float) - ((Standard_ShortReal*)((Standard_ShortReal**)myData)[islice])[ivalue] = data; -} - -Standard_ShortReal Voxel_FloatDS::Get(const Standard_Integer ix, const Standard_Integer iy, const Standard_Integer iz) const -{ - Standard_Integer ifloat = ix + myNbX * iy + myNbXY * iz; - Standard_Integer islice = ifloat >> 5; - - // If the slice of data is not allocated, it means that its values are 0. - if (!((Standard_ShortReal**)myData)[islice]) - return 0.0f; - - // Index of start-byte of the value within the slice. - Standard_Integer ivalue = ifloat - (islice << 5); - - // Value (floating-point value) - return ((Standard_ShortReal*)((Standard_ShortReal**)myData)[islice])[ivalue]; -} diff --git a/src/Voxel/Voxel_FloatDS.hxx b/src/Voxel/Voxel_FloatDS.hxx deleted file mode 100644 index fb91733490..0000000000 --- a/src/Voxel/Voxel_FloatDS.hxx +++ /dev/null @@ -1,96 +0,0 @@ -// Created on: 2008-05-15 -// Created by: Vladislav ROMASHKO -// Copyright (c) 2008-2014 OPEN CASCADE SAS -// -// This file is part of Open CASCADE Technology software library. -// -// This library is free software; you can redistribute it and/or modify it under -// the terms of the GNU Lesser General Public License version 2.1 as published -// by the Free Software Foundation, with special exception defined in the file -// OCCT_LGPL_EXCEPTION.txt. Consult the file LICENSE_LGPL_21.txt included in OCCT -// distribution for complete text of the license and disclaimer of any warranty. -// -// Alternatively, this file may be used under the terms of Open CASCADE -// commercial license or contractual agreement. - -#ifndef _Voxel_FloatDS_HeaderFile -#define _Voxel_FloatDS_HeaderFile - -#include -#include -#include - -#include -#include -#include -#include - - -//! A 3D voxel model keeping a foating-point -//! value for each voxel. -class Voxel_FloatDS : public Voxel_DS -{ -public: - - DEFINE_STANDARD_ALLOC - - - //! An empty constructor. - Standard_EXPORT Voxel_FloatDS(); - - //! A constructor initializing the voxel model. - //! (x, y, z) - the start point of the box. - //! (x_len, y_len, z_len) - lengths in x, y and z directions along axes of a co-ordinate system. - //! (nb_x, nb_y, nb_z) - number of splits (voxels) along x, y and z directions. - Standard_EXPORT Voxel_FloatDS(const Standard_Real x, const Standard_Real y, const Standard_Real z, const Standard_Real x_len, const Standard_Real y_len, const Standard_Real z_len, const Standard_Integer nb_x, const Standard_Integer nb_y, const Standard_Integer nb_z); - - //! Initialization of the voxel model. - //! (x, y, z) - the start point of the box. - //! (x_len, y_len, z_len) - lengths in x, y and z directions along axes of a co-ordinate system. - //! (nb_x, nb_y, nb_z) - number of splits (voxels) along x, y and z directions. - Standard_EXPORT virtual void Init (const Standard_Real x, const Standard_Real y, const Standard_Real z, const Standard_Real x_len, const Standard_Real y_len, const Standard_Real z_len, const Standard_Integer nb_x, const Standard_Integer nb_y, const Standard_Integer nb_z) Standard_OVERRIDE; - - //! A destructor of the voxel model. - Standard_EXPORT void Destroy(); -~Voxel_FloatDS() -{ - Destroy(); -} - - //! The method sets all values equal to 0 (false) and - //! releases the memory. - Standard_EXPORT void SetZero(); - - //! Defines a value for voxel with co-ordinates (ix, iy, iz). - //! Initial state of the model is so that all voxels have value 0.0f, - //! and this data doesn't occupy memory. - //! Memory for data is allocating during setting non-zero values. - Standard_EXPORT void Set (const Standard_Integer ix, const Standard_Integer iy, const Standard_Integer iz, const Standard_ShortReal data); - - //! Returns the value of voxel with co-ordinates (ix, iy, iz). - Standard_EXPORT Standard_ShortReal Get (const Standard_Integer ix, const Standard_Integer iy, const Standard_Integer iz) const; - - - - -protected: - - - - - -private: - - - - - -}; - - - - - - - -#endif // _Voxel_FloatDS_HeaderFile diff --git a/src/Voxel/Voxel_OctBoolDS.cxx b/src/Voxel/Voxel_OctBoolDS.cxx deleted file mode 100644 index 14d1183d5f..0000000000 --- a/src/Voxel/Voxel_OctBoolDS.cxx +++ /dev/null @@ -1,303 +0,0 @@ -// Created on: 2008-08-27 -// Created by: Vladislav ROMASHKO -// Copyright (c) 2008-2014 OPEN CASCADE SAS -// -// This file is part of Open CASCADE Technology software library. -// -// This library is free software; you can redistribute it and/or modify it under -// the terms of the GNU Lesser General Public License version 2.1 as published -// by the Free Software Foundation, with special exception defined in the file -// OCCT_LGPL_EXCEPTION.txt. Consult the file LICENSE_LGPL_21.txt included in OCCT -// distribution for complete text of the license and disclaimer of any warranty. -// -// Alternatively, this file may be used under the terms of Open CASCADE -// commercial license or contractual agreement. - - -#include -#include -#include -#include - -#include -static Standard_Byte gbits[8] = {1, 2, 4, 8, 16, 32, 64, 128}; -static Standard_Byte gnbits[8] = {255-1, 255-2, 255-4, 255-8, 255-16, 255-32, 255-64, 255-128}; -static iXYZ xyz; - -// Empty constructor -Voxel_OctBoolDS::Voxel_OctBoolDS():Voxel_DS(),mySubVoxels(0) -{ - -} - -// Constructor with intialization. -Voxel_OctBoolDS::Voxel_OctBoolDS(const Standard_Real x, const Standard_Real y, const Standard_Real z, - const Standard_Real xlen, const Standard_Real ylen, const Standard_Real zlen, - const Standard_Integer nbx, const Standard_Integer nby, const Standard_Integer nbz) -:Voxel_DS(),mySubVoxels(0) -{ - Init(x, y, z, xlen, ylen, zlen, nbx, nby, nbz); -} - -// Initialization. -void Voxel_OctBoolDS::Init(const Standard_Real x, const Standard_Real y, const Standard_Real z, - const Standard_Real xlen, const Standard_Real ylen, const Standard_Real zlen, - const Standard_Integer nbx, const Standard_Integer nby, const Standard_Integer nbz) -{ - Destroy(); - - Voxel_DS::Init(x, y, z, xlen, ylen, zlen, nbx, nby, nbz); - - if (!myNbX || !myNbY || !myNbZ) - return; - - Standard_Integer nb_bytes = RealToInt(ceil(myNbXY * myNbZ / 8.0)); - Standard_Integer nb_slices = RealToInt(ceil(nb_bytes / 8.0)); - myData = (Standard_Address) calloc(nb_slices, sizeof(Standard_Byte*)); -} - -// Destructor -void Voxel_OctBoolDS::Destroy() -{ - if (myData) - { - SetZero(); - free((Standard_Byte**)myData); - myData = 0; - if (mySubVoxels) - { - delete (iXYZBool*)mySubVoxels; - mySubVoxels = 0; - } - } -} - -void Voxel_OctBoolDS::SetZero() -{ - if (myData) - { - Standard_Integer nb_bytes = RealToInt(ceil(myNbXY * myNbZ / 8.0)); - Standard_Integer ix = 0, nb_slices = RealToInt(ceil(nb_bytes / 8.0)); - for (; ix < nb_slices; ix++) - { - if (((Standard_Byte**)myData)[ix]) - { - free(((Standard_Byte**)myData)[ix]); - ((Standard_Byte**)myData)[ix] = 0; - } - } - } - - if (mySubVoxels) - { - iXYZBool* map = (iXYZBool*) mySubVoxels; - map->Clear(); - } -} - -// Access to the boolean information attached to a particular voxel: -// Info: (ix >= 0 && ix < theNb_x), etc. -void Voxel_OctBoolDS::Set(const Standard_Integer ix, const Standard_Integer iy, const Standard_Integer iz, - const Standard_Boolean data) -{ - // All 8 sub-voxels have the same value. - // No need anymore to keep them in memory. - if (IsSplit(ix, iy, iz)) - { - UnSplit(ix, iy, iz); - } - - Standard_Integer ibit = ix + myNbX * iy + myNbXY * iz; - Standard_Integer islice = ibit >> 6; - - if (!data && !((Standard_Byte**)myData)[islice]) - return; // don't allocate a slice of data for setting a 0 value - - // Allocate the slice if it is not done yet. - if (!((Standard_Byte**)myData)[islice]) - { - ((Standard_Byte**)myData)[islice] = (Standard_Byte*) calloc(8/*number of bytes in slice*/, sizeof(Standard_Byte)); - } - - // Index within 8 bytes of the slice. - Standard_Integer ibit_in_current_slice = ibit - (islice << 6); - Standard_Integer ibyte = ibit_in_current_slice >> 3; - - // Value (byte) - Standard_Byte value = ((Standard_Byte*)((Standard_Byte**)myData)[islice])[ibyte]; - - // Position of data in the 8 bit-"value". - Standard_Integer shift = ibit_in_current_slice - (ibyte << 3); - - // Set data - if (data != ((value & gbits[shift]) ? Standard_True : Standard_False)) - { - if (data) - value |= gbits[shift]; - else - value &= gnbits[shift]; - ((Standard_Byte*)((Standard_Byte**)myData)[islice])[ibyte] = value; - } -} - -void Voxel_OctBoolDS::Set(const Standard_Integer ix, const Standard_Integer iy, const Standard_Integer iz, - const Standard_Integer ioct, const Standard_Boolean data) -{ - // If the voxel is not split yet, do it now. - if (!IsSplit(ix, iy, iz)) - { - Split(ix, iy, iz); - } - - // Voxel - xyz.ix = ix; - xyz.iy = iy; - xyz.iz = iz; - - // Take the value - Standard_Byte value = ((iXYZBool*)mySubVoxels)->Find(xyz); - - // Set data - if (data != ((value & gbits[ioct]) ? Standard_True : Standard_False)) - { - if (data) - value |= gbits[ioct]; - else - value &= gnbits[ioct]; - ((iXYZBool*)mySubVoxels)->ChangeFind(xyz) = value; - } -} - -Standard_Boolean Voxel_OctBoolDS::Get(const Standard_Integer ix, const Standard_Integer iy, const Standard_Integer iz) const -{ - Standard_Integer ibit = ix + myNbX * iy + myNbXY * iz; - Standard_Integer islice = ibit >> 6; - - // If the slice of data is not allocated, it means that its values are 0. - if (!((Standard_Byte**)myData)[islice]) - return Standard_False; - - // Index within 8 bytes of the slice. - Standard_Integer ibit_in_current_slice = ibit - (islice << 6); - Standard_Integer ibyte = ibit_in_current_slice >> 3; - - // Value (byte) - Standard_Byte value = ((Standard_Byte*)((Standard_Byte**)myData)[islice])[ibyte]; - - // Position of data in the 8 bit-"value". - Standard_Integer shift = ibit_in_current_slice - (ibyte << 3); - - return ((value & gbits[shift]) ? Standard_True : Standard_False); -} - -Standard_Boolean Voxel_OctBoolDS::Get(const Standard_Integer ix, const Standard_Integer iy, const Standard_Integer iz, - const Standard_Integer ioct) const -{ - // If the voxel is not split, return the value of the voxel. - if (!IsSplit(ix, iy, iz)) - { - return Get(ix, iy, iz); - } - - // Voxel - xyz.ix = ix; - xyz.iy = iy; - xyz.iz = iz; - - // Take the value - const Standard_Byte value = ((iXYZBool*)mySubVoxels)->Find(xyz); - - // Return data - return (value & gbits[ioct]) ? Standard_True : Standard_False; -} - -Standard_Boolean Voxel_OctBoolDS::IsSplit(const Standard_Integer ix, const Standard_Integer iy, const Standard_Integer iz) const -{ - if (!mySubVoxels) - return Standard_False; - - // Voxel - xyz.ix = ix; - xyz.iy = iy; - xyz.iz = iz; - - return ((iXYZBool*)mySubVoxels)->IsBound(xyz); -} - -void Voxel_OctBoolDS::Split(const Standard_Integer ix, const Standard_Integer iy, const Standard_Integer iz) -{ - // Voxel - xyz.ix = ix; - xyz.iy = iy; - xyz.iz = iz; - - if (mySubVoxels) - { - if (!((iXYZBool*)mySubVoxels)->IsBound(xyz)) - { - ((iXYZBool*)mySubVoxels)->Bind(xyz, 0); - } - } - else - { - mySubVoxels = (Standard_Address) new iXYZBool; - ((iXYZBool*)mySubVoxels)->Bind(xyz, 0); - } -} - -void Voxel_OctBoolDS::UnSplit(const Standard_Integer ix, const Standard_Integer iy, const Standard_Integer iz) -{ - if (!mySubVoxels) - return; - - // Voxel - xyz.ix = ix; - xyz.iy = iy; - xyz.iz = iz; - - if (((iXYZBool*)mySubVoxels)->IsBound(xyz)) - { - ((iXYZBool*)mySubVoxels)->UnBind(xyz); - } -} - -void Voxel_OctBoolDS::OptimizeMemory() -{ - if (!mySubVoxels) - return; - - Standard_Byte value; - TColStd_ListOfInteger ixs, iys, izs, values; - iXYZBool::Iterator itr(*((iXYZBool*)mySubVoxels)); - for (; itr.More(); itr.Next()) - { - value = itr.Value(); - if (value == 0 || value == 255) - { - xyz = itr.Key(); - ixs.Append(xyz.ix); - iys.Append(xyz.iy); - izs.Append(xyz.iz); - values.Append((Standard_Integer)value); - } - } - - TColStd_ListIteratorOfListOfInteger itrix(ixs), itriy(iys), itriz(izs), itrvalues(values); - for (; itrix.More(); itrix.Next(), itriy.Next(), itriz.Next(), itrvalues.Next()) - { - const Standard_Integer ix = itrix.Value(); - const Standard_Integer iy = itriy.Value(); - const Standard_Integer iz = itriz.Value(); - const Standard_Integer aValue = itrvalues.Value(); - - Set(ix, iy, iz, (aValue ? Standard_True : Standard_False)); - UnSplit(ix, iy, iz); - } - - // If the map is empty, release it. - if (((iXYZBool*)mySubVoxels)->IsEmpty()) - { - delete (iXYZBool*)mySubVoxels; - mySubVoxels = 0; - } -} diff --git a/src/Voxel/Voxel_OctBoolDS.hxx b/src/Voxel/Voxel_OctBoolDS.hxx deleted file mode 100644 index fa36dc268a..0000000000 --- a/src/Voxel/Voxel_OctBoolDS.hxx +++ /dev/null @@ -1,123 +0,0 @@ -// Created on: 2008-08-26 -// Created by: Vladislav ROMASHKO -// Copyright (c) 2008-2014 OPEN CASCADE SAS -// -// This file is part of Open CASCADE Technology software library. -// -// This library is free software; you can redistribute it and/or modify it under -// the terms of the GNU Lesser General Public License version 2.1 as published -// by the Free Software Foundation, with special exception defined in the file -// OCCT_LGPL_EXCEPTION.txt. Consult the file LICENSE_LGPL_21.txt included in OCCT -// distribution for complete text of the license and disclaimer of any warranty. -// -// Alternatively, this file may be used under the terms of Open CASCADE -// commercial license or contractual agreement. - -#ifndef _Voxel_OctBoolDS_HeaderFile -#define _Voxel_OctBoolDS_HeaderFile - -#include -#include -#include - -#include -#include -#include -#include -#include - - -//! A 3D voxel model keeping a boolean flag (1 or 0) -//! value for each voxel, and having an opportunity to split each voxel -//! into 8 sub-voxels. -class Voxel_OctBoolDS : public Voxel_DS -{ -public: - - DEFINE_STANDARD_ALLOC - - - //! An empty constructor. - Standard_EXPORT Voxel_OctBoolDS(); - - //! A constructor initializing the voxel model. - //! (x, y, z) - the start point of the box. - //! (x_len, y_len, z_len) - lengths in x, y and z directions along axes of a co-ordinate system. - //! (nb_x, nb_y, nb_z) - number of splits (voxels) along x, y and z directions. - Standard_EXPORT Voxel_OctBoolDS(const Standard_Real x, const Standard_Real y, const Standard_Real z, const Standard_Real x_len, const Standard_Real y_len, const Standard_Real z_len, const Standard_Integer nb_x, const Standard_Integer nb_y, const Standard_Integer nb_z); - - //! Initialization of the voxel model. - //! (x, y, z) - the start point of the box. - //! (x_len, y_len, z_len) - lengths in x, y and z directions along axes of a co-ordinate system. - //! (nb_x, nb_y, nb_z) - number of splits (voxels) along x, y and z directions. - Standard_EXPORT virtual void Init (const Standard_Real x, const Standard_Real y, const Standard_Real z, const Standard_Real x_len, const Standard_Real y_len, const Standard_Real z_len, const Standard_Integer nb_x, const Standard_Integer nb_y, const Standard_Integer nb_z) Standard_OVERRIDE; - - //! A destructor of the voxel model. - Standard_EXPORT void Destroy(); -~Voxel_OctBoolDS() -{ - Destroy(); -} - - //! The method sets all values equal to 0 (false) and - //! releases the memory. - Standard_EXPORT void SetZero(); - - //! The method searches voxels with equal-value of sub-voxels - //! and removes them (remaining the value for the voxel). - Standard_EXPORT void OptimizeMemory(); - - //! Defines a value for voxel with co-ordinates (ix, iy, iz). - //! If the voxel is split into 8 sub-voxels, the split disappears. - //! Initial state of the model is so that all voxels have value 0 (false), - //! and this data doesn't occupy memory. - //! Memory for data is allocating during setting non-zero values (true). - Standard_EXPORT void Set (const Standard_Integer ix, const Standard_Integer iy, const Standard_Integer iz, const Standard_Boolean data); - - //! Defines a value for a sub-voxel of a voxel with co-ordinates (ix, iy, iz). - //! If the voxel is not split into 8 sub-voxels yet, this method splits the voxel. - //! Range of sub-voxels is 0 - 7. - Standard_EXPORT void Set (const Standard_Integer ix, const Standard_Integer iy, const Standard_Integer iz, const Standard_Integer ioct, const Standard_Boolean data); - - //! Returns the value of voxel with co-ordinates (ix, iy, iz). - //! Warning!: the returned value may not coincide with the value of its 8 sub-voxels. - //! Use the method ::IsSplit() to check whether a voxel has sub-voxels. - Standard_EXPORT Standard_Boolean Get (const Standard_Integer ix, const Standard_Integer iy, const Standard_Integer iz) const; - - //! Returns the value of a sub-voxel of a voxel with co-ordinates (ix, iy, iz). - //! If the voxel is not split, it returns the value of the voxel. - //! Range of sub-voxels is 0 - 7. - Standard_EXPORT Standard_Boolean Get (const Standard_Integer ix, const Standard_Integer iy, const Standard_Integer iz, const Standard_Integer ioct) const; - - //! Returns true if the voxel is split into 8 sub-voxels. - Standard_EXPORT Standard_Boolean IsSplit (const Standard_Integer ix, const Standard_Integer iy, const Standard_Integer iz) const; - - - - -protected: - - - - - -private: - - - Standard_EXPORT void Split (const Standard_Integer ix, const Standard_Integer iy, const Standard_Integer iz); - - Standard_EXPORT void UnSplit (const Standard_Integer ix, const Standard_Integer iy, const Standard_Integer iz); - - - Standard_Address mySubVoxels; - - -}; - - - - - - - -#endif // _Voxel_OctBoolDS_HeaderFile diff --git a/src/Voxel/Voxel_Prs.cxx b/src/Voxel/Voxel_Prs.cxx deleted file mode 100644 index 95623cb2c8..0000000000 --- a/src/Voxel/Voxel_Prs.cxx +++ /dev/null @@ -1,307 +0,0 @@ -// Created on: 2008-05-13 -// Created by: Vladislav ROMASHKO -// Copyright (c) 2008-2014 OPEN CASCADE SAS -// -// This file is part of Open CASCADE Technology software library. -// -// This library is free software; you can redistribute it and/or modify it under -// the terms of the GNU Lesser General Public License version 2.1 as published -// by the Free Software Foundation, with special exception defined in the file -// OCCT_LGPL_EXCEPTION.txt. Consult the file LICENSE_LGPL_21.txt included in OCCT -// distribution for complete text of the license and disclaimer of any warranty. -// -// Alternatively, this file may be used under the terms of Open CASCADE -// commercial license or contractual agreement. - - -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "Voxel_VisData.h" -Voxel_Prs::Voxel_Prs():AIS_InteractiveObject(PrsMgr_TOP_AllView),myVisData(0) -{ - -} - -void Voxel_Prs::SetBoolVoxels(const Standard_Address theVoxels) -{ - Allocate(); - ((Voxel_VisData*)myVisData)->myBoolVoxels = (Voxel_BoolDS*) theVoxels; -} - -void Voxel_Prs::SetColorVoxels(const Standard_Address theVoxels) -{ - Allocate(); - ((Voxel_VisData*)myVisData)->myColorVoxels = (Voxel_ColorDS*) theVoxels; -} - -void Voxel_Prs::SetROctBoolVoxels(const Standard_Address theVoxels) -{ - Allocate(); - ((Voxel_VisData*)myVisData)->myROctBoolVoxels = (Voxel_ROctBoolDS*) theVoxels; -} - -void Voxel_Prs::SetTriangulation(const Handle(Poly_Triangulation)& theTriangulation) -{ - Allocate(); - ((Voxel_VisData*)myVisData)->myTriangulation = theTriangulation; -} - -void Voxel_Prs::SetColor(const Quantity_Color& theColor) -{ - Allocate(); - ((Voxel_VisData*)myVisData)->myDisplay.myColor = theColor; -} - -void Voxel_Prs::SetPointSize(const Standard_Real theSize) -{ - Allocate(); - ((Voxel_VisData*)myVisData)->myDisplay.myPointSize = theSize; -} - -void Voxel_Prs::SetQuadrangleSize(const Standard_Integer theSize) -{ - Allocate(); - ((Voxel_VisData*)myVisData)->myDisplay.myQuadrangleSize = theSize; -} - -void Voxel_Prs::SetColors(const Handle(Quantity_HArray1OfColor)& theColors) -{ - Allocate(); - ((Voxel_VisData*)myVisData)->myDisplay.myColors = theColors; -} - -void Voxel_Prs::SetDisplayMode(const Voxel_VoxelDisplayMode theMode) -{ - Allocate(); - ((Voxel_VisData*)myVisData)->myDisplay.myDisplayMode = theMode; -} - -void Voxel_Prs::SetTransparency(const Standard_Real theTransparency) -{ - Allocate(); - ((Voxel_VisData*)myVisData)->myDisplay.myTransparency = theTransparency; -} - -static void setMaterial(const Handle(Graphic3d_Group)& G, - const Quantity_Color& C, - const Standard_Real T) -{ - Graphic3d_MaterialAspect material(Graphic3d_NOM_PLASTIC); - material.SetColor(C); - material.SetTransparency(T); - Handle(Graphic3d_AspectFillArea3d) aspect = - new Graphic3d_AspectFillArea3d(Aspect_IS_SOLID, C, C, Aspect_TOL_SOLID, 1, material, material); - aspect->SetDistinguishOff(); - aspect->SetEdgeOff(); - aspect->SetTextureMapOff(); - G->SetPrimitivesAspect(aspect); -} - -void Voxel_Prs::Compute(const Handle(PrsMgr_PresentationManager3d)& /*thePresentationManager*/, - const Handle(Prs3d_Presentation)& thePresentation, - const Standard_Integer /*theMode*/) -{ - thePresentation->Clear(); - if (!myVisData) - return; - - if (((Voxel_VisData*)myVisData)->myBoolVoxels) - { - // Reset GL lists. - // BoolDS - ((Voxel_VisData*)myVisData)->myDisplay.myBoolPointsFirst = 1; - ((Voxel_VisData*)myVisData)->myDisplay.myBoolNearestPointsFirst = 1; - } - - if (((Voxel_VisData*)myVisData)->myColorVoxels) - { - // Reset GL lists. - // ColorDS - ((Voxel_VisData*)myVisData)->myDisplay.myColorPointsFirst = 1; - ((Voxel_VisData*)myVisData)->myDisplay.myColorNearestPointsFirst = 1; - } - - if (((Voxel_VisData*)myVisData)->myROctBoolVoxels) - { - // Reset GL lists. - // ROctBoolDS - ((Voxel_VisData*)myVisData)->myDisplay.myROctBoolPointsFirst = 1; - ((Voxel_VisData*)myVisData)->myDisplay.myROctBoolNearestPointsFirst = 1; - } - - // Set data to the user draw function. - Handle(Graphic3d_Group) G = Prs3d_Root::CurrentGroup(thePresentation); - if (((Voxel_VisData*)myVisData)->myDisplay.myDisplayMode == Voxel_VDM_BOXES || - ((Voxel_VisData*)myVisData)->myDisplay.myDisplayMode == Voxel_VDM_NEARESTBOXES || - !((Voxel_VisData*)myVisData)->myTriangulation.IsNull()) - { - setMaterial(G, ((Voxel_VisData*)myVisData)->myDisplay.myColor, - ((Voxel_VisData*)myVisData)->myDisplay.myTransparency); - - // Reset normals of triangulation - if (!((Voxel_VisData*)myVisData)->myTriangulation.IsNull()) - { - ((Voxel_VisData*)myVisData)->myNormalsOfNodes.Nullify(); - ((Voxel_VisData*)myVisData)->myDisplay.myTriangulationList = -1; - } - } - - ///Handle(OpenGl_Group) aGroup = Handle(OpenGl_Group)::DownCast (G); - ///aGroup->AddElement (myVisData); -} - -void Voxel_Prs::ComputeSelection(const Handle(SelectMgr_Selection)& /*theSelection*/, - const Standard_Integer /*theMode*/) -{ - -} - -// Destructor -void Voxel_Prs::Destroy() -{ - if (myVisData) - { - delete (Voxel_VisData*) myVisData; - myVisData = 0; - } -} - -void Voxel_Prs::Allocate() -{ - if (!myVisData) - { - myVisData = new Voxel_VisData; - - ((Voxel_VisData*)myVisData)->myBoolVoxels = 0; - ((Voxel_VisData*)myVisData)->myColorVoxels = 0; - ((Voxel_VisData*)myVisData)->myROctBoolVoxels = 0; - - // Points - - // BoolDS - ((Voxel_VisData*)myVisData)->myDisplay.myBoolPointsList = -1; - ((Voxel_VisData*)myVisData)->myDisplay.myBoolNearestPointsList[0] = -1; - ((Voxel_VisData*)myVisData)->myDisplay.myBoolNearestPointsList[1] = -1; - ((Voxel_VisData*)myVisData)->myDisplay.myBoolNearestPointsList[2] = -1; - ((Voxel_VisData*)myVisData)->myDisplay.myBoolNearestPointsList[3] = -1; - ((Voxel_VisData*)myVisData)->myDisplay.myBoolNearestPointsList[4] = -1; - ((Voxel_VisData*)myVisData)->myDisplay.myBoolNearestPointsList[5] = -1; - ((Voxel_VisData*)myVisData)->myDisplay.myBoolNearestPointsList[6] = -1; - - // ROctBoolDS - ((Voxel_VisData*)myVisData)->myDisplay.myROctBoolPointsList = -1; - ((Voxel_VisData*)myVisData)->myDisplay.myROctBoolNearestPointsList[0] = -1; - ((Voxel_VisData*)myVisData)->myDisplay.myROctBoolNearestPointsList[1] = -1; - ((Voxel_VisData*)myVisData)->myDisplay.myROctBoolNearestPointsList[2] = -1; - ((Voxel_VisData*)myVisData)->myDisplay.myROctBoolNearestPointsList[3] = -1; - ((Voxel_VisData*)myVisData)->myDisplay.myROctBoolNearestPointsList[4] = -1; - ((Voxel_VisData*)myVisData)->myDisplay.myROctBoolNearestPointsList[5] = -1; - ((Voxel_VisData*)myVisData)->myDisplay.myROctBoolNearestPointsList[6] = -1; - - // ColorDS: - ((Voxel_VisData*)myVisData)->myDisplay.myColorPointsList = -1; - ((Voxel_VisData*)myVisData)->myDisplay.myColorNearestPointsList[0] = -1; - ((Voxel_VisData*)myVisData)->myDisplay.myColorNearestPointsList[1] = -1; - ((Voxel_VisData*)myVisData)->myDisplay.myColorNearestPointsList[2] = -1; - ((Voxel_VisData*)myVisData)->myDisplay.myColorNearestPointsList[3] = -1; - ((Voxel_VisData*)myVisData)->myDisplay.myColorNearestPointsList[4] = -1; - ((Voxel_VisData*)myVisData)->myDisplay.myColorNearestPointsList[5] = -1; - ((Voxel_VisData*)myVisData)->myDisplay.myColorNearestPointsList[6] = -1; - - ((Voxel_VisData*)myVisData)->myDisplay.myColorMinValue = 1; - ((Voxel_VisData*)myVisData)->myDisplay.myColorMaxValue = 15; - - ((Voxel_VisData*)myVisData)->myDisplay.myPointSize = 1.0; - ((Voxel_VisData*)myVisData)->myDisplay.myQuadrangleSize = 100; - ((Voxel_VisData*)myVisData)->myDisplay.myTransparency = 0.0; - ((Voxel_VisData*)myVisData)->myDisplay.myDegenerateMode = 0; - ((Voxel_VisData*)myVisData)->myDisplay.myUsageOfGLlists = 1; - ((Voxel_VisData*)myVisData)->myDisplay.mySmoothPoints = 0; - - ((Voxel_VisData*)myVisData)->myDisplay.myDisplayedXMin = -DBL_MAX; - ((Voxel_VisData*)myVisData)->myDisplay.myDisplayedXMax = DBL_MAX; - ((Voxel_VisData*)myVisData)->myDisplay.myDisplayedYMin = -DBL_MAX; - ((Voxel_VisData*)myVisData)->myDisplay.myDisplayedYMax = DBL_MAX; - ((Voxel_VisData*)myVisData)->myDisplay.myDisplayedZMin = -DBL_MAX; - ((Voxel_VisData*)myVisData)->myDisplay.myDisplayedZMax = DBL_MAX; - - ((Voxel_VisData*)myVisData)->myDisplay.myTriangulationList = -1; - - ((Voxel_VisData*)myVisData)->myDisplay.myHighlightx = -1; - ((Voxel_VisData*)myVisData)->myDisplay.myHighlighty = -1; - ((Voxel_VisData*)myVisData)->myDisplay.myHighlightz = -1; - } -} - -void Voxel_Prs::SetDegenerateMode(const Standard_Boolean theDegenerate) -{ - Allocate(); - ((Voxel_VisData*)myVisData)->myDisplay.myDegenerateMode = (theDegenerate == Standard_True); -} - -void Voxel_Prs::SetUsageOfGLlists(const Standard_Boolean theUsage) -{ - Allocate(); - ((Voxel_VisData*)myVisData)->myDisplay.myUsageOfGLlists = (theUsage == Standard_True); -} - -void Voxel_Prs::SetSmoothPoints(const Standard_Boolean theSmooth) -{ - Allocate(); - ((Voxel_VisData*)myVisData)->myDisplay.mySmoothPoints = (theSmooth == Standard_True); -} - -void Voxel_Prs::SetColorRange(const Standard_Byte theMinValue, - const Standard_Byte theMaxValue) -{ - Allocate(); - - ((Voxel_VisData*)myVisData)->myDisplay.myColorMinValue = theMinValue; - ((Voxel_VisData*)myVisData)->myDisplay.myColorMaxValue = theMaxValue; - - // Reset GL lists - ((Voxel_VisData*)myVisData)->myDisplay.myColorPointsFirst = 1; - ((Voxel_VisData*)myVisData)->myDisplay.myColorNearestPointsFirst = 1; -} - -void Voxel_Prs::SetSizeRange(const Standard_Real theDisplayedXMin, - const Standard_Real theDisplayedXMax, - const Standard_Real theDisplayedYMin, - const Standard_Real theDisplayedYMax, - const Standard_Real theDisplayedZMin, - const Standard_Real theDisplayedZMax) -{ - Allocate(); - - ((Voxel_VisData*)myVisData)->myDisplay.myDisplayedXMin = theDisplayedXMin; - ((Voxel_VisData*)myVisData)->myDisplay.myDisplayedXMax = theDisplayedXMax; - ((Voxel_VisData*)myVisData)->myDisplay.myDisplayedYMin = theDisplayedYMin; - ((Voxel_VisData*)myVisData)->myDisplay.myDisplayedYMax = theDisplayedYMax; - ((Voxel_VisData*)myVisData)->myDisplay.myDisplayedZMin = theDisplayedZMin; - ((Voxel_VisData*)myVisData)->myDisplay.myDisplayedZMax = theDisplayedZMax; - - // Reset GL lists - ((Voxel_VisData*)myVisData)->myDisplay.myBoolPointsFirst = 1; - ((Voxel_VisData*)myVisData)->myDisplay.myBoolNearestPointsFirst = 1; - ((Voxel_VisData*)myVisData)->myDisplay.myColorPointsFirst = 1; - ((Voxel_VisData*)myVisData)->myDisplay.myColorNearestPointsFirst = 1; - ((Voxel_VisData*)myVisData)->myDisplay.myROctBoolPointsFirst = 1; - ((Voxel_VisData*)myVisData)->myDisplay.myROctBoolNearestPointsFirst = 1; -} - -void Voxel_Prs::Highlight(const Standard_Integer ix, const Standard_Integer iy, const Standard_Integer iz) -{ - Allocate(); - - ((Voxel_VisData*)myVisData)->myDisplay.myHighlightx = ix; - ((Voxel_VisData*)myVisData)->myDisplay.myHighlighty = iy; - ((Voxel_VisData*)myVisData)->myDisplay.myHighlightz = iz; -} diff --git a/src/Voxel/Voxel_Prs.hxx b/src/Voxel/Voxel_Prs.hxx deleted file mode 100644 index 05e225e8fc..0000000000 --- a/src/Voxel/Voxel_Prs.hxx +++ /dev/null @@ -1,149 +0,0 @@ -// Created on: 2008-05-06 -// Created by: Vladislav ROMASHKO -// Copyright (c) 2008-2014 OPEN CASCADE SAS -// -// This file is part of Open CASCADE Technology software library. -// -// This library is free software; you can redistribute it and/or modify it under -// the terms of the GNU Lesser General Public License version 2.1 as published -// by the Free Software Foundation, with special exception defined in the file -// OCCT_LGPL_EXCEPTION.txt. Consult the file LICENSE_LGPL_21.txt included in OCCT -// distribution for complete text of the license and disclaimer of any warranty. -// -// Alternatively, this file may be used under the terms of Open CASCADE -// commercial license or contractual agreement. - -#ifndef _Voxel_Prs_HeaderFile -#define _Voxel_Prs_HeaderFile - -#include -#include - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -class Poly_Triangulation; -class Quantity_Color; -class Prs3d_Presentation; - - -class Voxel_Prs; -DEFINE_STANDARD_HANDLE(Voxel_Prs, AIS_InteractiveObject) - -//! Interactive object for voxels. -class Voxel_Prs : public AIS_InteractiveObject -{ - -public: - - - //! An empty constructor. - Standard_EXPORT Voxel_Prs(); - - //! is a Voxel_BoolDS* object. - Standard_EXPORT void SetBoolVoxels (const Standard_Address theVoxels); - - //! is a Voxel_ColorDS* object. - Standard_EXPORT void SetColorVoxels (const Standard_Address theVoxels); - - //! is a Voxel_ROctBoolDS* object. - Standard_EXPORT void SetROctBoolVoxels (const Standard_Address theVoxels); - - //! Sets a triangulation for visualization. - Standard_EXPORT void SetTriangulation (const Handle(Poly_Triangulation)& theTriangulation); - - //! Sets a display mode for voxels. - Standard_EXPORT void SetDisplayMode (const Voxel_VoxelDisplayMode theMode); - - //! Defines the color of points, quadrangles ... for BoolDS. - Standard_EXPORT virtual void SetColor (const Quantity_Color& theColor) Standard_OVERRIDE; - - //! Defines the color of points, quadrangles... for ColorDS. - //! For ColorDS the size of array is 0 .. 15. - //! 0 - means no color, this voxel is not drawn. - Standard_EXPORT void SetColors (const Handle(Quantity_HArray1OfColor)& theColors); - - //! Defines the size of points for all types of voxels. - Standard_EXPORT void SetPointSize (const Standard_Real theSize); - - //! Defines the size of quadrangles in per cents (0 .. 100). - Standard_EXPORT void SetQuadrangleSize (const Standard_Integer theSize); - - //! Defines the transparency value [0 .. 1] for quadrangular visualization. - Standard_EXPORT virtual void SetTransparency (const Standard_Real theTransparency) Standard_OVERRIDE; - - //! Highlights a voxel. - //! It doesn't re-computes the whole interactive object, - //! but only marks a voxels as "highlighted". - //! The voxel becomes highlighted on next swapping of buffers. - //! In order to unhighlight a voxel, set ix = iy = iz = -1. - Standard_EXPORT void Highlight (const Standard_Integer ix, const Standard_Integer iy, const Standard_Integer iz); - - //! A destructor of presentation data. - Standard_EXPORT void Destroy(); -~Voxel_Prs() -{ - Destroy(); -} - - //! Simplifies visualization of voxels in case of view rotation, panning and zooming. - Standard_EXPORT void SetDegenerateMode (const Standard_Boolean theDegenerate); - - //! GL lists accelerate view rotation, panning and zooming operations, but - //! it takes additional memory... - //! It is up to the user of this interactive object to decide whether - //! he has enough memory and may use GL lists or - //! he is lack of memory and usage of GL lists is not recommended. - //! By default, usage of GL lists is on. - //! Also, as I noticed, the view without GL lists looks more precisely. - Standard_EXPORT void SetUsageOfGLlists (const Standard_Boolean theUsage); - - //! Switches visualization of points from smooth to rough. - Standard_EXPORT void SetSmoothPoints (const Standard_Boolean theSmooth); - - //! Defines min-max values for visualization of voxels of ColorDS structure. - //! By default, min value = 1, max value = 15 (all non-zero values). - Standard_EXPORT void SetColorRange (const Standard_Byte theMinValue, const Standard_Byte theMaxValue); - - //! Defines the displayed area of voxels. - //! By default, the range is equal to the box of voxels (all voxels are displayed). - Standard_EXPORT void SetSizeRange (const Standard_Real theDisplayedXMin, const Standard_Real theDisplayedXMax, const Standard_Real theDisplayedYMin, const Standard_Real theDisplayedYMax, const Standard_Real theDisplayedZMin, const Standard_Real theDisplayedZMax); - - - - DEFINE_STANDARD_RTTI(Voxel_Prs,AIS_InteractiveObject) - -protected: - - - Standard_EXPORT virtual void Compute (const Handle(PrsMgr_PresentationManager3d)& thePresentationManager, const Handle(Prs3d_Presentation)& thePresentation, const Standard_Integer theMode = 0) Standard_OVERRIDE; - - - -protected: - - - Standard_EXPORT void ComputeSelection (const Handle(SelectMgr_Selection)& theSelection, const Standard_Integer theMode); - - //! Allocates the data structure of visualization. - Standard_EXPORT void Allocate(); - - Standard_Address myVisData; - - -}; - - - - - - - -#endif // _Voxel_Prs_HeaderFile diff --git a/src/Voxel/Voxel_ROctBoolDS.cxx b/src/Voxel/Voxel_ROctBoolDS.cxx deleted file mode 100644 index 11b7c966a2..0000000000 --- a/src/Voxel/Voxel_ROctBoolDS.cxx +++ /dev/null @@ -1,727 +0,0 @@ -// Created on: 2008-09-01 -// Created by: Vladislav ROMASHKO -// Copyright (c) 2008-2014 OPEN CASCADE SAS -// -// This file is part of Open CASCADE Technology software library. -// -// This library is free software; you can redistribute it and/or modify it under -// the terms of the GNU Lesser General Public License version 2.1 as published -// by the Free Software Foundation, with special exception defined in the file -// OCCT_LGPL_EXCEPTION.txt. Consult the file LICENSE_LGPL_21.txt included in OCCT -// distribution for complete text of the license and disclaimer of any warranty. -// -// Alternatively, this file may be used under the terms of Open CASCADE -// commercial license or contractual agreement. - - -#include -#include - -#include -static Standard_Byte gbits[8] = {1, 2, 4, 8, 16, 32, 64, 128}; -static Standard_Byte gnbits[8] = {255-1, 255-2, 255-4, 255-8, 255-16, 255-32, 255-64, 255-128}; - -/* Data structure of the ROctBoolDS - -SplitData: 1 byte (8 values) - (a) SplitData: 8 bytes (64 values) - (b) SplitData: 64 bytes (512 values) - (c) SplitData: ... - (d) SplitData: ... -*/ - -// Empty constructor -Voxel_ROctBoolDS::Voxel_ROctBoolDS():Voxel_DS() -{ - -} - -// Constructor with intialization. -Voxel_ROctBoolDS::Voxel_ROctBoolDS(const Standard_Real x, const Standard_Real y, const Standard_Real z, - const Standard_Real xlen, const Standard_Real ylen, const Standard_Real zlen, - const Standard_Integer nbx, const Standard_Integer nby, const Standard_Integer nbz) -:Voxel_DS() -{ - Init(x, y, z, xlen, ylen, zlen, nbx, nby, nbz); -} - -// Initialization. -void Voxel_ROctBoolDS::Init(const Standard_Real x, const Standard_Real y, const Standard_Real z, - const Standard_Real xlen, const Standard_Real ylen, const Standard_Real zlen, - const Standard_Integer nbx, const Standard_Integer nby, const Standard_Integer nbz) -{ - Destroy(); - - Voxel_DS::Init(x, y, z, xlen, ylen, zlen, nbx, nby, nbz); - - if (!myNbX || !myNbY || !myNbZ) - return; - - Standard_Integer nb_slices = RealToInt(ceil(myNbXY * myNbZ / 8.0)); - myData = (Standard_Address) calloc(nb_slices, sizeof(Voxel_SplitData*)); -} - -// Destructor -void Voxel_ROctBoolDS::Destroy() -{ - if (myData) - { - SetZero(); - free((Voxel_SplitData**)myData); - myData = 0; - } -} - -// A recursive method of deletion of data. -static void SetZeroSplitData(Voxel_SplitData* data) -{ - // Values: - free((Standard_Byte*) data->GetValues()); - data->GetValues() = 0; - if (data->GetSplitData()) - { - SetZeroSplitData((Voxel_SplitData*) data->GetSplitData()); - } - delete data; -} - -void Voxel_ROctBoolDS::SetZero() -{ - if (myData) - { - Standard_Integer ix = 0, nb_slices = RealToInt(ceil(myNbXY * myNbZ / 8.0)); - for (; ix < nb_slices; ix++) - { - if (((Voxel_SplitData**)myData)[ix]) - { - SetZeroSplitData((Voxel_SplitData*)((Voxel_SplitData**)myData)[ix]); - ((Voxel_SplitData**)myData)[ix] = 0; - } - } - } -} - -// Access to the boolean information attached to a particular voxel: -// Info: (ix >= 0 && ix < theNb_x), etc. -void Voxel_ROctBoolDS::Set(const Standard_Integer ix, const Standard_Integer iy, const Standard_Integer iz, - const Standard_Boolean data) -{ - Standard_Integer ibit = ix + myNbX * iy + myNbXY * iz; - Standard_Integer islice = ibit >> 3; - - if (!data && !((Voxel_SplitData**)myData)[islice]) - return; // don't allocate a slice of data for setting a 0 value - - // Allocate the slice if it is not done yet. - if (!((Voxel_SplitData**)myData)[islice]) - { - ((Voxel_SplitData**)myData)[islice] = (Voxel_SplitData*) new Voxel_SplitData; - // Values: - ((Voxel_SplitData**)myData)[islice]->GetValues() = - (Standard_Byte*) calloc(1/*one byte: 8 1-bit values*/, sizeof(Standard_Byte)); - // Sub-voxels: - ((Voxel_SplitData*)((Voxel_SplitData**)myData)[islice])->GetSplitData() = 0; - } - - // Value - Standard_Byte value = *((Standard_Byte*)((Voxel_SplitData*)((Voxel_SplitData**)myData)[islice])->GetValues()); - - // Position of data in the 8 bit-"value". - Standard_Integer shift = ibit - (islice << 3); - - // Set data - if (data != ((value & gbits[shift]) ? Standard_True : Standard_False)) - { - if (data) - value |= gbits[shift]; - else - value &= gnbits[shift]; - *((Standard_Byte*)((Voxel_SplitData*)((Voxel_SplitData**)myData)[islice])->GetValues()) = value; - } - - // Set the same value to sub-voxels. - if (((Voxel_SplitData*)((Voxel_SplitData**)myData)[islice])->GetSplitData()) - { - // Get sub-value - Standard_Byte subvalue = ((Standard_Byte*)((Voxel_SplitData*)((Voxel_SplitData*)((Voxel_SplitData**)myData)[islice])->GetSplitData())->GetValues())[shift]; - - // Set sub-value - if (subvalue != (data ? 255 : 0)) - { - subvalue = data ? 255 : 0; - ((Standard_Byte*)((Voxel_SplitData*)((Voxel_SplitData*)((Voxel_SplitData**)myData)[islice])->GetSplitData())->GetValues())[shift] = subvalue; - } - - // Set the same value to sub-sub-voxels. - if (((Voxel_SplitData*)((Voxel_SplitData*)((Voxel_SplitData**)myData)[islice])->GetSplitData())->GetSplitData()) - { - // Start index of 64-bit value (index of byte of sub-sub-voxel). - Standard_Integer ibyte2 = (shift << 3); - for (Standard_Integer ioct2 = 0; ioct2 < 8; ioct2++) - { - // Get sub-sub-value - Standard_Byte subsubvalue = ((Standard_Byte*)((Voxel_SplitData*)((Voxel_SplitData*)((Voxel_SplitData*)((Voxel_SplitData**)myData)[islice])->GetSplitData())->GetSplitData())->GetValues())[ibyte2 + ioct2]; - - // Set sub-sub-value - if (subsubvalue != (data ? 255 : 0)) - { - subsubvalue = data ? 255 : 0; - ((Standard_Byte*)((Voxel_SplitData*)((Voxel_SplitData*)((Voxel_SplitData*)((Voxel_SplitData**)myData)[islice])->GetSplitData())->GetSplitData())->GetValues())[ibyte2 + ioct2] = subsubvalue; - } - } - } - } -} - -void Voxel_ROctBoolDS::Set(const Standard_Integer ix, const Standard_Integer iy, const Standard_Integer iz, - const Standard_Integer ioct1, const Standard_Boolean data) -{ - Standard_Integer ibit = ix + myNbX * iy + myNbXY * iz; - Standard_Integer islice = ibit >> 3; - - if (!data && !((Voxel_SplitData**)myData)[islice]) - return; // don't allocate a slice of data for setting a 0 value - - // Allocate the slice if it is not done yet. - if (!((Voxel_SplitData**)myData)[islice]) - { - ((Voxel_SplitData**)myData)[islice] = (Voxel_SplitData*) new Voxel_SplitData; - // Values: - ((Voxel_SplitData*)((Voxel_SplitData**)myData)[islice])->GetValues() = - (Standard_Byte*) calloc(1/*one byte: 8 1-bit values*/, sizeof(Standard_Byte)); - // Sub-voxels: - ((Voxel_SplitData*)((Voxel_SplitData**)myData)[islice])->GetSplitData() = 0; - } - - // Check sub-voxels of the first level - if (!((Voxel_SplitData*)((Voxel_SplitData**)myData)[islice])->GetSplitData()) - { - // Sub-voxels: - ((Voxel_SplitData*)((Voxel_SplitData**)myData)[islice])->GetSplitData() = (Voxel_SplitData*) new Voxel_SplitData; - // Value of sub-voxels: - ((Voxel_SplitData*)((Voxel_SplitData*)((Voxel_SplitData**)myData)[islice])->GetSplitData())->GetValues() = - (Standard_Byte*) calloc(8/*eight bytes: 8 sub-voxels for each voxel*/, sizeof(Standard_Byte)); - - // Set parent value - Standard_Byte parent_value = *((Standard_Byte*)((Voxel_SplitData*)((Voxel_SplitData**)myData)[islice])->GetValues()); - if (parent_value) - { - for (Standard_Integer shift = 0; shift < 8; shift++) - { - if ((parent_value & gbits[shift]) ? Standard_True : Standard_False) - { - ((Standard_Byte*)((Voxel_SplitData*)((Voxel_SplitData*)((Voxel_SplitData**)myData)[islice])->GetSplitData())->GetValues())[shift] = 255; - } - else - { - ((Standard_Byte*)((Voxel_SplitData*)((Voxel_SplitData*)((Voxel_SplitData**)myData)[islice])->GetSplitData())->GetValues())[shift] = 0; - } - } - } - - // Sub-sub-voxels - ((Voxel_SplitData*)((Voxel_SplitData*)((Voxel_SplitData**)myData)[islice])->GetSplitData())->GetSplitData() = 0; - } - - // Index of sub-voxel corresponding to ioct1: 8 voxels correspond to 64 sub-voxels. - Standard_Integer ibyte = ibit - (islice << 3); - - // Value - Standard_Byte value = ((Standard_Byte*)((Voxel_SplitData*)((Voxel_SplitData*)((Voxel_SplitData**)myData)[islice])->GetSplitData())->GetValues())[ibyte]; - - // Set data - if (data != ((value & gbits[ioct1]) ? Standard_True : Standard_False)) - { - if (data) - value |= gbits[ioct1]; - else - value &= gnbits[ioct1]; - ((Standard_Byte*)((Voxel_SplitData*)((Voxel_SplitData*)((Voxel_SplitData**)myData)[islice])->GetSplitData())->GetValues())[ibyte] = value; - } - - // Set the same value to sub-voxels. - if (((Voxel_SplitData*)((Voxel_SplitData*)((Voxel_SplitData**)myData)[islice])->GetSplitData())->GetSplitData()) - { - // Start index of 64-bit value (index of byte of sub-sub-voxel). - Standard_Integer ibyte2 = (ibyte << 3) + ioct1; - - // Get sub-sub-value - Standard_Byte subsubvalue = ((Standard_Byte*)((Voxel_SplitData*)((Voxel_SplitData*)((Voxel_SplitData*)((Voxel_SplitData**)myData)[islice])->GetSplitData())->GetSplitData())->GetValues())[ibyte2]; - - // Set sub-sub-value - if (subsubvalue != (data ? 255 : 0)) - { - subsubvalue = data ? 255 : 0; - ((Standard_Byte*)((Voxel_SplitData*)((Voxel_SplitData*)((Voxel_SplitData*)((Voxel_SplitData**)myData)[islice])->GetSplitData())->GetSplitData())->GetValues())[ibyte2] = subsubvalue; - } - } -} - -void Voxel_ROctBoolDS::Set(const Standard_Integer ix, const Standard_Integer iy, const Standard_Integer iz, - const Standard_Integer ioct1, const Standard_Integer ioct2, const Standard_Boolean data) -{ - Standard_Integer ibit = ix + myNbX * iy + myNbXY * iz; - Standard_Integer islice = ibit >> 3; - - if (!data && !((Voxel_SplitData**)myData)[islice]) - return; // don't allocate a slice of data for setting a 0 value - - // Allocate the slice if it is not done yet. - if (!((Voxel_SplitData**)myData)[islice]) - { - ((Voxel_SplitData**)myData)[islice] = (Voxel_SplitData*) new Voxel_SplitData; - // Values: - ((Voxel_SplitData*)((Voxel_SplitData**)myData)[islice])->GetValues() = - (Standard_Byte*) calloc(1/*one byte: 8 1-bit values*/, sizeof(Standard_Byte)); - // Sub-voxels: - ((Voxel_SplitData*)((Voxel_SplitData**)myData)[islice])->GetSplitData() = 0; - } - - // Check sub-voxels of the first level - if (!((Voxel_SplitData*)((Voxel_SplitData**)myData)[islice])->GetSplitData()) - { - // Sub-voxels: - ((Voxel_SplitData*)((Voxel_SplitData**)myData)[islice])->GetSplitData() = (Voxel_SplitData*) new Voxel_SplitData; - // Value of sub-voxels: - ((Voxel_SplitData*)((Voxel_SplitData*)((Voxel_SplitData**)myData)[islice])->GetSplitData())->GetValues() = - (Standard_Byte*) calloc(8/*eight bytes: 8 sub-voxels for each voxel*/, sizeof(Standard_Byte)); - - // Set parent value - Standard_Byte parent_value = *((Standard_Byte*)((Voxel_SplitData*)((Voxel_SplitData**)myData)[islice])->GetValues()); - if (parent_value) - { - for (Standard_Integer shift = 0; shift < 8; shift++) - { - if ((parent_value & gbits[shift]) ? Standard_True : Standard_False) - { - ((Standard_Byte*)((Voxel_SplitData*)((Voxel_SplitData*)((Voxel_SplitData**)myData)[islice])->GetSplitData())->GetValues())[shift] = 255; - } - else - { - ((Standard_Byte*)((Voxel_SplitData*)((Voxel_SplitData*)((Voxel_SplitData**)myData)[islice])->GetSplitData())->GetValues())[shift] = 0; - } - } - } - - // Sub-sub-voxels - ((Voxel_SplitData*)((Voxel_SplitData*)((Voxel_SplitData**)myData)[islice])->GetSplitData())->GetSplitData() = 0; - } - - // Check sub-voxels of the second level - if (!((Voxel_SplitData*)((Voxel_SplitData*)((Voxel_SplitData**)myData)[islice])->GetSplitData())->GetSplitData()) - { - // Sub-voxels 2: - ((Voxel_SplitData*)((Voxel_SplitData*)((Voxel_SplitData**)myData)[islice])->GetSplitData())->GetSplitData() = - (Voxel_SplitData*) new Voxel_SplitData; - // Value of sub-voxels 2: - ((Voxel_SplitData*)((Voxel_SplitData*)((Voxel_SplitData*)((Voxel_SplitData**)myData)[islice])->GetSplitData())->GetSplitData())->GetValues() = - (Standard_Byte*) calloc(64/*sixty four bytes: 8 sub-voxels for each sub-voxel for each voxel*/, - sizeof(Standard_Byte)); - - // Set parent value - for (Standard_Integer ibyte1 = 0; ibyte1 < 8; ibyte1++) - { - Standard_Byte parent_value = ((Standard_Byte*)((Voxel_SplitData*)((Voxel_SplitData*)((Voxel_SplitData**)myData)[islice])->GetSplitData())->GetValues())[ibyte1]; - if (parent_value) - { - Standard_Integer ibyte2 = (ibyte1 << 3); - for (Standard_Integer shift = 0; shift < 8; shift++) - { - if ((parent_value & gbits[shift]) ? Standard_True : Standard_False) - { - ((Standard_Byte*)((Voxel_SplitData*)((Voxel_SplitData*)((Voxel_SplitData*)((Voxel_SplitData**)myData)[islice])->GetSplitData())->GetSplitData())->GetValues())[ibyte2 + shift] = 255; - } - else - { - ((Standard_Byte*)((Voxel_SplitData*)((Voxel_SplitData*)((Voxel_SplitData*)((Voxel_SplitData**)myData)[islice])->GetSplitData())->GetSplitData())->GetValues())[ibyte2 + shift] = 0; - } - } - } - } - - // Sub-sub-sub-voxels - ((Voxel_SplitData*)((Voxel_SplitData*)((Voxel_SplitData*)((Voxel_SplitData**)myData)[islice])->GetSplitData())->GetSplitData())->GetSplitData() = 0; - } - - // Index of sub-voxel corresponding to ioct1: 8 voxels correspond to 64 sub-voxels. - Standard_Integer ibyte1 = ibit - (islice << 3); // imdex of byte of 8-byte value (sub-voxel 1). - Standard_Integer ibyte2 = (ibyte1 << 3) + ioct1; // index of byte of 64-byte value (sub-voxel 2) - - // Value - Standard_Byte value = ((Standard_Byte*)((Voxel_SplitData*)((Voxel_SplitData*)((Voxel_SplitData*)((Voxel_SplitData**)myData)[islice])->GetSplitData())->GetSplitData())->GetValues())[ibyte2]; - - // Set data - if (data != ((value & gbits[ioct2]) ? Standard_True : Standard_False)) - { - if (data) - value |= gbits[ioct2]; - else - value &= gnbits[ioct2]; - ((Standard_Byte*)((Voxel_SplitData*)((Voxel_SplitData*)((Voxel_SplitData*)((Voxel_SplitData**)myData)[islice])->GetSplitData())->GetSplitData())->GetValues())[ibyte2] = value; - } -} - -Standard_Boolean Voxel_ROctBoolDS::Get(const Standard_Integer ix, const Standard_Integer iy, const Standard_Integer iz) const -{ - Standard_Integer ibit = ix + myNbX * iy + myNbXY * iz; - Standard_Integer islice = ibit >> 3; - - // If the slice of data is not allocated, it means that its values are 0. - if (!((Voxel_SplitData**)myData)[islice]) - return Standard_False; - - // Value (byte) - Standard_Byte value = *((Standard_Byte*)((Voxel_SplitData*)((Voxel_SplitData**)myData)[islice])->GetValues()); - - // Position of data in the 8 bit-"value". - Standard_Integer shift = ibit - (islice << 3); - return ((value & gbits[shift]) ? Standard_True : Standard_False); -} - -Standard_Boolean Voxel_ROctBoolDS::Get(const Standard_Integer ix, const Standard_Integer iy, const Standard_Integer iz, - const Standard_Integer ioct1) const -{ - Standard_Integer ibit = ix + myNbX * iy + myNbXY * iz; - Standard_Integer islice = ibit >> 3; - - // If the slice of data is not allocated, it means that its values are 0. - if (!((Voxel_SplitData**)myData)[islice]) - return Standard_False; - - // If the voxel is not split, return the value of the voxel. - if (!((Voxel_SplitData*)((Voxel_SplitData**)myData)[islice])->GetSplitData()) - return Get(ix, iy, iz); - - // Index of sub-voxel corresponding to ioct1: 8 voxels correspond to 64 sub-voxels. - Standard_Integer ibyte = ibit - (islice << 3); - - // Value - Standard_Byte value = ((Standard_Byte*)((Voxel_SplitData*)((Voxel_SplitData*)((Voxel_SplitData**)myData)[islice])->GetSplitData())->GetValues())[ibyte]; - - return ((value & gbits[ioct1]) ? Standard_True : Standard_False); -} - -Standard_Boolean Voxel_ROctBoolDS::Get(const Standard_Integer ix, const Standard_Integer iy, const Standard_Integer iz, - const Standard_Integer ioct1, const Standard_Integer ioct2) const -{ - Standard_Integer ibit = ix + myNbX * iy + myNbXY * iz; - Standard_Integer islice = ibit >> 3; - - // If the slice of data is not allocated, it means that its values are 0. - if (!((Voxel_SplitData**)myData)[islice]) - return Standard_False; - - // If the voxel is not split, return the value of the voxel. - if (!((Voxel_SplitData*)((Voxel_SplitData**)myData)[islice])->GetSplitData()) - return Get(ix, iy, iz); - - // If the split voxel (sub-voxel 1) is not split, return the value of the sub-voxel 1. - if (!((Voxel_SplitData*)((Voxel_SplitData*)((Voxel_SplitData**)myData)[islice])->GetSplitData())->GetSplitData()) - return Get(ix, iy, iz, ioct1); - - // Index of sub-voxel corresponding to ioct1: 8 voxels correspond to 64 sub-voxels. - Standard_Integer ibyte1 = ibit - (islice << 3); // index of byte of 8-byte value (sub-voxel 1). - Standard_Integer ibyte2 = (ibyte1 << 3) + ioct1; // index of byte of 64-byte value (sub-voxel 2) - - // Value - Standard_Byte value = ((Standard_Byte*)((Voxel_SplitData*)((Voxel_SplitData*)((Voxel_SplitData*)((Voxel_SplitData**)myData)[islice])->GetSplitData())->GetSplitData())->GetValues())[ibyte2]; - - return ((value & gbits[ioct2]) ? Standard_True : Standard_False); -} - -Standard_Boolean Voxel_ROctBoolDS::IsSplit(const Standard_Integer ix, const Standard_Integer iy, const Standard_Integer iz) const -{ - Standard_Integer ibit = ix + myNbX * iy + myNbXY * iz; - Standard_Integer islice = ibit >> 3; - - // If the voxel has no value, it is not split. - if (!((Voxel_SplitData**)myData)[islice]) - return Standard_False; - - // Check existence of sub-voxels - if (((Voxel_SplitData*)((Voxel_SplitData**)myData)[islice])->GetSplitData()) - return Standard_True; - return Standard_False; -} - -Standard_Integer Voxel_ROctBoolDS::Deepness(const Standard_Integer ix, const Standard_Integer iy, const Standard_Integer iz) const -{ - Standard_Integer ibit = ix + myNbX * iy + myNbXY * iz; - Standard_Integer islice = ibit >> 3; - - // If the voxel has no value, it is not split. - if (!((Voxel_SplitData**)myData)[islice]) - return 0; - - // Test deepness. - if (((Voxel_SplitData*)((Voxel_SplitData**)myData)[islice])->GetSplitData()) - { - if (((Voxel_SplitData*)((Voxel_SplitData*)((Voxel_SplitData**)myData)[islice])->GetSplitData())->GetSplitData()) - { - return 2; - } - else - { - return 1; - } - } - return 0; -} - -void Voxel_ROctBoolDS::OptimizeMemory() -{ - // Iterate the array of voxels checking coincidence of values of sub-voxels. - Standard_Integer islice = 0, nb_slices = RealToInt(ceil(myNbXY * myNbZ / 8.0)); - for (; islice < nb_slices; islice++) - { - if (!((Voxel_SplitData**)myData)[islice]) - continue; - if (((Voxel_SplitData*)((Voxel_SplitData**)myData)[islice])->GetSplitData()) - { - Standard_Boolean suppress = Standard_False; - // Second level of sub-voxels - if (((Voxel_SplitData*)((Voxel_SplitData*)((Voxel_SplitData**)myData)[islice])->GetSplitData())->GetSplitData()) - { - suppress = Standard_False; - Standard_Byte value1 = ((Standard_Byte*)((Voxel_SplitData*)((Voxel_SplitData*)((Voxel_SplitData*)((Voxel_SplitData**)myData)[islice])->GetSplitData())->GetSplitData())->GetValues())[0]; - if (value1 == 0 || value1 == 255) - { - suppress = Standard_True; - for (Standard_Integer ibyte2 = 1; ibyte2 < 64; ibyte2++) - { - Standard_Byte value2 = ((Standard_Byte*)((Voxel_SplitData*)((Voxel_SplitData*)((Voxel_SplitData*)((Voxel_SplitData**)myData)[islice])->GetSplitData())->GetSplitData())->GetValues())[ibyte2]; - if (value2 != value1) - { - suppress = Standard_False; - break; - } - } - } - if (suppress) - { - SetZeroSplitData((Voxel_SplitData*)((Voxel_SplitData*)((Voxel_SplitData*)((Voxel_SplitData**)myData)[islice])->GetSplitData())->GetSplitData()); - ((Voxel_SplitData*)((Voxel_SplitData*)((Voxel_SplitData**)myData)[islice])->GetSplitData())->GetSplitData() = 0; - // Set value to upper level - for (Standard_Integer ibyte1 = 0; ibyte1 < 8; ibyte1++) - { - ((Standard_Byte*)((Voxel_SplitData*)((Voxel_SplitData*)((Voxel_SplitData**)myData)[islice])->GetSplitData())->GetValues())[ibyte1] = value1; - } - } - else - { - // If we don't suppress sub-sub-voxels, we don't touch sub-voxels. - continue; - } - } - // First level of sub-voxels - suppress = Standard_False; - Standard_Byte value1 = ((Standard_Byte*)((Voxel_SplitData*)((Voxel_SplitData*)((Voxel_SplitData**)myData)[islice])->GetSplitData())->GetValues())[0]; - if (value1 == 0 || value1 == 255) - { - suppress = Standard_True; - for (Standard_Integer ibyte1 = 1; ibyte1 < 8; ibyte1++) - { - Standard_Byte value2 = ((Standard_Byte*)((Voxel_SplitData*)((Voxel_SplitData*)((Voxel_SplitData**)myData)[islice])->GetSplitData())->GetValues())[ibyte1]; - if (value2 != value1) - { - suppress = Standard_False; - break; - } - } - } - if (suppress) - { - SetZeroSplitData((Voxel_SplitData*)((Voxel_SplitData*)((Voxel_SplitData**)myData)[islice])->GetSplitData()); - ((Voxel_SplitData*)((Voxel_SplitData**)myData)[islice])->GetSplitData() = 0; - // Set value to upper level - *((Standard_Byte*)((Voxel_SplitData*)((Voxel_SplitData**)myData)[islice])->GetValues()) = value1; - } - } - } -} - -void Voxel_ROctBoolDS::GetCenter(const Standard_Integer ix, const Standard_Integer iy, const Standard_Integer iz, - const Standard_Integer i, - Standard_Real& xc, Standard_Real& yc, Standard_Real& zc) const -{ - xc = myX + ix * myDX; - yc = myY + iy * myDY; - zc = myZ + iz * myDZ; - - switch (i) - { - case 0: - { - xc += 0.5 * myHalfDX; - yc += 0.5 * myHalfDY; - zc += 0.5 * myHalfDZ; - break; - } - case 1: - { - xc += 1.5 * myHalfDX; - yc += 0.5 * myHalfDY; - zc += 0.5 * myHalfDZ; - break; - } - case 2: - { - xc += 0.5 * myHalfDX; - yc += 1.5 * myHalfDY; - zc += 0.5 * myHalfDZ; - break; - } - case 3: - { - xc += 1.5 * myHalfDX; - yc += 1.5 * myHalfDY; - zc += 0.5 * myHalfDZ; - break; - } - case 4: - { - xc += 0.5 * myHalfDX; - yc += 0.5 * myHalfDY; - zc += 1.5 * myHalfDZ; - break; - } - case 5: - { - xc += 1.5 * myHalfDX; - yc += 0.5 * myHalfDY; - zc += 1.5 * myHalfDZ; - break; - } - case 6: - { - xc += 0.5 * myHalfDX; - yc += 1.5 * myHalfDY; - zc += 1.5 * myHalfDZ; - break; - } - case 7: - { - xc += 1.5 * myHalfDX; - yc += 1.5 * myHalfDY; - zc += 1.5 * myHalfDZ; - break; - } - } -} - -void Voxel_ROctBoolDS::GetCenter(const Standard_Integer ix, const Standard_Integer iy, const Standard_Integer iz, - const Standard_Integer i, const Standard_Integer j, - Standard_Real& xc, Standard_Real& yc, Standard_Real& zc) const -{ - xc = myX + ix * myDX; - yc = myY + iy * myDY; - zc = myZ + iz * myDZ; - - switch (i) - { - case 0: - { - break; - } - case 1: - { - xc += myHalfDX; - break; - } - case 2: - { - yc += myHalfDY; - break; - } - case 3: - { - xc += myHalfDX; - yc += myHalfDY; - break; - } - case 4: - { - zc += myHalfDZ; - break; - } - case 5: - { - xc += myHalfDX; - zc += myHalfDZ; - break; - } - case 6: - { - yc += myHalfDY; - zc += myHalfDZ; - break; - } - case 7: - { - xc += myHalfDX; - yc += myHalfDY; - zc += myHalfDZ; - break; - } - } - - switch (j) - { - case 0: - { - xc += 0.25 * myHalfDX; - yc += 0.25 * myHalfDY; - zc += 0.25 * myHalfDZ; - break; - } - case 1: - { - xc += 0.75 * myHalfDX; - yc += 0.25 * myHalfDY; - zc += 0.25 * myHalfDZ; - break; - } - case 2: - { - xc += 0.25 * myHalfDX; - yc += 0.75 * myHalfDY; - zc += 0.25 * myHalfDZ; - break; - } - case 3: - { - xc += 0.75 * myHalfDX; - yc += 0.75 * myHalfDY; - zc += 0.25 * myHalfDZ; - break; - } - case 4: - { - xc += 0.25 * myHalfDX; - yc += 0.25 * myHalfDY; - zc += 0.75 * myHalfDZ; - break; - } - case 5: - { - xc += 0.75 * myHalfDX; - yc += 0.25 * myHalfDY; - zc += 0.75 * myHalfDZ; - break; - } - case 6: - { - xc += 0.25 * myHalfDX; - yc += 0.75 * myHalfDY; - zc += 0.75 * myHalfDZ; - break; - } - case 7: - { - xc += 0.75 * myHalfDX; - yc += 0.75 * myHalfDY; - zc += 0.75 * myHalfDZ; - break; - } - } -} diff --git a/src/Voxel/Voxel_ROctBoolDS.hxx b/src/Voxel/Voxel_ROctBoolDS.hxx deleted file mode 100644 index 10bf8f9958..0000000000 --- a/src/Voxel/Voxel_ROctBoolDS.hxx +++ /dev/null @@ -1,141 +0,0 @@ -// Created on: 2008-09-01 -// Created by: Vladislav ROMASHKO -// Copyright (c) 2008-2014 OPEN CASCADE SAS -// -// This file is part of Open CASCADE Technology software library. -// -// This library is free software; you can redistribute it and/or modify it under -// the terms of the GNU Lesser General Public License version 2.1 as published -// by the Free Software Foundation, with special exception defined in the file -// OCCT_LGPL_EXCEPTION.txt. Consult the file LICENSE_LGPL_21.txt included in OCCT -// distribution for complete text of the license and disclaimer of any warranty. -// -// Alternatively, this file may be used under the terms of Open CASCADE -// commercial license or contractual agreement. - -#ifndef _Voxel_ROctBoolDS_HeaderFile -#define _Voxel_ROctBoolDS_HeaderFile - -#include -#include -#include - -#include -#include -#include -#include - - -//! A 3D voxel model keeping a boolean flag (1 or 0) -//! value for each voxel, and having an opportunity to split each voxel -//! into 8 sub-voxels recursively. -class Voxel_ROctBoolDS : public Voxel_DS -{ -public: - - DEFINE_STANDARD_ALLOC - - - //! An empty constructor. - Standard_EXPORT Voxel_ROctBoolDS(); - - //! A constructor initializing the voxel model. - //! (x, y, z) - the start point of the box. - //! (x_len, y_len, z_len) - lengths in x, y and z directions along axes of a co-ordinate system. - //! (nb_x, nb_y, nb_z) - number of splits (voxels) along x, y and z directions. - Standard_EXPORT Voxel_ROctBoolDS(const Standard_Real x, const Standard_Real y, const Standard_Real z, const Standard_Real x_len, const Standard_Real y_len, const Standard_Real z_len, const Standard_Integer nb_x, const Standard_Integer nb_y, const Standard_Integer nb_z); - - //! Initialization of the voxel model. - //! (x, y, z) - the start point of the box. - //! (x_len, y_len, z_len) - lengths in x, y and z directions along axes of a co-ordinate system. - //! (nb_x, nb_y, nb_z) - number of splits (voxels) along x, y and z directions. - Standard_EXPORT virtual void Init (const Standard_Real x, const Standard_Real y, const Standard_Real z, const Standard_Real x_len, const Standard_Real y_len, const Standard_Real z_len, const Standard_Integer nb_x, const Standard_Integer nb_y, const Standard_Integer nb_z) Standard_OVERRIDE; - - //! A destructor of the voxel model. - Standard_EXPORT void Destroy(); -~Voxel_ROctBoolDS() -{ - Destroy(); -} - - //! The method sets all values equal to 0 (false) and - //! releases the memory. - Standard_EXPORT void SetZero(); - - //! The method searches voxels with equal-value of sub-voxels - //! and removes them (remaining the value for the voxel). - Standard_EXPORT void OptimizeMemory(); - - //! Defines a value for voxel with co-ordinates (ix, iy, iz). - //! If the voxel is split into 8 sub-voxels, the split disappears. - //! Initial state of the model is so that all voxels have value 0 (false), - //! and this data doesn't occupy memory. - //! Memory for data is allocating during setting non-zero values (true). - Standard_EXPORT void Set (const Standard_Integer ix, const Standard_Integer iy, const Standard_Integer iz, const Standard_Boolean data); - - //! Defines a value for a sub-voxel of a voxel with co-ordinates (ix, iy, iz). - //! If the voxel is not split into 8 sub-voxels yet, this method splits the voxel. - //! Range of sub-voxels is 0 - 7. - Standard_EXPORT void Set (const Standard_Integer ix, const Standard_Integer iy, const Standard_Integer iz, const Standard_Integer ioct1, const Standard_Boolean data); - - //! Defines a value for a sub-voxel of a sub-voxel of a voxel with co-ordinates (ix, iy, iz). - //! If the voxel is not split into 8 sub-voxels yet, this method splits the voxel. - //! Range of sub-voxels is 0 - 7. - Standard_EXPORT void Set (const Standard_Integer ix, const Standard_Integer iy, const Standard_Integer iz, const Standard_Integer ioct1, const Standard_Integer ioct2, const Standard_Boolean data); - - //! Returns true if the voxel is split into 8 sub-voxels. - Standard_EXPORT Standard_Boolean IsSplit (const Standard_Integer ix, const Standard_Integer iy, const Standard_Integer iz) const; - - //! Returns the deepness of splits of a voxel. - //! 0 - no splits (::IsSplit() being called would return false). - //! 1 - the voxel is split into 8 sub-voxels. - //! 2 - the voxels is split into 8 sub-voxels, - //! and each of the sub-voxels is split into 8 sub-sub-voxels. - //! 3 - ... - Standard_EXPORT Standard_Integer Deepness (const Standard_Integer ix, const Standard_Integer iy, const Standard_Integer iz) const; - - //! Returns the value of voxel with co-ordinates (ix, iy, iz). - //! Warning!: the returned value may not coincide with the value of its 8 sub-voxels. - //! Use the method ::IsSplit() to check whether a voxel has sub-voxels. - Standard_EXPORT Standard_Boolean Get (const Standard_Integer ix, const Standard_Integer iy, const Standard_Integer iz) const; - - //! Returns the value of a sub-voxel of a voxel with co-ordinates (ix, iy, iz). - //! If the voxel is not split, it returns the value of the voxel. - //! Range of sub-voxels is 0 - 7. - Standard_EXPORT Standard_Boolean Get (const Standard_Integer ix, const Standard_Integer iy, const Standard_Integer iz, const Standard_Integer ioct1) const; - - //! Returns the value of a sub-voxel of a sub-voxel of a voxel with co-ordinates (ix, iy, iz). - //! If the voxel is not split, it returns the value of the voxel. - //! Range of sub-voxels is 0 - 7. - Standard_EXPORT Standard_Boolean Get (const Standard_Integer ix, const Standard_Integer iy, const Standard_Integer iz, const Standard_Integer ioct1, const Standard_Integer ioct2) const; - - //! Returns the center point of a sub-voxel with co-ordinates (ix, iy, iz, i). - Standard_EXPORT void GetCenter (const Standard_Integer ix, const Standard_Integer iy, const Standard_Integer iz, const Standard_Integer i, Standard_Real& xc, Standard_Real& yc, Standard_Real& zc) const; - - //! Returns the center point of a sub-voxel with co-ordinates (ix, iy, iz, i, j). - Standard_EXPORT void GetCenter (const Standard_Integer ix, const Standard_Integer iy, const Standard_Integer iz, const Standard_Integer i, const Standard_Integer j, Standard_Real& xc, Standard_Real& yc, Standard_Real& zc) const; - - - - -protected: - - - - - -private: - - - - - -}; - - - - - - - -#endif // _Voxel_ROctBoolDS_HeaderFile diff --git a/src/Voxel/Voxel_Reader.cxx b/src/Voxel/Voxel_Reader.cxx deleted file mode 100644 index 17a6e5bd9d..0000000000 --- a/src/Voxel/Voxel_Reader.cxx +++ /dev/null @@ -1,575 +0,0 @@ -// Created on: 2008-08-28 -// Created by: Vladislav ROMASHKO -// Copyright (c) 2008-2014 OPEN CASCADE SAS -// -// This file is part of Open CASCADE Technology software library. -// -// This library is free software; you can redistribute it and/or modify it under -// the terms of the GNU Lesser General Public License version 2.1 as published -// by the Free Software Foundation, with special exception defined in the file -// OCCT_LGPL_EXCEPTION.txt. Consult the file LICENSE_LGPL_21.txt included in OCCT -// distribution for complete text of the license and disclaimer of any warranty. -// -// Alternatively, this file may be used under the terms of Open CASCADE -// commercial license or contractual agreement. - - -#include -#include -#include -#include -#include -#include -#include -#include -#include - -Voxel_Reader::Voxel_Reader():myBoolVoxels(0),myColorVoxels(0),myFloatVoxels(0) -{ - -} - -Standard_Boolean Voxel_Reader::Read(const TCollection_ExtendedString& file) -{ - // Open file in ASCII mode to read header - FILE* f = OSD_OpenFile(file, "r"); - if (!f) - return Standard_False; - - // Read the header - Standard_Byte type; // 0 - bool, 1 - color, 2 - float - Voxel_VoxelFileFormat format; - Standard_Character svoxels[9], sformat[9], stype[9]; - if (fscanf(f, "%8s %8s %8s\n", svoxels, sformat, stype) != 3) - { - fclose(f); - return Standard_False; - } - fclose(f); - - // Take format, type of voxels. - // Voxels - if (strcmp(svoxels, VOXELS)) - return Standard_False; - // Format - if (strcmp(sformat, ASCII) == 0) - format = Voxel_VFF_ASCII; - else if (strcmp(sformat, BINARY) == 0) - format = Voxel_VFF_BINARY; - else - return Standard_False; - // Type of voxels - if (strcmp(stype, BOOL) == 0) - type = 0; - else if (strcmp(stype, COLOR) == 0) - type = 1; - else if (strcmp(stype, FLOAT) == 0) - type = 2; - else - return Standard_False; - - // Read the rest - switch (format) - { - case Voxel_VFF_ASCII: - { - switch (type) - { - case 0: - return ReadBoolAsciiVoxels(file); - case 1: - return ReadColorAsciiVoxels(file); - case 2: - return ReadFloatAsciiVoxels(file); - } - } - case Voxel_VFF_BINARY: - { - switch (type) - { - case 0: - return ReadBoolBinaryVoxels(file); - case 1: - return ReadColorBinaryVoxels(file); - case 2: - return ReadFloatBinaryVoxels(file); - } - } - } - - // No voxels or no format description is found: - return Standard_False; -} - -Standard_Boolean Voxel_Reader::IsBoolVoxels() const -{ - return (myBoolVoxels != 0); -} - -Standard_Boolean Voxel_Reader::IsColorVoxels() const -{ - return (myColorVoxels != 0); -} - -Standard_Boolean Voxel_Reader::IsFloatVoxels() const -{ - return (myFloatVoxels != 0); -} - -Standard_Address Voxel_Reader::GetBoolVoxels() const -{ - return myBoolVoxels; -} - -Standard_Address Voxel_Reader::GetColorVoxels() const -{ - return myColorVoxels; -} - -Standard_Address Voxel_Reader::GetFloatVoxels() const -{ - return myFloatVoxels; -} - -static Standard_Boolean has_slice(const Standard_CString line) -{ - Standard_Integer i = 0, nb_spaces = 0; - while (line[i] != '\0') - { - if (line[i] == ' ') - nb_spaces++; - i++; - } - return (nb_spaces == 2); -} - -Standard_Boolean Voxel_Reader::ReadBoolAsciiVoxels(const TCollection_ExtendedString& file) -{ - // Open file for reading - FILE* f = OSD_OpenFile(file, "r"); - if (!f) - return Standard_False; - Standard_Character line[65], sx[33], sy[33], sz[33]; - - // Header: skip it - if (fgets(line, 64, f) == NULL) - { - return Standard_False; - } - - // Location, size, number of splits - Standard_Integer nbx = 0, nby = 0, nbz = 0; - Standard_Real x = 0.0, y = 0.0, z = 0.0, xlen = 0.0, ylen = 0.0, zlen = 0.0; - if (fscanf(f, "%32s %32s %32s\n", sx, sy, sz) != 3) - { - fclose(f); - return Standard_False; - } - x = Atof(sx); y = Atof(sy); z = Atof(sz); - if (fscanf(f, "%32s %32s %32s\n", sx, sy, sz) != 3) - { - fclose(f); - return Standard_False; - } - xlen = Atof(sx); ylen = Atof(sy); zlen = Atof(sz); - if (fscanf(f, "%d %d %d\n", &nbx, &nby, &nbz) != 3) - { - fclose(f); - return Standard_False; - } - - // Allocate the voxels - myBoolVoxels = (Standard_Address) new Voxel_BoolDS(x, y, z, xlen, ylen, zlen, nbx, nby, nbz); - - // Data - // Copied from Voxel_BoolDS.cxx: - Standard_Integer nb_bytes = RealToInt(ceil(nbx * nby * nbz / 8.0)); - Standard_Integer nb_slices = RealToInt(ceil(nb_bytes / 8.0)); - // myData[0 .. nb_slices - 1][0 .. 7] - if (nb_slices) - { - Standard_Integer i1 = 0, i2 = 0, value = 0; - while (!feof(f) - && fgets(line, 64, f) != NULL) - { - if (has_slice(line)) - { - if (sscanf(line, "%d %d %d\n", &i1, &i2, &value) != 3) - { - fclose(f); - return Standard_False; - } - } - else - { - if (sscanf(line, "%d %d\n", &i2, &value) != 2) - { - fclose(f); - return Standard_False; - } - } - - // Set value - if (!((Standard_Byte**)((Voxel_DS*)myBoolVoxels)->myData)[i1]) - { - ((Standard_Byte**)((Voxel_DS*)myBoolVoxels)->myData)[i1] = - (Standard_Byte*) calloc(8/*number of bytes in slice*/, sizeof(Standard_Byte)); - } - (((Standard_Byte**)((Voxel_DS*)myBoolVoxels)->myData)[i1])[i2] = (Standard_Byte)value; - } - } - - fclose(f); - return Standard_True; -} - -Standard_Boolean Voxel_Reader::ReadColorAsciiVoxels(const TCollection_ExtendedString& file) -{ - // Open file for reading - FILE* f = OSD_OpenFile(file, "r"); - if (!f) - return Standard_False; - Standard_Character line[65], sx[33], sy[33], sz[33]; - - // Header: skip it - if (fgets(line, 64, f) == NULL) - { - fclose(f); - return Standard_False; - } - - // Location, size, number of splits - Standard_Integer nbx = 0, nby = 0, nbz = 0; - Standard_Real x = 0.0, y = 0.0, z = 0.0, xlen = 0.0, ylen = 0.0, zlen = 0.0; - if (fscanf(f, "%32s %32s %32s\n", sx, sy, sz) != 3) - { - fclose(f); - return Standard_False; - } - x = Atof(sx); y = Atof(sy); z = Atof(sz); - if (fscanf(f, "%32s %32s %32s\n", sx, sy, sz) != 3) - { - fclose(f); - return Standard_False; - } - xlen = Atof(sx); ylen = Atof(sy); zlen = Atof(sz); - if (fscanf(f, "%d %d %d\n", &nbx, &nby, &nbz) != 3) - { - fclose(f); - return Standard_False; - } - - // Allocate the voxels - myColorVoxels = (Standard_Address) new Voxel_ColorDS(x, y, z, xlen, ylen, zlen, nbx, nby, nbz); - - // Data - // Copied from Voxel_ColorDS.cxx: - Standard_Integer nb_bytes = RealToInt(ceil(nbx * nby * nbz / 2.0)); - Standard_Integer nb_slices = RealToInt(ceil(nb_bytes / 32.0)); - // myData[0 .. nb_slices - 1][0 .. 31] - if (nb_slices) - { - Standard_Integer i1 = 0, i2 = 0, value = 0; - while (!feof(f) - && fgets(line, 64, f) != NULL) - { - if (has_slice(line)) - { - if (sscanf(line, "%d %d %d\n", &i1, &i2, &value) != 3) - { - fclose(f); - return Standard_False; - } - } - else - { - if (sscanf(line, "%d %d\n", &i2, &value) != 2) - { - fclose(f); - return Standard_False; - } - } - - // Set value - if (!((Standard_Byte**)((Voxel_DS*)myColorVoxels)->myData)[i1]) - { - ((Standard_Byte**)((Voxel_DS*)myColorVoxels)->myData)[i1] = - (Standard_Byte*) calloc(32/*number of bytes in slice*/, sizeof(Standard_Byte)); - } - (((Standard_Byte**)((Voxel_DS*)myColorVoxels)->myData)[i1])[i2] = (Standard_Byte)value; - } - } - - fclose(f); - return Standard_True; -} - -Standard_Boolean Voxel_Reader::ReadFloatAsciiVoxels(const TCollection_ExtendedString& file) -{ - // Open file for reading - FILE* f = OSD_OpenFile(file, "r"); - if (!f) - return Standard_False; - Standard_Character line[65], sx[33], sy[33], sz[33]; - - // Header: skip it - if (fgets(line, 64, f) == NULL) - { - fclose(f); - return Standard_False; - } - - // Location, size, number of splits - Standard_Integer nbx = 0, nby = 0, nbz = 0; - Standard_Real x = 0.0, y = 0.0, z = 0.0, xlen = 0.0, ylen = 0.0, zlen = 0.0; - if (fscanf(f, "%32s %32s %32s\n", sx, sy, sz) != 3) - { - fclose(f); - return Standard_False; - } - x = Atof(sx); y = Atof(sy); z = Atof(sz); - if (fscanf(f, "%32s %32s %32s\n", sx, sy, sz) != 3) - { - fclose(f); - return Standard_False; - } - xlen = Atof(sx); ylen = Atof(sy); zlen = Atof(sz); - if (fscanf(f, "%d %d %d\n", &nbx, &nby, &nbz) != 3) - { - fclose(f); - return Standard_False; - } - - // Allocate the voxels - myFloatVoxels = (Standard_Address) new Voxel_FloatDS(x, y, z, xlen, ylen, zlen, nbx, nby, nbz); - - // Data - // Copied from Voxel_FloatDS.cxx: - Standard_Integer nb_floats = nbx * nby * nbz; - Standard_Integer nb_slices = RealToInt(ceil(nb_floats / 32.0)); // 32 values in 1 slice - // myData[0 .. nb_slices - 1][0 .. 31] - if (nb_slices) - { - Standard_Integer i1 = 0, i2 = 0; - Standard_ShortReal value = 0.0; - while (!feof(f) - && fgets(line, 64, f) != NULL) - { - if (has_slice(line)) - { - if (sscanf(line, "%d %d %64s\n", &i1, &i2, line) != 3) - { - fclose(f); - return Standard_False; - } - } - else - { - if (sscanf(line, "%d %64s\n", &i2, line) != 2) - { - fclose(f); - return Standard_False; - } - } - value = (Standard_ShortReal)Atof(line); - - // Set value - if (!((Standard_ShortReal**)((Voxel_DS*)myFloatVoxels)->myData)[i1]) - { - ((Standard_ShortReal**)((Voxel_DS*)myFloatVoxels)->myData)[i1] = - (Standard_ShortReal*) calloc(32/*number of floats in slice*/, sizeof(Standard_ShortReal)); - } - (((Standard_ShortReal**)((Voxel_DS*)myFloatVoxels)->myData)[i1])[i2] = value; - } - } - - fclose(f); - return Standard_True; -} - -Standard_Boolean Voxel_Reader::ReadBoolBinaryVoxels(const TCollection_ExtendedString& file) -{ - // Open file for reading - FILE* f = OSD_OpenFile(file, "r"); - if (!f) - return Standard_False; - - // Header: skip it - Standard_Character line[65]; - if (fgets(line, 64, f) == NULL) - { - fclose(f); - return Standard_False; - } - - // Location, size, number of splits - Standard_Integer nbx = 0, nby = 0, nbz = 0; - Standard_Real x = 0.0, y = 0.0, z = 0.0, xlen = 0.0, ylen = 0.0, zlen = 0.0; - if (fread(&x, sizeof(Standard_Real), 1, f) != 1 - || fread(&y, sizeof(Standard_Real), 1, f) != 1 - || fread(&z, sizeof(Standard_Real), 1, f) != 1 - || fread(&xlen, sizeof(Standard_Real), 1, f) != 1 - || fread(&ylen, sizeof(Standard_Real), 1, f) != 1 - || fread(&zlen, sizeof(Standard_Real), 1, f) != 1 - || fread(&nbx, sizeof(Standard_Integer), 1, f) != 1 - || fread(&nby, sizeof(Standard_Integer), 1, f) != 1 - || fread(&nbz, sizeof(Standard_Integer), 1, f) != 1) - { - fclose(f); - return Standard_False; - } - - // Allocate the voxels - myBoolVoxels = (Standard_Address) new Voxel_BoolDS(x, y, z, xlen, ylen, zlen, nbx, nby, nbz); - - // Data - // Copied from Voxel_BoolDS.cxx: - Standard_Integer nb_bytes = RealToInt(ceil(nbx * nby * nbz / 8.0)); - Standard_Integer nb_slices = RealToInt(ceil(nb_bytes / 8.0)); - // myData[0 .. nb_slices - 1][0 .. 7] - if (nb_slices) - { - Standard_Integer i1 = 0, i2 = 0, value = 0; - while (!feof(f) - && fread(&i1, sizeof(Standard_Integer), 1, f) == 1 - && fread(&i2, sizeof(Standard_Integer), 1, f) == 1 - && fread(&value, sizeof(Standard_Byte), 1, f) == 1) - { - // Set value - if (!((Standard_Byte**)((Voxel_DS*)myBoolVoxels)->myData)[i1]) - { - ((Standard_Byte**)((Voxel_DS*)myBoolVoxels)->myData)[i1] = - (Standard_Byte*) calloc(8/*number of bytes in slice*/, sizeof(Standard_Byte)); - } - (((Standard_Byte**)((Voxel_DS*)myBoolVoxels)->myData)[i1])[i2] = (Standard_Byte)value; - } - } - - fclose(f); - return Standard_True; -} - -Standard_Boolean Voxel_Reader::ReadColorBinaryVoxels(const TCollection_ExtendedString& file) -{ - // Open file for reading - FILE* f = OSD_OpenFile(file, "r"); - if (!f) - return Standard_False; - - // Header: skip it - Standard_Character line[65]; - if (fgets(line, 64, f) == NULL) - { - fclose(f); - return Standard_False; - } - - // Location, size, number of splits - Standard_Integer nbx = 0, nby = 0, nbz = 0; - Standard_Real x = 0.0, y = 0.0, z = 0.0, xlen = 0.0, ylen = 0.0, zlen = 0.0; - if (fread(&x, sizeof(Standard_Real), 1, f) != 1 - || fread(&y, sizeof(Standard_Real), 1, f) != 1 - || fread(&z, sizeof(Standard_Real), 1, f) != 1 - || fread(&xlen, sizeof(Standard_Real), 1, f) != 1 - || fread(&ylen, sizeof(Standard_Real), 1, f) != 1 - || fread(&zlen, sizeof(Standard_Real), 1, f) != 1 - || fread(&nbx, sizeof(Standard_Integer), 1, f) != 1 - || fread(&nby, sizeof(Standard_Integer), 1, f) != 1 - || fread(&nbz, sizeof(Standard_Integer), 1, f) != 1) - { - fclose(f); - return Standard_False; - } - - // Allocate the voxels - myColorVoxels = (Standard_Address) new Voxel_ColorDS(x, y, z, xlen, ylen, zlen, nbx, nby, nbz); - - // Data - // Copied from Voxel_ColorDS.cxx: - Standard_Integer nb_bytes = RealToInt(ceil(nbx * nby * nbz / 2.0)); - Standard_Integer nb_slices = RealToInt(ceil(nb_bytes / 32.0)); - // myData[0 .. nb_slices - 1][0 .. 31] - if (nb_slices) - { - Standard_Integer i1 = 0, i2 = 0, value = 0; - while (!feof(f) - && fread(&i1, sizeof(Standard_Integer), 1, f) == 1 - && fread(&i2, sizeof(Standard_Integer), 1, f) == 1 - && fread(&value, sizeof(Standard_Byte), 1, f) == 1) - { - // Set value - if (!((Standard_Byte**)((Voxel_DS*)myColorVoxels)->myData)[i1]) - { - ((Standard_Byte**)((Voxel_DS*)myColorVoxels)->myData)[i1] = - (Standard_Byte*) calloc(32/*number of bytes in slice*/, sizeof(Standard_Byte)); - } - (((Standard_Byte**)((Voxel_DS*)myColorVoxels)->myData)[i1])[i2] = (Standard_Byte)value; - } - } - - fclose(f); - return Standard_True; -} - -Standard_Boolean Voxel_Reader::ReadFloatBinaryVoxels(const TCollection_ExtendedString& file) -{ - // Open file for reading - FILE* f = OSD_OpenFile(file, "r"); - if (!f) - return Standard_False; - - // Header: skip it - Standard_Character line[65]; - if (fgets(line, 64, f) == NULL) - { - fclose(f); - return Standard_False; - } - - // Location, size, number of splits - Standard_Integer nbx = 0, nby = 0, nbz = 0; - Standard_Real x = 0.0, y = 0.0, z = 0.0, xlen = 0.0, ylen = 0.0, zlen = 0.0; - if (fread(&x, sizeof(Standard_Real), 1, f) != 1 - || fread(&y, sizeof(Standard_Real), 1, f) != 1 - || fread(&z, sizeof(Standard_Real), 1, f) != 1 - || fread(&xlen, sizeof(Standard_Real), 1, f) != 1 - || fread(&ylen, sizeof(Standard_Real), 1, f) != 1 - || fread(&zlen, sizeof(Standard_Real), 1, f) != 1 - || fread(&nbx, sizeof(Standard_Integer), 1, f) != 1 - || fread(&nby, sizeof(Standard_Integer), 1, f) != 1 - || fread(&nbz, sizeof(Standard_Integer), 1, f) != 1) - { - fclose(f); - return Standard_False; - } - - // Allocate the voxels - myFloatVoxels = (Standard_Address) new Voxel_FloatDS(x, y, z, xlen, ylen, zlen, nbx, nby, nbz); - - // Data - // Copied from Voxel_FloatDS.cxx: - Standard_Integer nb_floats = nbx * nby * nbz; - Standard_Integer nb_slices = RealToInt(ceil(nb_floats / 32.0)); // 32 values in 1 slice - // myData[0 .. nb_slices - 1][0 .. 31] - if (nb_slices) - { - Standard_Integer i1 = 0, i2 = 0; - Standard_ShortReal value = 0.0; - while (!feof(f) - && fread(&i1, sizeof(Standard_Integer), 1, f) == 1 - && fread(&i2, sizeof(Standard_Integer), 1, f) == 1 - && fread(&value, sizeof(Standard_ShortReal), 1, f) == 1) - { - // Set value - if (!((Standard_ShortReal**)((Voxel_DS*)myFloatVoxels)->myData)[i1]) - { - ((Standard_ShortReal**)((Voxel_DS*)myFloatVoxels)->myData)[i1] = - (Standard_ShortReal*) calloc(32/*number of floats in slice*/, sizeof(Standard_ShortReal)); - } - (((Standard_ShortReal**)((Voxel_DS*)myFloatVoxels)->myData)[i1])[i2] = value; - } - } - - fclose(f); - return Standard_True; -} diff --git a/src/Voxel/Voxel_Reader.hxx b/src/Voxel/Voxel_Reader.hxx deleted file mode 100644 index 4ea57ad743..0000000000 --- a/src/Voxel/Voxel_Reader.hxx +++ /dev/null @@ -1,105 +0,0 @@ -// Created on: 2008-08-28 -// Created by: Vladislav ROMASHKO -// Copyright (c) 2008-2014 OPEN CASCADE SAS -// -// This file is part of Open CASCADE Technology software library. -// -// This library is free software; you can redistribute it and/or modify it under -// the terms of the GNU Lesser General Public License version 2.1 as published -// by the Free Software Foundation, with special exception defined in the file -// OCCT_LGPL_EXCEPTION.txt. Consult the file LICENSE_LGPL_21.txt included in OCCT -// distribution for complete text of the license and disclaimer of any warranty. -// -// Alternatively, this file may be used under the terms of Open CASCADE -// commercial license or contractual agreement. - -#ifndef _Voxel_Reader_HeaderFile -#define _Voxel_Reader_HeaderFile - -#include -#include -#include - -#include -#include -class TCollection_ExtendedString; - - -//! Reads a cube of voxels from disk. -//! Beware, a caller of the reader is responsible for deletion of the read voxels. -class Voxel_Reader -{ -public: - - DEFINE_STANDARD_ALLOC - - - //! An empty constructor. - Standard_EXPORT Voxel_Reader(); - - //! Reads the voxels from disk - Standard_EXPORT Standard_Boolean Read (const TCollection_ExtendedString& file); - - //! Informs the user about the type of voxels he has read. - Standard_EXPORT Standard_Boolean IsBoolVoxels() const; - - //! Informs the user about the type of voxels he has read. - Standard_EXPORT Standard_Boolean IsColorVoxels() const; - - //! Informs the user about the type of voxels he has read. - Standard_EXPORT Standard_Boolean IsFloatVoxels() const; - - //! Returns a pointer to the read 1bit voxels. - Standard_EXPORT Standard_Address GetBoolVoxels() const; - - //! Returns a pointer to the read 4bit voxels. - Standard_EXPORT Standard_Address GetColorVoxels() const; - - //! Returns a pointer to the read 4bytes voxels. - Standard_EXPORT Standard_Address GetFloatVoxels() const; - - - - -protected: - - - - - -private: - - - //! Reads 1bit voxels from disk in ASCII format. - Standard_EXPORT Standard_Boolean ReadBoolAsciiVoxels (const TCollection_ExtendedString& file); - - //! Reads 4bit voxels from disk in ASCII format. - Standard_EXPORT Standard_Boolean ReadColorAsciiVoxels (const TCollection_ExtendedString& file); - - //! Reads 4bytes voxels from disk in ASCII format. - Standard_EXPORT Standard_Boolean ReadFloatAsciiVoxels (const TCollection_ExtendedString& file); - - //! Reads 1bit voxels from disk in BINARY format. - Standard_EXPORT Standard_Boolean ReadBoolBinaryVoxels (const TCollection_ExtendedString& file); - - //! Reads 4bit voxels from disk in BINARY format. - Standard_EXPORT Standard_Boolean ReadColorBinaryVoxels (const TCollection_ExtendedString& file); - - //! Reads 4bytes voxels from disk in BINARY format. - Standard_EXPORT Standard_Boolean ReadFloatBinaryVoxels (const TCollection_ExtendedString& file); - - - Standard_Address myBoolVoxels; - Standard_Address myColorVoxels; - Standard_Address myFloatVoxels; - - -}; - - - - - - - -#endif // _Voxel_Reader_HeaderFile diff --git a/src/Voxel/Voxel_Selector.cxx b/src/Voxel/Voxel_Selector.cxx deleted file mode 100644 index 1d01cd5115..0000000000 --- a/src/Voxel/Voxel_Selector.cxx +++ /dev/null @@ -1,581 +0,0 @@ -// Created on: 2008-07-30 -// Created by: Vladislav ROMASHKO -// Copyright (c) 2008-2014 OPEN CASCADE SAS -// -// This file is part of Open CASCADE Technology software library. -// -// This library is free software; you can redistribute it and/or modify it under -// the terms of the GNU Lesser General Public License version 2.1 as published -// by the Free Software Foundation, with special exception defined in the file -// OCCT_LGPL_EXCEPTION.txt. Consult the file LICENSE_LGPL_21.txt included in OCCT -// distribution for complete text of the license and disclaimer of any warranty. -// -// Alternatively, this file may be used under the terms of Open CASCADE -// commercial license or contractual agreement. - - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -Voxel_Selector::Voxel_Selector():myVoxels(0) -{ - -} - -Voxel_Selector::Voxel_Selector(const Handle(V3d_View)& view):myView(view),myVoxels(0) -{ - -} - -void Voxel_Selector::Init(const Handle(V3d_View)& view) -{ - myView = view; -} - -void Voxel_Selector::SetVoxels(const Voxel_BoolDS& voxels) -{ - myIsBool = 1; - myVoxels = (void*) &voxels; -} - -void Voxel_Selector::SetVoxels(const Voxel_ColorDS& voxels) -{ - myIsBool = 0; - myVoxels = (void*) &voxels; -} - -void Voxel_Selector::SetVoxels(const Voxel_ROctBoolDS& voxels) -{ - myIsBool = 2; - myVoxels = (void*) &voxels; -} - -// This function is copied from ViewerTest_RelationCommands.cxx -static Standard_Boolean ComputeIntersection(const gp_Lin& L,const gp_Pln& ThePl, gp_Pnt& TheInter) -{ - static IntAna_Quadric TheQuad; - TheQuad.SetQuadric(ThePl); - static IntAna_IntConicQuad QQ; - QQ.Perform(L,TheQuad); - if(QQ.IsDone()){ - if(QQ.NbPoints()>0){ - TheInter = QQ.Point(1); - return Standard_True; - } - } - return Standard_False; -} - -static inline Standard_Integer GetIVoxel(const Standard_Integer ix, const Standard_Integer iy, const Standard_Integer iz, - const Standard_Integer nbx, const Standard_Integer nbxy) -{ - return ix + iy * nbx + iz * nbxy; -} - -static inline Standard_Boolean Get(const Standard_Address voxels, const Standard_Integer isBool, - const Standard_Integer ix, const Standard_Integer iy, const Standard_Integer iz) -{ - switch (isBool) - { - case 0: - return ((Voxel_ColorDS*) voxels)->Get(ix, iy, iz) > 0; - case 1: - return ((Voxel_BoolDS*) voxels)->Get(ix, iy, iz); - case 2: - { - Standard_Integer deepness = ((Voxel_ROctBoolDS*) voxels)->Deepness(ix, iy, iz); - switch (deepness) - { - case 0: - return ((Voxel_ROctBoolDS*) voxels)->Get(ix, iy, iz); - case 1: - { - for (Standard_Integer i = 0; i < 8; i++) - { - if (((Voxel_ROctBoolDS*) voxels)->Get(ix, iy, iz, i) == Standard_True) - return Standard_True; - } - break; - } - case 2: - { - for (Standard_Integer i = 0; i < 8; i++) - { - for (Standard_Integer j = 0; j < 8; j++) - { - if (((Voxel_ROctBoolDS*) voxels)->Get(ix, iy, iz, i, j) == Standard_True) - return Standard_True; - } - } - break; - } - } - } - } - return Standard_False; -} - -Standard_Boolean Voxel_Selector::Detect(const Standard_Integer winx, const Standard_Integer winy, - Standard_Integer& ixdetect, Standard_Integer& iydetect, Standard_Integer& izdetect) -{ - ixdetect = -1; iydetect = -1; izdetect = -1; - if (myView.IsNull() || !myVoxels) - return Standard_False; - - Voxel_DS* ds = 0; - switch (myIsBool) - { - case 0: - ds = (Voxel_ColorDS*) myVoxels; - break; - case 1: - ds = (Voxel_BoolDS*) myVoxels; - break; - case 2: - ds = (Voxel_ROctBoolDS*) myVoxels; - break; - } - Standard_Integer nbx = ds->GetNbX(), nby = ds->GetNbY(), nbz = ds->GetNbZ(), nbxy = nbx * nby; - - // Construct a line perpendicular to the screen - Standard_Real eyex, eyey, eyez, nx, ny, nz; - myView->Convert(winx, winy, eyex, eyey, eyez); - myView->Proj(nx, ny, nz); - gp_Pnt peye(eyex, eyey, eyez); - gp_Lin line(peye, gp_Dir(nx, ny, nz)); - - // Find the first voxel meeting the line at entrance to the cube of voxels. - // Construct planes of the cube of voxels - Standard_Real xstart = ds->GetX(), ystart = ds->GetY(), zstart = ds->GetZ(); - Standard_Real xlen = ds->GetXLen(), ylen = ds->GetYLen(), zlen = ds->GetZLen(); - Standard_Real xend = xstart + xlen, yend = ystart + ylen, zend = zstart + zlen; - gp_Pln xplane_minus(gp_Pnt(xstart, ystart, zstart), -gp::DX()); - gp_Pln xplane_plus (gp_Pnt(xend, ystart, zstart), gp::DX()); - gp_Pln yplane_minus(gp_Pnt(xstart, ystart, zstart), -gp::DY()); - gp_Pln yplane_plus (gp_Pnt(xstart, yend, zstart), gp::DY()); - gp_Pln zplane_minus(gp_Pnt(xstart, ystart, zstart), -gp::DZ()); - gp_Pln zplane_plus (gp_Pnt(xstart, ystart, zend), gp::DZ()); - // Intersect the planes with the line. - gp_Pnt pintersection, p; - Standard_Real depth = DBL_MAX, d; - Standard_Integer iplane = -1; // not found - if (ComputeIntersection(line, xplane_minus, p)) // -X - { - if (p.Y() >= ystart && p.Y() <= yend && - p.Z() >= zstart && p.Z() <= zend) - { - p.SetX(xstart); - depth = peye.SquareDistance(p); - iplane = 0; - pintersection = p; - } - } - if (ComputeIntersection(line, xplane_plus, p)) // +X - { - if (p.Y() >= ystart && p.Y() <= yend && - p.Z() >= zstart && p.Z() <= zend) - { - d = peye.SquareDistance(p); - if (d < depth) - { - p.SetX(xend); - depth = d; - iplane = 1; - pintersection = p; - } - } - } - if (ComputeIntersection(line, yplane_minus, p)) // -Y - { - if (p.X() >= xstart && p.X() <= xend && - p.Z() >= zstart && p.Z() <= zend) - { - d = peye.SquareDistance(p); - if (d < depth) - { - p.SetY(ystart); - depth = d; - iplane = 2; - pintersection = p; - } - } - } - if (ComputeIntersection(line, yplane_plus, p)) // +Y - { - if (p.X() >= xstart && p.X() <= xend && - p.Z() >= zstart && p.Z() <= zend) - { - d = peye.SquareDistance(p); - if (d < depth) - { - p.SetY(yend); - depth = d; - iplane = 3; - pintersection = p; - } - } - } - if (ComputeIntersection(line, zplane_minus, p)) // -Z - { - if (p.X() >= xstart && p.X() <= xend && - p.Y() >= ystart && p.Y() <= yend) - { - d = peye.SquareDistance(p); - if (d < depth) - { - p.SetZ(zstart); - depth = d; - iplane = 4; - pintersection = p; - } - } - } - if (ComputeIntersection(line, zplane_plus, p)) // +Z - { - if (p.X() >= xstart && p.X() <= xend && - p.Y() >= ystart && p.Y() <= yend) - { - d = peye.SquareDistance(p); - if (d < depth) - { - p.SetZ(zend); - depth = d; - iplane = 5; - pintersection = p; - } - } - } - // Find the voxel on the detected plane - if (iplane == -1) - return Standard_False; - Standard_Integer ix, iy, iz; - if (!ds->GetVoxel(pintersection.X(), pintersection.Y(), pintersection.Z(), ix, iy, iz)) - return Standard_False; - ixdetect = ix; iydetect = iy; izdetect = iz; - - // Find a non-zero voxel at the line - Standard_Real xmin = xlen / Standard_Real(nbx), - ymin = ylen / Standard_Real(nby), - zmin = zlen / Standard_Real(nbz), - vmin = sqrt(xmin * xmin + ymin * ymin + zmin * zmin) / 2.0; - Standard_Real xc, yc, zc, dist, distmin = DBL_MAX; - TColStd_MapOfInteger passed; - while (!Get(myVoxels, myIsBool, ixdetect, iydetect, izdetect)) - { - // Memorize already checked voxels - if (!passed.Add(GetIVoxel(ixdetect, iydetect, izdetect, nbx, nbxy))) - return Standard_False; - - distmin = DBL_MAX; - ix = ixdetect; iy = iydetect; iz = izdetect; - - //1: -X neighbour - if (ix - 1 >= 0 && !passed.Contains(GetIVoxel(ix - 1, iy, iz, nbx, nbxy))) - { - ds->GetCenter(ix - 1, iy, iz, xc, yc, zc); - dist = line.Distance(gp_Pnt(xc, yc, zc)); - if (dist < vmin && dist < distmin) - { - ixdetect = ix - 1; iydetect = iy; izdetect = iz; - distmin = dist; - } - } - //2: +X neighbour - if (ix + 1 < nbx && !passed.Contains(GetIVoxel(ix + 1, iy, iz, nbx, nbxy))) - { - ds->GetCenter(ix + 1, iy, iz, xc, yc, zc); - dist = line.Distance(gp_Pnt(xc, yc, zc)); - if (dist < vmin && dist < distmin) - { - ixdetect = ix + 1; iydetect = iy; izdetect = iz; - distmin = dist; - } - } - //3: -Y neighbour - if (iy - 1 >= 0 && !passed.Contains(GetIVoxel(ix, iy - 1, iz, nbx, nbxy))) - { - ds->GetCenter(ix, iy - 1, iz, xc, yc, zc); - dist = line.Distance(gp_Pnt(xc, yc, zc)); - if (dist < vmin && dist < distmin) - { - ixdetect = ix; iydetect = iy - 1; izdetect = iz; - distmin = dist; - } - } - //4: +Y neighbour - if (iy + 1 < nby && !passed.Contains(GetIVoxel(ix, iy + 1, iz, nbx, nbxy))) - { - ds->GetCenter(ix, iy + 1, iz, xc, yc, zc); - dist = line.Distance(gp_Pnt(xc, yc, zc)); - if (dist < vmin && dist < distmin) - { - ixdetect = ix; iydetect = iy + 1; izdetect = iz; - distmin = dist; - } - } - //5: -Z neighbour - if (iz - 1 >= 0 && !passed.Contains(GetIVoxel(ix, iy, iz - 1, nbx, nbxy))) - { - ds->GetCenter(ix, iy, iz - 1, xc, yc, zc); - dist = line.Distance(gp_Pnt(xc, yc, zc)); - if (dist < vmin && dist < distmin) - { - ixdetect = ix; iydetect = iy; izdetect = iz - 1; - distmin = dist; - } - } - //6: +Z neighbour - if (iz + 1 < nbz && !passed.Contains(GetIVoxel(ix, iy, iz + 1, nbx, nbxy))) - { - ds->GetCenter(ix, iy, iz + 1, xc, yc, zc); - dist = line.Distance(gp_Pnt(xc, yc, zc)); - if (dist < vmin && dist < distmin) - { - ixdetect = ix; iydetect = iy; izdetect = iz + 1; - distmin = dist; - } - } - - // Diagonal voxels - //7: -X-Y neighbour - if (ix - 1 >= 0 && iy - 1 >= 0 && !passed.Contains(GetIVoxel(ix - 1, iy - 1, iz, nbx, nbxy))) - { - ds->GetCenter(ix - 1, iy - 1, iz, xc, yc, zc); - dist = line.Distance(gp_Pnt(xc, yc, zc)); - if (dist < vmin && dist < distmin) - { - ixdetect = ix - 1; iydetect = iy - 1; izdetect = iz; - distmin = dist; - } - } - //8: -X-Z neighbour - if (ix - 1 >= 0 && iz - 1 >= 0 && !passed.Contains(GetIVoxel(ix - 1, iy, iz - 1, nbx, nbxy))) - { - ds->GetCenter(ix - 1, iy, iz - 1, xc, yc, zc); - dist = line.Distance(gp_Pnt(xc, yc, zc)); - if (dist < vmin && dist < distmin) - { - ixdetect = ix - 1; iydetect = iy; izdetect = iz - 1; - distmin = dist; - } - } - //9: -Y-Z neighbour - if (iy - 1 >= 0 && iz - 1 >= 0 && !passed.Contains(GetIVoxel(ix, iy - 1, iz - 1, nbx, nbxy))) - { - ds->GetCenter(ix, iy - 1, iz - 1, xc, yc, zc); - dist = line.Distance(gp_Pnt(xc, yc, zc)); - if (dist < vmin && dist < distmin) - { - ixdetect = ix; iydetect = iy - 1; izdetect = iz - 1; - distmin = dist; - } - } - - //10: +X-Y neighbour - if (ix + 1 < nbx && iy - 1 >= 0 && !passed.Contains(GetIVoxel(ix + 1, iy - 1, iz, nbx, nbxy))) - { - ds->GetCenter(ix + 1, iy - 1, iz, xc, yc, zc); - dist = line.Distance(gp_Pnt(xc, yc, zc)); - if (dist < vmin && dist < distmin) - { - ixdetect = ix + 1; iydetect = iy - 1; izdetect = iz; - distmin = dist; - } - } - //11: +X-Z neighbour - if (ix + 1 < nbx && iz - 1 >= 0 && !passed.Contains(GetIVoxel(ix + 1, iy, iz - 1, nbx, nbxy))) - { - ds->GetCenter(ix + 1, iy, iz - 1, xc, yc, zc); - dist = line.Distance(gp_Pnt(xc, yc, zc)); - if (dist < vmin && dist < distmin) - { - ixdetect = ix + 1; iydetect = iy; izdetect = iz - 1; - distmin = dist; - } - } - //12: +Y-Z neighbour - if (iy + 1 < nby && iz - 1 >= 0 && !passed.Contains(GetIVoxel(ix, iy + 1, iz - 1, nbx, nbxy))) - { - ds->GetCenter(ix, iy + 1, iz - 1, xc, yc, zc); - dist = line.Distance(gp_Pnt(xc, yc, zc)); - if (dist < vmin && dist < distmin) - { - ixdetect = ix; iydetect = iy + 1; izdetect = iz - 1; - distmin = dist; - } - } - - //13: -X+Y neighbour - if (ix - 1 >= 0 && iy + 1 < nby && !passed.Contains(GetIVoxel(ix - 1, iy + 1, iz, nbx, nbxy))) - { - ds->GetCenter(ix - 1, iy + 1, iz, xc, yc, zc); - dist = line.Distance(gp_Pnt(xc, yc, zc)); - if (dist < vmin && dist < distmin) - { - ixdetect = ix - 1; iydetect = iy + 1; izdetect = iz; - distmin = dist; - } - } - //14: -X+Z neighbour - if (ix - 1 >= 0 && iz + 1 < nbz && !passed.Contains(GetIVoxel(ix - 1, iy, iz + 1, nbx, nbxy))) - { - ds->GetCenter(ix - 1, iy, iz + 1, xc, yc, zc); - dist = line.Distance(gp_Pnt(xc, yc, zc)); - if (dist < vmin && dist < distmin) - { - ixdetect = ix - 1; iydetect = iy; izdetect = iz + 1; - distmin = dist; - } - } - //15: -Y+Z neighbour - if (iy - 1 >= 0 && iz + 1 < nbz && !passed.Contains(GetIVoxel(ix, iy - 1, iz + 1, nbx, nbxy))) - { - ds->GetCenter(ix, iy - 1, iz + 1, xc, yc, zc); - dist = line.Distance(gp_Pnt(xc, yc, zc)); - if (dist < vmin && dist < distmin) - { - ixdetect = ix; iydetect = iy - 1; izdetect = iz + 1; - distmin = dist; - } - } - - //16: +X+Y neighbour - if (ix + 1 < nbx && iy + 1 < nby && !passed.Contains(GetIVoxel(ix + 1, iy + 1, iz, nbx, nbxy))) - { - ds->GetCenter(ix + 1, iy + 1, iz, xc, yc, zc); - dist = line.Distance(gp_Pnt(xc, yc, zc)); - if (dist < vmin && dist < distmin) - { - ixdetect = ix + 1; iydetect = iy + 1; izdetect = iz; - distmin = dist; - } - } - //17: +X+Z neighbour - if (ix + 1 < nbx && iz + 1 < nbz && !passed.Contains(GetIVoxel(ix + 1, iy, iz + 1, nbx, nbxy))) - { - ds->GetCenter(ix + 1, iy, iz + 1, xc, yc, zc); - dist = line.Distance(gp_Pnt(xc, yc, zc)); - if (dist < vmin && dist < distmin) - { - ixdetect = ix + 1; iydetect = iy; izdetect = iz + 1; - distmin = dist; - } - } - //18: +Y+Z neighbour - if (iy + 1 < nby && iz + 1 < nbz && !passed.Contains(GetIVoxel(ix, iy + 1, iz + 1, nbx, nbxy))) - { - ds->GetCenter(ix, iy + 1, iz + 1, xc, yc, zc); - dist = line.Distance(gp_Pnt(xc, yc, zc)); - if (dist < vmin && dist < distmin) - { - ixdetect = ix; iydetect = iy + 1; izdetect = iz + 1; - distmin = dist; - } - } - - // Farest neighbours - //19: -X-Y-Z neighbour - if (ix - 1 >= 0 && iy - 1 >= 0 && iz - 1 >= 0 && !passed.Contains(GetIVoxel(ix - 1, iy - 1, iz - 1, nbx, nbxy))) - { - ds->GetCenter(ix - 1, iy - 1, iz - 1, xc, yc, zc); - dist = line.Distance(gp_Pnt(xc, yc, zc)); - if (dist < vmin && dist < distmin) - { - ixdetect = ix - 1; iydetect = iy - 1; izdetect = iz - 1; - distmin = dist; - } - } - //20: +X-Y-Z neighbour - if (ix + 1 < nbx && iy - 1 >= 0 && iz - 1 >= 0 && !passed.Contains(GetIVoxel(ix + 1, iy - 1, iz - 1, nbx, nbxy))) - { - ds->GetCenter(ix + 1, iy - 1, iz - 1, xc, yc, zc); - dist = line.Distance(gp_Pnt(xc, yc, zc)); - if (dist < vmin && dist < distmin) - { - ixdetect = ix + 1; iydetect = iy - 1; izdetect = iz - 1; - distmin = dist; - } - } - //21: -X+Y-Z neighbour - if (ix - 1 >= 0 && iy + 1 < nby && iz - 1 >= 0 && !passed.Contains(GetIVoxel(ix - 1, iy + 1, iz - 1, nbx, nbxy))) - { - ds->GetCenter(ix - 1, iy + 1, iz - 1, xc, yc, zc); - dist = line.Distance(gp_Pnt(xc, yc, zc)); - if (dist < vmin && dist < distmin) - { - ixdetect = ix - 1; iydetect = iy + 1; izdetect = iz - 1; - distmin = dist; - } - } - //22: -X-Y+Z neighbour - if (ix - 1 >= 0 && iy - 1 >= 0 && iz + 1 < nbz && !passed.Contains(GetIVoxel(ix - 1, iy - 1, iz + 1, nbx, nbxy))) - { - ds->GetCenter(ix - 1, iy - 1, iz + 1, xc, yc, zc); - dist = line.Distance(gp_Pnt(xc, yc, zc)); - if (dist < vmin && dist < distmin) - { - ixdetect = ix - 1; iydetect = iy - 1; izdetect = iz + 1; - distmin = dist; - } - } - //23: +X+Y-Z neighbour - if (ix + 1 < nbx && iy + 1 < nby && iz - 1 >= 0 && !passed.Contains(GetIVoxel(ix + 1, iy + 1, iz - 1, nbx, nbxy))) - { - ds->GetCenter(ix + 1, iy + 1, iz - 1, xc, yc, zc); - dist = line.Distance(gp_Pnt(xc, yc, zc)); - if (dist < vmin && dist < distmin) - { - ixdetect = ix + 1; iydetect = iy + 1; izdetect = iz - 1; - distmin = dist; - } - } - //24: +X-Y+Z neighbour - if (ix + 1 < nbx && iy - 1 >= 0 && iz + 1 < nbz && !passed.Contains(GetIVoxel(ix + 1, iy - 1, iz + 1, nbx, nbxy))) - { - ds->GetCenter(ix + 1, iy - 1, iz + 1, xc, yc, zc); - dist = line.Distance(gp_Pnt(xc, yc, zc)); - if (dist < vmin && dist < distmin) - { - ixdetect = ix + 1; iydetect = iy - 1; izdetect = iz + 1; - distmin = dist; - } - } - //25: -X+Y+Z neighbour - if (ix - 1 >= 0 && iy + 1 < nby && iz + 1 < nbz && !passed.Contains(GetIVoxel(ix - 1, iy + 1, iz + 1, nbx, nbxy))) - { - ds->GetCenter(ix - 1, iy + 1, iz + 1, xc, yc, zc); - dist = line.Distance(gp_Pnt(xc, yc, zc)); - if (dist < vmin && dist < distmin) - { - ixdetect = ix - 1; iydetect = iy + 1; izdetect = iz + 1; - distmin = dist; - } - } - //26: +X+Y+Z neighbour - if (ix + 1 < nbx && iy + 1 < nby && iz + 1 < nbz && !passed.Contains(GetIVoxel(ix + 1, iy + 1, iz + 1, nbx, nbxy))) - { - ds->GetCenter(ix + 1, iy + 1, iz + 1, xc, yc, zc); - dist = line.Distance(gp_Pnt(xc, yc, zc)); - if (dist < vmin && dist < distmin) - { - ixdetect = ix + 1; iydetect = iy + 1; izdetect = iz + 1; - distmin = dist; - } - } - - } // End of while (zero-voxel... - - if (!Get(myVoxels, myIsBool, ixdetect, iydetect, izdetect)) - return Standard_False; - return Standard_True; -} diff --git a/src/Voxel/Voxel_Selector.hxx b/src/Voxel/Voxel_Selector.hxx deleted file mode 100644 index 9234c56199..0000000000 --- a/src/Voxel/Voxel_Selector.hxx +++ /dev/null @@ -1,90 +0,0 @@ -// Created on: 2008-07-30 -// Created by: Vladislav ROMASHKO -// Copyright (c) 2008-2014 OPEN CASCADE SAS -// -// This file is part of Open CASCADE Technology software library. -// -// This library is free software; you can redistribute it and/or modify it under -// the terms of the GNU Lesser General Public License version 2.1 as published -// by the Free Software Foundation, with special exception defined in the file -// OCCT_LGPL_EXCEPTION.txt. Consult the file LICENSE_LGPL_21.txt included in OCCT -// distribution for complete text of the license and disclaimer of any warranty. -// -// Alternatively, this file may be used under the terms of Open CASCADE -// commercial license or contractual agreement. - -#ifndef _Voxel_Selector_HeaderFile -#define _Voxel_Selector_HeaderFile - -#include -#include -#include - -#include -#include -#include -class V3d_View; -class Voxel_BoolDS; -class Voxel_ColorDS; -class Voxel_ROctBoolDS; - - -//! Detects voxels in the viewer 3d under the mouse cursor. -class Voxel_Selector -{ -public: - - DEFINE_STANDARD_ALLOC - - - //! An empty constructor. - Standard_EXPORT Voxel_Selector(); - - //! A constructor of the selector, - //! which initializes the classes - //! by a view, where the user selects the voxels. - Standard_EXPORT Voxel_Selector(const Handle(V3d_View)& view); - - //! Initializes the selector by a view, - //! where the user selects the voxels. - Standard_EXPORT void Init (const Handle(V3d_View)& view); - - //! Defines the voxels (1bit). - Standard_EXPORT void SetVoxels (const Voxel_BoolDS& voxels); - - //! Defines the voxels (4bit). - Standard_EXPORT void SetVoxels (const Voxel_ColorDS& voxels); - - //! Defines the voxels (1bit recursive splitting). - Standard_EXPORT void SetVoxels (const Voxel_ROctBoolDS& voxels); - - //! Detects a voxel under the mouse cursor. - Standard_EXPORT Standard_Boolean Detect (const Standard_Integer winx, const Standard_Integer winy, Standard_Integer& ix, Standard_Integer& iy, Standard_Integer& iz); - - - - -protected: - - - - - -private: - - - - Handle(V3d_View) myView; - Standard_Address myVoxels; - Standard_Integer myIsBool; - - -}; - - - - - - - -#endif // _Voxel_Selector_HeaderFile diff --git a/src/Voxel/Voxel_SplitData.cxx b/src/Voxel/Voxel_SplitData.cxx deleted file mode 100644 index ce59c60309..0000000000 --- a/src/Voxel/Voxel_SplitData.cxx +++ /dev/null @@ -1,32 +0,0 @@ -// Created on: 2008-09-01 -// Created by: Vladislav ROMASHKO -// Copyright (c) 2008-2014 OPEN CASCADE SAS -// -// This file is part of Open CASCADE Technology software library. -// -// This library is free software; you can redistribute it and/or modify it under -// the terms of the GNU Lesser General Public License version 2.1 as published -// by the Free Software Foundation, with special exception defined in the file -// OCCT_LGPL_EXCEPTION.txt. Consult the file LICENSE_LGPL_21.txt included in OCCT -// distribution for complete text of the license and disclaimer of any warranty. -// -// Alternatively, this file may be used under the terms of Open CASCADE -// commercial license or contractual agreement. - - -#include - -Voxel_SplitData::Voxel_SplitData():myValues(0),mySplitData(0) -{ - -} - -Standard_Address& Voxel_SplitData::GetValues() -{ - return myValues; -} - -Standard_Address& Voxel_SplitData::GetSplitData() -{ - return mySplitData; -} diff --git a/src/Voxel/Voxel_SplitData.hxx b/src/Voxel/Voxel_SplitData.hxx deleted file mode 100644 index 28b69fed50..0000000000 --- a/src/Voxel/Voxel_SplitData.hxx +++ /dev/null @@ -1,70 +0,0 @@ -// Created on: 2008-09-01 -// Created by: Vladislav ROMASHKO -// Copyright (c) 2008-2014 OPEN CASCADE SAS -// -// This file is part of Open CASCADE Technology software library. -// -// This library is free software; you can redistribute it and/or modify it under -// the terms of the GNU Lesser General Public License version 2.1 as published -// by the Free Software Foundation, with special exception defined in the file -// OCCT_LGPL_EXCEPTION.txt. Consult the file LICENSE_LGPL_21.txt included in OCCT -// distribution for complete text of the license and disclaimer of any warranty. -// -// Alternatively, this file may be used under the terms of Open CASCADE -// commercial license or contractual agreement. - -#ifndef _Voxel_SplitData_HeaderFile -#define _Voxel_SplitData_HeaderFile - -#include -#include -#include - -#include - - -//! A container of split information. -//! An instance of this class is used as a slice -//! in inner representation of recursive octtree voxels. -class Voxel_SplitData -{ -public: - - DEFINE_STANDARD_ALLOC - - - //! An empty constructor. - Standard_EXPORT Voxel_SplitData(); - - //! Gives access to the values. - Standard_EXPORT Standard_Address& GetValues(); - - //! Gives access to the next split data. - Standard_EXPORT Standard_Address& GetSplitData(); - - - - -protected: - - - - - -private: - - - - Standard_Address myValues; - Standard_Address mySplitData; - - -}; - - - - - - - -#endif // _Voxel_SplitData_HeaderFile diff --git a/src/Voxel/Voxel_TypeDef.hxx b/src/Voxel/Voxel_TypeDef.hxx deleted file mode 100644 index b49b53597b..0000000000 --- a/src/Voxel/Voxel_TypeDef.hxx +++ /dev/null @@ -1,53 +0,0 @@ -// Copyright (c) 1999-2014 OPEN CASCADE SAS -// -// This file is part of Open CASCADE Technology software library. -// -// This library is free software; you can redistribute it and/or modify it under -// the terms of the GNU Lesser General Public License version 2.1 as published -// by the Free Software Foundation, with special exception defined in the file -// OCCT_LGPL_EXCEPTION.txt. Consult the file LICENSE_LGPL_21.txt included in OCCT -// distribution for complete text of the license and disclaimer of any warranty. -// -// Alternatively, this file may be used under the terms of Open CASCADE -// commercial license or contractual agreement. - -#ifndef _VOXEL_TYPEDEF_HXX_ -#define _VOXEL_TYPEDEF_HXX_ - -// Basic data types - -typedef struct -{ - Standard_Integer ix; - Standard_Integer iy; - Standard_Integer iz; -} iXYZ; - - -// Maps and Tables - -inline Standard_Integer HashCode(const iXYZ& me, const Standard_Integer upper) -{ - return (Abs(me.ix + me.iy + me.iz) % upper) + 1; -} - -inline Standard_Boolean IsEqual(const iXYZ& one, const iXYZ& two) -{ - return one.ix == two.ix && one.iy == two.iy && one.iz == two.iz; -} - -#include - -typedef NCollection_DataMap iXYZIndex; -typedef NCollection_DataMap iXYZBool; - -// Defines - -#define VOXELS "Voxels" -#define ASCII "Ascii" -#define BINARY "Binary" -#define BOOL "Bool" -#define COLOR "Color" -#define FLOAT "Float" - -#endif // _VOXEL_TYPEDEF_HXX_ diff --git a/src/Voxel/Voxel_VisData.h b/src/Voxel/Voxel_VisData.h deleted file mode 100644 index 80e573377a..0000000000 --- a/src/Voxel/Voxel_VisData.h +++ /dev/null @@ -1,129 +0,0 @@ -/* - Copyright (c) 1999-2014 OPEN CASCADE SAS - - This file is part of Open CASCADE Technology software library. - - This library is free software; you can redistribute it and/or modify it under - the terms of the GNU Lesser General Public License version 2.1 as published - by the Free Software Foundation, with special exception defined in the file - OCCT_LGPL_EXCEPTION.txt. Consult the file LICENSE_LGPL_21.txt included in OCCT - distribution for complete text of the license and disclaimer of any warranty. - - Alternatively, this file may be used under the terms of Open CASCADE - commercial license or contractual agreement. -*/ - -#ifndef _VOXEL_VISDATA_H_ -#define _VOXEL_VISDATA_H_ - -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include - -enum VoxelDirection -{ - None, - Xminus, - Xplus, - Yminus, - Yplus, - Zminus, - Zplus -}; - -typedef struct -{ - - /* Display mode */ - Voxel_VoxelDisplayMode myDisplayMode; - - /* Range of displayed values */ - /* BoolDS */ - /* No range */ - /* ColorDS */ - Standard_Byte myColorMinValue; - Standard_Byte myColorMaxValue; - - /* Range of displayed size */ - Standard_Real myDisplayedXMin; - Standard_Real myDisplayedXMax; - Standard_Real myDisplayedYMin; - Standard_Real myDisplayedYMax; - Standard_Real myDisplayedZMin; - Standard_Real myDisplayedZMax; - - /* Colors */ - Quantity_Color myColor; - Handle(Quantity_HArray1OfColor) myColors; - - /* Size, width... */ - Standard_Real myPointSize; - Standard_Integer myQuadrangleSize; /* 0% .. 100% */ - Standard_Byte mySmoothPoints; - - /* Transparency */ - Standard_Real myTransparency; - - /* GL lists of each display mode */ - /* BoolDS */ - /* POINTS */ - Standard_Integer myBoolPointsList; - Standard_Byte myBoolPointsFirst; - /* NEAREST POINTS */ - Standard_Integer myBoolNearestPointsList[7]; - Standard_Byte myBoolNearestPointsFirst; - /* ColorDS */ - /* POINTS */ - Standard_Integer myColorPointsList; - Standard_Byte myColorPointsFirst; - /* NEAREST POINTS */ - Standard_Integer myColorNearestPointsList[7]; - Standard_Byte myColorNearestPointsFirst; - /* ROctBoolDS */ - /* POINTS */ - Standard_Integer myROctBoolPointsList; - Standard_Byte myROctBoolPointsFirst; - /* NEAREST POINTS */ - Standard_Integer myROctBoolNearestPointsList[7]; - Standard_Byte myROctBoolNearestPointsFirst; - /* TRIANGULATION */ - Standard_Integer myTriangulationList; - /* Usage of GL lists */ - Standard_Byte myUsageOfGLlists; - - /* Degenerate mode */ - Standard_Byte myDegenerateMode; - - /* Highlighted voxel */ - Standard_Integer myHighlightx; - Standard_Integer myHighlighty; - Standard_Integer myHighlightz; - -} DisplayData; - -typedef struct -{ - - // Voxels - Voxel_BoolDS* myBoolVoxels; - Voxel_ColorDS* myColorVoxels; - Voxel_ROctBoolDS* myROctBoolVoxels; - - // Triangulation - Handle(Poly_Triangulation) myTriangulation; - Handle(TColgp_HArray1OfDir) myNormalsOfNodes; - - // Display - DisplayData myDisplay; - -} Voxel_VisData; - -#endif // _VOXEL_VISDATA_H_ diff --git a/src/Voxel/Voxel_VoxelDisplayMode.hxx b/src/Voxel/Voxel_VoxelDisplayMode.hxx deleted file mode 100644 index 1d3ddb8678..0000000000 --- a/src/Voxel/Voxel_VoxelDisplayMode.hxx +++ /dev/null @@ -1,28 +0,0 @@ -// Created on: 2008-05-04 -// Created by: Vladislav ROMASHKO -// Copyright (c) 2008-2014 OPEN CASCADE SAS -// -// This file is part of Open CASCADE Technology software library. -// -// This library is free software; you can redistribute it and/or modify it under -// the terms of the GNU Lesser General Public License version 2.1 as published -// by the Free Software Foundation, with special exception defined in the file -// OCCT_LGPL_EXCEPTION.txt. Consult the file LICENSE_LGPL_21.txt included in OCCT -// distribution for complete text of the license and disclaimer of any warranty. -// -// Alternatively, this file may be used under the terms of Open CASCADE -// commercial license or contractual agreement. - -#ifndef _Voxel_VoxelDisplayMode_HeaderFile -#define _Voxel_VoxelDisplayMode_HeaderFile - - -enum Voxel_VoxelDisplayMode -{ -Voxel_VDM_POINTS, -Voxel_VDM_NEARESTPOINTS, -Voxel_VDM_BOXES, -Voxel_VDM_NEARESTBOXES -}; - -#endif // _Voxel_VoxelDisplayMode_HeaderFile diff --git a/src/Voxel/Voxel_VoxelFileFormat.hxx b/src/Voxel/Voxel_VoxelFileFormat.hxx deleted file mode 100644 index 751f199fbb..0000000000 --- a/src/Voxel/Voxel_VoxelFileFormat.hxx +++ /dev/null @@ -1,26 +0,0 @@ -// Created on: 2008-05-04 -// Created by: Vladislav ROMASHKO -// Copyright (c) 2008-2014 OPEN CASCADE SAS -// -// This file is part of Open CASCADE Technology software library. -// -// This library is free software; you can redistribute it and/or modify it under -// the terms of the GNU Lesser General Public License version 2.1 as published -// by the Free Software Foundation, with special exception defined in the file -// OCCT_LGPL_EXCEPTION.txt. Consult the file LICENSE_LGPL_21.txt included in OCCT -// distribution for complete text of the license and disclaimer of any warranty. -// -// Alternatively, this file may be used under the terms of Open CASCADE -// commercial license or contractual agreement. - -#ifndef _Voxel_VoxelFileFormat_HeaderFile -#define _Voxel_VoxelFileFormat_HeaderFile - - -enum Voxel_VoxelFileFormat -{ -Voxel_VFF_ASCII, -Voxel_VFF_BINARY -}; - -#endif // _Voxel_VoxelFileFormat_HeaderFile diff --git a/src/Voxel/Voxel_Writer.cxx b/src/Voxel/Voxel_Writer.cxx deleted file mode 100644 index 94aae86072..0000000000 --- a/src/Voxel/Voxel_Writer.cxx +++ /dev/null @@ -1,442 +0,0 @@ -// Created on: 2008-08-28 -// Created by: Vladislav ROMASHKO -// Copyright (c) 2008-2014 OPEN CASCADE SAS -// -// This file is part of Open CASCADE Technology software library. -// -// This library is free software; you can redistribute it and/or modify it under -// the terms of the GNU Lesser General Public License version 2.1 as published -// by the Free Software Foundation, with special exception defined in the file -// OCCT_LGPL_EXCEPTION.txt. Consult the file LICENSE_LGPL_21.txt included in OCCT -// distribution for complete text of the license and disclaimer of any warranty. -// -// Alternatively, this file may be used under the terms of Open CASCADE -// commercial license or contractual agreement. - - -#include -#include -#include -#include -#include -#include -#include -#include -#include - -Voxel_Writer::Voxel_Writer():myFormat(Voxel_VFF_ASCII),myBoolVoxels(0),myColorVoxels(0),myFloatVoxels(0) -{ - -} - -void Voxel_Writer::SetFormat(const Voxel_VoxelFileFormat format) -{ - myFormat = format; -} - -void Voxel_Writer::SetVoxels(const Voxel_BoolDS& voxels) -{ - myBoolVoxels = (Standard_Address) &voxels; - myColorVoxels = 0; - myFloatVoxels = 0; -} - -void Voxel_Writer::SetVoxels(const Voxel_ColorDS& voxels) -{ - myBoolVoxels = 0; - myColorVoxels = (Standard_Address) &voxels; - myFloatVoxels = 0; -} - -void Voxel_Writer::SetVoxels(const Voxel_FloatDS& voxels) -{ - myBoolVoxels = 0; - myColorVoxels = 0; - myFloatVoxels = (Standard_Address) &voxels; -} - -Standard_Boolean Voxel_Writer::Write(const TCollection_ExtendedString& file) const -{ - switch (myFormat) - { - case Voxel_VFF_ASCII: - { - if (myBoolVoxels) - return WriteBoolAsciiVoxels(file); - else if (myColorVoxels) - return WriteColorAsciiVoxels(file); - else if (myFloatVoxels) - return WriteFloatAsciiVoxels(file); - } - case Voxel_VFF_BINARY: - { - if (myBoolVoxels) - return WriteBoolBinaryVoxels(file); - else if (myColorVoxels) - return WriteColorBinaryVoxels(file); - else if (myFloatVoxels) - return WriteFloatBinaryVoxels(file); - } - } - - // No voxels or no format description is found: - return Standard_False; -} - -Standard_Boolean Voxel_Writer::WriteBoolAsciiVoxels(const TCollection_ExtendedString& file) const -{ - Voxel_BoolDS* ds = (Voxel_BoolDS*) myBoolVoxels; - if (!ds->myData) - return Standard_False; - - // Open file for writing - FILE* f = OSD_OpenFile(file, "w+"); - if (!f) - return Standard_False; - - // Header: file format, type of voxels - fprintf(f, VOXELS); - fprintf(f, " "); - fprintf(f, ASCII); - fprintf(f, " "); - fprintf(f, BOOL); - fprintf(f, "\n"); - - // Location, size, number of splits - fprintf(f, "%g %g %g\n", ds->GetX(), ds->GetY(), ds->GetZ()); - fprintf(f, "%g %g %g\n", ds->GetXLen(), ds->GetYLen(), ds->GetZLen()); - fprintf(f, "%d %d %d\n", ds->GetNbX(), ds->GetNbY(), ds->GetNbZ()); - - // Data - // Copied from Voxel_BoolDS.cxx: - Standard_Integer nb_bytes = RealToInt(ceil(ds->GetNbX() * ds->GetNbY() * ds->GetNbZ() / 8.0)); - Standard_Integer nb_slices = RealToInt(ceil(nb_bytes / 8.0)); - // myData[0 .. nb_slices - 1][0 .. 7] - if (nb_slices) - { - Standard_Integer i1 = 0, i2 = 0; - for (i1 = 0; i1 < nb_slices; i1++) - { - if (((Standard_Byte**)ds->myData)[i1]) - { - Standard_Boolean has_value = Standard_False; - fprintf(f, "%d ", i1); // index of slice - for (i2 = 0; i2 < 8; i2++) - { - Standard_Byte value = ((Standard_Byte*)((Standard_Byte**)ds->myData)[i1])[i2]; - if (value) - { - has_value = Standard_True; - fprintf(f, "%d %d\n", i2, value); - } - } - if (!has_value) - { - fprintf(f, "0 0\n"); - } - } - } - } - - fclose(f); - return Standard_True; -} - -Standard_Boolean Voxel_Writer::WriteColorAsciiVoxels(const TCollection_ExtendedString& file) const -{ - Voxel_ColorDS* ds = (Voxel_ColorDS*) myColorVoxels; - if (!ds->myData) - return Standard_False; - - // Open file for writing - FILE* f = OSD_OpenFile(file, "w+"); - if (!f) - return Standard_False; - - // Header: file format, type of voxels - fprintf(f, VOXELS); - fprintf(f, " "); - fprintf(f, ASCII); - fprintf(f, " "); - fprintf(f, COLOR); - fprintf(f, "\n"); - - // Location, size, number of splits - fprintf(f, "%g %g %g\n", ds->GetX(), ds->GetY(), ds->GetZ()); - fprintf(f, "%g %g %g\n", ds->GetXLen(), ds->GetYLen(), ds->GetZLen()); - fprintf(f, "%d %d %d\n", ds->GetNbX(), ds->GetNbY(), ds->GetNbZ()); - - // Data - // Copied from Voxel_ColorDS.cxx: - Standard_Integer nb_bytes = RealToInt(ceil(ds->GetNbX() * ds->GetNbY() * ds->GetNbZ() / 2.0)); - Standard_Integer nb_slices = RealToInt(ceil(nb_bytes / 32.0)); - // myData[0 .. nb_slices - 1][0 .. 31] - if (nb_slices) - { - Standard_Integer i1 = 0, i2 = 0; - for (i1 = 0; i1 < nb_slices; i1++) - { - if (((Standard_Byte**)ds->myData)[i1]) - { - Standard_Boolean has_value = Standard_False; - fprintf(f, "%d ", i1); // index of slice - for (i2 = 0; i2 < 32; i2++) - { - Standard_Byte value = ((Standard_Byte*)((Standard_Byte**)ds->myData)[i1])[i2]; - if (value) - { - has_value = Standard_True; - fprintf(f, "%d %d\n", i2, value); - } - } - if (!has_value) - { - fprintf(f, "0 0\n"); - } - } - } - } - - fclose(f); - return Standard_True; -} - -Standard_Boolean Voxel_Writer::WriteFloatAsciiVoxels(const TCollection_ExtendedString& file) const -{ - Voxel_FloatDS* ds = (Voxel_FloatDS*) myFloatVoxels; - if (!ds->myData) - return Standard_False; - - // Open file for writing - FILE* f = OSD_OpenFile(file, "w+"); - if (!f) - return Standard_False; - - // Header: file format, type of voxels - fprintf(f, VOXELS); - fprintf(f, " "); - fprintf(f, ASCII); - fprintf(f, " "); - fprintf(f, FLOAT); - fprintf(f, "\n"); - - // Location, size, number of splits - fprintf(f, "%g %g %g\n", ds->GetX(), ds->GetY(), ds->GetZ()); - fprintf(f, "%g %g %g\n", ds->GetXLen(), ds->GetYLen(), ds->GetZLen()); - fprintf(f, "%d %d %d\n", ds->GetNbX(), ds->GetNbY(), ds->GetNbZ()); - - // Data - // Copied from Voxel_FloatDS.cxx: - Standard_Integer nb_floats = ds->GetNbX() * ds->GetNbY() * ds->GetNbZ(); - Standard_Integer nb_slices = RealToInt(ceil(nb_floats / 32.0)); // 32 values in 1 slice - // myData[0 .. nb_slices - 1][0 .. 31] - if (nb_slices) - { - Standard_Integer i1 = 0, i2 = 0; - for (i1 = 0; i1 < nb_slices; i1++) - { - if (((Standard_ShortReal**)ds->myData)[i1]) - { - Standard_Boolean has_value = Standard_False; - fprintf(f, "%d ", i1); // index of slice - for (i2 = 0; i2 < 32; i2++) - { - Standard_ShortReal value = ((Standard_ShortReal*)((Standard_ShortReal**)ds->myData)[i1])[i2]; - if (value) - { - has_value = Standard_True; - fprintf(f, "%d %g\n", i2, value); - } - } - if (!has_value) - { - fprintf(f, "0 0\n"); - } - } - } - } - - fclose(f); - return Standard_True; -} - -Standard_Boolean Voxel_Writer::WriteBoolBinaryVoxels(const TCollection_ExtendedString& file) const -{ - Voxel_BoolDS* ds = (Voxel_BoolDS*) myBoolVoxels; - if (!ds->myData) - return Standard_False; - - // Open file for writing - FILE* f = OSD_OpenFile(file, "wb"); - if (!f) - return Standard_False; - - // Header: file format, type of voxels - fprintf(f, VOXELS); - fprintf(f, " "); - fprintf(f, BINARY); - fprintf(f, " "); - fprintf(f, BOOL); - fprintf(f, "\n"); - - // Location, size, number of splits - fwrite(&(ds->myX), sizeof(Standard_Real), 1, f); - fwrite(&(ds->myY), sizeof(Standard_Real), 1, f); - fwrite(&(ds->myZ), sizeof(Standard_Real), 1, f); - fwrite(&(ds->myXLen), sizeof(Standard_Real), 1, f); - fwrite(&(ds->myYLen), sizeof(Standard_Real), 1, f); - fwrite(&(ds->myZLen), sizeof(Standard_Real), 1, f); - fwrite(&(ds->myNbX), sizeof(Standard_Integer), 1, f); - fwrite(&(ds->myNbY), sizeof(Standard_Integer), 1, f); - fwrite(&(ds->myNbZ), sizeof(Standard_Integer), 1, f); - - // Data - // Copied from Voxel_BoolDS.cxx: - Standard_Integer nb_bytes = RealToInt(ceil(ds->GetNbX() * ds->GetNbY() * ds->GetNbZ() / 8.0)); - Standard_Integer nb_slices = RealToInt(ceil(nb_bytes / 8.0)); - // myData[0 .. nb_slices - 1][0 .. 7] - if (nb_slices) - { - Standard_Integer i1 = 0, i2 = 0; - for (i1 = 0; i1 < nb_slices; i1++) - { - if (((Standard_Byte**)ds->myData)[i1]) - { - for (i2 = 0; i2 < 8; i2++) - { - Standard_Byte value = ((Standard_Byte*)((Standard_Byte**)ds->myData)[i1])[i2]; - if (value) - { - fwrite(&i1, sizeof(Standard_Integer), 1, f); - fwrite(&i2, sizeof(Standard_Integer), 1, f); - fwrite(&value, sizeof(Standard_Byte), 1, f); - } - } - } - } - } - - fclose(f); - return Standard_True; -} - -Standard_Boolean Voxel_Writer::WriteColorBinaryVoxels(const TCollection_ExtendedString& file) const -{ - Voxel_ColorDS* ds = (Voxel_ColorDS*) myColorVoxels; - if (!ds->myData) - return Standard_False; - - // Open file for writing - FILE* f = OSD_OpenFile(file, "wb"); - if (!f) - return Standard_False; - - // Header: file format, type of voxels - fprintf(f, VOXELS); - fprintf(f, " "); - fprintf(f, BINARY); - fprintf(f, " "); - fprintf(f, COLOR); - fprintf(f, "\n"); - - // Location, size, number of splits - fwrite(&(ds->myX), sizeof(Standard_Real), 1, f); - fwrite(&(ds->myY), sizeof(Standard_Real), 1, f); - fwrite(&(ds->myZ), sizeof(Standard_Real), 1, f); - fwrite(&(ds->myXLen), sizeof(Standard_Real), 1, f); - fwrite(&(ds->myYLen), sizeof(Standard_Real), 1, f); - fwrite(&(ds->myZLen), sizeof(Standard_Real), 1, f); - fwrite(&(ds->myNbX), sizeof(Standard_Integer), 1, f); - fwrite(&(ds->myNbY), sizeof(Standard_Integer), 1, f); - fwrite(&(ds->myNbZ), sizeof(Standard_Integer), 1, f); - - // Data - // Copied from Voxel_ColorDS.cxx: - Standard_Integer nb_bytes = RealToInt(ceil(ds->myNbX * ds->myNbY * ds->myNbZ / 2.0)); - Standard_Integer nb_slices = RealToInt(ceil(nb_bytes / 32.0)); - // myData[0 .. nb_slices - 1][0 .. 31] - if (nb_slices) - { - Standard_Integer i1 = 0, i2 = 0; - for (i1 = 0; i1 < nb_slices; i1++) - { - if (((Standard_Byte**)ds->myData)[i1]) - { - for (i2 = 0; i2 < 32; i2++) - { - Standard_Byte value = ((Standard_Byte*)((Standard_Byte**)ds->myData)[i1])[i2]; - if (value) - { - fwrite(&i1, sizeof(Standard_Integer), 1, f); - fwrite(&i2, sizeof(Standard_Integer), 1, f); - fwrite(&value, sizeof(Standard_Byte), 1, f); - } - } - } - } - } - - fclose(f); - return Standard_True; -} - -Standard_Boolean Voxel_Writer::WriteFloatBinaryVoxels(const TCollection_ExtendedString& file) const -{ - Voxel_FloatDS* ds = (Voxel_FloatDS*) myFloatVoxels; - if (!ds->myData) - return Standard_False; - - // Open file for writing - FILE* f = OSD_OpenFile(file, "wb"); - if (!f) - return Standard_False; - - // Header: file format, type of voxels - fprintf(f, VOXELS); - fprintf(f, " "); - fprintf(f, BINARY); - fprintf(f, " "); - fprintf(f, FLOAT); - fprintf(f, "\n"); - - // Location, size, number of splits - fwrite(&(ds->myX), sizeof(Standard_Real), 1, f); - fwrite(&(ds->myY), sizeof(Standard_Real), 1, f); - fwrite(&(ds->myZ), sizeof(Standard_Real), 1, f); - fwrite(&(ds->myXLen), sizeof(Standard_Real), 1, f); - fwrite(&(ds->myYLen), sizeof(Standard_Real), 1, f); - fwrite(&(ds->myZLen), sizeof(Standard_Real), 1, f); - fwrite(&(ds->myNbX), sizeof(Standard_Integer), 1, f); - fwrite(&(ds->myNbY), sizeof(Standard_Integer), 1, f); - fwrite(&(ds->myNbZ), sizeof(Standard_Integer), 1, f); - - // Data - // Copied from Voxel_FloatDS.cxx: - Standard_Integer nb_floats = ds->myNbX * ds->myNbY * ds->myNbZ; - Standard_Integer nb_slices = RealToInt(ceil(nb_floats / 32.0)); // 32 values in 1 slice - // myData[0 .. nb_slices - 1][0 .. 31] - if (nb_slices) - { - Standard_Integer i1 = 0, i2 = 0; - Standard_Real small = Precision::SquareConfusion(); - for (i1 = 0; i1 < nb_slices; i1++) - { - if (((Standard_ShortReal**)ds->myData)[i1]) - { - for (i2 = 0; i2 < 32; i2++) - { - Standard_ShortReal value = ((Standard_ShortReal*)((Standard_ShortReal**)ds->myData)[i1])[i2]; - if (fabs(value) > small) - { - fwrite(&i1, sizeof(Standard_Integer), 1, f); - fwrite(&i2, sizeof(Standard_Integer), 1, f); - fwrite(&value, sizeof(Standard_ShortReal), 1, f); - } - } - } - } - } - - fclose(f); - return Standard_True; -} diff --git a/src/Voxel/Voxel_Writer.hxx b/src/Voxel/Voxel_Writer.hxx deleted file mode 100644 index de8bfd8143..0000000000 --- a/src/Voxel/Voxel_Writer.hxx +++ /dev/null @@ -1,106 +0,0 @@ -// Created on: 2008-08-28 -// Created by: Vladislav ROMASHKO -// Copyright (c) 2008-2014 OPEN CASCADE SAS -// -// This file is part of Open CASCADE Technology software library. -// -// This library is free software; you can redistribute it and/or modify it under -// the terms of the GNU Lesser General Public License version 2.1 as published -// by the Free Software Foundation, with special exception defined in the file -// OCCT_LGPL_EXCEPTION.txt. Consult the file LICENSE_LGPL_21.txt included in OCCT -// distribution for complete text of the license and disclaimer of any warranty. -// -// Alternatively, this file may be used under the terms of Open CASCADE -// commercial license or contractual agreement. - -#ifndef _Voxel_Writer_HeaderFile -#define _Voxel_Writer_HeaderFile - -#include -#include -#include - -#include -#include -#include -class Voxel_BoolDS; -class Voxel_ColorDS; -class Voxel_FloatDS; -class TCollection_ExtendedString; - - -//! Writes a cube of voxels on disk. -class Voxel_Writer -{ -public: - - DEFINE_STANDARD_ALLOC - - - //! An empty constructor. - Standard_EXPORT Voxel_Writer(); - - //! Defines the file format for voxels. - //! ASCII - slow and occupies more space on disk. - //! BINARY - fast and occupies less space on disk. - Standard_EXPORT void SetFormat (const Voxel_VoxelFileFormat format); - - //! Defines the voxels (1bit). - Standard_EXPORT void SetVoxels (const Voxel_BoolDS& voxels); - - //! Defines the voxels (4bit). - Standard_EXPORT void SetVoxels (const Voxel_ColorDS& voxels); - - //! Defines the voxels (4bytes). - Standard_EXPORT void SetVoxels (const Voxel_FloatDS& voxels); - - //! Writes the voxels on disk - //! using the defined format and file name. - Standard_EXPORT Standard_Boolean Write (const TCollection_ExtendedString& file) const; - - - - -protected: - - - - - -private: - - - //! Writes 1bit voxels on disk in ASCII format. - Standard_EXPORT Standard_Boolean WriteBoolAsciiVoxels (const TCollection_ExtendedString& file) const; - - //! Writes 4bit voxels on disk in ASCII format. - Standard_EXPORT Standard_Boolean WriteColorAsciiVoxels (const TCollection_ExtendedString& file) const; - - //! Writes 4bytes voxels on disk in ASCII format. - Standard_EXPORT Standard_Boolean WriteFloatAsciiVoxels (const TCollection_ExtendedString& file) const; - - //! Writes 1bit voxels on disk in BINARY format. - Standard_EXPORT Standard_Boolean WriteBoolBinaryVoxels (const TCollection_ExtendedString& file) const; - - //! Writes 4bit voxels on disk in BINARY format. - Standard_EXPORT Standard_Boolean WriteColorBinaryVoxels (const TCollection_ExtendedString& file) const; - - //! Writes 4bytes voxels on disk in BINARY format. - Standard_EXPORT Standard_Boolean WriteFloatBinaryVoxels (const TCollection_ExtendedString& file) const; - - - Voxel_VoxelFileFormat myFormat; - Standard_Address myBoolVoxels; - Standard_Address myColorVoxels; - Standard_Address myFloatVoxels; - - -}; - - - - - - - -#endif // _Voxel_Writer_HeaderFile diff --git a/tests/bugs/vis/bug19820 b/tests/bugs/vis/bug19820 deleted file mode 100755 index 2b45b2cb8f..0000000000 --- a/tests/bugs/vis/bug19820 +++ /dev/null @@ -1,73 +0,0 @@ -puts "============" -puts "OCC19820" -puts "============" -puts "" -####################################################################### -# 3D discrete topology (voxels) -####################################################################### - -set BugNumber OCC19820 - -set status 0 - -puts "1. voxelboolds ..." -if [catch {voxelboolds}] { - set status 1 -} - -puts "2. voxelcolords ..." -if [catch {voxelcolords}] { - set status 1 -} - -puts "3. voxelfloatds ..." -if [catch {voxelfloatds}] { - set status 1 -} - -puts "4. voxeloctboolds ..." -if [catch {voxeloctboolds}] { - set status 1 -} - -puts "5. voxelroctboolds ..." -if [catch {voxelroctboolds}] { - set status 1 -} - -puts "6. voxelfuseboolds ..." -if [catch {voxelfuseboolds}] { - set status 1 -} - -puts "7. voxelfusecolords ..." -if [catch {voxelfusecolords}] { - set status 1 -} - -puts "8. voxelfusefloatds ..." -if [catch {voxelfusefloatds}] { - set status 1 -} - -puts "9. voxelcutboolds ..." -if [catch {voxelcutboolds}] { - set status 1 -} - -puts "10. voxelcutcolords ..." -if [catch {voxelcutcolords}] { - set status 1 -} - -puts "11. voxelcutfloatds ..." -if [catch {voxelcutfloatds}] { - set status 1 -} - -if { ${status} != 0 } { - puts "Faulty ${BugNumber}" -} else { - puts "OK ${BugNumber}" -} - diff --git a/tests/bugs/vis/bug24019 b/tests/bugs/vis/bug24019 deleted file mode 100644 index d70ed56ff5..0000000000 --- a/tests/bugs/vis/bug24019 +++ /dev/null @@ -1,9 +0,0 @@ -puts "===========" -puts "OCC24019" -puts "===========" - -########################################## -# Voxel_FastConverter: filleng problem -########################################## - -OCC24019 [locate_data_file bug24019_boxes5.brep] diff --git a/tests/bugs/vis/bug24051 b/tests/bugs/vis/bug24051 deleted file mode 100644 index f8c3ffc4c5..0000000000 --- a/tests/bugs/vis/bug24051 +++ /dev/null @@ -1,9 +0,0 @@ -puts "============" -puts "OCC24051" -puts "============" -puts "" -####################################################################################### -# Voxel_FastConverter::Convert / ConvertUsingSAT - problems in multithreaded scenario -####################################################################################### - -OCC24051 diff --git a/tests/v3d/grids.list b/tests/v3d/grids.list index 6603b373a4..2c20c13f5e 100644 --- a/tests/v3d/grids.list +++ b/tests/v3d/grids.list @@ -9,7 +9,6 @@ 009 vertex_wire 010 wire 011 wire_solid -012 voxel 013 glsl 014 raytrace 015 materials diff --git a/tests/v3d/voxel/A1 b/tests/v3d/voxel/A1 deleted file mode 100644 index c1ea8fcb4e..0000000000 --- a/tests/v3d/voxel/A1 +++ /dev/null @@ -1 +0,0 @@ -voxelboolds -5 -5 -5 10 10 10 10 10 10 diff --git a/tests/v3d/voxel/A2 b/tests/v3d/voxel/A2 deleted file mode 100644 index 3862b61d63..0000000000 --- a/tests/v3d/voxel/A2 +++ /dev/null @@ -1 +0,0 @@ -voxelcolords -5 -5 -5 10 10 10 10 10 10 diff --git a/tests/v3d/voxel/A3 b/tests/v3d/voxel/A3 deleted file mode 100644 index 088d19451c..0000000000 --- a/tests/v3d/voxel/A3 +++ /dev/null @@ -1 +0,0 @@ -voxelfloatds -5 -5 -5 10 10 10 10 10 10 diff --git a/tests/v3d/voxel/A4 b/tests/v3d/voxel/A4 deleted file mode 100644 index 44286960f4..0000000000 --- a/tests/v3d/voxel/A4 +++ /dev/null @@ -1 +0,0 @@ -voxeloctboolds -5 -5 -5 10 10 10 10 10 10 diff --git a/tests/v3d/voxel/A5 b/tests/v3d/voxel/A5 deleted file mode 100644 index 6284750586..0000000000 --- a/tests/v3d/voxel/A5 +++ /dev/null @@ -1 +0,0 @@ -voxelroctboolds -5 -5 -5 10 10 10 10 10 10 diff --git a/tests/v3d/voxel/A6 b/tests/v3d/voxel/A6 deleted file mode 100644 index 0b06845350..0000000000 --- a/tests/v3d/voxel/A6 +++ /dev/null @@ -1 +0,0 @@ -voxelfuseboolds -5 -5 -5 10 10 10 10 10 10 diff --git a/tests/v3d/voxel/A7 b/tests/v3d/voxel/A7 deleted file mode 100644 index c1989bec1c..0000000000 --- a/tests/v3d/voxel/A7 +++ /dev/null @@ -1 +0,0 @@ -voxelfusecolords -5 -5 -5 10 10 10 10 10 10 diff --git a/tests/v3d/voxel/A8 b/tests/v3d/voxel/A8 deleted file mode 100644 index 191bebe4e4..0000000000 --- a/tests/v3d/voxel/A8 +++ /dev/null @@ -1 +0,0 @@ -voxelfusefloatds -5 -5 -5 10 10 10 10 10 10 diff --git a/tests/v3d/voxel/A9 b/tests/v3d/voxel/A9 deleted file mode 100644 index 87f668d864..0000000000 --- a/tests/v3d/voxel/A9 +++ /dev/null @@ -1 +0,0 @@ -voxelcutboolds -5 -5 -5 10 10 10 10 10 10 diff --git a/tests/v3d/voxel/B1 b/tests/v3d/voxel/B1 deleted file mode 100644 index 9d6372d197..0000000000 --- a/tests/v3d/voxel/B1 +++ /dev/null @@ -1 +0,0 @@ -voxelcutcolords -5 -5 -5 10 10 10 10 10 10 diff --git a/tests/v3d/voxel/B2 b/tests/v3d/voxel/B2 deleted file mode 100644 index a0914c38d4..0000000000 --- a/tests/v3d/voxel/B2 +++ /dev/null @@ -1 +0,0 @@ -voxelcutfloatds -5 -5 -5 10 10 10 10 10 10 diff --git a/tests/v3d/voxel/B3 b/tests/v3d/voxel/B3 deleted file mode 100644 index 958d733630..0000000000 --- a/tests/v3d/voxel/B3 +++ /dev/null @@ -1,9 +0,0 @@ -psphere s 100.0 -voxelbooldsconvert s 100 100 100 1.0 0 1 0 0 -voxelbooldsconvert s 100 100 100 1.0 1 1 0 0 -voxelbooldsconvert s 100 100 100 1.0 0 1 1 0 -voxelbooldsconvert s 100 100 100 1.0 1 1 1 0 -voxelbooldsconvert s 100 100 100 1.0 0 2 1 0 -voxelbooldsconvert s 100 100 100 1.0 1 2 1 0 -voxelbooldsconvert s 100 100 100 1.0 0 2 1 1 -voxelbooldsconvert s 100 100 100 1.0 1 2 1 2 diff --git a/tests/v3d/voxel/begin b/tests/v3d/voxel/begin deleted file mode 100644 index 6cabd388d9..0000000000 --- a/tests/v3d/voxel/begin +++ /dev/null @@ -1 +0,0 @@ -vinit View1 \ No newline at end of file