diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..70ee0e9 --- /dev/null +++ b/Makefile @@ -0,0 +1,68 @@ +# /******************************************************************************************** +# * File: Makefile +# * Author: $LastChangedBy: matthew $ +# * Revision: $Revision: 233 $ +# * Last Updated: $LastChangedDate: 2006-11-10 15:03:28 -0500 (Fri, 10 Nov 2006) $ +# ********************************************************************************************/ + +PR_TARGET=PoissonRecon +ST_TARGET=SurfaceTrimmer +PR_SOURCE=CmdLineParser.cpp Factor.cpp Geometry.cpp MarchingCubes.cpp PlyFile.cpp PoissonRecon.cpp +ST_SOURCE=CmdLineParser.cpp Factor.cpp Geometry.cpp MarchingCubes.cpp PlyFile.cpp SurfaceTrimmer.cpp + +CFLAGS += -fopenmp -Wno-deprecated +LFLAGS += -lgomp + +CFLAGS_DEBUG = -DDEBUG -g3 +LFLAGS_DEBUG = + +CFLAGS_RELEASE = -O3 -DRELEASE -funroll-loops -ffast-math +LFLAGS_RELEASE = -O3 + +SRC = Src/ +BIN = Bin/Linux/ +INCLUDE = /usr/include/ + +CC=gcc +CXX=g++ +MD=mkdir + +PR_OBJECTS=$(addprefix $(BIN), $(addsuffix .o, $(basename $(PR_SOURCE)))) +ST_OBJECTS=$(addprefix $(BIN), $(addsuffix .o, $(basename $(ST_SOURCE)))) + + +all: CFLAGS += $(CFLAGS_DEBUG) +all: LFLAGS += $(LFLAGS_DEBUG) +all: $(BIN) +all: $(BIN)$(PR_TARGET) +all: $(BIN)$(ST_TARGET) + +release: CFLAGS += $(CFLAGS_RELEASE) +release: LFLAGS += $(LFLAGS_RELEASE) +release: $(BIN) +release: $(BIN)$(PR_TARGET) +release: $(BIN)$(ST_TARGET) + +clean: + rm -f $(BIN)$(PR_TARGET) + rm -f $(BIN)$(ST_TARGET) + rm -f $(PR_OBJECTS) + rm -f $(ST_OBJECTS) + +$(BIN): + $(MD) -p $(BIN) + +$(BIN)$(PR_TARGET): $(PR_OBJECTS) + $(CXX) -o $@ $(PR_OBJECTS) $(LFLAGS) + +$(BIN)$(ST_TARGET): $(ST_OBJECTS) + $(CXX) -o $@ $(ST_OBJECTS) $(LFLAGS) + +$(BIN)%.o: $(SRC)%.c + mkdir -p $(BIN) + $(CC) -c -o $@ $(CFLAGS) -I$(INCLUDE) $< + +$(BIN)%.o: $(SRC)%.cpp + mkdir -p $(BIN) + $(CXX) -c -o $@ $(CFLAGS) -I$(INCLUDE) $< + diff --git a/PoissonRecon.sln b/PoissonRecon.sln new file mode 100644 index 0000000..2fc4c79 --- /dev/null +++ b/PoissonRecon.sln @@ -0,0 +1,45 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio 2013 +VisualStudioVersion = 12.0.30501.0 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "PoissonRecon", "PoissonRecon.vcxproj", "{46F87D0E-C53A-4F95-AB48-A5DBA8014340}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "SurfaceTrimmer", "SurfaceTrimmer.vcxproj", "{99BEAFED-8DB9-4B7D-A0BE-5186158193FE}" +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 + {46F87D0E-C53A-4F95-AB48-A5DBA8014340}.Debug|Win32.ActiveCfg = Debug|Win32 + {46F87D0E-C53A-4F95-AB48-A5DBA8014340}.Debug|Win32.Build.0 = Debug|Win32 + {46F87D0E-C53A-4F95-AB48-A5DBA8014340}.Debug|x64.ActiveCfg = Debug|x64 + {46F87D0E-C53A-4F95-AB48-A5DBA8014340}.Debug|x64.Build.0 = Debug|x64 + {46F87D0E-C53A-4F95-AB48-A5DBA8014340}.Release|Win32.ActiveCfg = Release|Win32 + {46F87D0E-C53A-4F95-AB48-A5DBA8014340}.Release|Win32.Build.0 = Release|Win32 + {46F87D0E-C53A-4F95-AB48-A5DBA8014340}.Release|x64.ActiveCfg = Release|x64 + {46F87D0E-C53A-4F95-AB48-A5DBA8014340}.Release|x64.Build.0 = Release|x64 + {99BEAFED-8DB9-4B7D-A0BE-5186158193FE}.Debug|Win32.ActiveCfg = Debug|Win32 + {99BEAFED-8DB9-4B7D-A0BE-5186158193FE}.Debug|Win32.Build.0 = Debug|Win32 + {99BEAFED-8DB9-4B7D-A0BE-5186158193FE}.Debug|x64.ActiveCfg = Debug|x64 + {99BEAFED-8DB9-4B7D-A0BE-5186158193FE}.Debug|x64.Build.0 = Debug|x64 + {99BEAFED-8DB9-4B7D-A0BE-5186158193FE}.Release|Win32.ActiveCfg = Release|Win32 + {99BEAFED-8DB9-4B7D-A0BE-5186158193FE}.Release|Win32.Build.0 = Release|Win32 + {99BEAFED-8DB9-4B7D-A0BE-5186158193FE}.Release|x64.ActiveCfg = Release|x64 + {99BEAFED-8DB9-4B7D-A0BE-5186158193FE}.Release|x64.Build.0 = Release|x64 + {7DB9957D-3644-4441-85A1-1A0928E087F0}.Debug|Win32.ActiveCfg = Debug|Win32 + {7DB9957D-3644-4441-85A1-1A0928E087F0}.Debug|Win32.Build.0 = Debug|Win32 + {7DB9957D-3644-4441-85A1-1A0928E087F0}.Debug|x64.ActiveCfg = Debug|x64 + {7DB9957D-3644-4441-85A1-1A0928E087F0}.Debug|x64.Build.0 = Debug|x64 + {7DB9957D-3644-4441-85A1-1A0928E087F0}.Release|Win32.ActiveCfg = Release|Win32 + {7DB9957D-3644-4441-85A1-1A0928E087F0}.Release|Win32.Build.0 = Release|Win32 + {7DB9957D-3644-4441-85A1-1A0928E087F0}.Release|x64.ActiveCfg = Release|x64 + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection +EndGlobal diff --git a/PoissonRecon.vcxproj b/PoissonRecon.vcxproj new file mode 100644 index 0000000..edbc0c2 --- /dev/null +++ b/PoissonRecon.vcxproj @@ -0,0 +1,232 @@ + + + + + Debug + Win32 + + + Debug + x64 + + + Release + Win32 + + + Release + x64 + + + + PoissonRecon + {46F87D0E-C53A-4F95-AB48-A5DBA8014340} + PoissonRecon + Win32Proj + + + + Application + MultiByte + true + v120 + + + Application + MultiByte + v120 + + + Application + MultiByte + true + v120 + + + Application + MultiByte + v120 + + + + + + + + + + + + + + + + + + + <_ProjectFileVersion>10.0.30319.1 + $(SolutionDir)\Bin\$(Platform)\$(Configuration)\ + $(SolutionDir)\Obj\$(Platform)\$(Configuration)\ + true + $(SolutionDir)\Bin\$(Platform)\$(Configuration)\ + $(SolutionDir)\Obj\$(Platform)\$(Configuration)\ + true + $(SolutionDir)Bin\$(Platform)\$(Configuration)\ + $(SolutionDir)\Obj\$(TargetName)\$(Platform)\$(Configuration)\ + false + $(SolutionDir)\Bin\$(Platform)\$(Configuration)\ + $(SolutionDir)\Obj\$(TargetName)\$(Platform)\$(Configuration)\ + false + .exe + + + + Disabled + WIN32;_DEBUG;_CONSOLE;_CRT_SECURE_NO_DEPRECATE;%(PreprocessorDefinitions) + true + EnableFastChecks + MultiThreadedDebugDLL + + + Level3 + EditAndContinue + + + true + Console + false + + + MachineX86 + + + + + X64 + + + Disabled + WIN32;_DEBUG;_CONSOLE;_CRT_SECURE_NO_DEPRECATE;%(PreprocessorDefinitions) + true + EnableFastChecks + MultiThreadedDebugDLL + + + Level3 + ProgramDatabase + + + true + Console + false + + + MachineX64 + + + + + WIN32;NDEBUG;_CONSOLE;_CRT_SECURE_NO_WARNINGS;_CRT_SECURE_NO_DEPRECATE;%(PreprocessorDefinitions) + MultiThreadedDLL + + + Level3 + ProgramDatabase + true + %(AdditionalIncludeDirectories) + + + true + Console + true + true + true + false + + + MachineX86 + psapi.lib;kernel32.lib;user32.lib;gdi32.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies) + + + + + X64 + + + %(AdditionalIncludeDirectories) + WIN32;NDEBUG;_CONSOLE;_CRT_SECURE_NO_DEPRECATE;%(PreprocessorDefinitions) + MultiThreadedDLL + + + Level3 + ProgramDatabase + Precise + true + false + AdvancedVectorExtensions2 + + + true + Console + true + true + false + + + MachineX64 + psapi.lib;kernel32.lib;user32.lib;gdi32.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies) + + + $(OutDir)$(TargetName)$(TargetExt) + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/PoissonRecon.vcxproj.filters b/PoissonRecon.vcxproj.filters new file mode 100644 index 0000000..852bbac --- /dev/null +++ b/PoissonRecon.vcxproj.filters @@ -0,0 +1,143 @@ + + + + + {4FC737F1-C7A5-4376-A066-2A32D752A2FF} + cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx + + + {93995380-89BD-4b04-88EB-625FBE52EBFB} + h;hpp;hxx;hm;inl;inc;xsd + + + {67DA6AB6-F800-4c08-8B7A-83BB121AAD01} + inc;inl + + + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + + + Include Files + + + Include Files + + + Include Files + + + Include Files + + + Include Files + + + Include Files + + + Include Files + + + Include Files + + + Include Files + + + Include Files + + + Include Files + + + Include Files + + + Include Files + + + Include Files + + + \ No newline at end of file diff --git a/README.md b/README.md index 0e6e59a..0578f41 100644 --- a/README.md +++ b/README.md @@ -1,2 +1,290 @@ -# PoissonRecon -Poisson Surface Reconstruction +

Screened Poisson Surface Reconstruction (Version 6.13a)

+
+links +executables +usage +changes +
+
+LINKS
+Papers: +SGP 2006, +ToG 2013 +
+Executables (Win32, Win64)
+(Older Versions: +V6.13, +V6.12, +V6.11, +V6.1, +V6, +V5.71, +V5.6, +V5.5a, +V5.1, +V5, +V4.51, +V4.5, +V4, +V3, +V2, +V1) +
+
+ +PoissonRecon:
+
+SurfaceTrimmer:
+ + +
+USAGE
+For testing purposes, two oriented point sets are provided: +
    + +
  1. Bunny: +A set of 362,271 oriented point samples (represented in PLY format) was obtained by merging the data from the original Stanford Bunny +range scans. The orientation of the sample points was estimated +using the connectivity information within individual range scans.
    +The original Poisson Reconstruction algorithm can be invoked by calling: +
    % PoissonRecon --in bunny.points.ply --out bunny.unscreened.ply --depth 10 --pointWeight 0
    +using the --pointWeight 0 argument to disable the screening.
    +By default, screening is enabled so the call: +
    % PoissonRecon --in bunny.points.ply --out bunny.screened.ply --depth 10
    +produces a reconstruction that more faithfully fits the input point positions.
    +A reconstruction of the bunny that does not close up the holes can be obtained by first calling: +
    % PoissonRecon --in bunny.points.ply --out bunny.screened.ply --depth 10 --density
    +to obtain a surface storing depth estimates with each vertex, and then calling: +
    % SurfaceTrimmer --in bunny.screened.ply --out bunny.screened.trimmed.ply --trim 7 --aRatio 0
    +to remove all subsets of the surface where the sampling density corresponds to a depth smaller than 7.
    +To fill in small holes in the reconstruction, the default value of the area ratio can be used instead: +
    % SurfaceTrimmer --in bunny.screened.ply --out bunny.screened.trimmed.ply --trim 7
    + +
  2. Horse: +A set of 100,000 oriented point samples (represented in ASCII format) was obtained by sampling a virtual horse model with a sampling density proportional to curvature, giving a set of non-uniformly distributed points.
    +The surface of the model can be reconstructed by calling the surface reconstructor as follows: +
    % PoissonRecon --in horse.npts --out horse.ply --depth 10
    + +
+ +To convert the binary PLY format to +Hugues Hoppe's ASCII +mesh format, a Perl script is provided.
+As an examples, the reconstructed bunny can be converted into the ASCII mesh format as follows: +
% ply2mesh.pl bunny.ply > bunny.m
+ +
+CHANGES
+Version 3: +
    +
  1. The implementation of the --samplesPerNode parameter has been modified so that a value of "1" more closely corresponds to a distribution with one sample per leaf node. +
  2. The code has been modified to support compilation under MSVC 2010 and the associated solution and project files are now provided. (Due to a bug in the Visual Studios compiler, this required modifying the implementation of some of the bit-shifting operators.) +
+Version 4: +
    +
  1. The code supports screened reconstruction, with interpolation weight specified through the --pointWeight parameter. +
  2. The code has been implemented to support parallel processing, with the number of threads used for parallelization specified by the --threads parameter. +
  3. The input point set can now also be in PLY format, and the file-type is determined by the extension, so that the --binary flag is now obsolete. +
  4. At depths coarser than the one specified by the value --minDepth the octree is no longer adaptive but rather complete, simplifying the prolongation operator. +
+Version 4.5: +
    +
  1. The algorithmic complexity of the solver was reduced from log-linear to linear. +
+Version 4.51: +
    +
  1. Smart pointers were added to ensure that memory accesses were in bounds. +
+Version 5: +
    +
  1. The --density flag was added to the reconstructor to output the estimated depth of the iso-vertices. +
  2. The SurfaceTrimmer executable was added to support trimming off the subset of the reconstructed surface that are far away from the input samples, thereby allowing for the generation of non-water-tight surface. +
+ +Version 5.1: +
    +
  1. Minor bug-fix to address incorrect neighborhood estimation in the octree finalization. +
+ +Version 5.5a: +
    +
  1. Modified to support depths greater than 14. (Should work up to 18 or 19 now.) +
  2. Improved speed and memory performance by removing the construction of integral and value tables. +
  3. Fixed a bug in Version 5.5 that used memory and took more time without doing anything useful. +
+ +Version 5.6: +
    +
  1. Added the --normalWeight flag to support setting a point's interpolation weight in proportion to the magnitude of its normal. +
+ +Version 5.7: +
    +
  1. Modified the setting of the constraints, replacing the map/reduce implementation with OpenMP atomics to reduce memory usage. +
  2. Fixed bugs that caused numerical overflow when processing large point clouds on multi-core machines. +
  3. Improved efficiency of the iso-surface extraction phse. +
+ +Version 5.71: +
    +
  1. Added the function GetSolutionValue to support the evaluation of the implicit function at a specific point. +
+ +Version 6: +
    +
  1. Modified the solver to use Gauss-Seidel relaxation instead of conjugate-gradients at finer resolution. +
  2. Re-ordered the implementation of the solver so that only a windowed subset of the matrix is in memory at any time, thereby reducing the memory usage during the solver phase. +
  3. Separated the storage of the data associated with the octree nodes from the topology. +
+ +Version 6.1: +
    +
  1. Re-ordered the implementation of the iso-surface extraction so that only a windowed subset of the octree is in memory at any time, thereby reducing the memory usage during the extracted phase. +
+ +Version 6.11: +
    +
  1. Fixed a bug that created a crash in the evaluation phase when --pointWeight is set zero. +
+ +Version 6.12: +
    +
  1. Removed the OpenMP firstprivate directive as it seemed to cause trouble under Linux compilations. +
+ +Version 6.13: +
    +
  1. Added a MemoryPointStream class in PointStream.inl to support in-memory point clouds. +
  2. Modified the signature of Octree::SetTree in MultiGridOctreeData.h to take in a pointer to an object of type PointStream rather than a file-name. +
+ +Version 6.13a: +
    +
  1. Modified the signature of Octree::SetIsoSurface to rerun a void. [cloudcompare] +
  2. Added a definition of SetIsoVertexValue supporting double precision vertices. [cloudcompare] +
  3. Removed Time.[h/cpp] from the repository. [cloudcompare/asmaloney] +
  4. Fixed assignment bug in Octree::SetSliceIsoVertices. [asmaloney] +
  5. Fixed initialization bug in SortedTreeNodes::SliceTableData and SortedTreeNodes::XSliceTableData. [asmaloney] +
  6. Included stdlib.h in Geometry.h. [asmaloney] +
  7. Fixed default value bug in declaration of Octree::SetTree. [asmaloney] +
+ +
+HOME diff --git a/Src/Allocator.h b/Src/Allocator.h new file mode 100644 index 0000000..e2723b9 --- /dev/null +++ b/Src/Allocator.h @@ -0,0 +1,163 @@ +/* +Copyright (c) 2006, Michael Kazhdan and Matthew Bolitho +All rights reserved. + +Redistribution and use in source and binary forms, with or without modification, +are permitted provided that the following conditions are met: + +Redistributions of source code must retain the above copyright notice, this list of +conditions and the following disclaimer. Redistributions in binary form must reproduce +the above copyright notice, this list of conditions and the following disclaimer +in the documentation and/or other materials provided with the distribution. + +Neither the name of the Johns Hopkins University nor the names of its contributors +may be used to endorse or promote products derived from this software without specific +prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY +EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO THE IMPLIED WARRANTIES +OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT +SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED +TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR +BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN +ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH +DAMAGE. +*/ + +#ifndef ALLOCATOR_INCLUDED +#define ALLOCATOR_INCLUDED +#include + +class AllocatorState{ +public: + int index,remains; +}; +/** This templated class assists in memory allocation and is well suited for instances + * when it is known that the sequence of memory allocations is performed in a stack-based + * manner, so that memory allocated last is released first. It also preallocates memory + * in chunks so that multiple requests for small chunks of memory do not require separate + * system calls to the memory manager. + * The allocator is templated off of the class of objects that we would like it to allocate, + * ensuring that appropriate constructors and destructors are called as necessary. + */ +template +class Allocator +{ + int blockSize; + int index , remains; + std::vector< T* > memory; +public: + Allocator( void ){ blockSize = index = remains = 0; } + ~Allocator( void ){ reset(); } + + /** This method is the allocators destructor. It frees up any of the memory that + * it has allocated. */ + void reset( void ) + { + for(size_t i=0;iblockSize = blockSize; + index=-1; + remains=0; + } + + /** This method returns a pointer to an array of elements objects. If there is left over pre-allocated + * memory, this method simply returns a pointer to the next free piece of memory, otherwise it pre-allocates + * more memory. Note that if the number of objects requested is larger than the value blockSize with which + * the allocator was initialized, the request for memory will fail. + */ + T* newElements( int elements=1 ) + { + T* mem; + if( !elements ) return NULL; + if( elements>blockSize ) fprintf( stderr , "[ERROR] Allocator: elements bigger than block-size: %d>%d\n" , elements , blockSize ) , exit( 0 ); + if( remains + +#define ARRAY_DEBUG 0 +#ifdef _WIN64 +#define ASSERT( x ) { if( !( x ) ) __debugbreak(); } +#else // !_WIN64 +#ifdef _WIN32 +#define ASSERT( x ) { if( !( x ) ) _asm{ int 0x03 } } +#else // !_WIN32 +#define ASSERT( x ) { if( !( x ) ) exit(0); } +#endif // _WIN32 +#endif // _WIN64 + +// Code from http://stackoverflow.com +void* aligned_malloc( size_t size , size_t align ) +{ + // Align enough for the data, the alignment padding, and room to store a pointer to the actual start of the memory + void* mem = malloc( size + align + sizeof( void* ) ); + // The position at which we could potentially start addressing + char* amem = ( (char*)mem ) + sizeof( void* ); + // Add align-1 to the start of the address and then zero out at most of the first align-1 bits. + amem = ( char* )( ( (size_t)( ( (char*)amem ) + (align-1) ) ) & ~( align-1 ) ); + // Pre-write the actual address + ( ( void** ) amem )[-1] = mem; + return amem; +} +void aligned_free( void* mem ) { free( ( ( void** )mem )[-1] ); } + +#if ARRAY_DEBUG +#pragma message ( "[WARNING] Array debugging is enabled" ) +#include "Array.inl" +#define Pointer( ... ) Array< __VA_ARGS__ > +#define ConstPointer( ... ) ConstArray< __VA_ARGS__ > +template< class C > void FreePointer( Array< C >& a ){ a.Free( ); } +template< class C > void AlignedFreePointer( Array< C >& a ){ a.Free( ); } +template< class C > void VFreePointer( Array< C >& a ){ a.Free( ); } +template< class C > void DeletePointer( Array< C >& a ){ a.Delete( ); } + +template< class C > Array< C > NewPointer( size_t size , const char* name=NULL ){ return Array< C >::New ( size , name ); } +template< class C > Array< C > AllocPointer( size_t size , const char* name=NULL ){ return Array< C >::Alloc ( size , false , name ); } +template< class C > Array< C > AlignedAllocPointer( size_t size , size_t alignment , const char* name=NULL ){ return Array< C >::AlignedAlloc( size , alignment , false , name ); } +template< class C > Array< C > ReAllocPointer( Array< C >& a , size_t size , const char* name=NULL ){ return Array< C >::ReAlloc ( a , size , false , name ); } + +template< class C > Array< C > NullPointer( void ){ return Array< C >( ); } + +template< class C > C* PointerAddress( Array< C >& a ) { return a.pointer(); } +template< class C > const C* PointerAddress( ConstArray< C >& a ) { return a.pointer(); } +template< class C > Array< C > GetPointer( C& c ) { return Array< C >::FromPointer( &c , 1 ); } +template< class C > ConstArray< C > GetPointer( const C& c ) { return ConstArray< C >::FromPointer( &c , 1 ); } +template< class C > Array< C > GetPointer( std::vector< C >& v ){ return Array< C >::FromPointer( &v[0] , v.size() ); } +template< class C > ConstArray< C > GetPointer( const std::vector< C >& v ){ return ConstArray< C >::FromPointer( &v[0] , v.size() ); } + +#else // !ARRAY_DEBUG +#define Pointer( ... ) __VA_ARGS__* +#define ConstPointer( ... ) const __VA_ARGS__* + +#define FreePointer( ... ) { if( __VA_ARGS__ ) free( __VA_ARGS__ ) , __VA_ARGS__ = NULL; } +#define AlignedFreePointer( ... ) { if( __VA_ARGS__ ) aligned_free( __VA_ARGS__ ) , __VA_ARGS__ = NULL; } +#define DeletePointer( ... ) { if( __VA_ARGS__ ) delete[] __VA_ARGS__ , __VA_ARGS__ = NULL; } + +template< class C > C* NewPointer( size_t size , const char* name=NULL ){ return new C[size]; } +template< class C > C* AllocPointer( size_t size , const char* name=NULL ){ return (C*) malloc( sizeof(C) * size ); } +template< class C > C* AlignedAllocPointer( size_t size , size_t alignment , const char* name=NULL ){ return (C*)aligned_malloc( sizeof(C) * size , alignment ); } +template< class C > C* ReAllocPointer( C* c , size_t size , const char* name=NULL ){ return (C*) realloc( c , sizeof(C) * size ); } + +template< class C > C* NullPointer( void ){ return NULL; } + +template< class C > C* PointerAddress( C* c ){ return c; } +template< class C > const C* PointerAddress( const C* c ){ return c; } +template< class C > C* GetPointer( C& c ){ return &c; } +template< class C > const C* GetPointer( const C& c ){ return &c; } +template< class C > C* GetPointer( std::vector< C >& v ){ return &v[0]; } +template< class C > const C* GetPointer( const std::vector< C >& v ){ return &v[0]; } +#endif // ARRAY_DEBUG +#endif // ARRAY_INCLUDED diff --git a/Src/Array.inl b/Src/Array.inl new file mode 100644 index 0000000..511b9a0 --- /dev/null +++ b/Src/Array.inl @@ -0,0 +1,658 @@ +/* +Copyright (c) 2011, Michael Kazhdan and Ming Chuang +All rights reserved. + +Redistribution and use in source and binary forms, with or without modification, +are permitted provided that the following conditions are met: + +Redistributions of source code must retain the above copyright notice, this list of +conditions and the following disclaimer. Redistributions in binary form must reproduce +the above copyright notice, this list of conditions and the following disclaimer +in the documentation and/or other materials provided with the distribution. + +Neither the name of the Johns Hopkins University nor the names of its contributors +may be used to endorse or promote products derived from this software without specific +prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY +EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO THE IMPLIED WARRANTIES +OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT +SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED +TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR +BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN +ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH +DAMAGE. +*/ +#define FULL_ARRAY_DEBUG 0 // Note that this is not thread-safe + +#include +#include +#include +#ifdef _WIN32 +#include +#endif // _WIN32 +#include + +inline bool isfinitef( float fp ){ float f=fp; return ((*(unsigned *)&f)&0x7f800000)!=0x7f800000; } + + +template< class C > bool IsValid( const C& c ); +#if _DEBUG +template< > inline bool IsValid< float >( const float& f ) { return isfinitef( f ) && ( f==0.f || abs(f)>1e-31f ); } +#else // !_DEBUG +template< > inline bool IsValid< float >( const float& f ) { return isfinitef( f ); } +#endif // _DEBUG +template< > inline bool IsValid< __m128 >( const __m128& m ) +{ + const __m128* addr = &m; + if( size_t(addr) & 15 ) return false; + else return true; +} +template< class C > inline bool IsValid( const C& c ){ return true; } + + +#if FULL_ARRAY_DEBUG +class DebugMemoryInfo +{ +public: + const void* address; + char name[512]; +}; +static std::vector< DebugMemoryInfo > memoryInfo; +#endif // FULL_ARRAY_DEBUG + +template< class C > +class Array +{ + void _assertBounds( long long idx ) const + { + if( idx=max ) + { + fprintf( stderr , "Array index out-of-bounds: %lld <= %lld < %lld\n" , min , idx , max ); + ASSERT( 0 ); + exit( 0 ); + } + } +protected: + C *data , *_data; + long long min , max; +#if FULL_ARRAY_DEBUG + static void _AddMemoryInfo( const void* ptr , const char* name ) + { + size_t sz = memoryInfo.size(); + memoryInfo.resize( sz + 1 ); + memoryInfo[sz].address = ptr; + if( name ) strcpy( memoryInfo[sz].name , name ); + else memoryInfo[sz].name[0] = 0; + } + static void _RemoveMemoryInfo( const void* ptr ) + { + { + size_t idx; + for( idx=0 ; idx + Array( Array< D >& a ) + { + _data = NULL; + if( !a ) + { + data = NULL; + min = max = 0; + } + else + { + // [WARNING] Chaning szC and szD to size_t causes some really strange behavior. + long long szC = sizeof( C ); + long long szD = sizeof( D ); + data = (C*)&a[0]; + min = ( a.minimum() * szD ) / szC; + max = ( a.maximum() * szD ) / szC; + if( min*szC!=a.minimum()*szD || max*szC!=a.maximum()*szD ) + { + fprintf( stderr , "Could not convert array [ %lld , %lld ] * %lld => [ %lld , %lld ] * %lld\n" , a.minimum() , a.maximum() , szD , min , max , szC ); + ASSERT( 0 ); + exit( 0 ); + } + } + } + static Array FromPointer( C* data , long long max ) + { + Array a; + a._data = NULL; + a.data = data; + a.min = 0; + a.max = max; + return a; + } + static Array FromPointer( C* data , long long min , long long max ) + { + Array a; + a._data = NULL; + a.data = data; + a.min = min; + a.max = max; + return a; + } + inline bool operator == ( const Array< C >& a ) const { return data==a.data; } + inline bool operator != ( const Array< C >& a ) const { return data!=a.data; } + inline bool operator == ( const C* c ) const { return data==c; } + inline bool operator != ( const C* c ) const { return data!=c; } + inline C* operator -> ( void ) + { + _assertBounds( 0 ); + return data; + } + inline const C* operator -> ( ) const + { + _assertBounds( 0 ); + return data; + } + inline C& operator[]( long long idx ) + { + _assertBounds( idx ); + return data[idx]; + } + inline const C& operator[]( long long idx ) const + { + _assertBounds( idx ); + return data[idx]; + } + inline Array operator + ( int idx ) const + { + Array a; + a._data = _data; + a.data = data+idx; + a.min = min-idx; + a.max = max-idx; + return a; + } + inline Array operator + ( long long idx ) const + { + Array a; + a._data = _data; + a.data = data+idx; + a.min = min-idx; + a.max = max-idx; + return a; + } + inline Array operator + ( unsigned int idx ) const + { + Array a; + a._data = _data; + a.data = data+idx; + a.min = min-idx; + a.max = max-idx; + return a; + } + inline Array operator + ( unsigned long long idx ) const + { + Array a; + a._data = _data; + a.data = data+idx; + a.min = min-idx; + a.max = max-idx; + return a; + } + inline Array& operator += ( int idx ) + { + min -= idx; + max -= idx; + data += idx; + return (*this); + } + inline Array& operator += ( long long idx ) + { + min -= idx; + max -= idx; + data += idx; + return (*this); + } + inline Array& operator += ( unsigned int idx ) + { + min -= idx; + max -= idx; + data += idx; + return (*this); + } + inline Array& operator += ( unsigned long long idx ) + { + min -= idx; + max -= idx; + data += idx; + return (*this); + } + inline Array& operator ++ ( void ) { return (*this) += 1; } + Array operator - ( int idx ) const { return (*this) + (-idx); } + Array operator - ( long long idx ) const { return (*this) + (-idx); } + Array operator - ( unsigned int idx ) const { return (*this) + (-idx); } + Array operator - ( unsigned long long idx ) const { return (*this) + (-idx); } + Array& operator -= ( int idx ) { return (*this) += (-idx); } + Array& operator -= ( long long idx ) { return (*this) += (-idx); } + Array& operator -= ( unsigned int idx ) { return (*this) += (-idx); } + Array& operator -= ( unsigned long long idx ) { return (*this) += (-idx); } + Array& operator -- ( void ) { return (*this) -= 1; } + long long operator - ( const Array& a ) const { return ( long long )( data - a.data ); } + + void Free( void ) + { + if( _data ) + { + free( _data ); +#if FULL_ARRAY_DEBUG + _RemoveMemoryInfo( _data ); +#endif // FULL_ARRAY_DEBUG + } + (*this) = Array( ); + } + void Delete( void ) + { + if( _data ) + { + delete[] _data; +#if FULL_ARRAY_DEBUG + _RemoveMemoryInfo( _data ); +#endif // FULL_ARRAY_DEBUG + } + (*this) = Array( ); + } + C* pointer( void ){ return data; } + const C* pointer( void ) const { return data; } + bool operator !( void ) const { return data==NULL; } + operator bool( ) const { return data!=NULL; } +}; + +template< class C > +class ConstArray +{ + void _assertBounds( long long idx ) const + { + if( idx=max ) + { + fprintf( stderr , "ConstArray index out-of-bounds: %lld <= %lld < %lld\n" , min , idx , max ); + ASSERT( 0 ); + exit( 0 ); + } + } +protected: + const C *data; + long long min , max; +public: + long long minimum( void ) const { return min; } + long long maximum( void ) const { return max; } + + inline ConstArray( void ) + { + data = NULL; + min = max = 0; + } + inline ConstArray( const Array< C >& a ) + { + // [WARNING] Changing szC and szD to size_t causes some really strange behavior. + data = ( const C* )a.pointer( ); + min = a.minimum(); + max = a.maximum(); + } + template< class D > + inline ConstArray( const Array< D >& a ) + { + // [WARNING] Changing szC and szD to size_t causes some really strange behavior. + long long szC = ( long long ) sizeof( C ); + long long szD = ( long long ) sizeof( D ); + data = ( const C* )a.pointer( ); + min = ( a.minimum() * szD ) / szC; + max = ( a.maximum() * szD ) / szC; + if( min*szC!=a.minimum()*szD || max*szC!=a.maximum()*szD ) + { +// fprintf( stderr , "Could not convert const array [ %lld , %lld ] * %lld => [ %lld , %lld ] * %lld\n" , a.minimum() , a.maximum() , szD , min , max , szC ); + fprintf( stderr , "Could not convert const array [ %lld , %lld ] * %lld => [ %lld , %lld ] * %lld\n %lld %lld %lld\n" , a.minimum() , a.maximum() , szD , min , max , szC , a.minimum() , a.minimum()*szD , (a.minimum()*szD)/szC ); + ASSERT( 0 ); + exit( 0 ); + } + } + template< class D > + inline ConstArray( const ConstArray< D >& a ) + { + // [WARNING] Chaning szC and szD to size_t causes some really strange behavior. + long long szC = sizeof( C ); + long long szD = sizeof( D ); + data = ( const C*)a.pointer( ); + min = ( a.minimum() * szD ) / szC; + max = ( a.maximum() * szD ) / szC; + if( min*szC!=a.minimum()*szD || max*szC!=a.maximum()*szD ) + { + fprintf( stderr , "Could not convert array [ %lld , %lld ] * %lld => [ %lld , %lld ] * %lld\n" , a.minimum() , a.maximum() , szD , min , max , szC ); + ASSERT( 0 ); + exit( 0 ); + } + } + static ConstArray FromPointer( const C* data , long long max ) + { + ConstArray a; + a.data = data; + a.min = 0; + a.max = max; + return a; + } + static ConstArray FromPointer( const C* data , long long min , long long max ) + { + ConstArray a; + a.data = data; + a.min = min; + a.max = max; + return a; + } + + inline bool operator == ( const ConstArray< C >& a ) const { return data==a.data; } + inline bool operator != ( const ConstArray< C >& a ) const { return data!=a.data; } + inline bool operator == ( const C* c ) const { return data==c; } + inline bool operator != ( const C* c ) const { return data!=c; } + inline const C* operator -> ( void ) + { + _assertBounds( 0 ); + return data; + } + inline const C& operator[]( long long idx ) const + { + _assertBounds( idx ); + return data[idx]; + } + inline ConstArray operator + ( int idx ) const + { + ConstArray a; + a.data = data+idx; + a.min = min-idx; + a.max = max-idx; + return a; + } + inline ConstArray operator + ( long long idx ) const + { + ConstArray a; + a.data = data+idx; + a.min = min-idx; + a.max = max-idx; + return a; + } + inline ConstArray operator + ( unsigned int idx ) const + { + ConstArray a; + a.data = data+idx; + a.min = min-idx; + a.max = max-idx; + return a; + } + inline ConstArray operator + ( unsigned long long idx ) const + { + ConstArray a; + a.data = data+idx; + a.min = min-idx; + a.max = max-idx; + return a; + } + inline ConstArray& operator += ( int idx ) + { + min -= idx; + max -= idx; + data += idx; + return (*this); + } + inline ConstArray& operator += ( long long idx ) + { + min -= idx; + max -= idx; + data += idx; + return (*this); + } + inline ConstArray& operator += ( unsigned int idx ) + { + min -= idx; + max -= idx; + data += idx; + return (*this); + } + inline ConstArray& operator += ( unsigned long long idx ) + { + min -= idx; + max -= idx; + data += idx; + return (*this); + } + inline ConstArray& operator ++ ( void ) { return (*this) += 1; } + ConstArray operator - ( int idx ) const { return (*this) + (-idx); } + ConstArray operator - ( long long idx ) const { return (*this) + (-idx); } + ConstArray operator - ( unsigned int idx ) const { return (*this) + (-idx); } + ConstArray operator - ( unsigned long long idx ) const { return (*this) + (-idx); } + ConstArray& operator -= ( int idx ) { return (*this) += (-idx); } + ConstArray& operator -= ( long long idx ) { return (*this) += (-idx); } + ConstArray& operator -= ( unsigned int idx ) { return (*this) += (-idx); } + ConstArray& operator -= ( unsigned long long idx ) { return (*this) += (-idx); } + ConstArray& operator -- ( void ) { return (*this) -= 1; } + long long operator - ( const ConstArray& a ) const { return ( long long )( data - a.data ); } + long long operator - ( const Array< C >& a ) const { return ( long long )( data - a.pointer() ); } + + const C* pointer( void ) const { return data; } + bool operator !( void ) { return data==NULL; } + operator bool( ) { return data!=NULL; } +}; + +#if FULL_ARRAY_DEBUG +inline void PrintMemoryInfo( void ){ for( size_t i=0 ; i +Array< C > memcpy( Array< C > destination , const void* source , size_t size ) +{ + if( size>destination.maximum()*sizeof(C) ) + { + fprintf( stderr , "Size of copy exceeds destination maximum: %lld > %lld\n" , ( long long )( size ) , ( long long )( destination.maximum()*sizeof( C ) ) ); + ASSERT( 0 ); + exit( 0 ); + } + if( size ) memcpy( &destination[0] , source , size ); + return destination; +} +template< class C , class D > +Array< C > memcpy( Array< C > destination , Array< D > source , size_t size ) +{ + if( size>destination.maximum()*sizeof( C ) ) + { + fprintf( stderr , "Size of copy exceeds destination maximum: %lld > %lld\n" , ( long long )( size ) , ( long long )( destination.maximum()*sizeof( C ) ) ); + ASSERT( 0 ); + exit( 0 ); + } + if( size>source.maximum()*sizeof( D ) ) + { + fprintf( stderr , "Size of copy exceeds source maximum: %lld > %lld\n" , ( long long )( size ) , ( long long )( source.maximum()*sizeof( D ) ) ); + ASSERT( 0 ); + exit( 0 ); + } + if( size ) memcpy( &destination[0] , &source[0] , size ); + return destination; +} +template< class C , class D > +Array< C > memcpy( Array< C > destination , ConstArray< D > source , size_t size ) +{ + if( size>destination.maximum()*sizeof( C ) ) + { + fprintf( stderr , "Size of copy exceeds destination maximum: %lld > %lld\n" , ( long long )( size ) , ( long long )( destination.maximum()*sizeof( C ) ) ); + ASSERT( 0 ); + exit( 0 ); + } + if( size>source.maximum()*sizeof( D ) ) + { + fprintf( stderr , "Size of copy exceeds source maximum: %lld > %lld\n" , ( long long )( size ) , ( long long )( source.maximum()*sizeof( D ) ) ); + ASSERT( 0 ); + exit( 0 ); + } + if( size ) memcpy( &destination[0] , &source[0] , size ); + return destination; +} +template< class D > +void* memcpy( void* destination , Array< D > source , size_t size ) +{ + if( size>source.maximum()*sizeof( D ) ) + { + fprintf( stderr , "Size of copy exceeds source maximum: %lld > %lld\n" , ( long long )( size ) , ( long long )( source.maximum()*sizeof( D ) ) ); + ASSERT( 0 ); + exit( 0 ); + } + if( size ) memcpy( destination , &source[0] , size ); + return destination; +} +template< class D > +void* memcpy( void* destination , ConstArray< D > source , size_t size ) +{ + if( size>source.maximum()*sizeof( D ) ) + { + fprintf( stderr , "Size of copy exceeds source maximum: %lld > %lld\n" , ( long long )( size ) , ( long long )( source.maximum()*sizeof( D ) ) ); + ASSERT( 0 ); + exit( 0 ); + } + if( size ) memcpy( destination , &source[0] , size ); + return destination; +} +template< class C > +Array< C > memset( Array< C > destination , int value , size_t size ) +{ + if( size>destination.maximum()*sizeof( C ) ) + { + fprintf( stderr , "Size of set exceeds destination maximum: %lld > %lld\n" , ( long long )( size ) , ( long long )( destination.maximum()*sizeof( C ) ) ); + ASSERT( 0 ); + exit( 0 ); + } + if( size ) memset( &destination[0] , value , size ); + return destination; +} + +template< class C > +size_t fread( Array< C > destination , size_t eSize , size_t count , FILE* fp ) +{ + if( count*eSize>destination.maximum()*sizeof( C ) ) + { + fprintf( stderr , "Size of read exceeds source maximum: %lld > %lld\n" , ( long long )( count*eSize ) , ( long long )( destination.maximum()*sizeof( C ) ) ); + ASSERT( 0 ); + exit( 0 ); + } + return fread( &destination[0] , eSize , count , fp ); +} +template< class C > +size_t fwrite( Array< C > source , size_t eSize , size_t count , FILE* fp ) +{ + if( count*eSize>source.maximum()*sizeof( C ) ) + { + fprintf( stderr , "Size of write exceeds source maximum: %lld > %lld\n" , ( long long )( count*eSize ) , ( long long )( source.maximum()*sizeof( C ) ) ); + ASSERT( 0 ); + exit( 0 ); + } + return fwrite( &source[0] , eSize , count , fp ); +} +template< class C > +size_t fwrite( ConstArray< C > source , size_t eSize , size_t count , FILE* fp ) +{ + if( count*eSize>source.maximum()*sizeof( C ) ) + { + fprintf( stderr , "Size of write exceeds source maximum: %lld > %lld\n" , ( long long )( count*eSize ) , ( long long )( source.maximum()*sizeof( C ) ) ); + ASSERT( 0 ); + exit( 0 ); + } + return fwrite( &source[0] , eSize , count , fp ); +} +template< class C > +void qsort( Array< C > base , size_t numElements , size_t elementSize , int (*compareFunction)( const void* , const void* ) ) +{ + if( sizeof(C)!=elementSize ) + { + fprintf( stderr , "Element sizes differ: %lld != %lld\n" , ( long long )( sizeof(C) ) , ( long long )( elementSize ) ); + ASSERT( 0 ); + exit( 0 ); + } + if( base.minimum()>0 || base.maximum() +struct BSplineElementCoefficients +{ + int coeffs[Degree+1]; + BSplineElementCoefficients( void ){ memset( coeffs , 0 , sizeof( int ) * ( Degree+1 ) ); } + int& operator[]( int idx ){ return coeffs[idx]; } + const int& operator[]( int idx ) const { return coeffs[idx]; } +}; +template< int Degree > +struct BSplineElements : public std::vector< BSplineElementCoefficients< Degree > > +{ + static const int _off = (Degree+1)/2; + void _addLeft ( int offset , int boundary ); + void _addRight( int offset , int boundary ); +public: + enum + { + NONE = 0, + DIRICHLET = -1, + NEUMANN = 1 + }; + // Coefficients are ordered as "/" "-" "\" + int denominator; + + BSplineElements( void ) { denominator = 1; } + BSplineElements( int res , int offset , int boundary=NONE , int inset=0 ); + + void upSample( BSplineElements& high ) const; + void differentiate( BSplineElements< Degree-1 >& d ) const; + + void print( FILE* fp=stdout ) const + { + for( int i=0 ; i >::size() ; i++ ) + { + printf( "%d]" , i ); + for( int j=0 ; j<=Degree ; j++ ) printf( " %d" , (*this)[i][j] ); + printf( " (%d)\n" , denominator ); + } + } +}; + +template< int Degree > +class BSplineData +{ + int _boundaryType; + double _vvIntegrals[Degree+1][Degree+1]; + double _vdIntegrals[Degree+1][Degree ]; + double _dvIntegrals[Degree ][Degree+1]; + double _ddIntegrals[Degree ][Degree ]; + +public: + struct Integrator + { + struct IntegralTables + { + double vv_ccIntegrals[2*Degree+1][2*Degree+1] , vv_cpIntegrals[(2*Degree+1)*2][2*Degree+1]; + double dv_ccIntegrals[2*Degree+1][2*Degree+1] , dv_cpIntegrals[(2*Degree+1)*2][2*Degree+1]; + double vd_ccIntegrals[2*Degree+1][2*Degree+1] , vd_cpIntegrals[(2*Degree+1)*2][2*Degree+1]; + double dd_ccIntegrals[2*Degree+1][2*Degree+1] , dd_cpIntegrals[(2*Degree+1)*2][2*Degree+1]; + }; + std::vector< IntegralTables > iTables; + double dot( int depth , int off1 , int off2 , bool d1 , bool d2 , bool childParent=false ) const; + }; + double dot( int depth1 , int off1 , int depth2 , int off2 , bool d1 , bool d2 , bool inset=false ) const; + void setIntegrator( Integrator& integrator , bool inset , bool useDotRatios=false ) const; + template< int Radius > + struct CenterEvaluator + { + struct ValueTables + { + double vValues[2*Degree+1][ 3*(2*Radius+1) ]; + double dValues[2*Degree+1][ 3*(2*Radius+1) ]; + }; + std::vector< ValueTables > vTables; + double value( int depth , int off1 , int off2 , bool d , bool childParent=false ) const; + }; + template< int Radius > + void setCenterEvaluator( CenterEvaluator< Radius >& evaluator , double smoothingRadius , double dSmoothingRadius, bool inset ) const; + double value( int depth , int off , double smoothingRadius , double s , bool d , bool inset=false ) const; + template< int Radius > + struct CornerEvaluator + { + struct ValueTables + { + double vValues[2*Degree+1][4*Radius+3]; + double dValues[2*Degree+1][4*Radius+3]; + }; + std::vector< ValueTables > vTables; + double value( int depth , int off1 , int c1 , int off2 , bool d , bool childParent=false ) const; + }; + template< int Radius > + void setCornerEvaluator( CornerEvaluator< Radius >& evaluator , double smoothingRadius , double dSmoothingRadius, bool inset ) const; + + struct BSplineComponents + { + Polynomial< Degree > polys[Degree+1]; + Polynomial< Degree >& operator[] ( int idx ) { return polys[idx]; } + const Polynomial< Degree >& operator[] ( int idx ) const { return polys[idx]; } + void printnl( void ) const { for( int d=0 ; d<=Degree ; d++ ) polys[d].printnl(); } + BSplineComponents scale( double s ) const { BSplineComponents b ; for( int d=0 ; d<=Degree ; d++ ) b[d] = polys[d].scale(s) ; return b; } + BSplineComponents shift( double s ) const { BSplineComponents b ; for( int d=0 ; d<=Degree ; d++ ) b[d] = polys[d].shift(s) ; return b; } + }; + + int depth; + size_t functionCount , sampleCount; + PPolynomial< Degree > baseFunction , leftBaseFunction , rightBaseFunction , leftRightBaseFunction; + PPolynomial< Degree-1 > dBaseFunction , dLeftBaseFunction , dRightBaseFunction , dLeftRightBaseFunction; + BSplineComponents baseBSpline , leftBSpline , rightBSpline , leftRightBSpline; + Pointer( PPolynomial< Degree > ) baseFunctions; + Pointer( BSplineComponents ) baseBSplines; + + BSplineData( void ); + + const static int VV_DOT_FLAG = 1; + const static int DV_DOT_FLAG = 2; + const static int DD_DOT_FLAG = 4; + const static int VALUE_FLAG = 1; + const static int D_VALUE_FLAG = 2; + template< class Real > + struct DotTables + { + size_t functionCount; + Pointer( Real ) vvDotTable; + Pointer( Real ) dvDotTable; + Pointer( Real ) ddDotTable; + + DotTables( void ); + ~DotTables( void ); + + inline size_t Index( int i1 , int i2 ) const; + static inline size_t SymmetricIndex( int i1 , int i2 ); + static inline int SymmetricIndex( int i1 , int i2 , size_t& index ); + }; + template< class Real > + struct ValueTables + { + size_t functionCount , sampleCount; + Pointer( Real ) valueTable; + Pointer( Real ) dValueTable; + + ValueTables( void ); + ~ValueTables( void ); + + inline size_t Index( int i1 , int i2 ) const; + void setSampleSpan( int idx , int& start , int& end , double smooth=0 ) const; + }; + void set( int maxDepth , int boundaryType=BSplineElements< Degree >::NONE ); + template< class Real > + typename BSplineData< Degree >::template DotTables< Real > getDotTables( int flags , bool useDotRatios=true , bool inset=false ) const; + template< class Real > + typename BSplineData< Degree >::template ValueTables< Real > getValueTables( int flags , double valueSmooth=0 , double normalSmooth=0 ) const; +}; + +template< int Degree1 , int Degree2 > void SetBSplineElementIntegrals( double integrals[Degree1+1][Degree2+1] ); + +#include "BSplineData.inl" +#endif // BSPLINE_DATA_INCLUDED \ No newline at end of file diff --git a/Src/BSplineData.inl b/Src/BSplineData.inl new file mode 100644 index 0000000..ef9cb7b --- /dev/null +++ b/Src/BSplineData.inl @@ -0,0 +1,733 @@ +/* +Copyright (c) 2006, Michael Kazhdan and Matthew Bolitho +All rights reserved. + +Redistribution and use in source and binary forms, with or without modification, +are permitted provided that the following conditions are met: + +Redistributions of source code must retain the above copyright notice, this list of +conditions and the following disclaimer. Redistributions in binary form must reproduce +the above copyright notice, this list of conditions and the following disclaimer +in the documentation and/or other materials provided with the distribution. + +Neither the name of the Johns Hopkins University nor the names of its contributors +may be used to endorse or promote products derived from this software without specific +prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY +EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO THE IMPLIED WARRANTIES +OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT +SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED +TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR +BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN +ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH +DAMAGE. +*/ + +///////////////// +// BSplineData // +///////////////// +// Support[i]: +// Odd: i +/- 0.5 * ( 1 + Degree ) +// i - 0.5 * ( 1 + Degree ) < 0 +// <=> i < 0.5 * ( 1 + Degree ) +// i + 0.5 * ( 1 + Degree ) > 0 +// <=> i > - 0.5 * ( 1 + Degree ) +// i + 0.5 * ( 1 + Degree ) > r +// <=> i > r - 0.5 * ( 1 + Degree ) +// i - 0.5 * ( 1 + Degree ) < r +// <=> i < r + 0.5 * ( 1 + Degree ) +// Even: i + 0.5 +/- 0.5 * ( 1 + Degree ) +// i - 0.5 * Degree < 0 +// <=> i < 0.5 * Degree +// i + 1 + 0.5 * Degree > 0 +// <=> i > -1 - 0.5 * Degree +// i + 1 + 0.5 * Degree > r +// <=> i > r - 1 - 0.5 * Degree +// i - 0.5 * Degree < r +// <=> i < r + 0.5 * Degree +template< int Degree > inline bool LeftOverlap( unsigned int depth , int offset ) +{ + offset <<= 1; + if( Degree & 1 ) return (offset < 1+Degree) && (offset > -1-Degree ); + else return (offset < Degree) && (offset > -2-Degree ); +} +template< int Degree > inline bool RightOverlap( unsigned int depth , int offset ) +{ + offset <<= 1; + int r = 1<<(depth+1); + if( Degree & 1 ) return (offset > 2-1-Degree) && (offset < 2+1+Degree ); + else return (offset > 2-2-Degree) && (offset < 2+ Degree ); +} +template< int Degree > inline int ReflectLeft( unsigned int depth , int offset ) +{ + if( Degree&1 ) return -offset; + else return -1-offset; +} +template< int Degree > inline int ReflectRight( unsigned int depth , int offset ) +{ + int r = 1<<(depth+1); + if( Degree&1 ) return r -offset; + else return r-1-offset; +} + +template< int Degree > +BSplineData< Degree >::BSplineData( void ) +{ + functionCount = sampleCount = 0; + SetBSplineElementIntegrals< Degree , Degree >( _vvIntegrals ); + SetBSplineElementIntegrals< Degree , Degree-1 >( _vdIntegrals ); + SetBSplineElementIntegrals< Degree-1 , Degree >( _dvIntegrals ); + SetBSplineElementIntegrals< Degree-1 , Degree-1 >( _ddIntegrals ); +} + +template< int Degree > +double BSplineData< Degree >::Integrator::dot( int depth , int off1 , int off2 , bool d1 , bool d2 , bool childParent ) const +{ + if( depth<0 || depth>=int( iTables.size() ) ) return 0.; + const typename Integrator::IntegralTables& iTable = iTables[depth]; + if( childParent ) + { + int c = off1&1; + off1 >>= 1 , depth--; + int ii , d = off2-off1 , res = (1<=res || off2>=res || d<-Degree || d>Degree ) return 0; + if ( off1< Degree ) ii = off1; + else if( off1>=res-Degree ) ii = 2*Degree + off1 - (res-1); + else ii = Degree; + if ( d1 && d2 ) return iTable.dd_cpIntegrals[2*ii+c][d+Degree]; + else if( d1 ) return iTable.dv_cpIntegrals[2*ii+c][d+Degree]; + else if( d2 ) return iTable.vd_cpIntegrals[2*ii+c][d+Degree]; + else return iTable.vv_cpIntegrals[2*ii+c][d+Degree]; + } + else + { + int ii , d = off2-off1 , res = (1<=res || off2>=res || d<-Degree || d>Degree ) return 0; + if ( off1< Degree ) ii = off1; + else if( off1>=res-Degree ) ii = 2*Degree + off1 - (res-1); + else ii = Degree; + if ( d1 && d2 ) return iTable.dd_ccIntegrals[ii][d+Degree]; + else if( d1 ) return iTable.dv_ccIntegrals[ii][d+Degree]; + else if( d2 ) return iTable.vd_ccIntegrals[ii][d+Degree]; + else return iTable.vv_ccIntegrals[ii][d+Degree]; + } +} +template< int Degree > +template< int Radius > +double BSplineData< Degree >::CenterEvaluator< Radius >::value( int depth , int off1 , int off2 , bool d , bool childParent ) const +{ + if( depth<0 || depth>=int( vTables.size() ) ) return 0.; + if( childParent ) + { + int c = off1&1; + off1 >>= 1 , depth--; + const typename CenterEvaluator::ValueTables& vTable = vTables[depth]; + int ii , dd = off1-off2 , res = (1<=res || off2>=res || dd<-Radius || dd>Radius ) return 0; + if ( off2< Degree ) ii = off2; + else if( off2>=res-Degree ) ii = 2*Degree + off2 - (res-1); + else ii = Degree; + if( d ) return vTable.dValues[ii][(dd+Radius)*3+2*c]; + else return vTable.vValues[ii][(dd+Radius)*3+2*c]; + } + else + { + const typename CenterEvaluator::ValueTables& vTable = vTables[depth]; + int ii , dd = off1-off2 , res = (1<=res || off2>=res || dd<-Radius || dd>Radius ) return 0; + if ( off2< Degree ) ii = off2; + else if( off2>=res-Degree ) ii = 2*Degree + off2 - (res-1); + else ii = Degree; + if( d ) return vTable.dValues[ii][(dd+Radius)*3+1]; + else return vTable.vValues[ii][(dd+Radius)*3+1]; + } +} +template< int Degree > +template< int Radius > +double BSplineData< Degree >::CornerEvaluator< Radius >::value( int depth , int off1 , int c1 , int off2 , bool d , bool childParent ) const +{ + if( c1<0 || c1>=2 ) + { + fprintf( stderr , "[WARNING] Clamping corner to {0,1}\n" ); + c1 = std::max< int >( 0 , std::min< int >( c1 , 1 ) ); + } + if( depth<0 || depth>=int( vTables.size() ) ) return 0.; + if( childParent ) + { + int c = off1&1; + off1 >>= 1 , depth--; + const typename CornerEvaluator::ValueTables& vTable = vTables[depth]; + int ii , dd = off1-off2 , res = (1<=res || off2>=res || dd<-Radius || dd>Radius ) return 0; + if ( off2< Degree ) ii = off2; + else if( off2>=res-Degree ) ii = 2*Degree + off2 - (res-1); + else ii = Degree; + if( d ) return vTable.dValues[ii][(dd+Radius)*2+c+c1]; + else return vTable.vValues[ii][(dd+Radius)*2+c+c1]; + } + else + { + const typename CornerEvaluator::ValueTables& vTable = vTables[depth]; + int ii , dd = off1-off2 , res = (1<=res || off2>=res || dd<-Radius || dd>Radius ) return 0; + if ( off2< Degree ) ii = off2; + else if( off2>=res-Degree ) ii = 2*Degree + off2 - (res-1); + else ii = Degree; + if( d ) return vTable.dValues[ii][(dd+Radius)*2+2*c1]; + else return vTable.vValues[ii][(dd+Radius)*2+2*c1]; + } +} +template< int Degree > +void BSplineData< Degree >::set( int maxDepth , int boundaryType ) +{ + _boundaryType = boundaryType; + + depth = maxDepth; + // [Warning] This assumes that the functions spacing is dual + functionCount = BinaryNode::CumulativeCenterCount( depth ); + sampleCount = BinaryNode::CenterCount( depth ) + BinaryNode::CornerCount( depth ); + baseFunctions = NewPointer< PPolynomial< Degree > >( functionCount ); + baseBSplines = NewPointer< BSplineComponents >( functionCount ); + + baseFunction = PPolynomial< Degree >::BSpline(); + for( int i=0 ; i<=Degree ; i++ ) baseBSpline[i] = Polynomial< Degree >::BSplineComponent( i ).shift( double(-(Degree+1)/2) + i - 0.5 ); + dBaseFunction = baseFunction.derivative(); + StartingPolynomial< Degree > sPolys[Degree+4]; + + for( int i=0 ; i=1 && i<=Degree+1 ) sPolys[i].p += baseBSpline[i-1]; + for( int j=0 ; j=1 && i<=Degree+1 ) sPolys[i].p += baseBSpline[i-1].shift( 1 ) * _boundaryType; + for( int j=0 ; j=1 && i<=Degree+1 ) sPolys[i].p += baseBSpline[i-1]; // The centered B-Spline + if( i>=2 && i<=Degree+2 ) sPolys[i].p += baseBSpline[i-2].shift( 1 ) * _boundaryType; // The right-shifted B-spline + for( int j=0 ; j +double BSplineData< Degree >::dot( int depth1 , int off1 , int depth2 , int off2 , bool d1 , bool d2 , bool inset ) const +{ + const int _Degree1 = (d1 ? (Degree-1) : Degree) , _Degree2 = (d2 ? (Degree-1) : Degree); + int sums[ Degree+1 ][ Degree+1 ]; + + int depth = std::max< int >( depth1 , depth2 ); + + BSplineElements< Degree > b1( 1< b; + while( depth1 db1 , db2; + b1.differentiate( db1 ) , b2.differentiate( db2 ); + + int start1=-1 , end1=-1 , start2=-1 , end2=-1; + for( int i=0 ; i=end2 || start2>=end1 ) return 0.; + int start = std::max< int >( start1 , start2 ) , end = std::min< int >( end1 , end2 ); + memset( sums , 0 , sizeof( sums ) ); + for( int i=start ; i +double BSplineData< Degree >::value( int depth , int off , double smoothingRadius , double s , bool d , bool inset ) const +{ + PPolynomial< Degree+1 > function; + PPolynomial< Degree > dFunction; + + if( off<0 || off>=(1<0 ) function = baseFunctions[idx].MovingAverage( smoothingRadius ); + else function = baseFunctions[idx]; + dFunction = function.derivative(); + + if( d ) return dFunction(s); + else return function(s); +} +template< int Degree > +void BSplineData< Degree >::setIntegrator( Integrator& integrator , bool inset , bool useDotRatios ) const +{ + integrator.iTables.resize( depth+1 ); + for( int d=0 ; d<=depth ; d++ ) for( int i=0 ; i<=2*Degree ; i++ ) for( int j=-Degree ; j<=Degree ; j++ ) + { + int res = 1< +template< int Radius > +void BSplineData< Degree >::setCenterEvaluator( CenterEvaluator< Radius >& evaluator , double smoothingRadius , double dSmoothingRadius , bool inset ) const +{ + evaluator.vTables.resize( depth+1 ); + for( int d=0 ; d<=depth ; d++ ) for( int i=0 ; i<=2*Degree ; i++ ) for( int j=-Radius ; j<=Radius ; j++ ) for( int k=-1 ; k<=1 ; k++ ) + { + int res = 1< +template< int Radius > +void BSplineData< Degree >::setCornerEvaluator( CornerEvaluator< Radius >& evaluator , double smoothingRadius , double dSmoothingRadius , bool inset ) const +{ + evaluator.vTables.resize( depth+1 ); + for( int d=0 ; d<=depth ; d++ ) for( int i=0 ; i<=2*Degree ; i++ ) for( int j=-Radius ; j<=Radius ; j++ ) for( int k=0 ; k<=2 ; k++ ) + { + int res = 1< +template< class Real > +BSplineData< Degree >::DotTables< Real >::DotTables( void ) +{ + vvDotTable = NullPointer< Real >(); + dvDotTable = NullPointer< Real >(); + ddDotTable = NullPointer< Real >(); +} +template< int Degree > +template< class Real > +BSplineData< Degree >::DotTables< Real >::~DotTables( void ) +{ + DeletePointer( vvDotTable ); + DeletePointer( dvDotTable ); + DeletePointer( ddDotTable ); +} +template< int Degree > +template< class Real > +inline size_t BSplineData< Degree >::DotTables< Real >::Index( int i1 , int i2 ) const { return size_t(i1)*functionCount + size_t(i2); } +template< int Degree > +template< class Real > +inline size_t BSplineData< Degree >::DotTables< Real >::SymmetricIndex( int i1 , int i2 ) +{ + size_t _i1 = i1 , _i2 = i2; + if( i1>i2 ) return ((_i1*_i1+i1)>>1)+_i2; + else return ((_i2*_i2+i2)>>1)+_i1; +} +template< int Degree > +template< class Real > +inline int BSplineData< Degree >::DotTables< Real >::SymmetricIndex( int i1 , int i2 , size_t& index ) +{ + size_t _i1 = i1 , _i2 = i2; + if( i1>1)+_i1; + return 1; + } + else + { + index = ((_i1*_i1+_i1)>>1)+_i2; + return 0; + } +} +template< int Degree > +template< class Real > +typename BSplineData< Degree >::template DotTables< Real > BSplineData< Degree >::getDotTables( int flags , bool useDotRatios , bool inset ) const +{ + typename BSplineData< Degree >::template DotTables< Real > dTables; + dTables.functionCount = functionCount; + + size_t size = ( functionCount*functionCount + functionCount )>>1; + size_t fullSize = functionCount*functionCount; + if( flags & VV_DOT_FLAG ) + { + dTables.vvDotTable = NewPointer< Real >( size ); + memset( dTables.vvDotTable , 0 , sizeof(Real)*size ); + } + if( flags & DV_DOT_FLAG ) + { + dTables.dvDotTable = NewPointer< Real >( fullSize ); + memset( dTables.dvDotTable , 0 , sizeof(Real)*fullSize ); + } + if( flags & DD_DOT_FLAG ) + { + dTables.ddDotTable = NewPointer< Real >( size ); + memset( dTables.ddDotTable , 0 , sizeof(Real)*size ); + } + int vvSums[Degree+1][Degree+1]; + int vdSums[Degree+1][Degree ]; + int dvSums[Degree ][Degree+1]; + int ddSums[Degree ][Degree ]; + double vvIntegrals[Degree+1][Degree+1]; + double vdIntegrals[Degree+1][Degree ]; + double dvIntegrals[Degree ][Degree+1]; + double ddIntegrals[Degree ][Degree ]; + SetBSplineElementIntegrals< Degree , Degree >( vvIntegrals ); + SetBSplineElementIntegrals< Degree , Degree-1 >( vdIntegrals ); + SetBSplineElementIntegrals< Degree-1 , Degree >( dvIntegrals ); + SetBSplineElementIntegrals< Degree-1 , Degree-1 >( ddIntegrals ); + + for( int d1=0 ; d1<=depth ; d1++ ) for( int off1=0 ; off1<(1< b1( 1< db1; + b1.differentiate( db1 ); + int start1 , end1; + + start1 = -1 , end1 = -1; + for( int i=0 ; i=end1 || start1>=end2 ) continue; + start2 = std::max< int >( start1 , start2 ); + end2 = std::min< int >( end1 , end2 ); + if( d1==d2 && off2 b2( 1< db2; + b2.differentiate( db2 ); + + size_t idx = DotTables< Real >::SymmetricIndex( ii , jj ); + size_t idx1 = DotTables< Real >::Index( ii , jj ) , idx2 = DotTables< Real >::Index( jj , ii ); + + memset( vvSums , 0 , sizeof( int ) * ( Degree+1 ) * ( Degree+1 ) ); + memset( vdSums , 0 , sizeof( int ) * ( Degree+1 ) * ( Degree ) ); + memset( dvSums , 0 , sizeof( int ) * ( Degree ) * ( Degree+1 ) ); + memset( ddSums , 0 , sizeof( int ) * ( Degree ) * ( Degree ) ); + for( int i=start2 ; i b; + b = b1; + b.upSample( b1 ); + b1.differentiate( db1 ); + start1 = -1; + for( int i=0 ; i +template< class Real > +BSplineData< Degree >::ValueTables< Real >::ValueTables( void ) +{ + valueTable = NullPointer< Real >(); + dValueTable = NullPointer< Real >(); +} +template< int Degree > +template< class Real > +BSplineData< Degree >::ValueTables< Real >::~ValueTables( void ) +{ + DeletePointer( valueTable ); + DeletePointer( dValueTable ); +} +template< int Degree > +template< class Real > +inline size_t BSplineData< Degree >::ValueTables< Real >::Index( int i1 , int i2 ) const { return size_t(i1)*functionCount + size_t(i2); } +template< int Degree > +template< class Real > +typename BSplineData< Degree >::template ValueTables< Real > BSplineData< Degree >::getValueTables( int flags , double valueSmooth , double derivativeSmooth ) const +{ + typename BSplineData< Degree >::template ValueTables< Real > vTables; + vTables.functionCount = functionCount; + vTables.sampleCount = sampleCount; + + if( flags & VALUE_FLAG ) vTables.valueTable = NewPointer< Real >( functionCount*sampleCount ); + if( flags & D_VALUE_FLAG ) vTables.dValueTable = NewPointer< Real >( functionCount*sampleCount ); + PPolynomial< Degree+1 > function; + PPolynomial< Degree > dFunction; + for( size_t i=0 ; i0 ) function=baseFunctions[i].MovingAverage( valueSmooth ); + else function=baseFunctions[i]; + if( derivativeSmooth>0 ) dFunction=baseFunctions[i].derivative().MovingAverage( derivativeSmooth ); + else dFunction=baseFunctions[i].derivative(); + + for( size_t j=0 ; j +template< class Real > +void BSplineData< Degree >::ValueTables< Real >::setSampleSpan( int idx , int& start , int& end , double smooth ) const +{ + int d , off , res; + BinaryNode::DepthAndOffset( idx , d , off ); + res = 1<_start && (start-1)/(sampleCount-1)<=_start + // => start > _start * (sampleCount-1 ) && start <= _start*(sampleCount-1) + 1 + // => _start * (sampleCount-1) + 1 >= start > _start * (sampleCount-1) + start = int( floor( _start * (sampleCount-1) + 1 ) ); + if( start<0 ) start = 0; + // (end)/(sampleCount-1)<_end && (end+1)/(sampleCount-1)>=_end + // => end < _end * (sampleCount-1 ) && end >= _end*(sampleCount-1) - 1 + // => _end * (sampleCount-1) > end >= _end * (sampleCount-1) - 1 + end = int( ceil( _end * (sampleCount-1) - 1 ) ); + if( end>=int(sampleCount) ) end = int(sampleCount)-1; +} + + +///////////////////// +// BSplineElements // +///////////////////// +template< int Degree > +BSplineElements< Degree >::BSplineElements( int res , int offset , int boundary , int inset ) +{ + denominator = 1; + std::vector< BSplineElementCoefficients< Degree > >::resize( res , BSplineElementCoefficients< Degree >() ); + + for( int i=0 ; i<=Degree ; i++ ) + { + int idx = -_off + offset + i; + if( idx>=0 && idx +void BSplineElements< Degree >::_addLeft( int offset , int boundary ) +{ + int res = int( std::vector< BSplineElementCoefficients< Degree > >::size() ); + bool set = false; + for( int i=0 ; i<=Degree ; i++ ) + { + int idx = -_off + offset + i; + if( idx>=0 && idx +void BSplineElements< Degree >::_addRight( int offset , int boundary ) +{ + int res = int( std::vector< BSplineElementCoefficients< Degree > >::size() ); + bool set = false; + for( int i=0 ; i<=Degree ; i++ ) + { + int idx = -_off + offset + i; + if( idx>=0 && idx +void BSplineElements< Degree >::upSample( BSplineElements< Degree >& high ) const +{ + fprintf( stderr , "[ERROR] B-spline up-sampling not supported for degree %d\n" , Degree ); + exit( 0 ); +} +template<> +void BSplineElements< 1 >::upSample( BSplineElements< 1 >& high ) const +{ + high.resize( size()*2 ); + high.assign( high.size() , BSplineElementCoefficients<1>() ); + for( int i=0 ; i +void BSplineElements< 2 >::upSample( BSplineElements< 2 >& high ) const +{ + // /----\ + // / \ + // / \ = 1 /--\ +3 /--\ +3 /--\ +1 /--\ + // / \ / \ / \ / \ / \ + // |----------| |----------| |----------| |----------| |----------| + + high.resize( size()*2 ); + high.assign( high.size() , BSplineElementCoefficients<2>() ); + for( int i=0 ; i +void BSplineElements< Degree >::differentiate( BSplineElements< Degree-1 >& d ) const +{ + d.resize( std::vector< BSplineElementCoefficients< Degree > >::size() ); + d.assign( d.size() , BSplineElementCoefficients< Degree-1 >() ); + for( int i=0 ; i >::size()) ; i++ ) for( int j=0 ; j<=Degree ; j++ ) + { + if( j-1>=0 ) d[i][j-1] -= (*this)[i][j]; + if( j +void SetBSplineElementIntegrals( double integrals[Degree1+1][Degree2+1] ) +{ + for( int i=0 ; i<=Degree1 ; i++ ) + { + Polynomial< Degree1 > p1 = Polynomial< Degree1 >::BSplineComponent( i ); + for( int j=0 ; j<=Degree2 ; j++ ) + { + Polynomial< Degree2 > p2 = Polynomial< Degree2 >::BSplineComponent( j ); + integrals[i][j] = ( p1 * p2 ).integral( 0 , 1 ); + } + } +} diff --git a/Src/BinaryNode.h b/Src/BinaryNode.h new file mode 100644 index 0000000..96f3061 --- /dev/null +++ b/Src/BinaryNode.h @@ -0,0 +1,78 @@ +/* +Copyright (c) 2006, Michael Kazhdan and Matthew Bolitho +All rights reserved. + +Redistribution and use in source and binary forms, with or without modification, +are permitted provided that the following conditions are met: + +Redistributions of source code must retain the above copyright notice, this list of +conditions and the following disclaimer. Redistributions in binary form must reproduce +the above copyright notice, this list of conditions and the following disclaimer +in the documentation and/or other materials provided with the distribution. + +Neither the name of the Johns Hopkins University nor the names of its contributors +may be used to endorse or promote products derived from this software without specific +prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY +EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO THE IMPLIED WARRANTIES +OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT +SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED +TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR +BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN +ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH +DAMAGE. +*/ + +#ifndef BINARY_NODE_INCLUDED +#define BINARY_NODE_INCLUDED + +#define MSVC_2010_FIX 1 + + +class BinaryNode +{ +public: + static inline int CenterCount( int depth ) { return 1< static inline Real CornerIndexPosition(int index,int maxDepth){ return Real(index)/(1< static inline Real Width(int depth){ return Real(1.0/(1< static inline void CenterAndWidth( int depth , int offset , Real& center , Real& width ) + { + width=Real (1.0/(1< static inline void CenterAndWidth( int idx , Real& center , Real& width ) + { + int depth , offset; + DepthAndOffset( idx , depth , offset ); + CenterAndWidth( depth , offset , center , width ); + } + static inline void DepthAndOffset( int idx , int& depth , int& offset ) + { + int i=idx+1; +#if MSVC_2010_FIX + depth = 0; +#else // !MSVC_2010_FIX + depth = -1; +#endif // MSVC_2010_FIX + while( i ) + { + i >>= 1; + depth++; + } +#if MSVC_2010_FIX + depth--; +#endif // MSVC_2010_FIX + offset = ( idx+1 ) - (1< +#include +#include +#include +#include "CmdLineParser.h" + + +#ifdef WIN32 +int strcasecmp(char* c1,char* c2){return _stricmp(c1,c2);} +#endif + +cmdLineReadable::cmdLineReadable(const char* name) +{ + set=false; + this->name=new char[strlen(name)+1]; + strcpy(this->name,name); +} +cmdLineReadable::~cmdLineReadable(void) +{ + if(name) delete[] name; + name=NULL; +} +int cmdLineReadable::read(char**,int){ + set=true; + return 0; +} +void cmdLineReadable::writeValue(char* str) +{ + str[0] = 0; +} + +//////////////// +// cmdLineInt // +//////////////// +cmdLineInt::cmdLineInt(const char* name) : cmdLineReadable(name) {value=0;} +cmdLineInt::cmdLineInt(const char* name,const int& v) : cmdLineReadable(name) {value=v;} +int cmdLineInt::read(char** argv,int argc){ + if(argc>0){ + value=atoi(argv[0]); + set=true; + return 1; + } + else{return 0;} +} +void cmdLineInt::writeValue(char* str) +{ + sprintf(str,"%d",value); +} + +////////////////// +// cmdLineFloat // +////////////////// +cmdLineFloat::cmdLineFloat(const char* name) : cmdLineReadable(name) {value=0;} +cmdLineFloat::cmdLineFloat(const char* name, const float& v) : cmdLineReadable(name) {value=v;} +int cmdLineFloat::read(char** argv,int argc){ + if(argc>0){ + value=(float)atof(argv[0]); + set=true; + return 1; + } + else{return 0;} +} +void cmdLineFloat::writeValue(char* str) +{ + sprintf(str,"%f",value); +} + +/////////////////// +// cmdLineString // +/////////////////// +cmdLineString::cmdLineString(const char* name) : cmdLineReadable(name) {value=NULL;} +cmdLineString::~cmdLineString(void) +{ + if(value) delete[] value; + value=NULL; +} +int cmdLineString::read(char** argv,int argc){ + if(argc>0) + { + value=new char[strlen(argv[0])+1]; + strcpy(value,argv[0]); + set=true; + return 1; + } + else{return 0;} +} +void cmdLineString::writeValue(char* str) +{ + sprintf(str,"%s",value); +} + +//////////////////// +// cmdLineStrings // +//////////////////// +cmdLineStrings::cmdLineStrings(const char* name,int Dim) : cmdLineReadable(name) +{ + this->Dim=Dim; + values=new char*[Dim]; + for(int i=0;i=Dim) + { + for(int i=0;i 0) + { + if (argv[0][0] == '-' && argv[0][1]=='-') + { + for(i=0;iname)) + { + argv++, argc--; + j=readable[i]->read(argv,argc); + argv+=j,argc-=j; + break; + } + } + if(i==num){ + if(dumpError) + { + fprintf(stderr, "invalid option: %s\n",*argv); + fprintf(stderr, "possible options are:\n"); + for(i=0;iname); + } + argv++, argc--; + } + } + else + { + if(dumpError) + { + fprintf(stderr, "invalid option: %s\n", *argv); + fprintf(stderr, " options must start with a \'--\'\n"); + } + argv++, argc--; + } + } +} +char** ReadWords(const char* fileName,int& cnt) +{ + char** names; + char temp[500]; + FILE* fp; + + fp=fopen(fileName,"r"); + if(!fp){return NULL;} + cnt=0; + while(fscanf(fp," %s ",temp)==1){cnt++;} + fclose(fp); + + names=new char*[cnt]; + if(!names){return NULL;} + + fp=fopen(fileName,"r"); + if(!fp){ + delete[] names; + cnt=0; + return NULL; + } + cnt=0; + while(fscanf(fp," %s ",temp)==1){ + names[cnt]=new char[strlen(temp)+1]; + if(!names){ + for(int j=0;j +#include + + +#ifdef WIN32 +int strcasecmp(char* c1,char* c2); +#endif + +class cmdLineReadable{ +public: + bool set; + char* name; + cmdLineReadable(const char* name); + virtual ~cmdLineReadable(void); + virtual int read(char** argv,int argc); + virtual void writeValue(char* str); +}; + +class cmdLineInt : public cmdLineReadable { +public: + int value; + cmdLineInt(const char* name); + cmdLineInt(const char* name,const int& v); + int read(char** argv,int argc); + void writeValue(char* str); +}; +template +class cmdLineIntArray : public cmdLineReadable { +public: + int values[Dim]; + cmdLineIntArray(const char* name); + cmdLineIntArray(const char* name,const int v[Dim]); + int read(char** argv,int argc); + void writeValue(char* str); +}; + +class cmdLineFloat : public cmdLineReadable { +public: + float value; + cmdLineFloat(const char* name); + cmdLineFloat(const char* name,const float& f); + int read(char** argv,int argc); + void writeValue(char* str); +}; +template +class cmdLineFloatArray : public cmdLineReadable { +public: + float values[Dim]; + cmdLineFloatArray(const char* name); + cmdLineFloatArray(const char* name,const float f[Dim]); + int read(char** argv,int argc); + void writeValue(char* str); +}; +class cmdLineString : public cmdLineReadable { +public: + char* value; + cmdLineString(const char* name); + ~cmdLineString(); + int read(char** argv,int argc); + void writeValue(char* str); +}; +class cmdLineStrings : public cmdLineReadable { + int Dim; +public: + char** values; + cmdLineStrings(const char* name,int Dim); + ~cmdLineStrings(void); + int read(char** argv,int argc); + void writeValue(char* str); +}; +template +class cmdLineStringArray : public cmdLineReadable { +public: + char* values[Dim]; + cmdLineStringArray(const char* name); + ~cmdLineStringArray(); + int read(char** argv,int argc); + void writeValue(char* str); +}; + +// This reads the arguments in argc, matches them against "names" and sets +// the values of "r" appropriately. Parameters start with "--" +void cmdLineParse(int argc, char **argv,int num,cmdLineReadable** r,int dumpError=1); + +char* GetFileExtension(char* fileName); +char* GetLocalFileName(char* fileName); +char** ReadWords(const char* fileName,int& cnt); + +#include "CmdLineParser.inl" +#endif // CMD_LINE_PARSER_INCLUDED diff --git a/Src/CmdLineParser.inl b/Src/CmdLineParser.inl new file mode 100644 index 0000000..eeded68 --- /dev/null +++ b/Src/CmdLineParser.inl @@ -0,0 +1,141 @@ +/* -*- C++ -*- +Copyright (c) 2006, Michael Kazhdan and Matthew Bolitho +All rights reserved. + +Redistribution and use in source and binary forms, with or without modification, +are permitted provided that the following conditions are met: + +Redistributions of source code must retain the above copyright notice, this list of +conditions and the following disclaimer. Redistributions in binary form must reproduce +the above copyright notice, this list of conditions and the following disclaimer +in the documentation and/or other materials provided with the distribution. + +Neither the name of the Johns Hopkins University nor the names of its contributors +may be used to endorse or promote products derived from this software without specific +prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY +EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO THE IMPLIED WARRANTIES +OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT +SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED +TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR +BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN +ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH +DAMAGE. +*/ + +///////////////////// +// cmdLineIntArray // +///////////////////// +template +cmdLineIntArray::cmdLineIntArray(const char* name) : cmdLineReadable(name) +{ + for(int i=0;i +cmdLineIntArray::cmdLineIntArray(const char* name,const int v[Dim]) : cmdLineReadable(name) +{ + for(int i=0;i +int cmdLineIntArray::read(char** argv,int argc) +{ + if(argc>=Dim) + { + for(int i=0;i +void cmdLineIntArray::writeValue(char* str) +{ + char* temp=str; + for(int i=0;i +cmdLineFloatArray::cmdLineFloatArray(const char* name) : cmdLineReadable(name) +{ + for(int i=0;i +cmdLineFloatArray::cmdLineFloatArray(const char* name,const float f[Dim]) : cmdLineReadable(name) +{ + for(int i=0;i +int cmdLineFloatArray::read(char** argv,int argc) +{ + if(argc>=Dim) + { + for(int i=0;i +void cmdLineFloatArray::writeValue(char* str) +{ + char* temp=str; + for(int i=0;i +cmdLineStringArray::cmdLineStringArray(const char* name) : cmdLineReadable(name) +{ + for(int i=0;i +cmdLineStringArray::~cmdLineStringArray(void) +{ + for(int i=0;i +int cmdLineStringArray::read(char** argv,int argc) +{ + if(argc>=Dim) + { + for(int i=0;i +void cmdLineStringArray::writeValue(char* str) +{ + char* temp=str; + for(int i=0;i +#include "Factor.h" +int Factor(double a1,double a0,double roots[1][2],double EPS){ + if(fabs(a1)<=EPS){return 0;} + roots[0][0]=-a0/a1; + roots[0][1]=0; + return 1; +} +int Factor(double a2,double a1,double a0,double roots[2][2],double EPS){ + double d; + if(fabs(a2)<=EPS){return Factor(a1,a0,roots,EPS);} + + d=a1*a1-4*a0*a2; + a1/=(2*a2); + if(d<0){ + d=sqrt(-d)/(2*a2); + roots[0][0]=roots[1][0]=-a1; + roots[0][1]=-d; + roots[1][1]= d; + } + else{ + d=sqrt(d)/(2*a2); + roots[0][1]=roots[1][1]=0; + roots[0][0]=-a1-d; + roots[1][0]=-a1+d; + } + return 2; +} +// Solution taken from: http://mathworld.wolfram.com/CubicFormula.html +// and http://www.csit.fsu.edu/~burkardt/f_src/subpak/subpak.f90 +int Factor(double a3,double a2,double a1,double a0,double roots[3][2],double EPS){ + double q,r,r2,q3; + + if(fabs(a3)<=EPS){return Factor(a2,a1,a0,roots,EPS);} + a2/=a3; + a1/=a3; + a0/=a3; + + q=-(3*a1-a2*a2)/9; + r=-(9*a2*a1-27*a0-2*a2*a2*a2)/54; + r2=r*r; + q3=q*q*q; + + if(r20){return PI/2.0;} + else{return -PI/2.0;} + } + if(x>=0){return atan(y/x);} + else{ + if(y>=0){return atan(y/x)+PI;} + else{return atan(y/x)-PI;} + } +} +double Angle(const double in[2]){ + if((in[0]*in[0]+in[1]*in[1])==0.0){return 0;} + else{return ArcTan2(in[1],in[0]);} +} +void Sqrt(const double in[2],double out[2]){ + double r=sqrt(sqrt(in[0]*in[0]+in[1]*in[1])); + double a=Angle(in)*0.5; + out[0]=r*cos(a); + out[1]=r*sin(a); +} +void Add(const double in1[2],const double in2[2],double out[2]){ + out[0]=in1[0]+in2[0]; + out[1]=in1[1]+in2[1]; +} +void Subtract(const double in1[2],const double in2[2],double out[2]){ + out[0]=in1[0]-in2[0]; + out[1]=in1[1]-in2[1]; +} +void Multiply(const double in1[2],const double in2[2],double out[2]){ + out[0]=in1[0]*in2[0]-in1[1]*in2[1]; + out[1]=in1[0]*in2[1]+in1[1]*in2[0]; +} +void Divide(const double in1[2],const double in2[2],double out[2]){ + double temp[2]; + double l=in2[0]*in2[0]+in2[1]*in2[1]; + temp[0]= in2[0]/l; + temp[1]=-in2[1]/l; + Multiply(in1,temp,out); +} +// Solution taken from: http://mathworld.wolfram.com/QuarticEquation.html +// and http://www.csit.fsu.edu/~burkardt/f_src/subpak/subpak.f90 +int Factor(double a4,double a3,double a2,double a1,double a0,double roots[4][2],double EPS){ + double R[2],D[2],E[2],R2[2]; + + if(fabs(a4)10e-8){ + double temp1[2],temp2[2]; + double p1[2],p2[2]; + + p1[0]=a3*a3*0.75-2.0*a2-R2[0]; + p1[1]=0; + + temp2[0]=((4.0*a3*a2-8.0*a1-a3*a3*a3)/4.0); + temp2[1]=0; + Divide(temp2,R,p2); + + Add (p1,p2,temp1); + Subtract(p1,p2,temp2); + + Sqrt(temp1,D); + Sqrt(temp2,E); + } + else{ + R[0]=R[1]=0; + double temp1[2],temp2[2]; + temp1[0]=roots[0][0]*roots[0][0]-4.0*a0; + temp1[1]=0; + Sqrt(temp1,temp2); + temp1[0]=a3*a3*0.75-2.0*a2+2.0*temp2[0]; + temp1[1]= 2.0*temp2[1]; + Sqrt(temp1,D); + temp1[0]=a3*a3*0.75-2.0*a2-2.0*temp2[0]; + temp1[1]= -2.0*temp2[1]; + Sqrt(temp1,E); + } + + roots[0][0]=-a3/4.0+R[0]/2.0+D[0]/2.0; + roots[0][1]= R[1]/2.0+D[1]/2.0; + + roots[1][0]=-a3/4.0+R[0]/2.0-D[0]/2.0; + roots[1][1]= R[1]/2.0-D[1]/2.0; + + roots[2][0]=-a3/4.0-R[0]/2.0+E[0]/2.0; + roots[2][1]= -R[1]/2.0+E[1]/2.0; + + roots[3][0]=-a3/4.0-R[0]/2.0-E[0]/2.0; + roots[3][1]= -R[1]/2.0-E[1]/2.0; + return 4; +} + +int Solve(const double* eqns,const double* values,double* solutions,int dim){ + int i,j,eIndex; + double v,m; + int *index=new int[dim]; + int *set=new int[dim]; + double* myEqns=new double[dim*dim]; + double* myValues=new double[dim]; + + for(i=0;im){ + m=fabs(myEqns[j*dim+i]); + eIndex=j; + } + } + if(eIndex==-1){ + delete[] index; + delete[] myValues; + delete[] myEqns; + delete[] set; + return 0; + } + // The position in which the solution for the i-th variable can be found + index[i]=eIndex; + set[eIndex]=1; + + // Normalize the equation + v=myEqns[eIndex*dim+i]; + for(j=0;j +class FunctionData{ + bool useDotRatios; + int normalize; +#if BOUNDARY_CONDITIONS + bool reflectBoundary; +#endif // BOUNDARY_CONDITIONS +public: + const static int DOT_FLAG = 1; + const static int D_DOT_FLAG = 2; + const static int D2_DOT_FLAG = 4; + const static int VALUE_FLAG = 1; + const static int D_VALUE_FLAG = 2; + + int depth , res , res2; + Real *dotTable , *dDotTable , *d2DotTable; + Real *valueTables , *dValueTables; +#if BOUNDARY_CONDITIONS + PPolynomial baseFunction , leftBaseFunction , rightBaseFunction; + PPolynomial dBaseFunction , dLeftBaseFunction , dRightBaseFunction; +#else // !BOUNDARY_CONDITIONS + PPolynomial baseFunction; + PPolynomial dBaseFunction; +#endif // BOUNDARY_CONDITIONS + PPolynomial* baseFunctions; + + FunctionData(void); + ~FunctionData(void); + + virtual void setDotTables(const int& flags); + virtual void clearDotTables(const int& flags); + + virtual void setValueTables(const int& flags,const double& smooth=0); + virtual void setValueTables(const int& flags,const double& valueSmooth,const double& normalSmooth); + virtual void clearValueTables(void); + + /******************************************************** + * Sets the translates and scales of the basis function + * up to the prescribed depth + * the maximum depth + * the basis function + * how the functions should be scaled + * 0] Value at zero equals 1 + * 1] Integral equals 1 + * 2] L2-norm equals 1 + * specifies if dot-products of derivatives + * should be pre-divided by function integrals + * spcifies if function space should be + * forced to be reflectively symmetric across the boundary + ********************************************************/ +#if BOUNDARY_CONDITIONS + void set( const int& maxDepth , const PPolynomial& F , const int& normalize , bool useDotRatios=true , bool reflectBoundary=false ); +#else // !BOUNDARY_CONDITIONS + void set(const int& maxDepth,const PPolynomial& F,const int& normalize , bool useDotRatios=true ); +#endif // BOUNDARY_CONDITIONS + +#if BOUNDARY_CONDITIONS + Real dotProduct( const double& center1 , const double& width1 , const double& center2 , const double& width2 , int boundary1 , int boundary2 ) const; + Real dDotProduct( const double& center1 , const double& width1 , const double& center2 , const double& width2 , int boundary1 , int boundary2 ) const; + Real d2DotProduct( const double& center1 , const double& width1 , const double& center2 , const double& width2 , int boundary1 , int boundary2 ) const; +#else // !BOUNDARY_CONDITIONS + Real dotProduct( const double& center1 , const double& width1 , const double& center2 , const double& width2 ) const; + Real dDotProduct( const double& center1 , const double& width1 , const double& center2 , const double& width2 ) const; + Real d2DotProduct( const double& center1 , const double& width1 , const double& center2 , const double& width2 ) const; +#endif // BOUNDARY_CONDITIONS + + static inline int SymmetricIndex( const int& i1 , const int& i2 ); + static inline int SymmetricIndex( const int& i1 , const int& i2 , int& index ); +}; + + +#include "FunctionData.inl" +#endif // FUNCTION_DATA_INCLUDED \ No newline at end of file diff --git a/Src/FunctionData.inl b/Src/FunctionData.inl new file mode 100644 index 0000000..4e61b96 --- /dev/null +++ b/Src/FunctionData.inl @@ -0,0 +1,415 @@ +/* +Copyright (c) 2006, Michael Kazhdan and Matthew Bolitho +All rights reserved. + +Redistribution and use in source and binary forms, with or without modification, +are permitted provided that the following conditions are met: + +Redistributions of source code must retain the above copyright notice, this list of +conditions and the following disclaimer. Redistributions in binary form must reproduce +the above copyright notice, this list of conditions and the following disclaimer +in the documentation and/or other materials provided with the distribution. + +Neither the name of the Johns Hopkins University nor the names of its contributors +may be used to endorse or promote products derived from this software without specific +prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY +EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO THE IMPLIED WARRANTIES +OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT +SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED +TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR +BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN +ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH +DAMAGE. +*/ + +////////////////// +// FunctionData // +////////////////// +template +FunctionData::FunctionData(void) +{ + dotTable=dDotTable=d2DotTable=NULL; + valueTables=dValueTables=NULL; + res=0; +} + +template +FunctionData::~FunctionData(void) +{ + if(res) + { + if( dotTable) delete[] dotTable; + if( dDotTable) delete[] dDotTable; + if(d2DotTable) delete[] d2DotTable; + if( valueTables) delete[] valueTables; + if(dValueTables) delete[] dValueTables; + } + dotTable=dDotTable=d2DotTable=NULL; + valueTables=dValueTables=NULL; + res=0; +} + +template +#if BOUNDARY_CONDITIONS +void FunctionData::set( const int& maxDepth , const PPolynomial& F , const int& normalize , bool useDotRatios , bool reflectBoundary ) +#else // !BOUNDARY_CONDITIONS +void FunctionData::set(const int& maxDepth,const PPolynomial& F,const int& normalize , bool useDotRatios ) +#endif // BOUNDARY_CONDITIONS +{ + this->normalize = normalize; + this->useDotRatios = useDotRatios; +#if BOUNDARY_CONDITIONS + this->reflectBoundary = reflectBoundary; +#endif // BOUNDARY_CONDITIONS + + depth = maxDepth; + res = BinaryNode::CumulativeCenterCount( depth ); + res2 = (1<<(depth+1))+1; + baseFunctions = new PPolynomial[res]; + // Scale the function so that it has: + // 0] Value 1 at 0 + // 1] Integral equal to 1 + // 2] Square integral equal to 1 + switch( normalize ) + { + case 2: + baseFunction=F/sqrt((F*F).integral(F.polys[0].start,F.polys[F.polyCount-1].start)); + break; + case 1: + baseFunction=F/F.integral(F.polys[0].start,F.polys[F.polyCount-1].start); + break; + default: + baseFunction=F/F(0); + } + dBaseFunction = baseFunction.derivative(); +#if BOUNDARY_CONDITIONS + leftBaseFunction = baseFunction + baseFunction.shift( -1 ); + rightBaseFunction = baseFunction + baseFunction.shift( 1 ); + dLeftBaseFunction = leftBaseFunction.derivative(); + dRightBaseFunction = rightBaseFunction.derivative(); +#endif // BOUNDARY_CONDITIONS + double c1,w1; + for( int i=0 ; i::CenterAndWidth( i , c1 , w1 ); +#if BOUNDARY_CONDITIONS + if( reflectBoundary ) + { + int d , off; + BinaryNode< double >::DepthAndOffset( i , d , off ); + if ( off==0 ) baseFunctions[i] = leftBaseFunction.scale( w1 ).shift( c1 ); + else if( off==((1< +void FunctionData::setDotTables( const int& flags ) +{ + clearDotTables( flags ); + int size; + size = ( res*res + res )>>1; + if( flags & DOT_FLAG ) + { + dotTable = new Real[size]; + memset( dotTable , 0 , sizeof(Real)*size ); + } + if( flags & D_DOT_FLAG ) + { + dDotTable = new Real[size]; + memset( dDotTable , 0 , sizeof(Real)*size ); + } + if( flags & D2_DOT_FLAG ) + { + d2DotTable = new Real[size]; + memset( d2DotTable , 0 , sizeof(Real)*size ); + } + double t1 , t2; + t1 = baseFunction.polys[0].start; + t2 = baseFunction.polys[baseFunction.polyCount-1].start; + for( int i=0 ; i::CenterAndWidth( i , c1 , w1 ); +#if BOUNDARY_CONDITIONS + int d1 , d2 , off1 , off2; + BinaryNode< double >::DepthAndOffset( i , d1 , off1 ); + int boundary1 = 0; + if ( reflectBoundary && off1==0 ) boundary1 = -1; + else if( reflectBoundary && off1==( (1<::CenterAndWidth( j , c2 , w2 ); +#if BOUNDARY_CONDITIONS + BinaryNode< double >::DepthAndOffset( j , d2 , off2 ); + int boundary2 = 0; + if ( reflectBoundary && off2==0 ) boundary2 = -1; + else if( reflectBoundary && off2==( (1<1 ) start = 1; + if( end <0 ) end = 0; + if( end >1 ) end = 1; + } +#endif // BOUNDARY_CONDITIONS + + if( start< start1 ) start = start1; + if( end > end1 ) end = end1; + if( start>= end ) continue; + +#if BOUNDARY_CONDITIONS + Real dot = dotProduct( c1 , w1 , c2 , w2 , boundary1 , boundary2 ); +#else // !BOUNDARY_CONDITIONS + Real dot = dotProduct( c1 , w1 , c2 , w2 ); +#endif // BOUNDARY_CONDITIONS + if( fabs(dot)<1e-15 ) continue; + if( flags & DOT_FLAG ) dotTable[idx]=dot; + if( useDotRatios ) + { +#if BOUNDARY_CONDITIONS + if( flags & D_DOT_FLAG ) dDotTable[idx] = -dDotProduct( c1 , w1 , c2 , w2 , boundary1 , boundary2 ) / dot; + if( flags & D2_DOT_FLAG ) d2DotTable[idx] = d2DotProduct( c1 , w1 , c2 , w2 , boundary1 , boundary2 ) / dot; +#else // !BOUNDARY_CONDITIONS + if( flags & D_DOT_FLAG ) dDotTable[idx] = -dDotProduct(c1,w1,c2,w2)/dot; + if( flags & D2_DOT_FLAG ) d2DotTable[idx] = d2DotProduct(c1,w1,c2,w2)/dot; +#endif // BOUNDARY_CONDITIONS + } + else + { +#if BOUNDARY_CONDITIONS + if( flags & D_DOT_FLAG ) dDotTable[idx] = dDotProduct( c1 , w1 , c2 , w2 , boundary1 , boundary2 ); + if( flags & D2_DOT_FLAG ) d2DotTable[idx] = d2DotProduct( c1 , w1 , c2 , w2 , boundary1 , boundary2 ); +#else // !BOUNDARY_CONDTIONS + if( flags & D_DOT_FLAG ) dDotTable[idx] = dDotProduct(c1,w1,c2,w2); + if( flags & D2_DOT_FLAG ) d2DotTable[idx] = d2DotProduct(c1,w1,c2,w2); +#endif // BOUNDARY_CONDITIONS + } + } + } +} +template +void FunctionData::clearDotTables( const int& flags ) +{ + if((flags & DOT_FLAG) && dotTable) + { + delete[] dotTable; + dotTable=NULL; + } + if((flags & D_DOT_FLAG) && dDotTable) + { + delete[] dDotTable; + dDotTable=NULL; + } + if((flags & D2_DOT_FLAG) && d2DotTable) + { + delete[] d2DotTable; + d2DotTable=NULL; + } +} +template +void FunctionData::setValueTables( const int& flags , const double& smooth ) +{ + clearValueTables(); + if( flags & VALUE_FLAG ) valueTables = new Real[res*res2]; + if( flags & D_VALUE_FLAG ) dValueTables = new Real[res*res2]; + PPolynomial function; + PPolynomial dFunction; + for( int i=0 ; i0) + { + function=baseFunctions[i].MovingAverage(smooth); + dFunction=baseFunctions[i].derivative().MovingAverage(smooth); + } + else + { + function=baseFunctions[i]; + dFunction=baseFunctions[i].derivative(); + } + for( int j=0 ; j +void FunctionData::setValueTables(const int& flags,const double& valueSmooth,const double& normalSmooth){ + clearValueTables(); + if(flags & VALUE_FLAG){ valueTables=new Real[res*res2];} + if(flags & D_VALUE_FLAG){dValueTables=new Real[res*res2];} + PPolynomial function; + PPolynomial dFunction; + for(int i=0;i0) { function=baseFunctions[i].MovingAverage(valueSmooth);} + else { function=baseFunctions[i];} + if(normalSmooth>0) {dFunction=baseFunctions[i].derivative().MovingAverage(normalSmooth);} + else {dFunction=baseFunctions[i].derivative();} + + for(int j=0;j +void FunctionData::clearValueTables(void){ + if( valueTables){delete[] valueTables;} + if(dValueTables){delete[] dValueTables;} + valueTables=dValueTables=NULL; +} + +#if BOUNDARY_CONDITIONS +template +Real FunctionData::dotProduct( const double& center1 , const double& width1 , const double& center2 , const double& width2 , int boundary1 , int boundary2 ) const +{ + const PPolynomial< Degree > *b1 , *b2; + if ( boundary1==-1 ) b1 = & leftBaseFunction; + else if( boundary1== 0 ) b1 = & baseFunction; + else if( boundary1== 1 ) b1 = &rightBaseFunction; + if ( boundary2==-1 ) b2 = & leftBaseFunction; + else if( boundary2== 0 ) b2 = & baseFunction; + else if( boundary2== 1 ) b2 = &rightBaseFunction; + double r=fabs( baseFunction.polys[0].start ); + switch( normalize ) + { + case 2: + return Real(((*b1)*b2->scale(width2/width1).shift((center2-center1)/width1)).integral(-2*r,2*r)*width1/sqrt(width1*width2)); + case 1: + return Real(((*b1)*b2->scale(width2/width1).shift((center2-center1)/width1)).integral(-2*r,2*r)*width1/(width1*width2)); + default: + return Real(((*b1)*b2->scale(width2/width1).shift((center2-center1)/width1)).integral(-2*r,2*r)*width1); + } +} +template +Real FunctionData::dDotProduct( const double& center1 , const double& width1 , const double& center2 , const double& width2 , int boundary1 , int boundary2 ) const +{ + const PPolynomial< Degree-1 > *b1; + const PPolynomial< Degree > *b2; + if ( boundary1==-1 ) b1 = & dLeftBaseFunction; + else if( boundary1== 0 ) b1 = & dBaseFunction; + else if( boundary1== 1 ) b1 = &dRightBaseFunction; + if ( boundary2==-1 ) b2 = & leftBaseFunction; + else if( boundary2== 0 ) b2 = & baseFunction; + else if( boundary2== 1 ) b2 = & rightBaseFunction; + double r=fabs(baseFunction.polys[0].start); + switch(normalize){ + case 2: + return Real(((*b1)*b2->scale(width2/width1).shift((center2-center1)/width1)).integral(-2*r,2*r)/sqrt(width1*width2)); + case 1: + return Real(((*b1)*b2->scale(width2/width1).shift((center2-center1)/width1)).integral(-2*r,2*r)/(width1*width2)); + default: + return Real(((*b1)*b2->scale(width2/width1).shift((center2-center1)/width1)).integral(-2*r,2*r)); + } +} +template +Real FunctionData::d2DotProduct( const double& center1 , const double& width1 , const double& center2 , const double& width2 , int boundary1 , int boundary2 ) const +{ + const PPolynomial< Degree-1 > *b1 , *b2; + if ( boundary1==-1 ) b1 = & dLeftBaseFunction; + else if( boundary1== 0 ) b1 = & dBaseFunction; + else if( boundary1== 1 ) b1 = &dRightBaseFunction; + if ( boundary2==-1 ) b2 = & dLeftBaseFunction; + else if( boundary2== 0 ) b2 = & dBaseFunction; + else if( boundary2== 1 ) b2 = &dRightBaseFunction; + double r=fabs(baseFunction.polys[0].start); + switch( normalize ) + { + case 2: + return Real(((*b1)*b2->scale(width2/width1).shift((center2-center1)/width1)).integral(-2*r,2*r)/width2/sqrt(width1*width2)); + case 1: + return Real(((*b1)*b2->scale(width2/width1).shift((center2-center1)/width1)).integral(-2*r,2*r)/width2/(width1*width2)); + default: + return Real(((*b1)*b2->scale(width2/width1).shift((center2-center1)/width1)).integral(-2*r,2*r)/width2); + } +} +#else // !BOUNDARY_CONDITIONS +template +Real FunctionData::dotProduct(const double& center1,const double& width1,const double& center2,const double& width2) const{ + double r=fabs(baseFunction.polys[0].start); + switch( normalize ) + { + case 2: + return Real((baseFunction*baseFunction.scale(width2/width1).shift((center2-center1)/width1)).integral(-2*r,2*r)*width1/sqrt(width1*width2)); + case 1: + return Real((baseFunction*baseFunction.scale(width2/width1).shift((center2-center1)/width1)).integral(-2*r,2*r)*width1/(width1*width2)); + default: + return Real((baseFunction*baseFunction.scale(width2/width1).shift((center2-center1)/width1)).integral(-2*r,2*r)*width1); + } +} +template +Real FunctionData::dDotProduct(const double& center1,const double& width1,const double& center2,const double& width2) const{ + double r=fabs(baseFunction.polys[0].start); + switch(normalize){ + case 2: + return Real((dBaseFunction*baseFunction.scale(width2/width1).shift((center2-center1)/width1)).integral(-2*r,2*r)/sqrt(width1*width2)); + case 1: + return Real((dBaseFunction*baseFunction.scale(width2/width1).shift((center2-center1)/width1)).integral(-2*r,2*r)/(width1*width2)); + default: + return Real((dBaseFunction*baseFunction.scale(width2/width1).shift((center2-center1)/width1)).integral(-2*r,2*r)); + } +} +template +Real FunctionData::d2DotProduct(const double& center1,const double& width1,const double& center2,const double& width2) const{ + double r=fabs(baseFunction.polys[0].start); + switch(normalize){ + case 2: + return Real((dBaseFunction*dBaseFunction.scale(width2/width1).shift((center2-center1)/width1)).integral(-2*r,2*r)/width2/sqrt(width1*width2)); + case 1: + return Real((dBaseFunction*dBaseFunction.scale(width2/width1).shift((center2-center1)/width1)).integral(-2*r,2*r)/width2/(width1*width2)); + default: + return Real((dBaseFunction*dBaseFunction.scale(width2/width1).shift((center2-center1)/width1)).integral(-2*r,2*r)/width2); + } +} +#endif // BOUNDARY_CONDITIONS +template +inline int FunctionData::SymmetricIndex( const int& i1 , const int& i2 ) +{ + if( i1>i2 ) return ((i1*i1+i1)>>1)+i2; + else return ((i2*i2+i2)>>1)+i1; +} +template +inline int FunctionData::SymmetricIndex( const int& i1 , const int& i2 , int& index ) +{ + if( i1>1)+i1; + return 1; + } + else{ + index = ((i1*i1+i1)>>1)+i2; + return 0; + } +} diff --git a/Src/Geometry.cpp b/Src/Geometry.cpp new file mode 100644 index 0000000..d6fa728 --- /dev/null +++ b/Src/Geometry.cpp @@ -0,0 +1,122 @@ +/* +Copyright (c) 2006, Michael Kazhdan and Matthew Bolitho +All rights reserved. + +Redistribution and use in source and binary forms, with or without modification, +are permitted provided that the following conditions are met: + +Redistributions of source code must retain the above copyright notice, this list of +conditions and the following disclaimer. Redistributions in binary form must reproduce +the above copyright notice, this list of conditions and the following disclaimer +in the documentation and/or other materials provided with the distribution. + +Neither the name of the Johns Hopkins University nor the names of its contributors +may be used to endorse or promote products derived from this software without specific +prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY +EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO THE IMPLIED WARRANTIES +OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT +SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED +TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR +BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN +ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH +DAMAGE. +*/ +#include "Geometry.h" +#include +#include +#ifdef _WIN32 +#include +#endif // _WIN32 + +/////////////////// +// CoredMeshData // +/////////////////// + +TriangulationEdge::TriangulationEdge(void){pIndex[0]=pIndex[1]=tIndex[0]=tIndex[1]=-1;} +TriangulationTriangle::TriangulationTriangle(void){eIndex[0]=eIndex[1]=eIndex[2]=-1;} + +/////////////////////////// +// BufferedReadWriteFile // +/////////////////////////// +BufferedReadWriteFile::BufferedReadWriteFile( char* fileName , int bufferSize ) +{ + _bufferIndex = 0; + _bufferSize = bufferSize; + if( fileName ) strcpy( _fileName , fileName ) , tempFile = false , _fp = fopen( _fileName , "w+b" ); + else + { + strcpy( _fileName , "PR_XXXXXX" ); +#ifdef _WIN32 + _mktemp( _fileName ); + _fp = fopen( _fileName , "w+b" ); +#else // !_WIN32 + _fp = fdopen( mkstemp( _fileName ) , "w+b" ); +#endif // _WIN32 + tempFile = true; + } + if( !_fp ) fprintf( stderr , "[ERROR] Failed to open file: %s\n" , _fileName ) , exit( 0 ); + _buffer = (char*) malloc( _bufferSize ); +} +BufferedReadWriteFile::~BufferedReadWriteFile( void ) +{ + free( _buffer ); + fclose( _fp ); + if( tempFile ) remove( _fileName ); +} +void BufferedReadWriteFile::reset( void ) +{ + if( _bufferIndex ) fwrite( _buffer , 1 , _bufferIndex , _fp ); + _bufferIndex = 0; + fseek( _fp , 0 , SEEK_SET ); + _bufferIndex = 0; + _bufferSize = fread( _buffer , 1 , _bufferSize , _fp ); +} +bool BufferedReadWriteFile::write( const void* data , size_t size ) +{ + if( !size ) return true; + char* _data = (char*) data; + size_t sz = _bufferSize - _bufferIndex; + while( sz<=size ) + { + memcpy( _buffer+_bufferIndex , _data , sz ); + fwrite( _buffer , 1 , _bufferSize , _fp ); + _data += sz; + size -= sz; + _bufferIndex = 0; + sz = _bufferSize; + } + if( size ) + { + memcpy( _buffer+_bufferIndex , _data , size ); + _bufferIndex += size; + } + return true; +} +bool BufferedReadWriteFile::read( void* data , size_t size ) +{ + if( !size ) return true; + char *_data = (char*) data; + size_t sz = _bufferSize - _bufferIndex; + while( sz<=size ) + { + if( size && !_bufferSize ) return false; + memcpy( _data , _buffer+_bufferIndex , sz ); + _bufferSize = fread( _buffer , 1 , _bufferSize , _fp ); + _data += sz; + size -= sz; + _bufferIndex = 0; + if( !size ) return true; + sz = _bufferSize; + } + if( size ) + { + if( !_bufferSize ) return false; + memcpy( _data , _buffer+_bufferIndex , size ); + _bufferIndex += size; + } + return true; +} \ No newline at end of file diff --git a/Src/Geometry.h b/Src/Geometry.h new file mode 100644 index 0000000..867e993 --- /dev/null +++ b/Src/Geometry.h @@ -0,0 +1,378 @@ +/* +Copyright (c) 2006, Michael Kazhdan and Matthew Bolitho +All rights reserved. + +Redistribution and use in source and binary forms, with or without modification, +are permitted provided that the following conditions are met: + +Redistributions of source code must retain the above copyright notice, this list of +conditions and the following disclaimer. Redistributions in binary form must reproduce +the above copyright notice, this list of conditions and the following disclaimer +in the documentation and/or other materials provided with the distribution. + +Neither the name of the Johns Hopkins University nor the names of its contributors +may be used to endorse or promote products derived from this software without specific +prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY +EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO THE IMPLIED WARRANTIES +OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT +SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED +TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR +BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN +ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH +DAMAGE. +*/ + +#ifndef GEOMETRY_INCLUDED +#define GEOMETRY_INCLUDED + +#include +#include +#include +#include "Hash.h" + +template +Real Random(void); + +template< class Real > +struct Point3D +{ + Real coords[3]; + Point3D( void ) { coords[0] = coords[1] = coords[2] = Real(0); } + template< class _Real > Point3D( _Real v0 , _Real v1 , _Real v2 ){ coords[0] = Real(v0) , coords[1] = Real(v1) , coords[2] = Real(v2); } + template< class _Real > Point3D( const Point3D< _Real >& p ){ coords[0] = Real( p[0] ) , coords[1] = Real( p[1] ) , coords[2] = Real( p[2] ); } + inline Real& operator[] ( int i ) { return coords[i]; } + inline const Real& operator[] ( int i ) const { return coords[i]; } + inline Point3D operator - ( void ) const { Point3D q ; q.coords[0] = -coords[0] , q.coords[1] = -coords[1] , q.coords[2] = -coords[2] ; return q; } + + template< class _Real > inline Point3D& operator += ( Point3D< _Real > p ){ coords[0] += Real(p.coords[0]) , coords[1] += Real(p.coords[1]) , coords[2] += Real(p.coords[2]) ; return *this; } + template< class _Real > inline Point3D operator + ( Point3D< _Real > p ) const { Point3D q ; q.coords[0] = coords[0] + Real(p.coords[0]) , q.coords[1] = coords[1] + Real(p.coords[1]) , q.coords[2] = coords[2] + Real(p.coords[2]) ; return q; } + template< class _Real > inline Point3D& operator *= ( _Real r ) { coords[0] *= Real(r) , coords[1] *= Real(r) , coords[2] *= Real(r) ; return *this; } + template< class _Real > inline Point3D operator * ( _Real r ) const { Point3D q ; q.coords[0] = coords[0] * Real(r) , q.coords[1] = coords[1] * Real(r) , q.coords[2] = coords[2] * Real(r) ; return q; } + + template< class _Real > inline Point3D& operator -= ( Point3D< _Real > p ){ return ( (*this)+=(-p) ); } + template< class _Real > inline Point3D operator - ( Point3D< _Real > p ) const { return (*this)+(-p); } + template< class _Real > inline Point3D& operator /= ( _Real r ){ return ( (*this)*=Real(1./r) ); } + template< class _Real > inline Point3D operator / ( _Real r ) const { return (*this) * ( Real(1.)/r ); } + + static Real Dot( const Point3D< Real >& p1 , const Point3D< Real >& p2 ){ return p1.coords[0]*p2.coords[0] + p1.coords[1]*p2.coords[1] + p1.coords[2]*p2.coords[2]; } + template< class Real1 , class Real2 > + static Real Dot( const Point3D< Real1 >& p1 , const Point3D< Real2 >& p2 ){ return Real( p1.coords[0]*p2.coords[0] + p1.coords[1]*p2.coords[1] + p1.coords[2]*p2.coords[2] ); } +}; + +template< class Real > +struct XForm3x3 +{ + Real coords[3][3]; + XForm3x3( void ) { for( int i=0 ; i<3 ; i++ ) for( int j=0 ; j<3 ; j++ ) coords[i][j] = Real(0.); } + static XForm3x3 Identity( void ) + { + XForm3x3 xForm; + xForm(0,0) = xForm(1,1) = xForm(2,2) = Real(1.); + return xForm; + } + Real& operator() ( int i , int j ){ return coords[i][j]; } + const Real& operator() ( int i , int j ) const { return coords[i][j]; } + template< class _Real > Point3D< _Real > operator * ( const Point3D< _Real >& p ) const + { + Point3D< _Real > q; + for( int i=0 ; i<3 ; i++ ) for( int j=0 ; j<3 ; j++ ) q[i] += _Real( coords[j][i] * p[j] ); + return q; + } + XForm3x3 operator * ( const XForm3x3& m ) const + { + XForm3x3 n; + for( int i=0 ; i<3 ; i++ ) for( int j=0 ; j<3 ; j++ ) for( int k=0 ; k<3 ; k++ ) n.coords[i][j] += m.coords[i][k]*coords[k][j]; + return n; + } + XForm3x3 transpose( void ) const + { + XForm3x3 xForm; + for( int i=0 ; i<3 ; i++ ) for( int j=0 ; j<3 ; j++ ) xForm( i , j ) = coords[j][i]; + return xForm; + } + Real subDeterminant( int i , int j ) const + { + int i1 = (i+1)%3 , i2 = (i+2)%3; + int j1 = (j+1)%3 , j2 = (j+2)%3; + return coords[i1][j1] * coords[i2][j2] - coords[i1][j2] * coords[i2][j1]; + } + Real determinant( void ) const { return coords[0][0] * subDeterminant( 0 , 0 ) + coords[1][0] * subDeterminant( 1 , 0 ) + coords[2][0] * subDeterminant( 2 , 0 ); } + XForm3x3 inverse( void ) const + { + XForm3x3 xForm; + Real d = determinant(); + for( int i=0 ; i<3 ; i++ ) for( int j=0 ; j<3 ;j++ ) xForm.coords[j][i] = subDeterminant( i , j ) / d; + return xForm; + } +}; + +template< class Real > +struct XForm4x4 +{ + Real coords[4][4]; + XForm4x4( void ) { for( int i=0 ; i<4 ; i++ ) for( int j=0 ; j<4 ; j++ ) coords[i][j] = Real(0.); } + static XForm4x4 Identity( void ) + { + XForm4x4 xForm; + xForm(0,0) = xForm(1,1) = xForm(2,2) = xForm(3,3) = Real(1.); + return xForm; + } + Real& operator() ( int i , int j ){ return coords[i][j]; } + const Real& operator() ( int i , int j ) const { return coords[i][j]; } + template< class _Real > Point3D< _Real > operator * ( const Point3D< _Real >& p ) const + { + Point3D< _Real > q; + for( int i=0 ; i<3 ; i++ ) + { + for( int j=0 ; j<3 ; j++ ) q[i] += (_Real)( coords[j][i] * p[j] ); + q[i] += (_Real)coords[3][i]; + } + return q; + } + XForm4x4 operator * ( const XForm4x4& m ) const + { + XForm4x4 n; + for( int i=0 ; i<4 ; i++ ) for( int j=0 ; j<4 ; j++ ) for( int k=0 ; k<4 ; k++ ) n.coords[i][j] += m.coords[i][k]*coords[k][j]; + return n; + } + XForm4x4 transpose( void ) const + { + XForm4x4 xForm; + for( int i=0 ; i<4 ; i++ ) for( int j=0 ; j<4 ; j++ ) xForm( i , j ) = coords[j][i]; + return xForm; + } + Real subDeterminant( int i , int j ) const + { + XForm3x3< Real > xForm; + int ii[] = { (i+1)%4 , (i+2)%4 , (i+3)%4 } , jj[] = { (j+1)%4 , (j+2)%4 , (j+3)%4 }; + for( int _i=0 ; _i<3 ; _i++ ) for( int _j=0 ; _j<3 ; _j++ ) xForm( _i , _j ) = coords[ ii[_i] ][ jj[_j] ]; + return xForm.determinant(); + } + Real determinant( void ) const { return coords[0][0] * subDeterminant( 0 , 0 ) - coords[1][0] * subDeterminant( 1 , 0 ) + coords[2][0] * subDeterminant( 2 , 0 ) - coords[3][0] * subDeterminant( 3 , 0 ); } + XForm4x4 inverse( void ) const + { + XForm4x4 xForm; + Real d = determinant(); + for( int i=0 ; i<4 ; i++ ) for( int j=0 ; j<4 ;j++ ) + if( (i+j)%2==0 ) xForm.coords[j][i] = subDeterminant( i , j ) / d; + else xForm.coords[j][i] = -subDeterminant( i , j ) / d; + return xForm; + } +}; + + +template +Point3D RandomBallPoint(void); + +template +Point3D RandomSpherePoint(void); + +template +double Length(const Point3D& p); + +template +double SquareLength(const Point3D& p); + +template +double Distance(const Point3D& p1,const Point3D& p2); + +template +double SquareDistance(const Point3D& p1,const Point3D& p2); + +template +void CrossProduct(const Point3D& p1,const Point3D& p2,Point3D& p); + + +class Edge{ +public: + double p[2][2]; + double Length(void) const{ + double d[2]; + d[0]=p[0][0]-p[1][0]; + d[1]=p[0][1]-p[1][1]; + + return sqrt(d[0]*d[0]+d[1]*d[1]); + } +}; +class Triangle{ +public: + double p[3][3]; + double Area(void) const{ + double v1[3] , v2[3] , v[3]; + for( int d=0 ; d<3 ; d++ ) + { + v1[d] = p[1][d] - p[0][d]; + v2[d] = p[2][d] - p[0][d]; + } + v[0] = v1[1]*v2[2] - v1[2]*v2[1]; + v[1] = -v1[0]*v2[2] + v1[2]*v2[0]; + v[2] = v1[0]*v2[1] - v1[1]*v2[0]; + return sqrt( v[0]*v[0] + v[1]*v[1] + v[2]*v[2] ) / 2; + } + double AspectRatio(void) const{ + double d=0; + int i,j; + for(i=0;i<3;i++){ + for(i=0;i<3;i++) + for(j=0;j<3;j++){d+=(p[(i+1)%3][j]-p[i][j])*(p[(i+1)%3][j]-p[i][j]);} + } + return Area()/d; + } + +}; +class CoredPointIndex +{ +public: + int index; + char inCore; + + int operator == (const CoredPointIndex& cpi) const {return (index==cpi.index) && (inCore==cpi.inCore);}; + int operator != (const CoredPointIndex& cpi) const {return (index!=cpi.index) || (inCore!=cpi.inCore);}; +}; +class EdgeIndex{ +public: + int idx[2]; +}; +class CoredEdgeIndex +{ +public: + CoredPointIndex idx[2]; +}; +class TriangleIndex{ +public: + int idx[3]; +}; + +class TriangulationEdge +{ +public: + TriangulationEdge(void); + int pIndex[2]; + int tIndex[2]; +}; + +class TriangulationTriangle +{ +public: + TriangulationTriangle(void); + int eIndex[3]; +}; + +template +class Triangulation +{ +public: + + std::vector > points; + std::vector edges; + std::vector triangles; + + int factor( int tIndex,int& p1,int& p2,int& p3); + double area(void); + double area( int tIndex ); + double area( int p1 , int p2 , int p3 ); + int flipMinimize( int eIndex); + int addTriangle( int p1 , int p2 , int p3 ); + +protected: + hash_map edgeMap; + static long long EdgeIndex( int p1 , int p2 ); + double area(const Triangle& t); +}; + + +template +void EdgeCollapse(const Real& edgeRatio,std::vector& triangles,std::vector< Point3D >& positions,std::vector >* normals); +template +void TriangleCollapse(const Real& edgeRatio,std::vector& triangles,std::vector >& positions,std::vector >* normals); + +struct CoredVertexIndex +{ + int idx; + bool inCore; +}; +template< class Vertex > +class CoredMeshData +{ +public: + std::vector< Vertex > inCorePoints; + virtual void resetIterator( void ) = 0; + + virtual int addOutOfCorePoint( const Vertex& p ) = 0; + virtual int addOutOfCorePoint_s( const Vertex& p ) = 0; + virtual int addPolygon_s( const std::vector< CoredVertexIndex >& vertices ) = 0; + virtual int addPolygon_s( const std::vector< int >& vertices ) = 0; + + virtual int nextOutOfCorePoint( Vertex& p )=0; + virtual int nextPolygon( std::vector< CoredVertexIndex >& vertices ) = 0; + + virtual int outOfCorePointCount(void)=0; + virtual int polygonCount( void ) = 0; +}; + +template< class Vertex > +class CoredVectorMeshData : public CoredMeshData< Vertex > +{ + std::vector< Vertex > oocPoints; + std::vector< std::vector< int > > polygons; + int polygonIndex; + int oocPointIndex; +public: + CoredVectorMeshData(void); + + void resetIterator(void); + + int addOutOfCorePoint( const Vertex& p ); + int addOutOfCorePoint_s( const Vertex& p ); + int addPolygon_s( const std::vector< CoredVertexIndex >& vertices ); + int addPolygon_s( const std::vector< int >& vertices ); + + int nextOutOfCorePoint( Vertex& p ); + int nextPolygon( std::vector< CoredVertexIndex >& vertices ); + + int outOfCorePointCount(void); + int polygonCount( void ); +}; +class BufferedReadWriteFile +{ + bool tempFile; + FILE* _fp; + char *_buffer , _fileName[1024]; + size_t _bufferIndex , _bufferSize; +public: + BufferedReadWriteFile( char* fileName=NULL , int bufferSize=(1<<20) ); + ~BufferedReadWriteFile( void ); + bool write( const void* data , size_t size ); + bool read ( void* data , size_t size ); + void reset( void ); +}; +template< class Vertex > +class CoredFileMeshData : public CoredMeshData< Vertex > +{ + char pointFileName[1024] , polygonFileName[1024]; + BufferedReadWriteFile *oocPointFile , *polygonFile; + int oocPoints , polygons; +public: + CoredFileMeshData( void ); + ~CoredFileMeshData( void ); + + void resetIterator( void ); + + int addOutOfCorePoint( const Vertex& p ); + int addOutOfCorePoint_s( const Vertex& p ); + int addPolygon_s( const std::vector< CoredVertexIndex >& vertices ); + int addPolygon_s( const std::vector< int >& vertices ); + + int nextOutOfCorePoint( Vertex& p ); + int nextPolygon( std::vector< CoredVertexIndex >& vertices ); + + int outOfCorePointCount( void ); + int polygonCount( void ); +}; +#include "Geometry.inl" + +#endif // GEOMETRY_INCLUDED diff --git a/Src/Geometry.inl b/Src/Geometry.inl new file mode 100644 index 0000000..97be936 --- /dev/null +++ b/Src/Geometry.inl @@ -0,0 +1,590 @@ +/* +Copyright (c) 2006, Michael Kazhdan and Matthew Bolitho +All rights reserved. + +Redistribution and use in source and binary forms, with or without modification, +are permitted provided that the following conditions are met: + +Redistributions of source code must retain the above copyright notice, this list of +conditions and the following disclaimer. Redistributions in binary form must reproduce +the above copyright notice, this list of conditions and the following disclaimer +in the documentation and/or other materials provided with the distribution. + +Neither the name of the Johns Hopkins University nor the names of its contributors +may be used to endorse or promote products derived from this software without specific +prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY +EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO THE IMPLIED WARRANTIES +OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT +SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED +TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR +BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN +ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH +DAMAGE. +*/ + +#include + +template +Real Random(void){return Real(rand())/RAND_MAX;} + +template +Point3D RandomBallPoint(void){ + Point3D p; + while(1){ + p.coords[0]=Real(1.0-2.0*Random()); + p.coords[1]=Real(1.0-2.0*Random()); + p.coords[2]=Real(1.0-2.0*Random()); + double l=SquareLength(p); + if(l<=1){return p;} + } +} +template +Point3D RandomSpherePoint(void){ + Point3D p=RandomBallPoint(); + Real l=Real(Length(p)); + p.coords[0]/=l; + p.coords[1]/=l; + p.coords[2]/=l; + return p; +} + +template +double SquareLength(const Point3D& p){return p.coords[0]*p.coords[0]+p.coords[1]*p.coords[1]+p.coords[2]*p.coords[2];} + +template +double Length(const Point3D& p){return sqrt(SquareLength(p));} + +template +double SquareDistance(const Point3D& p1,const Point3D& p2){ + return (p1.coords[0]-p2.coords[0])*(p1.coords[0]-p2.coords[0])+(p1.coords[1]-p2.coords[1])*(p1.coords[1]-p2.coords[1])+(p1.coords[2]-p2.coords[2])*(p1.coords[2]-p2.coords[2]); +} + +template +double Distance(const Point3D& p1,const Point3D& p2){return sqrt(SquareDistance(p1,p2));} + +template +void CrossProduct(const Point3D& p1,const Point3D& p2,Point3D& p){ + p.coords[0]= p1.coords[1]*p2.coords[2]-p1.coords[2]*p2.coords[1]; + p.coords[1]=-p1.coords[0]*p2.coords[2]+p1.coords[2]*p2.coords[0]; + p.coords[2]= p1.coords[0]*p2.coords[1]-p1.coords[1]*p2.coords[0]; +} +template +void EdgeCollapse(const Real& edgeRatio,std::vector& triangles,std::vector< Point3D >& positions,std::vector< Point3D >* normals){ + int i,j,*remapTable,*pointCount,idx[3]; + Point3D p[3],q[2],c; + double d[3],a; + double Ratio=12.0/sqrt(3.0); // (Sum of Squares Length / Area) for and equilateral triangle + + remapTable=new int[positions.size()]; + pointCount=new int[positions.size()]; + for(i=0;i=0;i--){ + for(j=0;j<3;j++){ + idx[j]=triangles[i].idx[j]; + while(remapTable[idx[j]] a*Ratio){ + // Find the smallest edge + j=0; + if(d[1]=0;i--){ + for(j=0;j<3;j++){ + idx[j]=triangles[i].idx[j]; + while(remapTable[idx[j]] +void TriangleCollapse(const Real& edgeRatio,std::vector& triangles,std::vector< Point3D >& positions,std::vector< Point3D >* normals){ + int i,j,*remapTable,*pointCount,idx[3]; + Point3D p[3],q[2],c; + double d[3],a; + double Ratio=12.0/sqrt(3.0); // (Sum of Squares Length / Area) for and equilateral triangle + + remapTable=new int[positions.size()]; + pointCount=new int[positions.size()]; + for(i=0;i=0;i--){ + for(j=0;j<3;j++){ + idx[j]=triangles[i].idx[j]; + while(remapTable[idx[j]] a*Ratio){ + // Find the smallest edge + j=0; + if(d[1]=0;i--){ + for(j=0;j<3;j++){ + idx[j]=triangles[i].idx[j]; + while(remapTable[idx[j]] +long long Triangulation::EdgeIndex( int p1 , int p2 ) +{ + if(p1>p2) {return ((long long)(p1)<<32) | ((long long)(p2));} + else {return ((long long)(p2)<<32) | ((long long)(p1));} +} + +template +int Triangulation::factor(int tIndex,int& p1,int& p2,int & p3){ + if(triangles[tIndex].eIndex[0]<0 || triangles[tIndex].eIndex[1]<0 || triangles[tIndex].eIndex[2]<0){return 0;} + if(edges[triangles[tIndex].eIndex[0]].tIndex[0]==tIndex){p1=edges[triangles[tIndex].eIndex[0]].pIndex[0];} + else {p1=edges[triangles[tIndex].eIndex[0]].pIndex[1];} + if(edges[triangles[tIndex].eIndex[1]].tIndex[0]==tIndex){p2=edges[triangles[tIndex].eIndex[1]].pIndex[0];} + else {p2=edges[triangles[tIndex].eIndex[1]].pIndex[1];} + if(edges[triangles[tIndex].eIndex[2]].tIndex[0]==tIndex){p3=edges[triangles[tIndex].eIndex[2]].pIndex[0];} + else {p3=edges[triangles[tIndex].eIndex[2]].pIndex[1];} + return 1; +} +template +double Triangulation::area(int p1,int p2,int p3){ + Point3D q1,q2,q; + for(int i=0;i<3;i++){ + q1.coords[i]=points[p2].coords[i]-points[p1].coords[i]; + q2.coords[i]=points[p3].coords[i]-points[p1].coords[i]; + } + CrossProduct(q1,q2,q); + return Length(q); +} +template +double Triangulation::area(int tIndex){ + int p1,p2,p3; + factor(tIndex,p1,p2,p3); + return area(p1,p2,p3); +} +template +double Triangulation::area(void){ + double a=0; + for(int i=0;i +int Triangulation::addTriangle(int p1,int p2,int p3){ + hash_map::iterator iter; + int tIdx,eIdx,p[3]; + p[0]=p1; + p[1]=p2; + p[2]=p3; + triangles.push_back(TriangulationTriangle()); + tIdx=int(triangles.size())-1; + + for(int i=0;i<3;i++) + { + long long e = EdgeIndex(p[i],p[(i+1)%3]); + iter=edgeMap.find(e); + if(iter==edgeMap.end()) + { + TriangulationEdge edge; + edge.pIndex[0]=p[i]; + edge.pIndex[1]=p[(i+1)%3]; + edges.push_back(edge); + eIdx=int(edges.size())-1; + edgeMap[e]=eIdx; + edges[eIdx].tIndex[0]=tIdx; + } + else{ + eIdx=edgeMap[e]; + if(edges[eIdx].pIndex[0]==p[i]){ + if(edges[eIdx].tIndex[0]<0){edges[eIdx].tIndex[0]=tIdx;} + else{printf("Edge Triangle in use 1\n");return 0;} + } + else{ + if(edges[eIdx].tIndex[1]<0){edges[eIdx].tIndex[1]=tIdx;} + else{printf("Edge Triangle in use 2\n");return 0;} + } + + } + triangles[tIdx].eIndex[i]=eIdx; + } + return tIdx; +} +template +int Triangulation::flipMinimize(int eIndex){ + double oldArea,newArea; + int oldP[3],oldQ[3],newP[3],newQ[3]; + TriangulationEdge newEdge; + + if(edges[eIndex].tIndex[0]<0 || edges[eIndex].tIndex[1]<0){return 0;} + + if(!factor(edges[eIndex].tIndex[0],oldP[0],oldP[1],oldP[2])){return 0;} + if(!factor(edges[eIndex].tIndex[1],oldQ[0],oldQ[1],oldQ[2])){return 0;} + + oldArea=area(oldP[0],oldP[1],oldP[2])+area(oldQ[0],oldQ[1],oldQ[2]); + int idxP,idxQ; + for(idxP=0;idxP<3;idxP++){ + int i; + for(i=0;i<3;i++){if(oldP[idxP]==oldQ[i]){break;}} + if(i==3){break;} + } + for(idxQ=0;idxQ<3;idxQ++){ + int i; + for(i=0;i<3;i++){if(oldP[i]==oldQ[idxQ]){break;}} + if(i==3){break;} + } + if(idxP==3 || idxQ==3){return 0;} + newP[0]=oldP[idxP]; + newP[1]=oldP[(idxP+1)%3]; + newP[2]=oldQ[idxQ]; + newQ[0]=oldQ[idxQ]; + newQ[1]=oldP[(idxP+2)%3]; + newQ[2]=oldP[idxP]; + + newArea=area(newP[0],newP[1],newP[2])+area(newQ[0],newQ[1],newQ[2]); + if(oldArea<=newArea){return 0;} + + // Remove the entry in the hash_table for the old edge + edgeMap.erase(EdgeIndex(edges[eIndex].pIndex[0],edges[eIndex].pIndex[1])); + // Set the new edge so that the zero-side is newQ + edges[eIndex].pIndex[0]=newP[0]; + edges[eIndex].pIndex[1]=newQ[0]; + // Insert the entry into the hash_table for the new edge + edgeMap[EdgeIndex(newP[0],newQ[0])]=eIndex; + // Update the triangle information + for(int i=0;i<3;i++){ + int idx; + idx=edgeMap[EdgeIndex(newQ[i],newQ[(i+1)%3])]; + triangles[edges[eIndex].tIndex[0]].eIndex[i]=idx; + if(idx!=eIndex){ + if(edges[idx].tIndex[0]==edges[eIndex].tIndex[1]){edges[idx].tIndex[0]=edges[eIndex].tIndex[0];} + if(edges[idx].tIndex[1]==edges[eIndex].tIndex[1]){edges[idx].tIndex[1]=edges[eIndex].tIndex[0];} + } + + idx=edgeMap[EdgeIndex(newP[i],newP[(i+1)%3])]; + triangles[edges[eIndex].tIndex[1]].eIndex[i]=idx; + if(idx!=eIndex){ + if(edges[idx].tIndex[0]==edges[eIndex].tIndex[0]){edges[idx].tIndex[0]=edges[eIndex].tIndex[1];} + if(edges[idx].tIndex[1]==edges[eIndex].tIndex[0]){edges[idx].tIndex[1]=edges[eIndex].tIndex[1];} + } + } + return 1; +} +///////////////////////// +// CoredVectorMeshData // +///////////////////////// +template< class Vertex > +CoredVectorMeshData< Vertex >::CoredVectorMeshData( void ) { oocPointIndex = polygonIndex = 0; } +template< class Vertex > +void CoredVectorMeshData< Vertex >::resetIterator ( void ) { oocPointIndex = polygonIndex = 0; } +template< class Vertex > +int CoredVectorMeshData< Vertex >::addOutOfCorePoint( const Vertex& p ) +{ + oocPoints.push_back(p); + return int(oocPoints.size())-1; +} +template< class Vertex > +int CoredVectorMeshData< Vertex >::addOutOfCorePoint_s( const Vertex& p ) +{ + size_t sz; +#pragma omp critical (CoredVectorMeshData_addOutOfCorePoint_s ) + { + sz = oocPoints.size(); + oocPoints.push_back(p); + } + return (int)sz; +} +template< class Vertex > +int CoredVectorMeshData< Vertex >::addPolygon_s( const std::vector< int >& polygon ) +{ + size_t sz; +#pragma omp critical (CoredVectorMeshData_addPolygon_s) + { + sz = polygon.size(); + polygons.push_back( polygon ); + } + return (int)sz; +} +template< class Vertex > +int CoredVectorMeshData< Vertex >::addPolygon_s( const std::vector< CoredVertexIndex >& vertices ) +{ + std::vector< int > polygon( vertices.size() ); + for( int i=0 ; i<(int)vertices.size() ; i++ ) + if( vertices[i].inCore ) polygon[i] = vertices[i].idx; + else polygon[i] = -vertices[i].idx-1; + return addPolygon_s( polygon ); +} +template< class Vertex > +int CoredVectorMeshData< Vertex >::nextOutOfCorePoint( Vertex& p ) +{ + if( oocPointIndex +int CoredVectorMeshData< Vertex >::nextPolygon( std::vector< CoredVertexIndex >& vertices ) +{ + if( polygonIndex& polygon = polygons[ polygonIndex++ ]; + vertices.resize( polygon.size() ); + for( int i=0 ; i +int CoredVectorMeshData< Vertex >::outOfCorePointCount(void){return int(oocPoints.size());} +template< class Vertex > +int CoredVectorMeshData< Vertex >::polygonCount( void ) { return int( polygons.size() ); } + +/////////////////////// +// CoredFileMeshData // +/////////////////////// +template< class Vertex > +CoredFileMeshData< Vertex >::CoredFileMeshData( void ) +{ + oocPoints = polygons = 0; + + oocPointFile = new BufferedReadWriteFile(); + polygonFile = new BufferedReadWriteFile(); +} +template< class Vertex > +CoredFileMeshData< Vertex >::~CoredFileMeshData( void ) +{ + delete oocPointFile; + delete polygonFile; +} +template< class Vertex > +void CoredFileMeshData< Vertex >::resetIterator ( void ) +{ + oocPointFile->reset(); + polygonFile->reset(); +} +template< class Vertex > +int CoredFileMeshData< Vertex >::addOutOfCorePoint( const Vertex& p ) +{ + oocPointFile->write( &p , sizeof( Vertex ) ); + oocPoints++; + return oocPoints-1; +} +template< class Vertex > +int CoredFileMeshData< Vertex >::addOutOfCorePoint_s( const Vertex& p ) +{ + int sz; +#pragma omp critical (CoredFileMeshData_addOutOfCorePoint_s) + { + sz = oocPoints; + oocPointFile->write( &p , sizeof( Vertex ) ); + oocPoints++; + } + return sz; +} +template< class Vertex > +int CoredFileMeshData< Vertex >::addPolygon_s( const std::vector< int >& vertices ) +{ + int sz , vSize = (int)vertices.size(); +#pragma omp critical (CoredFileMeshData_addPolygon_s ) + { + sz = polygons; + polygonFile->write( &vSize , sizeof(int) ); + polygonFile->write( &vertices[0] , sizeof(int) * vSize ); + polygons++; + } + return sz; +} +template< class Vertex > +int CoredFileMeshData< Vertex >::addPolygon_s( const std::vector< CoredVertexIndex >& vertices ) +{ + std::vector< int > polygon( vertices.size() ); + for( int i=0 ; i<(int)vertices.size() ; i++ ) + if( vertices[i].inCore ) polygon[i] = vertices[i].idx; + else polygon[i] = -vertices[i].idx-1; + return addPolygon_s( polygon ); +} +template< class Vertex > +int CoredFileMeshData< Vertex >::nextOutOfCorePoint( Vertex& p ) +{ + if( oocPointFile->read( &p , sizeof( Vertex ) ) ) return 1; + else return 0; +} +template< class Vertex > +int CoredFileMeshData< Vertex >::nextPolygon( std::vector< CoredVertexIndex >& vertices ) +{ + int pSize; + if( polygonFile->read( &pSize , sizeof(int) ) ) + { + std::vector< int > polygon( pSize ); + if( polygonFile->read( &polygon[0] , sizeof(int)*pSize ) ) + { + vertices.resize( pSize ); + for( int i=0 ; i +int CoredFileMeshData< Vertex >::outOfCorePointCount( void ){ return oocPoints; } +template< class Vertex > +int CoredFileMeshData< Vertex >::polygonCount( void ) { return polygons; } diff --git a/Src/Hash.h b/Src/Hash.h new file mode 100644 index 0000000..b66c073 --- /dev/null +++ b/Src/Hash.h @@ -0,0 +1,29 @@ +#ifndef HASH_INCLUDED +#define HASH_INCLUDED +#ifdef WIN32 +#include +using stdext::hash_map; +#else // !WIN32 +#include +using namespace __gnu_cxx; + +namespace __gnu_cxx +{ + template<> struct hash { + size_t operator()(long long __x) const { return __x; } + }; + template<> struct hash { + size_t operator()(const long long __x) const { return __x; } + }; + + + template<> struct hash { + size_t operator()(unsigned long long __x) const { return __x; } + }; + template<> struct hash { + size_t operator()(const unsigned long long __x) const { return __x; } + }; +} +#endif // WIN32 +#endif // HASH_INCLUDED + diff --git a/Src/MAT.h b/Src/MAT.h new file mode 100644 index 0000000..c090450 --- /dev/null +++ b/Src/MAT.h @@ -0,0 +1,48 @@ +/* +Copyright (c) 2007, Michael Kazhdan +All rights reserved. + +Redistribution and use in source and binary forms, with or without modification, +are permitted provided that the following conditions are met: + +Redistributions of source code must retain the above copyright notice, this list of +conditions and the following disclaimer. Redistributions in binary form must reproduce +the above copyright notice, this list of conditions and the following disclaimer +in the documentation and/or other materials provided with the distribution. + +Neither the name of the Johns Hopkins University nor the names of its contributors +may be used to endorse or promote products derived from this software without specific +prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY +EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO THE IMPLIED WARRANTIES +OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT +SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED +TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR +BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN +ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH +DAMAGE. +*/ +#ifndef MAT_INCLUDED +#define MAT_INCLUDED +#include "Geometry.h" + +template +class MinimalAreaTriangulation +{ + Real* bestTriangulation; + int* midPoint; + Real GetArea(const size_t& i,const size_t& j,const std::vector >& vertices); + void GetTriangulation(const size_t& i,const size_t& j,const std::vector >& vertices,std::vector& triangles); +public: + MinimalAreaTriangulation(void); + ~MinimalAreaTriangulation(void); + Real GetArea(const std::vector >& vertices); + void GetTriangulation(const std::vector >& vertices,std::vector& triangles); +}; + +#include "MAT.inl" + +#endif // MAT_INCLUDED diff --git a/Src/MAT.inl b/Src/MAT.inl new file mode 100644 index 0000000..eaa5ad0 --- /dev/null +++ b/Src/MAT.inl @@ -0,0 +1,213 @@ +/* +Copyright (c) 2007, Michael Kazhdan +All rights reserved. + +Redistribution and use in source and binary forms, with or without modification, +are permitted provided that the following conditions are met: + +Redistributions of source code must retain the above copyright notice, this list of +conditions and the following disclaimer. Redistributions in binary form must reproduce +the above copyright notice, this list of conditions and the following disclaimer +in the documentation and/or other materials provided with the distribution. + +Neither the name of the Johns Hopkins University nor the names of its contributors +may be used to endorse or promote products derived from this software without specific +prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY +EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO THE IMPLIED WARRANTIES +OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT +SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED +TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR +BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN +ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH +DAMAGE. +*/ +////////////////////////////// +// MinimalAreaTriangulation // +////////////////////////////// +template +MinimalAreaTriangulation::MinimalAreaTriangulation(void) +{ + bestTriangulation=NULL; + midPoint=NULL; +} +template +MinimalAreaTriangulation::~MinimalAreaTriangulation(void) +{ + if(bestTriangulation) + delete[] bestTriangulation; + bestTriangulation=NULL; + if(midPoint) + delete[] midPoint; + midPoint=NULL; +} +template +void MinimalAreaTriangulation::GetTriangulation(const std::vector >& vertices,std::vector& triangles) +{ + if(vertices.size()==3) + { + triangles.resize(1); + triangles[0].idx[0]=0; + triangles[0].idx[1]=1; + triangles[0].idx[2]=2; + return; + } + else if(vertices.size()==4) + { + TriangleIndex tIndex[2][2]; + Real area[2]; + + area[0]=area[1]=0; + triangles.resize(2); + + tIndex[0][0].idx[0]=0; + tIndex[0][0].idx[1]=1; + tIndex[0][0].idx[2]=2; + tIndex[0][1].idx[0]=2; + tIndex[0][1].idx[1]=3; + tIndex[0][1].idx[2]=0; + + tIndex[1][0].idx[0]=0; + tIndex[1][0].idx[1]=1; + tIndex[1][0].idx[2]=3; + tIndex[1][1].idx[0]=3; + tIndex[1][1].idx[1]=1; + tIndex[1][1].idx[2]=2; + + Point3D n,p1,p2; + for(int i=0;i<2;i++) + for(int j=0;j<2;j++) + { + p1=vertices[tIndex[i][j].idx[1]]-vertices[tIndex[i][j].idx[0]]; + p2=vertices[tIndex[i][j].idx[2]]-vertices[tIndex[i][j].idx[0]]; + CrossProduct(p1,p2,n); + area[i] += Real( Length(n) ); + } + if(area[0]>area[1]) + { + triangles[0]=tIndex[1][0]; + triangles[1]=tIndex[1][1]; + } + else + { + triangles[0]=tIndex[0][0]; + triangles[1]=tIndex[0][1]; + } + return; + } + if(bestTriangulation) + delete[] bestTriangulation; + if(midPoint) + delete[] midPoint; + bestTriangulation=NULL; + midPoint=NULL; + size_t eCount=vertices.size(); + bestTriangulation=new Real[eCount*eCount]; + midPoint=new int[eCount*eCount]; + for(size_t i=0;i +Real MinimalAreaTriangulation::GetArea(const std::vector >& vertices) +{ + if(bestTriangulation) + delete[] bestTriangulation; + if(midPoint) + delete[] midPoint; + bestTriangulation=NULL; + midPoint=NULL; + int eCount=vertices.size(); + bestTriangulation=new double[eCount*eCount]; + midPoint=new int[eCount*eCount]; + for(int i=0;i +void MinimalAreaTriangulation::GetTriangulation(const size_t& i,const size_t& j,const std::vector >& vertices,std::vector& triangles) +{ + TriangleIndex tIndex; + size_t eCount=vertices.size(); + size_t ii=i; + if(i=ii) + return; + ii=midPoint[i*eCount+j]; + if(ii>=0) + { + tIndex.idx[0] = int( i ); + tIndex.idx[1] = int( j ); + tIndex.idx[2] = int( ii ); + triangles.push_back(tIndex); + GetTriangulation(i,ii,vertices,triangles); + GetTriangulation(ii,j,vertices,triangles); + } +} + +template +Real MinimalAreaTriangulation::GetArea(const size_t& i,const size_t& j,const std::vector >& vertices) +{ + Real a=FLT_MAX,temp; + size_t eCount=vertices.size(); + size_t idx=i*eCount+j; + size_t ii=i; + if(i=ii) + { + bestTriangulation[idx]=0; + return 0; + } + if(midPoint[idx]!=-1) + return bestTriangulation[idx]; + int mid=-1; + for(size_t r=j+1;r p,p1,p2; + p1=vertices[i]-vertices[rr]; + p2=vertices[j]-vertices[rr]; + CrossProduct(p1,p2,p); + temp = Real( Length(p) ); + if(bestTriangulation[idx1]>=0) + { + temp+=bestTriangulation[idx1]; + if(temp>a) + continue; + if(bestTriangulation[idx2]>0) + temp+=bestTriangulation[idx2]; + else + temp+=GetArea(rr,j,vertices); + } + else + { + if(bestTriangulation[idx2]>=0) + temp+=bestTriangulation[idx2]; + else + temp+=GetArea(rr,j,vertices); + if(temp>a) + continue; + temp+=GetArea(i,rr,vertices); + } + + if(temp +#include "MarchingCubes.h" + +//////////// +// Square // +//////////// +int Square::AntipodalCornerIndex(int idx){ + int x,y; + FactorCornerIndex(idx,x,y); + return CornerIndex( (x+1)%2 , (y+1)%2 ); +} +int Square::CornerIndex( int x , int y ){ return (y<<1)|x; } +void Square::FactorCornerIndex( int idx , int& x , int& y ){ x=(idx>>0)&1 , y=(idx>>1)&1; } +int Square::EdgeIndex( int orientation , int i ) +{ + switch( orientation ) + { + case 0: // x + if( !i ) return 0; // (0,0) -> (1,0) + else return 2; // (0,1) -> (1,1) + case 1: // y + if( !i ) return 3; // (0,0) -> (0,1) + else return 1; // (1,0) -> (1,1) + }; + return -1; +} +void Square::FactorEdgeIndex(int idx,int& orientation,int& i){ + switch(idx){ + case 0: case 2: + orientation=0; + i=idx/2; + return; + case 1: case 3: + orientation=1; + i=((idx/2)+1)%2; + return; + }; +} +void Square::EdgeCorners(int idx,int& c1,int& c2){ + int orientation,i; + FactorEdgeIndex(idx,orientation,i); + switch(orientation){ + case 0: + c1 = CornerIndex(0,i); + c2 = CornerIndex(1,i); + break; + case 1: + c1 = CornerIndex(i,0); + c2 = CornerIndex(i,1); + break; + }; +} +int Square::ReflectEdgeIndex(int idx,int edgeIndex){ + int orientation=edgeIndex%2; + int o,i; + FactorEdgeIndex(idx,o,i); + if(o!=orientation){return idx;} + else{return EdgeIndex(o,(i+1)%2);} +} +int Square::ReflectCornerIndex(int idx,int edgeIndex){ + int orientation=edgeIndex%2; + int x,y; + FactorCornerIndex(idx,x,y); + switch(orientation){ + case 0: return CornerIndex((x+1)%2,y); + case 1: return CornerIndex(x,(y+1)%2); + }; + return -1; +} + + + +////////// +// Cube // +////////// +int Cube::CornerIndex( int x , int y , int z ){ return (z<<2)|(y<<1)|x; } +void Cube::FactorCornerIndex( int idx , int& x , int& y , int& z ){ x = (idx>>0)&1 , y = (idx>>1)&1 , z = (idx>>2)&1; } +int Cube::EdgeIndex(int orientation,int i,int j){return (i | (j<<1))|(orientation<<2);} +void Cube::FactorEdgeIndex( int idx , int& orientation , int& i , int &j ) +{ + orientation=idx>>2; + i = (idx&1); + j = (idx&2)>>1; +} +int Cube::FaceIndex( int x , int y , int z ) +{ + if ( x<0 ) return 0; + else if( x>0 ) return 1; + else if( y<0 ) return 2; + else if( y>0 ) return 3; + else if( z<0 ) return 4; + else if( z>0 ) return 5; + else return -1; +} +int Cube::FaceIndex( int dir , int offSet ){ return (dir<<1)|offSet; } + +void Cube::FactorFaceIndex( int idx , int& x , int& y , int& z ) +{ + x=y=z=0; + switch( idx ) + { + case 0: x=-1; break; + case 1: x= 1; break; + case 2: y=-1; break; + case 3: y= 1; break; + case 4: z=-1; break; + case 5: z= 1; break; + }; +} +void Cube::FactorFaceIndex( int idx , int& dir , int& offSet ) +{ + dir = idx>>1; + offSet=idx &1; +} +bool Cube::IsEdgeCorner( int cIndex , int e ) +{ + int o , i , j; + FactorEdgeIndex( e , o , i , j ); + switch( o ) + { + case 0: return (cIndex && 2)==(i<<1) && (cIndex && 4)==(j<<2); + case 1: return (cIndex && 1)==(i<<0) && (cIndex && 4)==(j<<2); + case 2: return (cIndex && 4)==(i<<2) && (cIndex && 2)==(j<<1); + default: return false; + } +} +bool Cube::IsFaceCorner( int cIndex , int f ) +{ + int dir , off; + FactorFaceIndex( f , dir , off ); + return ( cIndex & (1< (1,0) +1} // (1,0) -> (1,1) +2} // (0,1) -> (1,1) +3} // (0,0) -> (0,1) +*/ +const int MarchingSquares::edgeMask[1< -> -> + 9, // 1 -> 0 -> (0,0) -> 0,3 -> 9 + 3, // 2 -> 1 -> (1,0) -> 0,1 -> 3 + 10, // 3 -> 0,1 -> (0,0) (1,0) -> 1,3 -> 10 + 12, // 4 -> 2 -> (0,1) -> 2,3 -> 12 + 5, // 5 -> 0,2 -> (0,0) (0,1) -> 0,2 -> 5 + 15, // 6 -> 1,2 -> (1,0) (0,1) -> 0,1,2,3 -> 15 + 6, // 7 -> 0,1,2 -> (0,0) (1,0) (0,1) -> 1,2 -> 6 + 6, // 8 -> 3 -> (1,1) -> 1,2 -> 6 + 15, // 9 -> 0,3 -> (0,0) (1,1) -> 0,1,2,3 -> 15 + 5, // 10 -> 1,3 -> (1,0) (1,1) -> 0,2 -> 5 + 12, // 11 -> 0,1,3 -> (0,0) (1,0) (1,1) -> 2,3 -> 12 + 10, // 12 -> 2,3 -> (0,1) (1,1) -> 1,3 -> 10 + 3, // 13 -> 0,2,3 -> (0,0) (0,1) (1,1) -> 0,1 -> 3 + 9, // 14 -> 1,2,3 -> (1,0) (0,1) (1,1) -> 0,3 -> 9 + 0, // 15 -> 0,1,2,3 -> (0,0) (1,0) (0,1) (1,1) -> +}; +#if NEW_ORDERING +/* +0} // (0,0) -> (1,0) +1} // (1,0) -> (1,1) +2} // (0,1) -> (1,1) +3} // (0,0) -> (0,1) +*/ +const int MarchingSquares::edges[1<0){for(i=0;i<2;i++){for(j=0;j<2;j++){v[i][j]=values[Cube::CornerIndex(1,i,j)];}}} + else if (y<0){for(i=0;i<2;i++){for(j=0;j<2;j++){v[i][j]=values[Cube::CornerIndex(i,0,j)];}}} + else if (y>0){for(i=0;i<2;i++){for(j=0;j<2;j++){v[i][j]=values[Cube::CornerIndex(i,1,j)];}}} + else if (z<0){for(i=0;i<2;i++){for(j=0;j<2;j++){v[i][j]=values[Cube::CornerIndex(i,j,0)];}}} + else if (z>0){for(i=0;i<2;i++){for(j=0;j<2;j++){v[i][j]=values[Cube::CornerIndex(i,j,1)];}}} + if (v[0][0] < iso) idx |= 1; + if (v[1][0] < iso) idx |= 2; + if (v[1][1] < iso) idx |= 4; + if (v[0][1] < iso) idx |= 8; + return idx; +} +bool MarchingCubes::IsAmbiguous( const double v[Cube::CORNERS] , double isoValue , int faceIndex ){ return MarchingSquares::IsAmbiguous( GetFaceIndex( v , isoValue , faceIndex ) ); } +bool MarchingCubes::HasRoots( const double v[Cube::CORNERS] , double isoValue , int faceIndex ){ return MarchingSquares::HasRoots( GetFaceIndex( v , isoValue , faceIndex ) ); } +bool MarchingCubes::HasRoots( const double v[Cube::CORNERS] , double isoValue ){ return HasRoots( GetIndex( v , isoValue ) ); } +bool MarchingCubes::HasRoots( unsigned char mcIndex ){ return !(mcIndex==0 || mcIndex==255); } +int MarchingCubes::AddTriangles( const double v[Cube::CORNERS] , double iso , Triangle* isoTriangles ) +{ + unsigned char idx; + int ntriang=0; + Triangle tri; + + idx=GetIndex(v,iso); + + /* Cube is entirely in/out of the surface */ + if (!edgeMask[idx]) return 0; + + /* Find the vertices where the surface intersects the cube */ + int i,j,ii=1; + for(i=0;i<12;i++){ + if(edgeMask[idx] & ii){SetVertex(i,v,iso);} + ii<<=1; + } + /* Create the triangle */ + for( i=0 ; triangles[idx][i]!=-1 ; i+=3 ) + { + for(j=0;j<3;j++){ + tri.p[0][j]=vertexList[triangles[idx][i+0]][j]; + tri.p[1][j]=vertexList[triangles[idx][i+1]][j]; + tri.p[2][j]=vertexList[triangles[idx][i+2]][j]; + } + isoTriangles[ntriang++]=tri; + } + return ntriang; +} + +int MarchingCubes::AddTriangleIndices(const double v[Cube::CORNERS],double iso,int* isoIndices){ + unsigned char idx; + int ntriang=0; + + idx=GetIndex(v,iso); + + /* Cube is entirely in/out of the surface */ + if (!edgeMask[idx]) return 0; + + /* Create the triangle */ + for(int i=0;triangles[idx][i]!=-1;i+=3){ + for(int j=0;j<3;j++){isoIndices[i+j]=triangles[idx][i+j];} + ntriang++; + } + return ntriang; +} + +void MarchingCubes::SetVertex( int e , const double values[Cube::CORNERS] , double iso ) +{ + double t; + int o , i1 , i2; + Cube::FactorEdgeIndex( e , o , i1 , i2 ); + switch( o ) + { + case 0: + t = Interpolate( values[ Cube::CornerIndex( 0 , i1 , i2 ) ] - iso , values[ Cube::CornerIndex( 1 , i1 , i2 ) ] - iso ); + vertexList[e][0] = t , vertexList[e][1] = i1 , vertexList[e][2] = i2; + break; + case 1: + t = Interpolate( values[ Cube::CornerIndex( i1 , 0 , i2 ) ] - iso , values[ Cube::CornerIndex( i1 , 1 , i2 ) ] - iso ); + vertexList[e][0] = i1 , vertexList[e][1] = t , vertexList[e][2] = i2; + break; + case 2: + t = Interpolate( values[ Cube::CornerIndex( i1 , i2 , 0 ) ] - iso , values[ Cube::CornerIndex( i1 , i2 , 1 ) ] - iso ); + vertexList[e][0] = i1 , vertexList[e][1] = i2 , vertexList[e][2] = t; + break; + } +} +double MarchingCubes::Interpolate( double v1 , double v2 ) { return v1/(v1-v2); } + + +/////////////////////////////////// +unsigned char MarchingCubes::GetIndex(const float v[Cube::CORNERS],float iso){ + unsigned char idx=0; + if (v[Cube::CornerIndex(0,0,0)] < iso) idx |= 1; + if (v[Cube::CornerIndex(1,0,0)] < iso) idx |= 2; + if (v[Cube::CornerIndex(1,1,0)] < iso) idx |= 4; + if (v[Cube::CornerIndex(0,1,0)] < iso) idx |= 8; + if (v[Cube::CornerIndex(0,0,1)] < iso) idx |= 16; + if (v[Cube::CornerIndex(1,0,1)] < iso) idx |= 32; + if (v[Cube::CornerIndex(1,1,1)] < iso) idx |= 64; + if (v[Cube::CornerIndex(0,1,1)] < iso) idx |= 128; + return idx; +} +unsigned char MarchingCubes::GetFaceIndex( const float values[Cube::CORNERS] , float iso , int faceIndex ) +{ + int i,j,x,y,z; + unsigned char idx=0; + double v[2][2]; + Cube::FactorFaceIndex(faceIndex,x,y,z); + if (x<0){for(i=0;i<2;i++){for(j=0;j<2;j++){v[i][j]=values[Cube::CornerIndex(0,i,j)];}}} + else if (x>0){for(i=0;i<2;i++){for(j=0;j<2;j++){v[i][j]=values[Cube::CornerIndex(1,i,j)];}}} + else if (y<0){for(i=0;i<2;i++){for(j=0;j<2;j++){v[i][j]=values[Cube::CornerIndex(i,0,j)];}}} + else if (y>0){for(i=0;i<2;i++){for(j=0;j<2;j++){v[i][j]=values[Cube::CornerIndex(i,1,j)];}}} + else if (z<0){for(i=0;i<2;i++){for(j=0;j<2;j++){v[i][j]=values[Cube::CornerIndex(i,j,0)];}}} + else if (z>0){for(i=0;i<2;i++){for(j=0;j<2;j++){v[i][j]=values[Cube::CornerIndex(i,j,1)];}}} + if (v[0][0] < iso) idx |= 1; + if (v[1][0] < iso) idx |= 2; + if (v[1][1] < iso) idx |= 4; + if (v[0][1] < iso) idx |= 8; + return idx; +} +unsigned char MarchingCubes::GetFaceIndex( unsigned char mcIndex , int faceIndex ) +{ + int i,j,x,y,z; + unsigned char idx=0; + int v[2][2]; + Cube::FactorFaceIndex(faceIndex,x,y,z); + if (x<0){for(i=0;i<2;i++){for(j=0;j<2;j++){v[i][j]=mcIndex&(1<0){for(i=0;i<2;i++){for(j=0;j<2;j++){v[i][j]=mcIndex&(1<0){for(i=0;i<2;i++){for(j=0;j<2;j++){v[i][j]=mcIndex&(1<0){for(i=0;i<2;i++){for(j=0;j<2;j++){v[i][j]=mcIndex&(1< +#include "Geometry.h" + +#define NEW_ORDERING 1 + +class Square +{ +public: + const static unsigned int CORNERS=4 , EDGES=4 , FACES=1; + static int CornerIndex (int x,int y); + static int AntipodalCornerIndex(int idx); + static void FactorCornerIndex (int idx,int& x,int& y); + static int EdgeIndex (int orientation,int i); + static void FactorEdgeIndex (int idx,int& orientation,int& i); + + static int ReflectCornerIndex (int idx,int edgeIndex); + static int ReflectEdgeIndex (int idx,int edgeIndex); + + static void EdgeCorners(int idx,int& c1,int &c2); +}; + +class Cube{ +public: + const static unsigned int CORNERS=8 , EDGES=12 , FACES=6; + + static int CornerIndex ( int x , int y , int z ); + static void FactorCornerIndex ( int idx , int& x , int& y , int& z ); + static int EdgeIndex ( int orientation , int i , int j ); + static void FactorEdgeIndex ( int idx , int& orientation , int& i , int &j); + static int FaceIndex ( int dir , int offSet ); + static int FaceIndex ( int x , int y , int z ); + static void FactorFaceIndex ( int idx , int& x , int &y , int& z ); + static void FactorFaceIndex ( int idx , int& dir , int& offSet ); + + static int AntipodalCornerIndex ( int idx ); + static int FaceReflectCornerIndex ( int idx , int faceIndex ); + static int FaceReflectEdgeIndex ( int idx , int faceIndex ); + static int FaceReflectFaceIndex ( int idx , int faceIndex ); + static int EdgeReflectCornerIndex ( int idx , int edgeIndex ); + static int EdgeReflectEdgeIndex ( int edgeIndex ); + + static int FaceAdjacentToEdges ( int eIndex1 , int eIndex2 ); + static void FacesAdjacentToEdge ( int eIndex , int& f1Index , int& f2Index ); + + static void EdgeCorners( int idx , int& c1 , int &c2 ); + static void FaceCorners( int idx , int& c1 , int &c2 , int& c3 , int& c4 ); + + static bool IsEdgeCorner( int cIndex , int e ); + static bool IsFaceCorner( int cIndex , int f ); +}; + +class MarchingSquares +{ + static double Interpolate(double v1,double v2); + static void SetVertex(int e,const double values[Square::CORNERS],double iso); +public: + const static unsigned int MAX_EDGES=2; + static const int edgeMask[1< +class MemoryInfo +{ +public: + size_t TotalPhysicalMemory; + size_t FreePhysicalMemory; + size_t TotalSwapSpace; + size_t FreeSwapSpace; + size_t TotalVirtualAddressSpace; + size_t FreeVirtualAddressSpace; + size_t PageSize; + + void set(void){ + MEMORYSTATUSEX Mem; + SYSTEM_INFO Info; + ZeroMemory( &Mem, sizeof(Mem)); + ZeroMemory( &Info, sizeof(Info)); + Mem.dwLength = sizeof(Mem); + ::GlobalMemoryStatusEx( &Mem ); + ::GetSystemInfo( &Info ); + + TotalPhysicalMemory = (size_t)Mem.ullTotalPhys; + FreePhysicalMemory = (size_t)Mem.ullAvailPhys; + TotalSwapSpace = (size_t)Mem.ullTotalPageFile; + FreeSwapSpace = (size_t)Mem.ullAvailPageFile; + TotalVirtualAddressSpace = (size_t)Mem.ullTotalVirtual; + FreeVirtualAddressSpace = (size_t)Mem.ullAvailVirtual; + PageSize = (size_t)Info.dwPageSize; + } + size_t usage(void) const {return TotalVirtualAddressSpace-FreeVirtualAddressSpace;} + + static size_t Usage(void){ + MEMORY_BASIC_INFORMATION mbi; + size_t dwMemUsed = 0; + PVOID pvAddress = 0; + + + memset(&mbi, 0, sizeof(MEMORY_BASIC_INFORMATION)); + while(VirtualQuery(pvAddress, &mbi, sizeof(MEMORY_BASIC_INFORMATION)) == sizeof(MEMORY_BASIC_INFORMATION)){ + if(mbi.State == MEM_COMMIT && mbi.Type == MEM_PRIVATE){dwMemUsed += mbi.RegionSize;} + pvAddress = ((BYTE*)mbi.BaseAddress) + mbi.RegionSize; + } + return dwMemUsed; + } +}; + +#else // !WIN32 + +#ifndef __APPLE__ // Linux variants + +#include +#include + +class MemoryInfo +{ + public: + static size_t Usage(void) + { + FILE* f = fopen("/proc/self/stat","rb"); + + int d; + long ld; + unsigned long lu; + unsigned long long llu; + char s[1024]; + char c; + + int pid; + unsigned long vm; + + int n = fscanf(f, "%d %s %c %d %d %d %d %d %lu %lu %lu %lu %lu %lu %lu %ld %ld %ld %ld %d %ld %llu %lu %ld %lu %lu %lu %lu %lu %lu %lu %lu %lu %lu %lu %lu %lu %d %d %lu %lu" + ,&pid ,s ,&c ,&d ,&d ,&d ,&d ,&d ,&lu ,&lu ,&lu ,&lu ,&lu ,&lu ,&lu ,&ld ,&ld ,&ld ,&ld ,&d ,&ld ,&llu ,&vm ,&ld ,&lu ,&lu ,&lu ,&lu ,&lu ,&lu ,&lu ,&lu ,&lu ,&lu ,&lu ,&lu ,&lu ,&d ,&d ,&lu ,&lu ); + + fclose(f); +/* +pid %d +comm %s +state %c +ppid %d +pgrp %d +session %d +tty_nr %d +tpgid %d +flags %lu +minflt %lu +cminflt %lu +majflt %lu +cmajflt %lu +utime %lu +stime %lu +cutime %ld +cstime %ld +priority %ld +nice %ld +0 %ld +itrealvalue %ld +starttime %lu +vsize %lu +rss %ld +rlim %lu +startcode %lu +endcode %lu +startstack %lu +kstkesp %lu +kstkeip %lu +signal %lu +blocked %lu +sigignore %lu +sigcatch %lu +wchan %lu +nswap %lu +cnswap %lu +exit_signal %d +processor %d +rt_priority %lu (since kernel 2.5.19) +policy %lu (since kernel 2.5.19) +*/ + return vm; + } + +}; +#else // __APPLE__: has no "/proc" pseudo-file system + +// Thanks to David O'Gwynn for providing this fix. +// This comes from a post by Michael Knight: +// +// http://miknight.blogspot.com/2005/11/resident-set-size-in-mac-os-x.html + +#include +#include +#include +#include +#include +#include +#include + +void getres(task_t task, unsigned long *rss, unsigned long *vs) +{ + struct task_basic_info t_info; + mach_msg_type_number_t t_info_count = TASK_BASIC_INFO_COUNT; + + task_info(task, TASK_BASIC_INFO, (task_info_t)&t_info, &t_info_count); + *rss = t_info.resident_size; + *vs = t_info.virtual_size; +} + +class MemoryInfo +{ + public: + static size_t Usage(void) + { + unsigned long rss, vs, psize; + task_t task = MACH_PORT_NULL; + + if (task_for_pid(current_task(), getpid(), &task) != KERN_SUCCESS) + abort(); + getres(task, &rss, &vs); + return rss; + } + +}; + +#endif // !__APPLE__ + +#endif // WIN32 + +#endif // MEMORY_USAGE_INCLUDE diff --git a/Src/MultiGridOctreeData.IsoSurface.inl b/Src/MultiGridOctreeData.IsoSurface.inl new file mode 100644 index 0000000..8a90e42 --- /dev/null +++ b/Src/MultiGridOctreeData.IsoSurface.inl @@ -0,0 +1,1128 @@ +/* +Copyright (c) 2006, Michael Kazhdan and Matthew Bolitho +All rights reserved. + +Redistribution and use in source and binary forms, with or without modification, +are permitted provided that the following conditions are met: + +Redistributions of source code must retain the above copyright notice, this list of +conditions and the following disclaimer. Redistributions in binary form must reproduce +the above copyright notice, this list of conditions and the following disclaimer +in the documentation and/or other materials provided with the distribution. + +Neither the name of the Johns Hopkins University nor the names of its contributors +may be used to endorse or promote products derived from this software without specific +prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY +EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO THE IMPLIED WARRANTIES +OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT +SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED +TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR +BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN +ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH +DAMAGE. +*/ + +#include "Octree.h" +#include "MyTime.h" +#include "MemoryUsage.h" +#include "MAT.h" + + + +template< class Real > +template< class Vertex > +Octree< Real >::SliceValues< Vertex >::SliceValues( void ) +{ + _oldCCount = _oldECount = _oldFCount = _oldNCount = 0; + cornerValues = NullPointer< Real >() ; cornerNormals = NullPointer< Point3D< Real > >() ; cornerSet = NullPointer< char >(); + edgeKeys = NullPointer< long long >() ; edgeSet = NullPointer< char >(); + faceEdges = NullPointer< FaceEdges >() ; faceSet = NullPointer< char >(); + mcIndices = NullPointer< char >(); +} +template< class Real > +template< class Vertex > +Octree< Real >::SliceValues< Vertex >::~SliceValues( void ) +{ + _oldCCount = _oldECount = _oldFCount = _oldNCount = 0; + FreePointer( cornerValues ) ; FreePointer( cornerNormals ) ; FreePointer( cornerSet ); + FreePointer( edgeKeys ) ; FreePointer( edgeSet ); + FreePointer( faceEdges ) ; FreePointer( faceSet ); + FreePointer( mcIndices ); +} +template< class Real > +template< class Vertex > +void Octree< Real >::SliceValues< Vertex >::reset( bool nonLinearFit ) +{ + faceEdgeMap.clear() , edgeVertexMap.clear() , vertexPairMap.clear(); + + if( _oldNCount0 ) mcIndices = AllocPointer< char >( _oldNCount ); + } + if( _oldCCount0 ) + { + cornerValues = AllocPointer< Real >( _oldCCount ); + if( nonLinearFit ) cornerNormals = AllocPointer< Point3D< Real > >( _oldCCount ); + cornerSet = AllocPointer< char >( _oldCCount ); + } + } + if( _oldECount( _oldECount ); + edgeSet = AllocPointer< char >( _oldECount ); + } + if( _oldFCount( _oldFCount ); + faceSet = AllocPointer< char >( _oldFCount ); + } + + if( sliceData.cCount>0 ) memset( cornerSet , 0 , sizeof( char ) * sliceData.cCount ); + if( sliceData.eCount>0 ) memset( edgeSet , 0 , sizeof( char ) * sliceData.eCount ); + if( sliceData.fCount>0 ) memset( faceSet , 0 , sizeof( char ) * sliceData.fCount ); +} +template< class Real > +template< class Vertex > +Octree< Real >::XSliceValues< Vertex >::XSliceValues( void ) +{ + _oldECount = _oldFCount = 0; + edgeKeys = NullPointer< long long >() ; edgeSet = NullPointer< char >(); + faceEdges = NullPointer< FaceEdges >() ; faceSet = NullPointer< char >(); +} +template< class Real > +template< class Vertex > +Octree< Real >::XSliceValues< Vertex >::~XSliceValues( void ) +{ + _oldECount = _oldFCount = 0; + FreePointer( edgeKeys ) ; FreePointer( edgeSet ); + FreePointer( faceEdges ) ; FreePointer( faceSet ); +} +template< class Real > +template< class Vertex > +void Octree< Real >::XSliceValues< Vertex >::reset( void ) +{ + faceEdgeMap.clear() , edgeVertexMap.clear() , vertexPairMap.clear(); + + if( _oldECount( _oldECount ); + edgeSet = AllocPointer< char >( _oldECount ); + } + if( _oldFCount( _oldFCount ); + faceSet = AllocPointer< char >( _oldFCount ); + } + if( xSliceData.eCount>0 ) memset( edgeSet , 0 , sizeof( char ) * xSliceData.eCount ); + if( xSliceData.fCount>0 ) memset( faceSet , 0 , sizeof( char ) * xSliceData.fCount ); +} + +template< class Real > +template< class Vertex > +void Octree< Real >::GetMCIsoSurface( ConstPointer( Real ) kernelDensityWeights , ConstPointer( Real ) solution , Real isoValue , CoredMeshData< Vertex >& mesh , bool nonLinearFit , bool addBarycenter , bool polygonMesh ) +{ + typename BSplineData< 2 >::template CornerEvaluator< 2 > evaluator; + _fData.setCornerEvaluator( evaluator , 0 , 0 , _boundaryType==0 ); + + int maxDepth = tree.maxDepth(); + + std::vector< Real > coarseSolution( _sNodes.nodeCount[maxDepth] , 0 ); +#pragma omp parallel for num_threads( threads ) + for( int i=_sNodes.nodeCount[_minDepth] ; i<_sNodes.nodeCount[maxDepth] ; i++ ) coarseSolution[i] = solution[i]; + for( int d=_minDepth ; d vStencils( maxDepth+1 ); + std::vector< CornerNormalStencil > nStencils( maxDepth+1 ); + for( int d=_minDepth ; d<=maxDepth ; d++ ) + { + SetCornerEvaluationStencil ( evaluator , d , vStencils[d].stencil ); + SetCornerEvaluationStencils( evaluator , d , vStencils[d].stencils ); + SetCornerNormalEvaluationStencil ( evaluator , d , nStencils[d].stencil ); + SetCornerNormalEvaluationStencils( evaluator , d , nStencils[d].stencils ); + } + int vertexOffset = 0; + std::vector< SlabValues< Vertex > > slabValues( maxDepth+1 ); + + // Initialize the back slice + for( int d=maxDepth ; d>=_minDepth ; d-- ) + { + _sNodes.setSliceTableData ( slabValues[d].sliceValues(0).sliceData , d , 0 , threads ); + _sNodes.setSliceTableData ( slabValues[d].sliceValues(1).sliceData , d , 1 , threads ); + _sNodes.setXSliceTableData( slabValues[d].xSliceValues(0).xSliceData , d , 0 , threads ); + slabValues[d].sliceValues (0).reset( nonLinearFit ); + slabValues[d].sliceValues (1).reset( nonLinearFit ); + slabValues[d].xSliceValues(0).reset( ); + } + for( int d=maxDepth ; d>=_minDepth ; d-- ) + { + // Copy edges from finer + if( d=_minDepth ; d-- , o>>=1 ) + { + // Copy edges from finer (required to ensure we correctly track edge cancellations) + if( d=_minDepth ; d-- , o>>=1 ) + { + // Initialize for the next pass + if( o<(1< +Real Octree< Real >::GetIsoValue( ConstPointer( Real ) solution , const std::vector< Real >& centerWeights ) +{ + Real isoValue=0 , weightSum=0; + int maxDepth = tree.maxDepth(); + + typename BSplineData< 2 >::template CenterEvaluator< 1 > evaluator; + _fData.setCenterEvaluator( evaluator , 0 , 0 , _boundaryType==0 ); + std::vector< CenterValueStencil > vStencils( maxDepth+1 ); + for( int d=_minDepth ; d<=maxDepth ; d++ ) + { + SetCenterEvaluationStencil ( evaluator , d , vStencils[d].stencil ); + SetCenterEvaluationStencils( evaluator , d , vStencils[d].stencils ); + } + std::vector< Real > metSolution( _sNodes.nodeCount[maxDepth] , 0 ); + std::vector< Real > centerValues( _sNodes.nodeCount[maxDepth+1] ); +#pragma omp parallel for num_threads( threads ) + for( int i=_sNodes.nodeCount[_minDepth] ; i<_sNodes.nodeCount[maxDepth] ; i++ ) metSolution[i] = solution[i]; + for( int d=_minDepth ; d=_minDepth ; d-- ) + { + std::vector< typename TreeOctNode::ConstNeighborKey3 > neighborKeys( std::max< int >( 1 , threads ) ); + for( int i=0 ; ichildren ) + { + for( int c=0 ; cchildren[c].nodeData.nodeIndex ]; + value /= Cube::CORNERS; + } + else + { + neighborKey.getNeighbors( node ); + int c=0 , x , y , z; + if( node->parent ) c = int( node - node->parent->children ); + Cube::FactorCornerIndex( c , x , y , z ); + + int d , off[3]; + node->depthAndOffset( d , off ); + int o = _boundaryType==0 ? (1<<(d-2)) : 0; + int mn = 2+o , mx = (1<=mn && off[0]=mn && off[1]=mn && off[2]nodeData.nodeIndex ]; + if( w!=0 ) isoValue += value * w , weightSum += w; + } + } + if( _boundaryType==-1 ) return isoValue/weightSum - Real(0.5); + else return isoValue/weightSum; +} + +template< class Real > +template< class Vertex > +void Octree< Real >::SetSliceIsoCorners( ConstPointer( Real ) solution , ConstPointer( Real ) coarseSolution , Real isoValue , int depth , int slice , std::vector< SlabValues< Vertex > >& slabValues , const typename BSplineData< 2 >::template CornerEvaluator< 2 >& evaluator , const Stencil< double , 3 > vStencil[8] , const Stencil< double , 3 > vStencils[8][8] , const Stencil< Point3D< double > , 3 > nStencil[8] , const Stencil< Point3D< double > , 3 > nStencils[8][8] , int threads ) +{ + if( slice>0 ) SetSliceIsoCorners( solution , coarseSolution , isoValue , depth , slice , 1 , slabValues , evaluator , vStencil , vStencils , nStencil , nStencils , threads ); + if( slice<(1< +template< class Vertex > +void Octree< Real >::SetSliceIsoCorners( ConstPointer( Real ) solution , ConstPointer( Real ) coarseSolution , Real isoValue , int depth , int slice , int z , std::vector< SlabValues< Vertex > >& slabValues , const typename BSplineData< 2 >::template CornerEvaluator< 2 >& evaluator , const Stencil< double , 3 > vStencil[8] , const Stencil< double , 3 > vStencils[8][8] , const Stencil< Point3D< double > , 3 > nStencil[8] , const Stencil< Point3D< double > , 3 > nStencils[8][8] , int threads ) +{ + typename Octree< Real >::template SliceValues< Vertex >& sValues = slabValues[depth].sliceValues( slice ); + std::vector< typename TreeOctNode::ConstNeighborKey3 > neighborKeys( std::max< int >( 1 , threads ) ); + for( int i=0 ; ichildren ) + { + const typename SortedTreeNodes::SquareCornerIndices& cIndices = sValues.sliceData.cornerIndices( leaf ); + + bool isInterior; + { + int d , off[3]; + leaf->depthAndOffset( d , off ); + int o = _boundaryType==0 ? (1<<(d-2)) : 0; + int mn = 2+o , mx = (1<=mn && off[0]=mn && off[1]=mn && off[2] > p = getCornerValueAndNormal( neighborKey , leaf , cc , solution , coarseSolution , evaluator , vStencil[cc] , vStencils[cc] , nStencil[cc] , nStencils[cc] , isInterior ); + sValues.cornerValues[vIndex] = p.first , sValues.cornerNormals[vIndex] = p.second; + } + else sValues.cornerValues[vIndex] = getCornerValue( neighborKey , leaf , cc , solution , coarseSolution , evaluator , vStencil[cc] , vStencils[cc] , isInterior ); + sValues.cornerSet[vIndex] = 1; + } + squareValues[fc] = sValues.cornerValues[ vIndex ]; + TreeOctNode* node = leaf; + int _depth = depth , _slice = slice; + while( node->parent && (node-node->parent->children)==cc ) + { + node = node->parent , _depth-- , _slice >>= 1; + typename Octree< Real >::template SliceValues< Vertex >& _sValues = slabValues[_depth].sliceValues( _slice ); + const typename SortedTreeNodes::SquareCornerIndices& _cIndices = _sValues.sliceData.cornerIndices( node ); + int _vIndex = _cIndices[fc]; + _sValues.cornerValues[_vIndex] = sValues.cornerValues[vIndex]; + if( _sValues.cornerNormals ) _sValues.cornerNormals[_vIndex] = sValues.cornerNormals[vIndex]; + _sValues.cornerSet[_vIndex] = 1; + } + } + sValues.mcIndices[ i - sValues.sliceData.nodeOffset ] = MarchingSquares::GetIndex( squareValues , isoValue ); + } + } +} + +template< class Real > +template< class Vertex > +void Octree< Real >::SetSliceIsoVertices( ConstPointer( Real ) kernelDensityWeight , Real isoValue , int depth , int slice , int& vOffset , CoredMeshData< Vertex >& mesh , std::vector< SlabValues< Vertex > >& slabValues , int threads ) +{ + if( slice>0 ) SetSliceIsoVertices( kernelDensityWeight , isoValue , depth , slice , 1 , vOffset , mesh , slabValues , threads ); + if( slice<(1< +template< class Vertex > +void Octree< Real >::SetSliceIsoVertices( ConstPointer( Real ) kernelDensityWeight , Real isoValue , int depth , int slice , int z , int& vOffset , CoredMeshData< Vertex >& mesh , std::vector< SlabValues< Vertex > >& slabValues , int threads ) +{ + typename Octree< Real >::template SliceValues< Vertex >& sValues = slabValues[depth].sliceValues( slice ); + std::vector< typename TreeOctNode::ConstNeighborKey3 > neighborKeys( std::max< int >( 1 , threads ) ); + for( int i=0 ; ichildren ) + { + int idx = i - sValues.sliceData.nodeOffset; + const typename SortedTreeNodes::SquareEdgeIndices& eIndices = sValues.sliceData.edgeIndices( leaf ); + if( MarchingSquares::HasRoots( sValues.mcIndices[idx] ) ) + { + neighborKey.getNeighbors( leaf ); + for( int e=0 ; e hashed_vertex; +#pragma omp critical (add_point_access) + { + if( !sValues.edgeSet[vIndex] ) + { + mesh.addOutOfCorePoint( vertex ); + sValues.edgeSet[ vIndex ] = 1; + sValues.edgeKeys[ vIndex ] = key; + sValues.edgeVertexMap[key] = hashed_vertex = std::pair< int , Vertex >( vOffset , vertex ); + vOffset++; + stillOwner = true; + } + } + if( stillOwner ) + { + // We only need to pass the iso-vertex down if the edge it lies on is adjacent to a coarser leaf + bool isNeeded; + switch( o ) + { + case 0: isNeeded = ( neighborKey.neighbors[depth].neighbors[1][2*y][1]==NULL || neighborKey.neighbors[depth].neighbors[1][2*y][2*z]==NULL || neighborKey.neighbors[depth].neighbors[1][1][2*z]==NULL ) ; break; + case 1: isNeeded = ( neighborKey.neighbors[depth].neighbors[2*y][1][1]==NULL || neighborKey.neighbors[depth].neighbors[2*y][1][2*z]==NULL || neighborKey.neighbors[depth].neighbors[1][1][2*z]==NULL ) ; break; + } + if( isNeeded ) + { + int f[2]; + Cube::FacesAdjacentToEdge( Cube::EdgeIndex( o , y , z ) , f[0] , f[1] ); + for( int k=0 ; k<2 ; k++ ) + { + TreeOctNode* node = leaf; + int _depth = depth , _slice = slice; + bool _isNeeded = isNeeded; +#if 1 + // Bug fix courtesy of asmaloney + while( _isNeeded && node->parent && Cube::IsFaceCorner( (int)(node-node->parent->children) , f[k] ) ) +#else + while( _isNeeded = node->parent && Cube::IsFaceCorner( (int)(node-node->parent->children) , f[k] ) ) +#endif + { + node = node->parent , _depth-- , _slice >>= 1; + typename Octree< Real >::template SliceValues< Vertex >& _sValues = slabValues[_depth].sliceValues( _slice ); +#pragma omp critical (add_coarser_point_access) + _sValues.edgeVertexMap[key] = hashed_vertex; + switch( o ) + { + case 0: _isNeeded = ( neighborKey.neighbors[_depth].neighbors[1][2*y][1]==NULL || neighborKey.neighbors[_depth].neighbors[1][2*y][2*z]==NULL || neighborKey.neighbors[_depth].neighbors[1][1][2*z]==NULL ) ; break; + case 1: _isNeeded = ( neighborKey.neighbors[_depth].neighbors[2*y][1][1]==NULL || neighborKey.neighbors[_depth].neighbors[2*y][1][2*z]==NULL || neighborKey.neighbors[_depth].neighbors[1][1][2*z]==NULL ) ; break; + } + } + } + } + } + } + } + } + } + } +} +template< class Real > +template< class Vertex > +void Octree< Real >::SetXSliceIsoVertices( ConstPointer( Real ) kernelDensityWeight , Real isoValue , int depth , int slab , int& vOffset , CoredMeshData< Vertex >& mesh , std::vector< SlabValues< Vertex > >& slabValues , int threads ) +{ + typename Octree< Real >::template SliceValues< Vertex >& bValues = slabValues[depth].sliceValues ( slab ); + typename Octree< Real >::template SliceValues< Vertex >& fValues = slabValues[depth].sliceValues ( slab+1 ); + typename Octree< Real >::template XSliceValues< Vertex >& xValues = slabValues[depth].xSliceValues( slab ); + + std::vector< typename TreeOctNode::ConstNeighborKey3 > neighborKeys( std::max< int >( 1 , threads ) ); + for( int i=0 ; ichildren ) + { + unsigned char mcIndex = ( bValues.mcIndices[ i - bValues.sliceData.nodeOffset ] ) | ( fValues.mcIndices[ i - fValues.sliceData.nodeOffset ] )<<4; + const typename SortedTreeNodes::SquareCornerIndices& eIndices = xValues.xSliceData.edgeIndices( leaf ); + if( MarchingCubes::HasRoots( mcIndex ) ) + { + neighborKey.getNeighbors( leaf ); + for( int x=0 ; x<2 ; x++ ) for( int y=0 ; y<2 ; y++ ) + { + int c = Square::CornerIndex( x , y ); + int e = Cube::EdgeIndex( 2 , x , y ); + if( MarchingCubes::HasEdgeRoots( mcIndex , e ) ) + { + int vIndex = eIndices[c]; + if( !xValues.edgeSet[vIndex] ) + { + Vertex vertex; + long long key = VertexData::EdgeIndex( leaf , e , _sNodes.maxDepth ); + GetIsoVertex( kernelDensityWeight , isoValue , neighborKey , leaf , c , bValues , fValues , vertex ); + vertex.point = vertex.point * _scale + _center; + bool stillOwner = false; + std::pair< int , Vertex > hashed_vertex; +#pragma omp critical (add_x_point_access) + { + if( !xValues.edgeSet[vIndex] ) + { + mesh.addOutOfCorePoint( vertex ); + xValues.edgeSet[ vIndex ] = 1; + xValues.edgeKeys[ vIndex ] = key; + xValues.edgeVertexMap[key] = hashed_vertex = std::pair< int , Vertex >( vOffset , vertex ); + stillOwner = true; + vOffset++; + } + } + if( stillOwner ) + { + // We only need to pass the iso-vertex down if the edge it lies on is adjacent to a coarser leaf + bool isNeeded = ( neighborKey.neighbors[depth].neighbors[2*x][1][1]==NULL || neighborKey.neighbors[depth].neighbors[2*x][2*y][1]==NULL || neighborKey.neighbors[depth].neighbors[1][2*y][1]==NULL ); + if( isNeeded ) + { + int f[2]; + Cube::FacesAdjacentToEdge( e , f[0] , f[1] ); + for( int k=0 ; k<2 ; k++ ) + { + TreeOctNode* node = leaf; + int _depth = depth , _slab = slab; + bool _isNeeded = isNeeded; + while( _isNeeded && node->parent && Cube::IsFaceCorner( (int)(node-node->parent->children) , f[k] ) ) + { + node = node->parent , _depth-- , _slab >>= 1; + typename Octree< Real >::template XSliceValues< Vertex >& _xValues = slabValues[_depth].xSliceValues( _slab ); +#pragma omp critical (add_x_coarser_point_access) + _xValues.edgeVertexMap[key] = hashed_vertex; + _isNeeded = ( neighborKey.neighbors[_depth].neighbors[2*x][1][1]==NULL || neighborKey.neighbors[_depth].neighbors[2*x][2*y][1]==NULL || neighborKey.neighbors[_depth].neighbors[1][2*y][1]==NULL ); + } + } + } + } + } + } + } + } + } + } +} +template< class Real > +template< class Vertex > +void Octree< Real >::CopyFinerSliceIsoEdgeKeys( int depth , int slice , std::vector< SlabValues< Vertex > >& slabValues , int threads ) +{ + if( slice>0 ) CopyFinerSliceIsoEdgeKeys( depth , slice , 1 , slabValues , threads ); + if( slice<(1< +template< class Vertex > +void Octree< Real >::CopyFinerSliceIsoEdgeKeys( int depth , int slice , int z , std::vector< SlabValues< Vertex > >& slabValues , int threads ) +{ + SliceValues< Vertex >& pSliceValues = slabValues[depth ].sliceValues(slice ); + SliceValues< Vertex >& cSliceValues = slabValues[depth+1].sliceValues(slice<<1); + typename SortedTreeNodes::SliceTableData& pSliceData = pSliceValues.sliceData; + typename SortedTreeNodes::SliceTableData& cSliceData = cSliceValues.sliceData; +#pragma omp parallel for num_threads( threads ) + for( int i=_sNodes.nodeCount[depth] + _sNodes.sliceOffsets[depth][slice-z] ; i<_sNodes.nodeCount[depth] + _sNodes.sliceOffsets[depth][slice-z+1] ; i++ ) + if( _sNodes.treeNodes[i]->children ) + { + typename SortedTreeNodes::SquareEdgeIndices& pIndices = pSliceData.edgeIndices( i ); + // Copy the edges that overlap the coarser edges + for( int orientation=0 ; orientation<2 ; orientation++ ) for( int y=0 ; y<2 ; y++ ) + { + int fe = Square::EdgeIndex( orientation , y ); + int pIndex = pIndices[fe]; + if( !pSliceValues.edgeSet[ pIndex ] ) + { + int ce = Cube::EdgeIndex( orientation , y , z ); + int c1 , c2; + switch( orientation ) + { + case 0: c1 = Cube::CornerIndex( 0 , y , z ) , c2 = Cube::CornerIndex( 1 , y , z ) ; break; + case 1: c1 = Cube::CornerIndex( y , 0 , z ) , c2 = Cube::CornerIndex( y , 1 , z ) ; break; + } + int cIndex1 = cSliceData.edgeIndices( _sNodes.treeNodes[i]->children + c1 )[fe]; + int cIndex2 = cSliceData.edgeIndices( _sNodes.treeNodes[i]->children + c2 )[fe]; + if( cSliceValues.edgeSet[cIndex1] != cSliceValues.edgeSet[cIndex2] ) + { + long long key; + if( cSliceValues.edgeSet[cIndex1] ) key = cSliceValues.edgeKeys[cIndex1]; + else key = cSliceValues.edgeKeys[cIndex2]; + std::pair< int , Vertex > vPair = cSliceValues.edgeVertexMap.find( key )->second; +#pragma omp critical ( copy_finer_edge_keys ) + pSliceValues.edgeVertexMap[key] = vPair; + pSliceValues.edgeKeys[pIndex] = key; + pSliceValues.edgeSet[pIndex] = 1; + } + else if( cSliceValues.edgeSet[cIndex1] && cSliceValues.edgeSet[cIndex2] ) + { + long long key1 = cSliceValues.edgeKeys[cIndex1] , key2 = cSliceValues.edgeKeys[cIndex2]; +#pragma omp critical ( set_edge_pairs ) + pSliceValues.vertexPairMap[ key1 ] = key2 , pSliceValues.vertexPairMap[ key2 ] = key1; + + const TreeOctNode* node = _sNodes.treeNodes[i]; + int _depth = depth , _slice = slice; + while( node->parent && Cube::IsEdgeCorner( (int)( node - node->parent->children ) , ce ) ) + { + node = node->parent , _depth-- , _slice >>= 1; + SliceValues< Vertex >& _pSliceValues = slabValues[_depth].sliceValues(_slice); +#pragma omp critical ( set_edge_pairs ) + _pSliceValues.vertexPairMap[ key1 ] = key2 , _pSliceValues.vertexPairMap[ key2 ] = key1; + } + } + } + } + } +} +template< class Real > +template< class Vertex > +void Octree< Real >::CopyFinerXSliceIsoEdgeKeys( int depth , int slab , std::vector< SlabValues< Vertex > >& slabValues , int threads ) +{ + XSliceValues< Vertex >& pSliceValues = slabValues[depth ].xSliceValues(slab); + XSliceValues< Vertex >& cSliceValues0 = slabValues[depth+1].xSliceValues( (slab<<1)|0 ); + XSliceValues< Vertex >& cSliceValues1 = slabValues[depth+1].xSliceValues( (slab<<1)|1 ); + typename SortedTreeNodes::XSliceTableData& pSliceData = pSliceValues.xSliceData; + typename SortedTreeNodes::XSliceTableData& cSliceData0 = cSliceValues0.xSliceData; + typename SortedTreeNodes::XSliceTableData& cSliceData1 = cSliceValues1.xSliceData; +#pragma omp parallel for num_threads( threads ) + for( int i=_sNodes.nodeCount[depth] + _sNodes.sliceOffsets[depth][slab] ; i<_sNodes.nodeCount[depth] + _sNodes.sliceOffsets[depth][slab+1] ; i++ ) + if( _sNodes.treeNodes[i]->children ) + { + typename SortedTreeNodes::SquareCornerIndices& pIndices = pSliceData.edgeIndices( i ); + for( int x=0 ; x<2 ; x++ ) for( int y=0 ; y<2 ; y++ ) + { + int fc = Square::CornerIndex( x , y ); + int pIndex = pIndices[fc]; + if( !pSliceValues.edgeSet[pIndex] ) + { + int c0 = Cube::CornerIndex( x , y , 0 ) , c1 = Cube::CornerIndex( x , y , 1 ); + int cIndex0 = cSliceData0.edgeIndices( _sNodes.treeNodes[i]->children + c0 )[fc]; + int cIndex1 = cSliceData1.edgeIndices( _sNodes.treeNodes[i]->children + c1 )[fc]; + if( cSliceValues0.edgeSet[cIndex0] != cSliceValues1.edgeSet[cIndex1] ) + { + long long key; + std::pair< int , Vertex > vPair; + if( cSliceValues0.edgeSet[cIndex0] ) key = cSliceValues0.edgeKeys[cIndex0] , vPair = cSliceValues0.edgeVertexMap.find( key )->second; + else key = cSliceValues1.edgeKeys[cIndex1] , vPair = cSliceValues1.edgeVertexMap.find( key )->second; +#pragma omp critical ( copy_finer_x_edge_keys ) + pSliceValues.edgeVertexMap[key] = vPair; + pSliceValues.edgeKeys[ pIndex ] = key; + pSliceValues.edgeSet[ pIndex ] = 1; + } + else if( cSliceValues0.edgeSet[cIndex0] && cSliceValues1.edgeSet[cIndex1] ) + { + long long key0 = cSliceValues0.edgeKeys[cIndex0] , key1 = cSliceValues1.edgeKeys[cIndex1]; +#pragma omp critical ( set_x_edge_pairs ) + pSliceValues.vertexPairMap[ key0 ] = key1 , pSliceValues.vertexPairMap[ key1 ] = key0; + const TreeOctNode* node = _sNodes.treeNodes[i]; + int _depth = depth , _slab = slab , ce = Cube::CornerIndex( 2 , x , y ); + while( node->parent && Cube::IsEdgeCorner( (int)( node - node->parent->children ) , ce ) ) + { + node = node->parent , _depth-- , _slab>>= 1; + SliceValues< Vertex >& _pSliceValues = slabValues[_depth].sliceValues(_slab); +#pragma omp critical ( set_x_edge_pairs ) + _pSliceValues.vertexPairMap[ key0 ] = key1 , _pSliceValues.vertexPairMap[ key1 ] = key0; + } + } + } + } + } +} +template< class Real > +template< class Vertex > +void Octree< Real >::SetSliceIsoEdges( int depth , int slice , std::vector< SlabValues< Vertex > >& slabValues , int threads ) +{ + if( slice>0 ) SetSliceIsoEdges( depth , slice , 1 , slabValues , threads ); + if( slice<(1< +template< class Vertex > +void Octree< Real >::SetSliceIsoEdges( int depth , int slice , int z , std::vector< SlabValues< Vertex > >& slabValues , int threads ) +{ + typename Octree< Real >::template SliceValues< Vertex >& sValues = slabValues[depth].sliceValues( slice ); + std::vector< typename TreeOctNode::ConstNeighborKey3 > neighborKeys( std::max< int >( 1 , threads ) ); + for( int i=0 ; ichildren ) + { + int idx = i - sValues.sliceData.nodeOffset; + const typename SortedTreeNodes::SquareEdgeIndices& eIndices = sValues.sliceData.edgeIndices( leaf ); + const typename SortedTreeNodes::SquareFaceIndices& fIndices = sValues.sliceData.faceIndices( leaf ); + unsigned char mcIndex = sValues.mcIndices[idx]; + if( !sValues.faceSet[ fIndices[0] ] ) + { + neighborKey.getNeighbors( leaf ); + if( !neighborKey.neighbors[depth].neighbors[1][1][2*z] || !neighborKey.neighbors[depth].neighbors[1][1][2*z]->children ) + { + FaceEdges fe; + fe.count = MarchingSquares::AddEdgeIndices( mcIndex , isoEdges ); + for( int j=0 ; j edges; + edges.resize( fe.count ); + for( int j=0 ; jparent && Cube::IsFaceCorner( (int)(node-node->parent->children) , f ) ) + { + node = node->parent , _depth-- , _slice >>= 1; + if( neighborKey.neighbors[_depth].neighbors[1][1][2*z] && neighborKey.neighbors[_depth].neighbors[1][1][2*z]->children ) break; + long long key = VertexData::FaceIndex( node , f , _sNodes.maxDepth ); +#pragma omp critical( add_iso_edge_access ) + { + typename Octree< Real >::template SliceValues< Vertex >& _sValues = slabValues[_depth].sliceValues( _slice ); + typename hash_map< long long , std::vector< IsoEdge > >::iterator iter = _sValues.faceEdgeMap.find(key); + if( iter==_sValues.faceEdgeMap.end() ) _sValues.faceEdgeMap[key] = edges; + else for( int j=0 ; jsecond.push_back( fe.edges[j] ); + } + } + } + } + } + } +} +template< class Real > +template< class Vertex > +void Octree< Real >::SetXSliceIsoEdges( int depth , int slab , std::vector< SlabValues< Vertex > >& slabValues , int threads ) +{ + typename Octree< Real >::template SliceValues< Vertex >& bValues = slabValues[depth].sliceValues ( slab ); + typename Octree< Real >::template SliceValues< Vertex >& fValues = slabValues[depth].sliceValues ( slab+1 ); + typename Octree< Real >::template XSliceValues< Vertex >& xValues = slabValues[depth].xSliceValues( slab ); + + std::vector< typename TreeOctNode::ConstNeighborKey3 > neighborKeys( std::max< int >( 1 , threads ) ); + for( int i=0 ; ichildren ) + { + const typename SortedTreeNodes::SquareCornerIndices& cIndices = xValues.xSliceData.edgeIndices( leaf ); + const typename SortedTreeNodes::SquareEdgeIndices& eIndices = xValues.xSliceData.faceIndices( leaf ); + unsigned char mcIndex = ( bValues.mcIndices[ i - bValues.sliceData.nodeOffset ] ) | ( fValues.mcIndices[ i - fValues.sliceData.nodeOffset ]<<4 ); + { + neighborKey.getNeighbors( leaf ); + for( int o=0 ; o<2 ; o++ ) for( int x=0 ; x<2 ; x++ ) + { + int e = Square::EdgeIndex( o , x ); + int f = Cube::FaceIndex( 1-o , x ); + unsigned char _mcIndex = MarchingCubes::GetFaceIndex( mcIndex , f ); + int xx = o==1 ? 2*x : 1 , yy = o==0 ? 2*x : 1 , zz = 1; + if( !xValues.faceSet[ eIndices[e] ] && ( !neighborKey.neighbors[depth].neighbors[xx][yy][zz] || !neighborKey.neighbors[depth].neighbors[xx][yy][zz]->children ) ) + { + FaceEdges fe; + fe.count = MarchingSquares::AddEdgeIndices( _mcIndex , isoEdges ); + for( int j=0 ; j::template SliceValues< Vertex >& sValues = (_x==0) ? bValues : fValues; + int idx = sValues.sliceData.edgeIndices(i)[ Square::EdgeIndex(o,x) ]; + if( !sValues.edgeSet[ idx ] ) fprintf( stderr , "[ERROR] Edge not set 5: %d / %d\n" , slab , 1< edges; + edges.resize( fe.count ); + for( int j=0 ; jparent && Cube::IsFaceCorner( (int)(node-node->parent->children) , f ) ) + { + node = node->parent , _depth-- , _slab >>= 1; + if( neighborKey.neighbors[_depth].neighbors[xx][yy][zz] && neighborKey.neighbors[_depth].neighbors[xx][yy][zz]->children ) break; + long long key = VertexData::FaceIndex( node , f , _sNodes.maxDepth ); +#pragma omp critical( add_x_iso_edge_access ) + { + typename Octree< Real >::template XSliceValues< Vertex >& _xValues = slabValues[_depth].xSliceValues( _slab ); + typename hash_map< long long , std::vector< IsoEdge > >::iterator iter = _xValues.faceEdgeMap.find(key); + if( iter==_xValues.faceEdgeMap.end() ) _xValues.faceEdgeMap[key] = edges; + else for( int j=0 ; jsecond.push_back( fe.edges[j] ); + } + } + } + } + } + } + } +} +template< class Real > +template< class Vertex > +void Octree< Real >::SetIsoSurface( int depth , int offset , const SliceValues< Vertex >& bValues , const SliceValues< Vertex >& fValues , const XSliceValues< Vertex >& xValues , CoredMeshData< Vertex >& mesh , bool polygonMesh , bool addBarycenter , int& vOffset , int threads ) +{ + std::vector< std::pair< int , Vertex > > polygon; + std::vector< typename TreeOctNode::ConstNeighborKey3 > neighborKeys( std::max< int >( 1 , threads ) ); + std::vector< std::vector< IsoEdge > > edgess( std::max< int >( 1 , threads ) ); + for( int i=0 ; i& edges = edgess[ omp_get_thread_num() ]; + TreeOctNode* leaf = _sNodes.treeNodes[i]; + if( !leaf->children ) + { + edges.clear(); + unsigned char mcIndex = ( bValues.mcIndices[ i - bValues.sliceData.nodeOffset ] ) | ( fValues.mcIndices[ i - fValues.sliceData.nodeOffset ]<<4 ); + // [WARNING] Just because the node looks empty doesn't mean it doesn't get eges from finer neighbors + { + // Gather the edges from the faces (with the correct orientation) + for( int f=0 ; f& sValues = (o==0) ? bValues : fValues; + int fIdx = sValues.sliceData.faceIndices(i)[0]; + if( sValues.faceSet[fIdx] ) + { + const FaceEdges& fe = sValues.faceEdges[ fIdx ]; + for( int j=0 ; j >::const_iterator iter = sValues.faceEdgeMap.find( key ); + if( iter!=sValues.faceEdgeMap.end() ) + { + const std::vector< IsoEdge >& _edges = iter->second; + for( int j=0 ; j<_edges.size() ; j++ ) edges.push_back( IsoEdge( _edges[j][flip] , _edges[j][1-flip] ) ); + } + else fprintf( stderr , "[ERROR] Invalid faces: %d %d %d\n" , i , d , o ) , exit( 0 ); + } + } + else + { + int fIdx = xValues.xSliceData.faceIndices(i)[ Square::EdgeIndex( 1-d , o ) ]; + if( xValues.faceSet[fIdx] ) + { + const FaceEdges& fe = xValues.faceEdges[ fIdx ]; + for( int j=0 ; j >::const_iterator iter = xValues.faceEdgeMap.find( key ); + if( iter!=xValues.faceEdgeMap.end() ) + { + const std::vector< IsoEdge >& _edges = iter->second; + for( int j=0 ; j<_edges.size() ; j++ ) edges.push_back( IsoEdge( _edges[j][flip] , _edges[j][1-flip] ) ); + } + else fprintf( stderr , "[ERROR] Invalid faces: %d %d %d\n" , i , d , o ) , exit( 0 ); + } + } + } + // Get the edge loops + std::vector< std::vector< long long > > loops; + while( edges.size() ) + { + loops.resize( loops.size()+1 ); + IsoEdge edge = edges.back(); + edges.pop_back(); + long long start = edge[0] , current = edge[1]; + while( current!=start ) + { + int idx; + for( idx=0 ; idx::const_iterator iter; + if ( (iter=bValues.vertexPairMap.find(current))!=bValues.vertexPairMap.end() ) loops.back().push_back( current ) , current = iter->second; + else if( (iter=fValues.vertexPairMap.find(current))!=fValues.vertexPairMap.end() ) loops.back().push_back( current ) , current = iter->second; + else if( (iter=xValues.vertexPairMap.find(current))!=xValues.vertexPairMap.end() ) loops.back().push_back( current ) , current = iter->second; + else fprintf( stderr , "[ERROR] Failed to close loop @ depth %d / %d (%d): %lld\n" , depth , _sNodes.maxDepth-1 , i , current ) , exit( 0 ); + } + else + { + loops.back().push_back( current ); + current = edges[idx][1]; + edges[idx] = edges.back() , edges.pop_back(); + } + } + loops.back().push_back( start ); + } + // Add the loops to the mesh + for( int j=0 ; j > polygon( loops[j].size() ); + for( int k=0 ; k >::const_iterator iter; + if ( ( iter=bValues.edgeVertexMap.find( key ) )!=bValues.edgeVertexMap.end() ) polygon[k] = iter->second; + else if( ( iter=fValues.edgeVertexMap.find( key ) )!=fValues.edgeVertexMap.end() ) polygon[k] = iter->second; + else if( ( iter=xValues.edgeVertexMap.find( key ) )!=xValues.edgeVertexMap.end() ) polygon[k] = iter->second; + else fprintf( stderr , "[ERROR] Couldn't find vertex in edge map\n" ) , exit( 0 ); + } + AddIsoPolygons( mesh , polygon , polygonMesh , addBarycenter , vOffset ); + } + } + } + } +} +template< class Real > void SetIsoVertexValue( PlyVertex< float >& vertex , Real value ){ ; } +template< class Real > void SetIsoVertexValue( PlyValueVertex< float >& vertex , Real value ){ vertex.value = float(value); } +template< class Real > void SetIsoVertexValue( PlyVertex< double >& vertex , Real value ){ ; } +template< class Real > void SetIsoVertexValue( PlyValueVertex< double >& vertex , Real value ){ vertex.value = double(value); } +template< class Real > +template< class Vertex > +bool Octree< Real >::GetIsoVertex( ConstPointer( Real ) kernelDensityWeights , Real isoValue , typename TreeOctNode::ConstNeighborKey3& neighborKey3 , const TreeOctNode* node , int edgeIndex , int z , const SliceValues< Vertex >& sValues , Vertex& vertex ) +{ + Point3D< Real > position; + int c0 , c1; + Square::EdgeCorners( edgeIndex , c0 , c1 ); + + Real x0 , x1; + Point3D< Real > n0 , n1; + const typename SortedTreeNodes::SquareCornerIndices& idx = sValues.sliceData.cornerIndices( node ); + x0 = sValues.cornerValues[idx[c0]] , x1 = sValues.cornerValues[idx[c1]]; + if( sValues.cornerNormals ) n0 = sValues.cornerNormals[idx[c0]] , n1 = sValues.cornerNormals[idx[c1]]; + + int o , y; + Square::FactorEdgeIndex( edgeIndex , o , y ); + + Point3D< Real > c; + Real center , width; + node->centerAndWidth( c , width ); + center = c[o]; + for( int i=0 ; i P; + P.coefficients[0] = x0; + P.coefficients[1] = dx0; + P.coefficients[2] = 3*(x1-x0)-dx1-2*dx0; + + Real averageRoot=0; + double roots[2]; + int rCount = 0 , rootCount = P.getSolutions( isoValue , roots , EPSILON ); + for( int i=0 ; i=0 && roots[i]<=1 ) averageRoot += Real( roots[i] ) , rCount++; + if( rCount && sValues.cornerNormals ) averageRoot /= rCount; + else averageRoot = Real( ( x0-isoValue ) / ( x0-x1 ) ); + if( averageRoot<0 || averageRoot>1 ) + { + fprintf( stderr , "[WARNING] Bad average root: %f\n" , averageRoot ); + fprintf( stderr , "\t(%f %f) , (%f %f) (%f)\n" , x0 , x1 , dx0 , dx1 , isoValue ); + if( averageRoot<0 ) averageRoot = 0; + if( averageRoot>1 ) averageRoot = 1; + } + position[o] = Real( center - width/2 + width*averageRoot ); + vertex.point = position; + if( kernelDensityWeights ) + { + Real depth , weight; + const TreeOctNode* temp = node; + while( temp->depth()>_splatDepth ) temp=temp->parent; + GetSampleDepthAndWeight( kernelDensityWeights , temp , position , neighborKey3 , _samplesPerNode , depth , weight ); + SetIsoVertexValue( vertex , depth ); + } + return true; +} +template< class Real > +template< class Vertex > +bool Octree< Real >::GetIsoVertex( ConstPointer( Real ) kernelDensityWeights , Real isoValue , typename TreeOctNode::ConstNeighborKey3& neighborKey3 , const TreeOctNode* node , int cornerIndex , const SliceValues< Vertex >& bValues , const SliceValues< Vertex >& fValues , Vertex& vertex ) +{ + Point3D< Real > position; + + Real x0 , x1; + Point3D< Real > n0 , n1; + const typename SortedTreeNodes::SquareCornerIndices& idx0 = bValues.sliceData.cornerIndices( node ); + const typename SortedTreeNodes::SquareCornerIndices& idx1 = fValues.sliceData.cornerIndices( node ); + x0 = bValues.cornerValues[ idx0[cornerIndex] ] , x1 = fValues.cornerValues[ idx1[cornerIndex] ]; + if( bValues.cornerNormals || fValues.cornerNormals ) n0 = bValues.cornerNormals[ idx0[cornerIndex] ] , n1 = fValues.cornerNormals[ idx1[cornerIndex] ]; + + int x , y; + Square::FactorCornerIndex( cornerIndex , x , y ); + + Point3D< Real > c; + Real center , width; + node->centerAndWidth( c , width ); + center = c[2]; + for( int i=0 ; i P; + P.coefficients[0] = x0; + P.coefficients[1] = dx0; + P.coefficients[2] = 3*(x1-x0)-dx1-2*dx0; + + Real averageRoot=0; + double roots[2]; + int rCount = 0 , rootCount = P.getSolutions( isoValue , roots , EPSILON ); + for( int i=0 ; i=0 && roots[i]<=1 ) averageRoot += Real( roots[i] ) , rCount++; + if( rCount && bValues.cornerNormals && fValues.cornerNormals ) averageRoot /= rCount; + else averageRoot = Real( ( x0-isoValue ) / ( x0-x1 ) ); + if( averageRoot<0 || averageRoot>1 ) + { + fprintf( stderr , "[WARNING] Bad average root: %f\n" , averageRoot ); + fprintf( stderr , "\t(%f %f) , (%f %f) (%f)\n" , x0 , x1 , dx0 , dx1 , isoValue ); + if( averageRoot<0 ) averageRoot = 0; + if( averageRoot>1 ) averageRoot = 1; + } + position[2] = Real( center - width/2 + width*averageRoot ); + vertex.point = position; + if( kernelDensityWeights ) + { + Real depth , weight; + const TreeOctNode* temp = node; + while( temp->depth()>_splatDepth ) temp=temp->parent; + GetSampleDepthAndWeight( kernelDensityWeights , temp , position , neighborKey3 , _samplesPerNode , depth , weight ); + SetIsoVertexValue( vertex , depth ); + } + return true; +} + +template< class Real > +template< class Vertex > +int Octree< Real >::AddIsoPolygons( CoredMeshData< Vertex >& mesh , std::vector< std::pair< int , Vertex > >& polygon , bool polygonMesh , bool addBarycenter , int& vOffset ) +{ + if( polygonMesh ) + { + std::vector< int > vertices( polygon.size() ); + for( int i=0 ; i<(int)polygon.size() ; i++ ) vertices[i] = polygon[polygon.size()-1-i].first; + mesh.addPolygon_s( vertices ); + return 1; + } + if( polygon.size()>3 ) + { + bool isCoplanar = false; + std::vector< int > triangle( 3 ); + + if( addBarycenter ) + for( int i=0 ; i<(int)polygon.size() ; i++ ) + for( int j=0 ; j MAT; + std::vector< Point3D< Real > > vertices; + std::vector< TriangleIndex > triangles; + vertices.resize( polygon.size() ); + // Add the points + for( int i=0 ; i<(int)polygon.size() ; i++ ) vertices[i] = polygon[i].second.point; + MAT.GetTriangulation( vertices , triangles ); + for( int i=0 ; i<(int)triangles.size() ; i++ ) + { + for( int j=0 ; j<3 ; j++ ) triangle[2-j] = polygon[ triangles[i].idx[j] ].first; + mesh.addPolygon_s( triangle ); + } + } + } + else if( polygon.size()==3 ) + { + std::vector< int > vertices( 3 ); + for( int i=0 ; i<3 ; i++ ) vertices[2-i] = polygon[i].first; + mesh.addPolygon_s( vertices ); + } + return (int)polygon.size()-2; +} diff --git a/Src/MultiGridOctreeData.SortedTreeNodes.inl b/Src/MultiGridOctreeData.SortedTreeNodes.inl new file mode 100644 index 0000000..e02fdfd --- /dev/null +++ b/Src/MultiGridOctreeData.SortedTreeNodes.inl @@ -0,0 +1,348 @@ +/* +Copyright (c) 2006, Michael Kazhdan and Matthew Bolitho +All rights reserved. + +Redistribution and use in source and binary forms, with or without modification, +are permitted provided that the following conditions are met: + +Redistributions of source code must retain the above copyright notice, this list of +conditions and the following disclaimer. Redistributions in binary form must reproduce +the above copyright notice, this list of conditions and the following disclaimer +in the documentation and/or other materials provided with the distribution. + +Neither the name of the Johns Hopkins University nor the names of its contributors +may be used to endorse or promote products derived from this software without specific +prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY +EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO THE IMPLIED WARRANTIES +OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT +SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED +TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR +BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN +ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH +DAMAGE. +*/ + +///////////////////// +// SortedTreeNodes // +///////////////////// +SortedTreeNodes::SortedTreeNodes( void ) +{ + nodeCount = NULL; + treeNodes = NullPointer< TreeOctNode* >(); + maxDepth = 0; + sliceOffsets = NullPointer< Pointer( int ) >(); +} +SortedTreeNodes::~SortedTreeNodes( void ) +{ + if( nodeCount ) delete[] nodeCount , nodeCount = NULL; + if( treeNodes ) DeletePointer( treeNodes ); + if( sliceOffsets ) + { + for( int d=0 ; d* map ) +{ + if( nodeCount ) delete[] nodeCount; + if( treeNodes ) DeletePointer( treeNodes ); + if( sliceOffsets ) + { + for( int d=0 ; d( root.nodes() ); + + int startDepth = 0; + nodeCount[0] = 0 , nodeCount[1] = 1; + treeNodes[0] = &root; + for( int d=startDepth+1 ; dchildren ) for( int c=0 ; c<8 ; c++ ) treeNodes[ nodeCount[d+1]++ ] = temp->children + c; + } + } + _sortByZCoordinate(); + if( map ) + { + map->resize( nodeCount[maxDepth] ); + for( int i=0 ; inodeData.nodeIndex; + } + for( int i=0 ; inodeData.nodeIndex = i; +} +int SortedTreeNodes::Slices( int depth ){ return 1< SortedTreeNodes::sliceSpan( int depth , int off , int d ) const +{ + int dd = d-depth; + return std::pair< int , int >( nodeCount[d] + sliceOffsets[d][off<( maxDepth ); + for( int d=0 ; d( slices+1 ); + memset( sliceOffsets[d] , 0 , sizeof(int)*(slices+1) ); + for( int i=nodeCount[d] ; idepthAndOffset( _d , _off ); + sliceOffsets[d][ _off[2] ]++; + } + for( int i=1 ; i=1 ; i-- ) sliceOffsets[d][i] = sliceOffsets[d][i-1]; + sliceOffsets[d][0] = 0; + } + for( TreeOctNode* node=treeNodes[0]->nextNode() ; node ; node=treeNodes[0]->nextNode( node ) ) + { + int d , off[3]; + node->depthAndOffset( d , off ); + treeNodes[ nodeCount[d] + sliceOffsets[d][ off[2] ] ] = node; + sliceOffsets[d][ off[2] ]++; + } + for( int d=0 ; d=1 ; i-- ) sliceOffsets[d][i] = sliceOffsets[d][i-1]; + sliceOffsets[d][0] = 0; + } +} +SortedTreeNodes::SquareCornerIndices& SortedTreeNodes::SliceTableData::cornerIndices( const TreeOctNode* node ) { return cTable[ node->nodeData.nodeIndex - nodeOffset ]; } +SortedTreeNodes::SquareCornerIndices& SortedTreeNodes::SliceTableData::cornerIndices( int idx ) { return cTable[ idx - nodeOffset ]; } +const SortedTreeNodes::SquareCornerIndices& SortedTreeNodes::SliceTableData::cornerIndices( const TreeOctNode* node ) const { return cTable[ node->nodeData.nodeIndex - nodeOffset ]; } +const SortedTreeNodes::SquareCornerIndices& SortedTreeNodes::SliceTableData::cornerIndices( int idx ) const { return cTable[ idx - nodeOffset ]; } +SortedTreeNodes::SquareEdgeIndices& SortedTreeNodes::SliceTableData::edgeIndices( const TreeOctNode* node ) { return eTable[ node->nodeData.nodeIndex - nodeOffset ]; } +SortedTreeNodes::SquareEdgeIndices& SortedTreeNodes::SliceTableData::edgeIndices( int idx ) { return eTable[ idx - nodeOffset ]; } +const SortedTreeNodes::SquareEdgeIndices& SortedTreeNodes::SliceTableData::edgeIndices( const TreeOctNode* node ) const { return eTable[ node->nodeData.nodeIndex - nodeOffset ]; } +const SortedTreeNodes::SquareEdgeIndices& SortedTreeNodes::SliceTableData::edgeIndices( int idx ) const { return eTable[ idx - nodeOffset ]; } +SortedTreeNodes::SquareFaceIndices& SortedTreeNodes::SliceTableData::faceIndices( const TreeOctNode* node ) { return fTable[ node->nodeData.nodeIndex - nodeOffset ]; } +SortedTreeNodes::SquareFaceIndices& SortedTreeNodes::SliceTableData::faceIndices( int idx ) { return fTable[ idx - nodeOffset ]; } +const SortedTreeNodes::SquareFaceIndices& SortedTreeNodes::SliceTableData::faceIndices( const TreeOctNode* node ) const { return fTable[ node->nodeData.nodeIndex - nodeOffset ]; } +const SortedTreeNodes::SquareFaceIndices& SortedTreeNodes::SliceTableData::faceIndices( int idx ) const { return fTable[ idx - nodeOffset ]; } +SortedTreeNodes::SquareCornerIndices& SortedTreeNodes::XSliceTableData::edgeIndices( const TreeOctNode* node ) { return eTable[ node->nodeData.nodeIndex - nodeOffset ]; } +SortedTreeNodes::SquareCornerIndices& SortedTreeNodes::XSliceTableData::edgeIndices( int idx ) { return eTable[ idx - nodeOffset ]; } +const SortedTreeNodes::SquareCornerIndices& SortedTreeNodes::XSliceTableData::edgeIndices( const TreeOctNode* node ) const { return eTable[ node->nodeData.nodeIndex - nodeOffset ]; } +const SortedTreeNodes::SquareCornerIndices& SortedTreeNodes::XSliceTableData::edgeIndices( int idx ) const { return eTable[ idx - nodeOffset ]; } +SortedTreeNodes::SquareEdgeIndices& SortedTreeNodes::XSliceTableData::faceIndices( const TreeOctNode* node ) { return fTable[ node->nodeData.nodeIndex - nodeOffset ]; } +SortedTreeNodes::SquareEdgeIndices& SortedTreeNodes::XSliceTableData::faceIndices( int idx ) { return fTable[ idx - nodeOffset ]; } +const SortedTreeNodes::SquareEdgeIndices& SortedTreeNodes::XSliceTableData::faceIndices( const TreeOctNode* node ) const { return fTable[ node->nodeData.nodeIndex - nodeOffset ]; } +const SortedTreeNodes::SquareEdgeIndices& SortedTreeNodes::XSliceTableData::faceIndices( int idx ) const { return fTable[ idx - nodeOffset ]; } + + +void SortedTreeNodes::setSliceTableData( SliceTableData& sData , int depth , int offset , int threads ) const +{ + if( offset<0 || offset>(1< span( nodeCount[depth] + sliceOffsets[depth][ std::max(0,offset-1) ] , nodeCount[depth] + sliceOffsets[depth][ std::min(1< neighborKeys( std::max< int >( 1 , threads ) ); + for( int i=0 ; idepthAndOffset( d , off ); + int z; + if ( off[2]==offset-1 ) z = 1; + else if( off[2]==offset ) z = 0; + else fprintf( stderr , "[ERROR] Node out of bounds: %d %d\n" , offset , off[2] ) , exit( 0 ); + // Process the corners + for( int x=0 ; x<2 ; x++ ) for( int y=0 ; y<2 ; y++ ) + { + int c = Cube::CornerIndex( x , y , z ); + int fc = Square::CornerIndex( x , y ); + bool cornerOwner = true; + int ac = Cube::AntipodalCornerIndex(c); // The index of the node relative to the corner + for( int cc=0 ; cc=(1< span( nodeCount[depth] + sliceOffsets[depth][offset] , nodeCount[depth] + sliceOffsets[depth][offset+1] ); + sData.nodeOffset = span.first; + sData.nodeCount = span.second - span.first; + + sData._eMap.clear() , sData._fMap.clear(); + sData._eMap.resize( sData.nodeCount * Square::CORNERS , 0 ) , sData._fMap.resize( sData.nodeCount * Square::EDGES , 0 ); + sData.eTable.resize( sData.nodeCount ) , sData.fTable.resize( sData.nodeCount ); + std::vector< TreeOctNode::ConstNeighborKey3 > neighborKeys( std::max< int >( 1 , threads ) ); + for( int i=0 ; idepthAndOffset( d , off ); + // Process the edges + int o=2; + for( int x=0 ; x<2 ; x++ ) for( int y=0 ; y<2 ; y++ ) + { + int fc = Square::CornerIndex( x , y ); + bool edgeOwner = true; + + int ac = Square::AntipodalCornerIndex( Square::CornerIndex( x , y ) ); + for( int cc=0 ; cc + // b_1[i] = < \nabla B_i(p) , V(p) > + // 2] Formulate this as a Poisson equation: + // \sum_i x_i \Delta B_i(p) = \nabla \cdot V(p) + // which is solved by the system A_2x = b_2 where: + // A_2[i,j] = - < \Delta B_i(p) , B_j(p) > + // b_2[i] = - < B_i(p) , \nabla \cdot V(p) > + // Although the two system matrices should be the same (assuming that the B_i satisfy dirichlet/neumann boundary conditions) + // the constraint vectors can differ when V does not satisfy the Neumann boundary conditions: + // A_1[i,j] = \int_R < \nabla B_i(p) , \nabla B_j(p) > + // = \int_R [ \nabla \cdot ( B_i(p) \nabla B_j(p) ) - B_i(p) \Delta B_j(p) ] + // = \int_dR < N(p) , B_i(p) \nabla B_j(p) > + A_2[i,j] + // and the first integral is zero if either f_i is zero on the boundary dR or the derivative of B_i across the boundary is zero. + // However, for the constraints we have: + // b_1(i) = \int_R < \nabla B_i(p) , V(p) > + // = \int_R [ \nabla \cdot ( B_i(p) V(p) ) - B_i(p) \nabla \cdot V(p) ] + // = \int_dR < N(p) , B_i(p) V(p) > + b_2[i] + // In particular, this implies that if the B_i satisfy the Neumann boundary conditions (rather than Dirichlet), + // and V is not zero across the boundary, then the two constraints are different. + // Forcing the < V(p) , N(p) > = 0 on the boundary, by killing off the component of the vector-field in the normal direction + // (FORCE_NEUMANN_FIELD), makes the two systems equal, and the value of this flag should be immaterial. + // Note that under interpretation 1, we have: + // \sum_i b_1(i) = < \nabla \sum_ i B_i(p) , V(p) > = 0 + // because the B_i's sum to one. However, in general, we could have + // \sum_i b_2(i) \neq 0. + // This could cause trouble because the constant functions are in the kernel of the matrix A, so CG will misbehave if the constraint + // has a non-zero DC term. (Again, forcing < V(p) , N(p) > = 0 along the boundary resolves this problem.) + +#define FORCE_NEUMANN_FIELD 1 // This flag forces the normal component across the boundary of the integration domain to be zero. + // This should be enabled if GRADIENT_DOMAIN_SOLUTION is not, so that CG doesn't run into trouble. + +#define ROBERTO_TOLDO_FIX 1 + +#if !FORCE_NEUMANN_FIELD +#pragma message( "[WARNING] Not zeroing out normal component on boundary" ) +#endif // !FORCE_NEUMANN_FIELD + +#include "Hash.h" +#include "BSplineData.h" +#include "PointStream.h" + +#ifndef _OPENMP +int omp_get_num_procs( void ){ return 1; } +int omp_get_thread_num( void ){ return 0; } +#endif // _OPENMP + +class TreeNodeData +{ +public: + static int NodeCount; + int nodeIndex; + + TreeNodeData( void ); + ~TreeNodeData( void ); +}; + +class VertexData +{ + typedef OctNode< TreeNodeData > TreeOctNode; +public: + static const int VERTEX_COORDINATE_SHIFT = ( sizeof( long long ) * 8 ) / 3; + static long long EdgeIndex( const TreeOctNode* node , int eIndex , int maxDepth , int index[DIMENSION] ); + static long long EdgeIndex( const TreeOctNode* node , int eIndex , int maxDepth ); + static long long FaceIndex( const TreeOctNode* node , int fIndex , int maxDepth,int index[DIMENSION] ); + static long long FaceIndex( const TreeOctNode* node , int fIndex , int maxDepth ); + static long long CornerIndex( const TreeOctNode* node , int cIndex , int maxDepth , int index[DIMENSION] ); + static long long CornerIndex( const TreeOctNode* node , int cIndex , int maxDepth ); + static long long CenterIndex( const TreeOctNode* node , int maxDepth , int index[DIMENSION] ); + static long long CenterIndex( const TreeOctNode* node , int maxDepth ); + static long long CornerIndex( int depth , const int offSet[DIMENSION] , int cIndex , int maxDepth , int index[DIMENSION] ); + static long long CenterIndex( int depth , const int offSet[DIMENSION] , int maxDepth , int index[DIMENSION] ); + static long long CornerIndexKey( const int index[DIMENSION] ); +}; + +class SortedTreeNodes +{ + typedef OctNode< TreeNodeData > TreeOctNode; +protected: + void _sortByZCoordinate( void ); +public: + Pointer( TreeOctNode* ) treeNodes; + int *nodeCount; + int maxDepth; + SortedTreeNodes( void ); + ~SortedTreeNodes( void ); + void set( TreeOctNode& root , std::vector< int >* map ); + Pointer( Pointer( int ) ) sliceOffsets; + static int Slices( int depth ); + std::pair< int , int > sliceSpan( int depth , int off , int d ) const; + + template< int Indices > + struct _Indices + { + int idx[Indices]; + _Indices( void ){ memset( idx , -1 , sizeof( int ) * Indices ); } + int& operator[] ( int i ) { return idx[i]; } + const int& operator[] ( int i ) const { return idx[i]; } + }; + typedef _Indices< Square::CORNERS > SquareCornerIndices; + typedef _Indices< Square::EDGES > SquareEdgeIndices; + typedef _Indices< Square::FACES > SquareFaceIndices; + + struct SliceTableData + { + std::vector< SquareCornerIndices > cTable; + std::vector< SquareEdgeIndices > eTable; + std::vector< SquareFaceIndices > fTable; + int cCount , eCount , fCount , nodeOffset , nodeCount; + SliceTableData( void ){ fCount = eCount = cCount = 0; } + ~SliceTableData( void ){ clear(); } + void clear( void ) { cTable.clear() , eTable.clear() , fTable.clear() , fCount = eCount = cCount = 0; } + SquareCornerIndices& cornerIndices( const TreeOctNode* node ); + SquareCornerIndices& cornerIndices( int idx ); + const SquareCornerIndices& cornerIndices( const TreeOctNode* node ) const; + const SquareCornerIndices& cornerIndices( int idx ) const; + SquareEdgeIndices& edgeIndices( const TreeOctNode* node ); + SquareEdgeIndices& edgeIndices( int idx ); + const SquareEdgeIndices& edgeIndices( const TreeOctNode* node ) const; + const SquareEdgeIndices& edgeIndices( int idx ) const; + SquareFaceIndices& faceIndices( const TreeOctNode* node ); + SquareFaceIndices& faceIndices( int idx ); + const SquareFaceIndices& faceIndices( const TreeOctNode* node ) const; + const SquareFaceIndices& faceIndices( int idx ) const; + protected: + std::vector< int > _cMap , _eMap , _fMap; + friend class SortedTreeNodes; + }; + struct XSliceTableData + { + std::vector< SquareCornerIndices > eTable; + std::vector< SquareEdgeIndices > fTable; + int fCount , eCount , nodeOffset , nodeCount; + XSliceTableData( void ){ fCount = eCount = 0; } + ~XSliceTableData( void ){ clear(); } + void clear( void ) { fTable.clear() , eTable.clear() , fCount = eCount = 0; } + SquareCornerIndices& edgeIndices( const TreeOctNode* node ); + SquareCornerIndices& edgeIndices( int idx ); + const SquareCornerIndices& edgeIndices( const TreeOctNode* node ) const; + const SquareCornerIndices& edgeIndices( int idx ) const; + SquareEdgeIndices& faceIndices( const TreeOctNode* node ); + SquareEdgeIndices& faceIndices( int idx ); + const SquareEdgeIndices& faceIndices( const TreeOctNode* node ) const; + const SquareEdgeIndices& faceIndices( int idx ) const; + protected: + std::vector< int > _eMap , _fMap; + friend class SortedTreeNodes; + }; + void setSliceTableData ( SliceTableData& sData , int depth , int offset , int threads ) const; + void setXSliceTableData( XSliceTableData& sData , int depth , int offset , int threads ) const; +}; + + +template< class Real > +class Octree +{ + typedef OctNode< TreeNodeData > TreeOctNode; + struct _PointData + { + Point3D< Real > position; + Real weightedCoarserValue; + Real weight; + _PointData( Point3D< Real > p=Point3D< Real >() , Real w=0 ) { position = p , weight = w , weightedCoarserValue = Real(0); } + }; +public: + struct NormalInfo + { + std::vector< int > normalIndices; + std::vector< Point3D< Real > > normals; + int normalIndex( const TreeOctNode* node ) const { return node->nodeData.nodeIndex>=normalIndices.size() ? -1 : normalIndices[ node->nodeData.nodeIndex ]; } + }; + struct PointInfo + { + std::vector< int > pointIndices; + std::vector< _PointData > points; + int pointIndex( const TreeOctNode* node ) const { return node->nodeData.nodeIndex>=pointIndices.size() ? -1 : pointIndices[ node->nodeData.nodeIndex ]; } + }; +protected: + SortedTreeNodes _sNodes; + Real _samplesPerNode; + int _splatDepth; + int _minDepth; + int _fullDepth; + bool _constrainValues; + int _boundaryType; + Real _scale; + Point3D< Real > _center; + std::vector< int > _pointCount; + Real _normalSmooth; + BSplineData< 2 > _fData; + + bool _InBounds( Point3D< Real > ) const; + + double GetLaplacian ( const typename BSplineData< 2 >::Integrator& integrator , int d , const int off1[3] , const int off2[3] , bool childParent ) const; + double GetDivergence1( const typename BSplineData< 2 >::Integrator& integrator , int d , const int off1[3] , const int off2[3] , bool childParent , const Point3D< Real >& normal1 ) const; + double GetDivergence2( const typename BSplineData< 2 >::Integrator& integrator , int d , const int off1[3] , const int off2[3] , bool childParent , const Point3D< Real >& normal2 ) const; + Point3D< double > GetDivergence1( const typename BSplineData< 2 >::Integrator& integrator , int d , const int off1[3] , const int off2[3] , bool childParent ) const; + Point3D< double > GetDivergence2( const typename BSplineData< 2 >::Integrator& integrator , int d , const int off1[3] , const int off2[3] , bool childParent ) const; + + template< class C , int N > struct Stencil{ C values[N][N][N]; }; + struct CenterValueStencil + { + Stencil< double , 3 > stencil; + Stencil< double , 3 > stencils[8]; + }; + struct CornerValueStencil + { + Stencil< double , 3 > stencil[8]; + Stencil< double , 3 > stencils[8][8]; + }; + struct CornerNormalStencil + { + Stencil< Point3D< double > , 3 > stencil[8]; + Stencil< Point3D< double > , 3 > stencils[8][8]; + }; + + void _setMultiColorIndices( int start , int end , std::vector< std::vector< int > >& indices ) const; + int _SolveSystemGS( PointInfo& pointInfo , int depth , const typename BSplineData< 2 >::Integrator& integrator , const SortedTreeNodes& sNodes , Pointer( Real ) solution , Pointer( Real ) constraints , Pointer( Real ) metSolutionConstraints , int iters , bool coarseToFine , bool showResidual=false , double* bNorm2=NULL , double* inRNorm2=NULL , double* outRNorm2=NULL , bool forceSilent=false ); + int _SolveSystemCG( PointInfo& pointInfo , int depth , const typename BSplineData< 2 >::Integrator& integrator , const SortedTreeNodes& sNodes , Pointer( Real ) solution , Pointer( Real ) constraints , Pointer( Real ) metSolutionConstraints , int iters , bool coarseToFine , bool showResidual=false , double* bNorm2=NULL , double* inRNorm2=NULL , double* outRNorm2=NULL , double accuracy=0 ); + + int GetMatrixRowSize( const typename TreeOctNode::Neighbors5& neighbors5 , bool symmetric ) const; + int SetMatrixRow( const PointInfo& pointInfo , const typename TreeOctNode::Neighbors5& neighbors5 , Pointer( MatrixEntry< Real > ) row , int offset , const typename BSplineData< 2 >::Integrator& integrator , const Stencil< double , 5 >& stencil , bool symmetric ) const; + + void SetDivergenceStencil ( int depth , const typename BSplineData< 2 >::Integrator& integrator , Stencil< Point3D< double > , 5 >& stencil , bool scatter ) const; + void SetDivergenceStencils( int depth , const typename BSplineData< 2 >::Integrator& integrator , Stencil< Point3D< double > , 5 > stencil[2][2][2] , bool scatter ) const; + void SetLaplacianStencil ( int depth , const typename BSplineData< 2 >::Integrator& integrator , Stencil< double , 5 >& stencil ) const; + void SetLaplacianStencils ( int depth , const typename BSplineData< 2 >::Integrator& integrator , Stencil< double , 5 > stencil[2][2][2] ) const; + void SetCenterEvaluationStencil ( const typename BSplineData< 2 >::template CenterEvaluator< 1 >& evaluator , int depth , Stencil< double , 3 >& stencil ) const; + void SetCenterEvaluationStencils( const typename BSplineData< 2 >::template CenterEvaluator< 1 >& evaluator , int depth , Stencil< double , 3 > stencil[8] ) const; + void SetCornerEvaluationStencil ( const typename BSplineData< 2 >::template CornerEvaluator< 2 >& evaluator , int depth , Stencil< double , 3 > stencil [8] ) const; + void SetCornerEvaluationStencils( const typename BSplineData< 2 >::template CornerEvaluator< 2 >& evaluator , int depth , Stencil< double , 3 > stencils[8][8] ) const; + void SetCornerNormalEvaluationStencil ( const typename BSplineData< 2 >::template CornerEvaluator< 2 >& evaluator , int depth , Stencil< Point3D< double > , 3 > stencil [8] ) const; + void SetCornerNormalEvaluationStencils( const typename BSplineData< 2 >::template CornerEvaluator< 2 >& evaluator , int depth , Stencil< Point3D< double > , 3 > stencils[8][8] ) const; + void SetCornerNormalEvaluationStencil ( const typename BSplineData< 2 >::template CornerEvaluator< 2 >& evaluator , int depth , Stencil< Point3D< double > , 5 > stencil [8] ) const; + void SetCornerNormalEvaluationStencils( const typename BSplineData< 2 >::template CornerEvaluator< 2 >& evaluator , int depth , Stencil< Point3D< double > , 5 > stencils[8][8] ) const; + + static void UpdateCoarserSupportBounds( const TreeOctNode* node , int& startX , int& endX , int& startY , int& endY , int& startZ , int& endZ ); + + void UpdateConstraintsFromCoarser( const PointInfo& pointInfo , const typename TreeOctNode::Neighbors5& neighbors5 , const typename TreeOctNode::Neighbors5& pNeighbors5 , TreeOctNode* node , Pointer( Real ) constraints , ConstPointer( Real ) metSolution , const typename BSplineData< 2 >::Integrator& integrator , const Stencil< double , 5 >& stencil ) const; + // Updates the constraints @(depth-1) based on the solution coefficients @(depth) + void UpdateConstraintsFromFiner( const typename BSplineData< 2 >::Integrator& integrator , int depth , const SortedTreeNodes& sNodes , ConstPointer( Real ) fineSolution , Pointer( Real ) coarseConstraints ) const; + // Evaluate the points @(depth) using coefficients @(depth-1) + void SetPointValuesFromCoarser( PointInfo& pointInfo , int depth , const SortedTreeNodes& sNodes , ConstPointer( Real ) coarseCoefficients ); + // Evalutes the solution @(depth) at the points @(depth-1) and updates the met constraints @(depth-1) + void SetPointConstraintsFromFiner( const PointInfo& pointInfo , int depth , const SortedTreeNodes& sNodes , ConstPointer( Real ) finerCoefficients , Pointer( Real ) metConstraints) const; + Real _WeightedCoarserFunctionValue( const _PointData& pointData , const typename TreeOctNode::NeighborKey3& neighborKey3 , const TreeOctNode* node , ConstPointer( Real ) coarseCoefficients ) const; + Real _WeightedFinerFunctionValue ( const _PointData& pointData , const typename TreeOctNode::NeighborKey3& neighborKey3 , const TreeOctNode* node , ConstPointer( Real ) finerCoefficients ) const; + + // Down samples constraints @(depth) to constraints @(depth-1) + template< class C > void DownSample( int depth , const SortedTreeNodes& sNodes , ConstPointer( C ) fineConstraints , Pointer( C ) coarseConstraints ) const; + // Up samples solution @(depth-1) to solution @(depth) + template< class C > void UpSample ( int depth , const SortedTreeNodes& sNodes , ConstPointer( C ) coarseCoefficients , Pointer( C ) fineCoefficients ) const; + int GetSliceMatrixAndUpdateConstraints( const PointInfo& pointInfo , SparseMatrix< Real >& matrix , Pointer( Real ) constraints , const typename BSplineData< 2 >::Integrator& integrator , int depth , const SortedTreeNodes& sNodes , ConstPointer( Real ) metSolution , bool coarseToFine , int nStart , int nEnd ); + int GetMatrixAndUpdateConstraints( const PointInfo& pointInfo , SparseSymmetricMatrix< Real >& matrix , Pointer( Real ) constraints , const typename BSplineData< 2 >::Integrator& integrator , int depth , const SortedTreeNodes& sNodes , ConstPointer( Real ) metSolution , bool coarseToFine ); + + + int UpdateWeightContribution( std::vector< Real >& kernelDensityWeights , TreeOctNode* node , const Point3D& position , typename TreeOctNode::NeighborKey3& neighborKey , Real weight=Real(1.0) ); + Real GetSampleWeight( ConstPointer( Real ) kernelDensityWeight , const Point3D& position , typename TreeOctNode::NeighborKey3& neighborKey , int splatDepth ); + Real GetSampleWeight( ConstPointer( Real ) kernelDensityWeight , const TreeOctNode* node , const Point3D& position , typename TreeOctNode::ConstNeighborKey3& neighborKey ); + void GetSampleDepthAndWeight( ConstPointer( Real ) kernelDensityWeight , const TreeOctNode* node , const Point3D& position , typename TreeOctNode::ConstNeighborKey3& neighborKey , Real samplesPerNode , Real& depth , Real& weight ); + Real GetSampleWeight( ConstPointer( Real ) kernelDensityWeight , TreeOctNode* node , const Point3D& position , typename TreeOctNode::NeighborKey3& neighborKey ); + void GetSampleDepthAndWeight( ConstPointer( Real ) kernelDensityWeight , TreeOctNode* node , const Point3D& position , typename TreeOctNode::NeighborKey3& neighborKey , Real samplesPerNode , Real& depth , Real& weight ); + int SplatOrientedPoint( ConstPointer( Real ) kernelDensityWeights , TreeOctNode* node , const Point3D& point , const Point3D< Real >& normal , NormalInfo& normalInfo , typename TreeOctNode::NeighborKey3& neighborKey ); + Real SplatOrientedPoint( ConstPointer( Real ) kernelDensityWeights , const Point3D& point , const Point3D& normal , NormalInfo& normalInfo , typename TreeOctNode::NeighborKey3& neighborKey , int kernelDepth , Real samplesPerNode , int minDepth , int maxDepth ); + + int HasNormals( TreeOctNode* node , const NormalInfo& normalInfo ); + + /////////////////////////// + // Iso-Surfacing Methods // + /////////////////////////// + struct IsoEdge + { + long long edges[2]; + IsoEdge( void ){ edges[0] = edges[1] = 0; } + IsoEdge( long long v1 , long long v2 ){ edges[0] = v1 , edges[1] = v2; } + long long& operator[]( int idx ){ return edges[idx]; } + const long long& operator[]( int idx ) const { return edges[idx]; } + }; + struct FaceEdges + { + IsoEdge edges[2]; + int count; + }; + template< class Vertex > + struct SliceValues + { + typename SortedTreeNodes::SliceTableData sliceData; + Pointer( Real ) cornerValues ; Pointer( Point3D< Real > ) cornerNormals ; Pointer( char ) cornerSet; + Pointer( long long ) edgeKeys ; Pointer( char ) edgeSet; + Pointer( FaceEdges ) faceEdges ; Pointer( char ) faceSet; + Pointer( char ) mcIndices; + hash_map< long long , std::vector< IsoEdge > > faceEdgeMap; + hash_map< long long , std::pair< int , Vertex > > edgeVertexMap; + hash_map< long long , long long > vertexPairMap; + + SliceValues( void ); + ~SliceValues( void ); + void reset( bool nonLinearFit ); + protected: + int _oldCCount , _oldECount , _oldFCount , _oldNCount; + }; + template< class Vertex > + struct XSliceValues + { + typename SortedTreeNodes::XSliceTableData xSliceData; + Pointer( long long ) edgeKeys ; Pointer( char ) edgeSet; + Pointer( FaceEdges ) faceEdges ; Pointer( char ) faceSet; + hash_map< long long , std::vector< IsoEdge > > faceEdgeMap; + hash_map< long long , std::pair< int , Vertex > > edgeVertexMap; + hash_map< long long , long long > vertexPairMap; + + XSliceValues( void ); + ~XSliceValues( void ); + void reset( void ); + protected: + int _oldECount , _oldFCount; + }; + template< class Vertex > + struct SlabValues + { + XSliceValues< Vertex > _xSliceValues[2]; + SliceValues< Vertex > _sliceValues[2]; + SliceValues< Vertex >& sliceValues( int idx ){ return _sliceValues[idx&1]; } + const SliceValues< Vertex >& sliceValues( int idx ) const { return _sliceValues[idx&1]; } + XSliceValues< Vertex >& xSliceValues( int idx ){ return _xSliceValues[idx&1]; } + const XSliceValues< Vertex >& xSliceValues( int idx ) const { return _xSliceValues[idx&1]; } + }; + template< class Vertex > + void SetSliceIsoCorners( ConstPointer( Real ) solution , ConstPointer( Real ) coarseSolution , Real isoValue , int depth , int slice , std::vector< SlabValues< Vertex > >& sValues , const typename BSplineData< 2 >::template CornerEvaluator< 2 >& evaluator , const Stencil< double , 3 > stencil[8] , const Stencil< double , 3 > stencils[8][8] , const Stencil< Point3D< double > , 3 > nStencil[8] , const Stencil< Point3D< double > , 3 > nStencils[8][8] , int threads ); + template< class Vertex > + void SetSliceIsoCorners( ConstPointer( Real ) solution , ConstPointer( Real ) coarseSolution , Real isoValue , int depth , int slice , int z , std::vector< SlabValues< Vertex > >& sValues , const typename BSplineData< 2 >::template CornerEvaluator< 2 >& evaluator , const Stencil< double , 3 > stencil[8] , const Stencil< double , 3 > stencils[8][8] , const Stencil< Point3D< double > , 3 > nStencil[8] , const Stencil< Point3D< double > , 3 > nStencils[8][8] , int threads ); + template< class Vertex > + void SetSliceIsoVertices( ConstPointer( Real ) kernelDensityWeights , Real isoValue , int depth , int slice , int& vOffset , CoredMeshData< Vertex >& mesh , std::vector< SlabValues< Vertex > >& sValues , int threads ); + template< class Vertex > + void SetSliceIsoVertices( ConstPointer( Real ) kernelDensityWeights , Real isoValue , int depth , int slice , int z , int& vOffset , CoredMeshData< Vertex >& mesh , std::vector< SlabValues< Vertex > >& sValues , int threads ); + template< class Vertex > + void SetXSliceIsoVertices( ConstPointer( Real ) kernelDensityWeights , Real isoValue , int depth , int slab , int& vOffset , CoredMeshData< Vertex >& mesh , std::vector< SlabValues< Vertex > >& sValues , int threads ); + template< class Vertex > + void CopyFinerSliceIsoEdgeKeys( int depth , int slice , std::vector< SlabValues< Vertex > >& sValues , int threads ); + template< class Vertex > + void CopyFinerSliceIsoEdgeKeys( int depth , int slice , int z , std::vector< SlabValues< Vertex > >& sValues , int threads ); + template< class Vertex > + void CopyFinerXSliceIsoEdgeKeys( int depth , int slab , std::vector< SlabValues< Vertex > >& sValues , int threads ); + template< class Vertex > + void SetSliceIsoEdges( int depth , int slice , std::vector< SlabValues< Vertex > >& slabValues , int threads ); + template< class Vertex > + void SetSliceIsoEdges( int depth , int slice , int z , std::vector< SlabValues< Vertex > >& slabValues , int threads ); + template< class Vertex > + void SetXSliceIsoEdges( int depth , int slice , std::vector< SlabValues< Vertex > >& slabValues , int threads ); + + template< class Vertex > + void SetIsoSurface( int depth , int offset , const SliceValues< Vertex >& bValues , const SliceValues< Vertex >& fValues , const XSliceValues< Vertex >& xValues , CoredMeshData< Vertex >& mesh , bool polygonMesh , bool addBarycenter , int& vOffset , int threads ); + + template< class Vertex > + static int AddIsoPolygons( CoredMeshData< Vertex >& mesh , std::vector< std::pair< int , Vertex > >& polygon , bool polygonMesh , bool addBarycenter , int& vOffset ); + + template< class Vertex > + bool GetIsoVertex( ConstPointer( Real ) kernelDensityWeights , Real isoValue , typename TreeOctNode::ConstNeighborKey3& neighborKey3 , const TreeOctNode* node , int edgeIndex , int z , const SliceValues< Vertex >& sValues , Vertex& vertex ); + template< class Vertex > + bool GetIsoVertex( ConstPointer( Real ) kernelDensityWeights , Real isoValue , typename TreeOctNode::ConstNeighborKey3& neighborKey3 , const TreeOctNode* node , int cornerIndex , const SliceValues< Vertex >& bValues , const SliceValues< Vertex >& fValues , Vertex& vertex ); + + + //////////////////////// + // Evaluation Methods // + //////////////////////// + Real getCornerValue( const typename TreeOctNode::ConstNeighborKey3& neighborKey3 , const TreeOctNode* node , int corner , ConstPointer( Real ) solution , ConstPointer( Real ) metSolution , const typename BSplineData< 2 >::template CornerEvaluator< 2 >& evaluator , const Stencil< double , 3 >& stencil , const Stencil< double , 3 > stencils[8] , bool isInterior ) const; + Point3D< Real > getCornerNormal( const typename TreeOctNode::ConstNeighbors5& neighbors5 , const typename TreeOctNode::ConstNeighbors5& pNeighbors5 , const TreeOctNode* node , int corner , ConstPointer( Real ) solution , ConstPointer( Real ) metSolution , const typename BSplineData< 2 >::template CornerEvaluator< 2 >& evaluator , const Stencil< Point3D< double > , 5 >& nStencil , const Stencil< Point3D< double > , 5 > nStencils[8] , bool isInterior ) const; + std::pair< Real , Point3D< Real > > getCornerValueAndNormal( const typename TreeOctNode::ConstNeighborKey3& neighborKey3 , const TreeOctNode* node , int corner , ConstPointer( Real ) solution , ConstPointer( Real ) metSolution , const typename BSplineData< 2 >::template CornerEvaluator< 2 >& evaluator , const Stencil< double , 3 >& vStencil , const Stencil< double , 3 > vStencils[8] , const Stencil< Point3D< double > , 3 >& nStencil , const Stencil< Point3D< double > , 3 > nStencils[8] , bool isInterior ) const; + Real getCenterValue( const typename TreeOctNode::ConstNeighborKey3& neighborKey3 , const TreeOctNode* node , ConstPointer( Real ) solution , ConstPointer( Real ) metSolution , const typename BSplineData< 2 >::template CenterEvaluator< 1 >& evaluator , const Stencil< double , 3 >& stencil , const Stencil< double , 3 >& pStencil , bool isInterior ) const; + + static bool _IsInset( const TreeOctNode* node ); + static bool _IsInsetSupported( const TreeOctNode* node ); + + void refineBoundary( std::vector< int >* map ); +public: + int threads; + static double maxMemoryUsage; + TreeOctNode tree; + + static double MemoryUsage( void ); + Octree( void ); + + void MakeComplete( std::vector< int >* map=NULL ); + void Finalize( std::vector< int >* map=NULL ); + void ClipTree( const NormalInfo& normalInfo ); + + Real Evaluate( ConstPointer( Real ) coefficients , Point3D< Real > p , const BSplineData< 2 >* fData=NULL ) const; + Pointer( Real ) Evaluate( ConstPointer( Real ) coefficients , int& res , Real isoValue=0.f , int depth=-1 ); + template< class PointReal > + int SetTree( PointStream< PointReal >* pointStream , int minDepth , int maxDepth , int fullDepth , int splatDepth , Real samplesPerNode , + Real scaleFactor , bool useConfidence , bool useNormalWeight , Real constraintWeight , int adaptiveExponent , + PointInfo& pointInfo , NormalInfo& normalInfo , std::vector< Real >& kernelDensityWeights , std::vector< Real >& centerWeights , +#if 1 + // Bug fix courtesy of asmaloney + int boundaryType=BSplineElements< 2 >::NONE , XForm4x4< Real > xForm=XForm4x4< Real >::Identity() , bool makeComplete=false ); +#else + int boundaryType=BSplineElements< 2 >::NONE , XForm4x4< Real > xForm=XForm4x4< Real >::Identity , bool makeComplete=false ); +#endif + Pointer( Real ) SetLaplacianConstraints( const NormalInfo& normalInfo ); + Pointer( Real ) SolveSystem( PointInfo& pointInfo , Pointer( Real ) constraints , bool showResidual , int iters , int maxSolveDepth , int cgDepth=0 , double cgAccuracy=0 ); + + Real GetIsoValue( ConstPointer( Real ) solution , const std::vector< Real >& centerWeights ); + template< class Vertex > + void GetMCIsoSurface( ConstPointer( Real ) kernelDensityWeights , ConstPointer( Real ) solution , Real isoValue , CoredMeshData< Vertex >& mesh , bool nonLinearFit=true , bool addBarycenter=false , bool polygonMesh=false ); +}; +template< class Real > +void Reset( void ) +{ + TreeNodeData::NodeCount=0; + Octree< Real >::maxMemoryUsage = 0; +} + +#include "MultiGridOctreeData.inl" +#include "MultiGridOctreeData.SortedTreeNodes.inl" +#include "MultiGridOctreeData.IsoSurface.inl" +#endif // MULTI_GRID_OCTREE_DATA_INCLUDED diff --git a/Src/MultiGridOctreeData.inl b/Src/MultiGridOctreeData.inl new file mode 100644 index 0000000..fc8d35b --- /dev/null +++ b/Src/MultiGridOctreeData.inl @@ -0,0 +1,2645 @@ +/* +Copyright (c) 2006, Michael Kazhdan and Matthew Bolitho +All rights reserved. + +Redistribution and use in source and binary forms, with or without modification, +are permitted provided that the following conditions are met: + +Redistributions of source code must retain the above copyright notice, this list of +conditions and the following disclaimer. Redistributions in binary form must reproduce +the above copyright notice, this list of conditions and the following disclaimer +in the documentation and/or other materials provided with the distribution. + +Neither the name of the Johns Hopkins University nor the names of its contributors +may be used to endorse or promote products derived from this software without specific +prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY +EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO THE IMPLIED WARRANTIES +OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT +SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED +TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR +BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN +ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH +DAMAGE. +*/ + +#include "Octree.h" +#include "MyTime.h" +#include "MemoryUsage.h" +#include "PointStream.h" +#include "MAT.h" + +#define ITERATION_POWER 1.0/3 +#define MEMORY_ALLOCATOR_BLOCK_SIZE 1<<12 +#define SPLAT_ORDER 2 + +const double MATRIX_ENTRY_EPSILON = 0; +const double EPSILON = 1e-6; +const double ROUND_EPS = 1e-5; + + + +////////////////// +// TreeNodeData // +////////////////// +int TreeNodeData::NodeCount = 0; +TreeNodeData::TreeNodeData( void ){ nodeIndex = NodeCount++; } +TreeNodeData::~TreeNodeData( void ) { } + + +//////////// +// Octree // +//////////// +template< class Real > double Octree< Real >::maxMemoryUsage=0; + +template< class Real > +double Octree< Real >::MemoryUsage(void) +{ + double mem = double( MemoryInfo::Usage() ) / (1<<20); + if( mem>maxMemoryUsage ) maxMemoryUsage=mem; + return mem; +} + +template< class Real > +Octree< Real >::Octree( void ) +{ + threads = 1; + _normalSmooth = 0; + _constrainValues = false; +} + +template< class Real > +bool Octree< Real >::_IsInset( const TreeOctNode* node ) +{ + int d , off[3]; + node->depthAndOffset( d , off ); + int res = 1<=o && off[0]=o && off[1]=o && off[2] +bool Octree< Real >::_IsInsetSupported( const TreeOctNode* node ) +{ + int d , off[3]; + node->depthAndOffset( d , off ); + int res = 1<=o && off[0]=o && off[1]=o && off[2] +int Octree< Real >::SplatOrientedPoint( ConstPointer( Real ) kernelDensityWeights , TreeOctNode* node , const Point3D& position , const Point3D& normal , NormalInfo& normalInfo , typename TreeOctNode::NeighborKey3& neighborKey ) +{ + double x , dxdy , dxdydz , dx[DIMENSION][SPLAT_ORDER+1]; + double width; + int off[3]; + typename TreeOctNode::Neighbors3& neighbors = neighborKey.setNeighbors( node ); + Point3D center; + Real w; + node->centerAndWidth( center , w ); + width=w; + for( int i=0 ; i<3 ; i++ ) + { +#if SPLAT_ORDER==2 + off[i] = 0; + x = ( center[i] - position[i] - width ) / width; + dx[i][0] = 1.125+1.500*x+0.500*x*x; + x = ( center[i] - position[i] ) / width; + dx[i][1] = 0.750 - x*x; + + dx[i][2] = 1. - dx[i][1] - dx[i][0]; +#elif SPLAT_ORDER==1 + x = ( position[i] - center[i] ) / width; + if( x<0 ) + { + off[i] = 0; + dx[i][0] = -x; + } + else + { + off[i] = 1; + dx[i][0] = 1. - x; + } + dx[i][1] = 1. - dx[i][0]; +#elif SPLAT_ORDER==0 + off[i] = 1; + dx[i][0] = 1.; +#else +# error Splat order not supported +#endif // SPLAT_ORDER + } + for( int i=off[0] ; i<=off[0]+SPLAT_ORDER ; i++ ) for( int j=off[1] ; j<=off[1]+SPLAT_ORDER ; j++ ) + { + dxdy = dx[0][i] * dx[1][j]; + for( int k=off[2] ; k<=off[2]+SPLAT_ORDER ; k++ ) + if( neighbors.neighbors[i][j][k] ) + { + dxdydz = dxdy * dx[2][k]; + TreeOctNode* _node = neighbors.neighbors[i][j][k]; + if( normalInfo.normalIndices.size() n; + n[0] = n[1] = n[2] = 0; + idx = normalInfo.normalIndices[ _node->nodeData.nodeIndex ] = (int)normalInfo.normals.size(); + normalInfo.normals.push_back( n ); + } + normalInfo.normals[idx] += normal * Real( dxdydz ); + } + } + return 0; +} +template< class Real > +Real Octree< Real >::SplatOrientedPoint( ConstPointer( Real ) kernelDensityWeights , const Point3D& position , const Point3D& normal , NormalInfo& normalInfo , typename TreeOctNode::NeighborKey3& neighborKey , int splatDepth , Real samplesPerNode , int minDepth , int maxDepth ) +{ + double dx; + Point3D n; + TreeOctNode* temp; + int cnt=0; + double width; + Point3D< Real > myCenter; + Real myWidth; + myCenter[0] = myCenter[1] = myCenter[2] = Real(0.5); + myWidth = Real(1.0); + + temp = &tree; + while( temp->depth()children ) + { + fprintf( stderr , "Octree::SplatOrientedPoint error\n" ); + return -1; + } + int cIndex=TreeOctNode::CornerIndex(myCenter,position); + temp=&temp->children[cIndex]; + myWidth/=2; + if(cIndex&1) myCenter[0] += myWidth/2; + else myCenter[0] -= myWidth/2; + if(cIndex&2) myCenter[1] += myWidth/2; + else myCenter[1] -= myWidth/2; + if(cIndex&4) myCenter[2] += myWidth/2; + else myCenter[2] -= myWidth/2; + } + Real weight , depth; + GetSampleDepthAndWeight( kernelDensityWeights , temp , position , neighborKey , samplesPerNode , depth , weight ); + + if( depthmaxDepth ) depth=Real(maxDepth); + int topDepth=int(ceil(depth)); + + dx = 1.0-(topDepth-depth); + if( topDepth<=minDepth ) + { + topDepth=minDepth; + dx=1; + } + else if( topDepth>maxDepth ) + { + topDepth=maxDepth; + dx=1; + } + while( temp->depth()>topDepth ) temp=temp->parent; + while( temp->depth()children) temp->initChildren(); + int cIndex=TreeOctNode::CornerIndex(myCenter,position); + temp=&temp->children[cIndex]; + myWidth/=2; + if(cIndex&1) myCenter[0] += myWidth/2; + else myCenter[0] -= myWidth/2; + if(cIndex&2) myCenter[1] += myWidth/2; + else myCenter[1] -= myWidth/2; + if(cIndex&4) myCenter[2] += myWidth/2; + else myCenter[2] -= myWidth/2; + } + width = 1.0 / ( 1<depth() ); + n = normal * weight / Real( pow( width , 3 ) ) * Real( dx ); + SplatOrientedPoint( kernelDensityWeights , temp , position , n , normalInfo , neighborKey ); + if( fabs(1.0-dx) > EPSILON ) + { + dx = Real(1.0-dx); + temp = temp->parent; + width = 1.0 / ( 1<depth() ); + + n = normal * weight / Real( pow( width , 3 ) ) * Real( dx ); + SplatOrientedPoint( kernelDensityWeights , temp , position , n , normalInfo , neighborKey ); + } + return weight; +} + +template< class Real > +void Octree< Real >::GetSampleDepthAndWeight( ConstPointer( Real ) kernelDensityWeights , const TreeOctNode* node , const Point3D& position , typename TreeOctNode::ConstNeighborKey3& neighborKey , Real samplesPerNode , Real& depth , Real& weight ) +{ + const TreeOctNode* temp=node; + weight = Real(1.0)/GetSampleWeight( kernelDensityWeights , temp , position , neighborKey ); + if( weight>=samplesPerNode ) depth = Real( temp->depth() + log( weight / samplesPerNode ) / log(double(1<<(DIMENSION-1))) ); + else + { + Real oldWeight , newWeight; + oldWeight = newWeight = weight; + while( newWeightparent ) + { + temp=temp->parent; + oldWeight = newWeight; + newWeight = Real(1.0)/GetSampleWeight( kernelDensityWeights , temp , position, neighborKey ); + } + depth = Real( temp->depth() + log( newWeight / samplesPerNode ) / log( newWeight / oldWeight ) ); + } + weight = Real( pow( double(1<<(DIMENSION-1)) , -double(depth) ) ); +} +template< class Real > +void Octree< Real >::GetSampleDepthAndWeight( ConstPointer( Real ) kernelDensityWeights , TreeOctNode* node , const Point3D& position , typename TreeOctNode::NeighborKey3& neighborKey , Real samplesPerNode , Real& depth , Real& weight ) +{ + TreeOctNode* temp=node; + weight = Real(1.0)/GetSampleWeight( kernelDensityWeights , temp , position , neighborKey ); + if( weight>=samplesPerNode ) depth = Real( temp->depth() + log( weight / samplesPerNode ) / log(double(1<<(DIMENSION-1))) ); + else + { + Real oldWeight , newWeight; + oldWeight = newWeight = weight; + while( newWeightparent ) + { + temp=temp->parent; + oldWeight = newWeight; + newWeight = Real(1.0)/GetSampleWeight( kernelDensityWeights , temp , position, neighborKey ); + } + depth = Real( temp->depth() + log( newWeight / samplesPerNode ) / log( newWeight / oldWeight ) ); + } + weight = Real( pow( double(1<<(DIMENSION-1)) , -double(depth) ) ); +} +template< class Real > +Real Octree< Real >::GetSampleWeight( ConstPointer( Real ) kernelDensityWeights , const Point3D& position , typename TreeOctNode::NeighborKey3& neighborKey , int splatDepth ) +{ + Point3D< Real > myCenter; + Real myWidth; + myCenter[0] = myCenter[1] = myCenter[2] = Real(0.5); + myWidth = Real(1.0); + + TreeOctNode* temp = &tree; + int d = 0; + while( dchildren ) + { + fprintf( stderr , "Octree::SplatOrientedPoint error\n" ); + return -1; + } + int cIndex = TreeOctNode::CornerIndex( myCenter , position ); + temp = &temp->children[cIndex]; + myWidth /= 2; + if( cIndex&1 ) myCenter[0] += myWidth/2; + else myCenter[0] -= myWidth/2; + if( cIndex&2 ) myCenter[1] += myWidth/2; + else myCenter[1] -= myWidth/2; + if( cIndex&4 ) myCenter[2] += myWidth/2; + else myCenter[2] -= myWidth/2; + d++; + } + return GetSampleWeight( kernelDensityWeights , temp , position , neighborKey ); +} +template< class Real > +Real Octree< Real >::GetSampleWeight( ConstPointer( Real ) kernelDensityWeights , TreeOctNode* node , const Point3D& position , typename TreeOctNode::NeighborKey3& neighborKey ) +{ + Real weight=0; + double x , dxdy , dx[DIMENSION][3]; + double width; + typename TreeOctNode::Neighbors3& neighbors = neighborKey.setNeighbors( node ); + Point3D center; + Real w; + node->centerAndWidth(center,w); + width=w; + + for( int i=0 ; inodeData.nodeIndex ] ); + } + return Real( 1.0 / weight ); +} +template< class Real > +Real Octree< Real >::GetSampleWeight( ConstPointer( Real ) kernelDensityWeights , const TreeOctNode* node , const Point3D& position , typename TreeOctNode::ConstNeighborKey3& neighborKey ) +{ + Real weight=0; + double x,dxdy,dx[DIMENSION][3]; + double width; + typename TreeOctNode::ConstNeighbors3& neighbors = neighborKey.getNeighbors( node ); + Point3D center; + Real w; + node->centerAndWidth( center , w ); + width=w; + + for( int i=0 ; inodeData.nodeIndex ] ); + } + return Real( 1.0 / weight ); +} +template< class Real > +int Octree< Real >::UpdateWeightContribution( std::vector< Real >& kernelDensityWeights , TreeOctNode* node , const Point3D& position , typename TreeOctNode::NeighborKey3& neighborKey , Real weight ) +{ + typename TreeOctNode::Neighbors3& neighbors = neighborKey.setNeighbors( node ); + if( kernelDensityWeights.size() center; + Real w; + node->centerAndWidth( center , w ); + width=w; + const double SAMPLE_SCALE = 1. / ( 0.125 * 0.125 + 0.75 * 0.75 + 0.125 * 0.125 ); + + for( int i=0 ; inodeData.nodeIndex ] += Real( dxdy * dx[2][k] ); + } + return 0; +} +template< class Real > +bool Octree< Real >::_InBounds( Point3D< Real > p ) const +{ + if( _boundaryType==0 ){ if( p[0]Real(0.75) || p[1]Real(0.75) || p[2]Real(0.75) ) return false; } + else { if( p[0]Real(1.00) || p[1]Real(1.00) || p[2]Real(1.00) ) return false; } + return true; +} +template< class Real > +template< class PointReal > +int Octree< Real >::SetTree( PointStream< PointReal >* pointStream , int minDepth , int maxDepth , int fullDepth , + int splatDepth , Real samplesPerNode , Real scaleFactor , + bool useConfidence , bool useNormalWeights , Real constraintWeight , int adaptiveExponent , + PointInfo& pointInfo , NormalInfo& normalInfo , std::vector< Real >& kernelDensityWeights , std::vector< Real >& centerWeights , + int boundaryType , XForm4x4< Real > xForm , bool makeComplete ) +{ + if( splatDepth<0 ) splatDepth = 0; + + _boundaryType = boundaryType; + if ( _boundaryType<0 ) _boundaryType = -1; + else if( _boundaryType>0 ) _boundaryType = 1; + _samplesPerNode = samplesPerNode; + _splatDepth = splatDepth; + _constrainValues = (constraintWeight>0); + + XForm3x3< Real > xFormN; + for( int i=0 ; i<3 ; i++ ) for( int j=0 ; j<3 ; j++ ) xFormN(i,j) = xForm(i,j); + xFormN = xFormN.transpose().inverse(); + minDepth = std::min< int >( minDepth , maxDepth ); // minDepth <= maxDepth + fullDepth = std::max< int >( minDepth , std::min< int >( fullDepth , maxDepth ) ); // minDepth <= fullDepth <= maxDepth + // If _boundaryType==0, points are scaled to be in the [0.25,0.75]^3 cube so all depths have to be offset by + // and the minDepth has to be 2. + if( _boundaryType==0 ) + { + minDepth++ , maxDepth++ , fullDepth++; + if( splatDepth ) splatDepth++; + minDepth = std::max< int >( minDepth , 2 ); + } + // Otherwise the points are in the [0,1]^3 cube. + // However, for Neumann constraints, the function at depth 0 is constant so the system matrix is zero if there + // is no screening. +#if 0 + else if( _boundaryType==1 && !_constrainValues ) minDepth = std::max< int >( minDepth , 1 ); +#endif + + _fData.set( maxDepth , _boundaryType ); + + _minDepth = minDepth; + _fullDepth = fullDepth; + double pointWeightSum = 0; + Point3D< Real > min , max , myCenter; + Real myWidth; + int i , cnt=0; + TreeOctNode* temp; + + typename TreeOctNode::NeighborKey3 neighborKey; + neighborKey.set( maxDepth ); + + tree.setFullDepth( _fullDepth ); + + // Read through once to get the center and scale + { + double t = Time(); + Point3D< Real > p; + Point3D< PointReal > _p , _n; + while( pointStream->nextPoint( _p , _n ) ) + { + p = xForm * Point3D< Real >(_p); + for( i=0 ; imax[i] ) max[i] = p[i]; + } + cnt++; + } + + if( _boundaryType==0 ) _scale = std::max< Real >( max[0]-min[0] , std::max< Real >( max[1]-min[1] , max[2]-min[2] ) ) * 2; + else _scale = std::max< Real >( max[0]-min[0] , std::max< Real >( max[1]-min[1] , max[2]-min[2] ) ); + _center = ( max+min ) /2; + } + + _scale *= scaleFactor; + for( i=0 ; i0 ) + { + double t = Time(); + cnt = 0; + pointStream->reset(); + Point3D< Real > p , n; + Point3D< PointReal > _p , _n; + while( pointStream->nextPoint( _p , _n ) ) + { + p = xForm * Point3D< Real >(_p) , n = xFormN * Point3D< Real >(_n); + p = ( p - _center ) / _scale; + if( !_InBounds(p) ) continue; + myCenter = Point3D< Real >( Real(0.5) , Real(0.5) , Real(0.5) ); + myWidth = Real(1.0); + Real weight=Real( 1. ); + if( useConfidence ) weight = Real( Length(n) ); + temp = &tree; + int d=0; + while( dchildren ) temp->initChildren(); + int cIndex=TreeOctNode::CornerIndex( myCenter , p ); + temp = temp->children + cIndex; + myWidth/=2; + if( cIndex&1 ) myCenter[0] += myWidth/2; + else myCenter[0] -= myWidth/2; + if( cIndex&2 ) myCenter[1] += myWidth/2; + else myCenter[1] -= myWidth/2; + if( cIndex&4 ) myCenter[2] += myWidth/2; + else myCenter[2] -= myWidth/2; + d++; + } + UpdateWeightContribution( kernelDensityWeights , temp , p , neighborKey , weight ); + cnt++; + } + } + kernelDensityWeights.resize( TreeNodeData::NodeCount , 0 ); + + std::vector< _PointData >& points = pointInfo.points; + + cnt = 0; + pointStream->reset(); + Point3D< Real > p , n; + Point3D< PointReal > _p , _n; + while( pointStream->nextPoint( _p , _n ) ) + { + p = xForm * Point3D< Real >(_p) , n = xFormN * Point3D< Real >(_n); + n *= Real(-1.); + p = ( p - _center ) / _scale; + if( !_InBounds(p) ) continue; + myCenter = Point3D< Real >( Real(0.5) , Real(0.5) , Real(0.5) ); + myWidth = Real(1.0); + Real normalLength = Real( Length( n ) ); + if( normalLength!=normalLength || normalLength<=EPSILON ) continue; + if( !useConfidence ) n /= normalLength; + + Real pointWeight = Real(1.f); + if( samplesPerNode>0 && splatDepth ) pointWeight = SplatOrientedPoint( GetPointer( kernelDensityWeights ) , p , n , normalInfo , neighborKey , splatDepth , samplesPerNode , _minDepth , maxDepth ); + else + { + temp = &tree; + int d=0; + if( splatDepth ) + { + while( dchildren[cIndex]; + myWidth /= 2; + if(cIndex&1) myCenter[0] += myWidth/2; + else myCenter[0] -= myWidth/2; + if(cIndex&2) myCenter[1] += myWidth/2; + else myCenter[1] -= myWidth/2; + if(cIndex&4) myCenter[2] += myWidth/2; + else myCenter[2] -= myWidth/2; + d++; + } + pointWeight = GetSampleWeight( GetPointer( kernelDensityWeights ) , temp , p , neighborKey ); + } + for( i=0 ; ichildren ) temp->initChildren(); + int cIndex=TreeOctNode::CornerIndex(myCenter,p); + temp=&temp->children[cIndex]; + myWidth/=2; + if(cIndex&1) myCenter[0] += myWidth/2; + else myCenter[0] -= myWidth/2; + if(cIndex&2) myCenter[1] += myWidth/2; + else myCenter[1] -= myWidth/2; + if(cIndex&4) myCenter[2] += myWidth/2; + else myCenter[2] -= myWidth/2; + d++; + } + SplatOrientedPoint( GetPointer( kernelDensityWeights ) , temp , p , n , normalInfo , neighborKey ); + } + pointWeightSum += pointWeight; + if( _constrainValues ) + { + Real pointScreeningWeight = useNormalWeights ? Real( normalLength ) : Real(1.f); + int d = 0; + TreeOctNode* temp = &tree; + myCenter = Point3D< Real >( Real(0.5) , Real(0.5) , Real(0.5) ); + myWidth = Real(1.0); + while( 1 ) + { + if( pointInfo.pointIndices.size()nodeData.nodeIndex ] = idx; + } + else + { + points[idx].weight += pointScreeningWeight; + points[idx].position += p*pointScreeningWeight; + } + + int cIndex = TreeOctNode::CornerIndex( myCenter , p ); + if( !temp->children ) break; + temp = &temp->children[cIndex]; + myWidth /= 2; + if( cIndex&1 ) myCenter[0] += myWidth/2; + else myCenter[0] -= myWidth/2; + if( cIndex&2 ) myCenter[1] += myWidth/2; + else myCenter[1] -= myWidth/2; + if( cIndex&4 ) myCenter[2] += myWidth/2; + else myCenter[2] -= myWidth/2; + d++; + } + } + cnt++; + } + + if( _boundaryType==0 ) pointWeightSum *= Real(4.); + constraintWeight *= Real( pointWeightSum ); + constraintWeight /= cnt; + + MemoryUsage( ); + if( _constrainValues ) + // Set the average position and scale the weights + for( TreeOctNode* node=tree.nextNode() ; node ; node=tree.nextNode(node) ) + if( pointInfo.pointIndex( node )!=-1 ) + { + int idx = pointInfo.pointIndex( node ); + points[idx].position /= points[idx].weight; + int e = ( _boundaryType==0 ? node->depth()-1 : node->depth() ) * adaptiveExponent - ( _boundaryType==0 ? maxDepth-1 : maxDepth ) * (adaptiveExponent-1); + if( e<0 ) points[idx].weight /= Real( 1<<(-e) ); + else points[idx].weight *= Real( 1<< e ); + points[idx].weight *= Real( constraintWeight ); + } +#if FORCE_NEUMANN_FIELD + if( _boundaryType==1 ) + for( TreeOctNode* node=tree.nextNode() ; node ; node=tree.nextNode( node ) ) + { + int d , off[3] , res; + node->depthAndOffset( d , off ); + res = 1<& normal = normalInfo.normals[ idx ]; + for( int d=0 ; d<3 ; d++ ) if( off[d]==0 || off[d]==res-1 ) normal[d] = 0; + } +#endif // FORCE_NEUMANN_FIELD + centerWeights.resize( tree.nodes() , 0 ); + kernelDensityWeights.resize( tree.nodes() , 0 ); + // Set the point weights for evaluating the iso-value + for( TreeOctNode* node=tree.nextNode() ; node ; node=tree.nextNode(node) ) + { + int idx = normalInfo.normalIndex( node ); + if( idx<0 ) centerWeights[ node->nodeData.nodeIndex ] = 0; + else centerWeights[ node->nodeData.nodeIndex ] = Real( Length( normalInfo.normals[ idx ] ) ); + } + MemoryUsage(); + { + std::vector< int > indexMap; + if( makeComplete ) MakeComplete( &indexMap ); + else ClipTree( normalInfo ) , Finalize( &indexMap ); + + { + std::vector< int > temp = pointInfo.pointIndices; + pointInfo.pointIndices.resize( indexMap.size() ); + for( int i=0 ; i temp = normalInfo.normalIndices; + normalInfo.normalIndices.resize( indexMap.size() ); + for( int i=0 ; i temp = centerWeights; + centerWeights.resize( indexMap.size() ); + for( int i=0 ; i temp = kernelDensityWeights; + kernelDensityWeights.resize( indexMap.size() ); + for( int i=0 ; i +void Octree< Real >::MakeComplete( std::vector< int >* map ) +{ + tree.setFullDepth( tree.maxDepth() ); + refineBoundary( map ); + MemoryUsage(); +} +template< class Real > +void Octree< Real >::ClipTree( const NormalInfo& normalInfo ) +{ + int maxDepth = tree.maxDepth(); + for( TreeOctNode* temp=tree.nextNode() ; temp ; temp=tree.nextNode(temp) ) + if( temp->children && temp->depth()>=_fullDepth ) + { + int hasNormals=0; + for( int i=0 ; ichildren[i] , normalInfo ); + if( !hasNormals ) temp->children=NULL; + } + MemoryUsage(); +} + +template< class Real > +void Octree< Real >::Finalize( std::vector< int >* map ) +{ + int maxDepth = tree.maxDepth( ); + typename TreeOctNode::NeighborKey3 neighborKey; + neighborKey.set( maxDepth ); + for( int d=maxDepth ; d>1 ; d-- ) + for( TreeOctNode* node=tree.nextNode() ; node ; node=tree.nextNode( node ) ) if( node->depth()==d ) + { + typename TreeOctNode::Neighbors3& neighbors = neighborKey.setNeighbors( node->parent->parent ); + for( int i=0 ; i<3 ; i++ ) for( int j=0 ; j<3 ; j++ ) for( int k=0 ; k<3 ; k++ ) + if( neighbors.neighbors[i][j][k] && !neighbors.neighbors[i][j][k]->children ) + neighbors.neighbors[i][j][k]->initChildren(); + } + refineBoundary( map ); +} +template< class Real > +double Octree< Real >::GetLaplacian( const typename BSplineData< 2 >::Integrator& integrator , int d , const int off1[] , const int off2[] , bool childParent ) const +{ + double vv[] = + { + integrator.dot( d , off1[0] , off2[0] , false , false , childParent ) , + integrator.dot( d , off1[1] , off2[1] , false , false , childParent ) , + integrator.dot( d , off1[2] , off2[2] , false , false , childParent ) + }; + double dd[] = + { + integrator.dot( d , off1[0] , off2[0] , true , true , childParent ) , + integrator.dot( d , off1[1] , off2[1] , true , true , childParent ) , + integrator.dot( d , off1[2] , off2[2] , true , true , childParent ) + }; + return dd[0]*vv[1]*vv[2] + vv[0]*dd[1]*vv[2] + vv[0]*vv[1]*dd[2]; +} +template< class Real > +double Octree< Real >::GetDivergence1( const typename BSplineData< 2 >::Integrator& integrator , int d , const int off1[] , const int off2[] , bool childParent , const Point3D< Real >& normal1 ) const +{ + return Point3D< double >::Dot( GetDivergence1( integrator , d , off1 , off2 , childParent ) , normal1 ); +} +template< class Real > +double Octree< Real >::GetDivergence2( const typename BSplineData< 2 >::Integrator& integrator , int d , const int off1[] , const int off2[] , bool childParent , const Point3D< Real >& normal2 ) const +{ + return Point3D< double >::Dot( GetDivergence2( integrator , d , off1 , off2 , childParent ) , normal2 ); +} +template< class Real > +Point3D< double > Octree< Real >::GetDivergence1( const typename BSplineData< 2 >::Integrator& integrator , int d , const int off1[] , const int off2[] , bool childParent ) const +{ + double vv[] = + { + integrator.dot( d , off1[0] , off2[0] , false , false , childParent ) , + integrator.dot( d , off1[1] , off2[1] , false , false , childParent ) , + integrator.dot( d , off1[2] , off2[2] , false , false , childParent ) + }; +#if GRADIENT_DOMAIN_SOLUTION + // Take the dot-product of the vector-field with the gradient of the basis function + double vd[] = + { + integrator.dot( d , off1[0] , off2[0] , false , true , childParent ) , + integrator.dot( d , off1[1] , off2[1] , false , true , childParent ) , + integrator.dot( d , off1[2] , off2[2] , false , true , childParent ) + }; + return Point3D< double >( vd[0]*vv[1]*vv[2] , vv[0]*vd[1]*vv[2] , vv[0]*vv[1]*vd[2] ); +#else // !GRADIENT_DOMAIN_SOLUTION + // Take the dot-product of the divergence of the vector-field with the basis function + double dv[] = + { + integrator.dot( d , off1[0] , off2[0] , true , false , childParent ) , + integrator.dot( d , off1[1] , off2[1] , true , false , childParent ) , + integrator.dot( d , off1[2] , off2[2] , true , false , childParent ) + }; + return -Point3D< double >( dv[0]*vv[1]*vv[2] , vv[0]*dv[1]*vv[2] , vv[0]*vv[1]*dv[2] ); +#endif // GRADIENT_DOMAIN_SOLUTION +} +template< class Real > +Point3D< double > Octree< Real >::GetDivergence2( const typename BSplineData< 2 >::Integrator& integrator , int d , const int off1[] , const int off2[] , bool childParent ) const +{ + double vv[] = + { + integrator.dot( d , off1[0] , off2[0] , false , false , childParent ) , + integrator.dot( d , off1[1] , off2[1] , false , false , childParent ) , + integrator.dot( d , off1[2] , off2[2] , false , false , childParent ) + }; +#if GRADIENT_DOMAIN_SOLUTION + // Take the dot-product of the vector-field with the gradient of the basis function + double dv[] = + { + integrator.dot( d , off1[0] , off2[0] , true , false , childParent ) , + integrator.dot( d , off1[1] , off2[1] , true , false , childParent ) , + integrator.dot( d , off1[2] , off2[2] , true , false , childParent ) + }; + return Point3D< double >( dv[0]*vv[1]*vv[2] , vv[0]*dv[1]*vv[2] , vv[0]*vv[1]*dv[2] ); +#else // !GRADIENT_DOMAIN_SOLUTION + // Take the dot-product of the divergence of the vector-field with the basis function + double vd[] = + { + integrator.dot( d , off1[0] , off2[0] , false , true , childParent ) , + integrator.dot( d , off1[1] , off2[1] , false , true , childParent ) , + integrator.dot( d , off1[2] , off2[2] , false , true , childParent ) + }; + return -Point3D< double >( vd[0]*vv[1]*vv[2] , vv[0]*vd[1]*vv[2] , vv[0]*vv[1]*vd[2] ); +#endif // GRADIENT_DOMAIN_SOLUTION +} + +template< class Real > +int Octree< Real >::GetMatrixRowSize( const typename TreeOctNode::Neighbors5& neighbors5 , bool symmetric ) const +{ + int count = 0; + int nodeIndex = neighbors5.neighbors[2][2][2]->nodeData.nodeIndex; + const TreeOctNode* const * _nodes = &neighbors5.neighbors[0][0][0]; + if( symmetric ) + { + for( int i=0 ; i<125 ; i++ ) if( _nodes[i] && _nodes[i]->nodeData.nodeIndex>=nodeIndex ) count++; + } + else + { + for( int i=0 ; i<125 ; i++ ) if( _nodes[i] ) count++; + } + return count; +} + +template< class Real > +int Octree< Real >::SetMatrixRow( const PointInfo& pointInfo , const typename TreeOctNode::Neighbors5& neighbors5 , Pointer( MatrixEntry< Real > ) row , int offset , const typename BSplineData< 2 >::Integrator& integrator , const Stencil< double , 5 >& stencil , bool symmetric ) const +{ + const std::vector< _PointData >& points = pointInfo.points; + bool hasYZPoints[3] , hasZPoints[3][3]; + Real diagonal = 0; + Real splineValues[3*3*3*3*3]; + memset( splineValues , 0 , sizeof( Real ) * 3 * 3 * 3 * 3 * 3 ); + + int count = 0; + const TreeOctNode* node = neighbors5.neighbors[2][2][2]; + + bool isInterior; + int d , off[3]; + node->depthAndOffset( d , off ); + + int o = _boundaryType==0 ? ( 1<<(d-2) ) : 0; + int mn = 2+o , mx = (1<=mn && off[0]=mn && off[1]=mn && off[2]centerIndex( idx ); + for( int j=0 ; j<3 ; j++ ) + { + hasYZPoints[j] = false; + for( int k=0 ; k<3 ; k++ ) + { + hasZPoints[j][k] = false; + for( int l=0 ; l<3 ; l++ ) + { + const TreeOctNode* _node = neighbors5.neighbors[j+1][k+1][l+1]; + if( _node && pointInfo.pointIndex( _node )!=-1 ) + { + const _PointData& pData = points[ pointInfo.pointIndex( _node ) ]; + Real* _splineValues = splineValues + 3*3*(3*(3*j+k)+l); + Real weight = pData.weight; + Point3D< Real > p = pData.position; + for( int s=0 ; s<3 ; s++ ) + { +#if ROBERTO_TOLDO_FIX + if( idx[0]+j-s>=0 && idx[0]+j-s<((2<=0 && idx[1]+k-s<((2<=0 && idx[2]+l-s<((2<nodeData.nodeIndex; + if( isInterior ) // General case, so try to make fast + { + const TreeOctNode* const * _nodes = &neighbors5.neighbors[0][0][0]; + const double* _stencil = &stencil.values[0][0][0]; + Real* _values = &pointValues[0][0][0]; + if( _constrainValues ) for( int i=0 ; i<125 ; i++ ) _values[i] = Real( _stencil[i] + _values[i] ); + else for( int i=0 ; i<125 ; i++ ) _values[i] = Real( _stencil[i] ); + if( symmetric ) pointValues[2][2][2] /= 2; + row[count++] = MatrixEntry< Real >( nodeIndex-offset , _values[5*5*2+5*2+2] ); + if( symmetric ) + { + for( int i=0 ; i<125 ; i++ ) if( i!=(5*5*2+5*2+2) && _nodes[i] && _nodes[i]->nodeData.nodeIndex>=nodeIndex ) + row[count++] = MatrixEntry< Real >( _nodes[i]->nodeData.nodeIndex-offset , _values[i] ); + } + else + { + for( int i=0 ; i<125 ; i++ ) if( i!=(5*5*2+5*2+2) && _nodes[i] ) + row[count++] = MatrixEntry< Real >( _nodes[i]->nodeData.nodeIndex-offset , _values[i] ); + } + } + else + { + int d , off[3]; + node->depthAndOffset( d , off ); + Real temp = Real( GetLaplacian( integrator , d , off , off , false ) ); + if( _constrainValues ) temp += pointValues[2][2][2]; + if( symmetric ) temp /= 2; + row[count++] = MatrixEntry< Real >( nodeIndex-offset , temp ); + for( int x=0 ; x<5 ; x++ ) for( int y=0 ; y<5 ; y++ ) for( int z=0 ; z<5 ; z++ ) + if( (x!=2 || y!=2 || z!=2) && neighbors5.neighbors[x][y][z] && neighbors5.neighbors[x][y][z]->nodeData.nodeIndex>=0 && ( !symmetric || neighbors5.neighbors[x][y][z]->nodeData.nodeIndex>=nodeIndex ) ) + { + const TreeOctNode* _node = neighbors5.neighbors[x][y][z]; + int _d , _off[3]; + _node->depthAndOffset( _d , _off ); + Real temp = Real( GetLaplacian( integrator , d , off , _off , false ) ); + if( _constrainValues ) temp += pointValues[x][y][z]; + if( symmetric && x==2 && y==2 && z==2 ) temp /= 2; + row[count++] = MatrixEntry< Real >( _node->nodeData.nodeIndex-offset , temp ); + } + } + return count; +} +// if( scatter ) normals come from the center ndoe +// else normals come from the neighbors +template< class Real > +void Octree< Real >::SetDivergenceStencil( int depth , const typename BSplineData< 2 >::Integrator& integrator , Stencil< Point3D< double > , 5 >& stencil , bool scatter ) const +{ + if( depth<2 ) return; + int center = 1<<(depth-1); + int offset[] = { center , center , center }; + for( int x=0 ; x<5 ; x++ ) for( int y=0 ; y<5 ; y++ ) for( int z=0 ; z<5 ; z++ ) + { + int _offset[] = { x+center-2 , y+center-2 , z+center-2 }; + if( scatter ) stencil.values[x][y][z] = GetDivergence1( integrator , depth , offset , _offset , false ); + else stencil.values[x][y][z] = GetDivergence2( integrator , depth , offset , _offset , false ); + } +} +template< class Real > +void Octree< Real >::SetDivergenceStencils( int depth , const typename BSplineData< 2 >::Integrator& integrator , Stencil< Point3D< double > , 5 > stencils[2][2][2] , bool scatter ) const +{ + if( depth<2 ) return; + int center = 1<<(depth-1); + for( int i=0 ; i<2 ; i++ ) for( int j=0 ; j<2 ; j++ ) for( int k=0 ; k<2 ; k++ ) + { + int offset[] = { center+i , center+j , center+k }; + for( int x=0 ; x<5 ; x++ ) for( int y=0 ; y<5 ; y++ ) for( int z=0 ; z<5 ; z++ ) + { + int _offset[] = { x-2+center/2 , y-2+center/2 , z-2+center/2 }; + if( scatter ) stencils[i][j][k].values[x][y][z] = GetDivergence1( integrator , depth , offset , _offset , true ); + else stencils[i][j][k].values[x][y][z] = GetDivergence2( integrator , depth , offset , _offset , true ); + } + } +} +template< class Real > +void Octree< Real >::SetLaplacianStencil( int depth , const typename BSplineData< 2 >::Integrator& integrator , Stencil< double , 5 >& stencil ) const +{ + if( depth<2 ) return; + int center = 1<<(depth-1); + int offset[] = { center , center , center }; + for( int x=-2 ; x<=2 ; x++ ) for( int y=-2 ; y<=2 ; y++ ) for( int z=-2 ; z<=2 ; z++ ) + { + int _offset[] = { x+center , y+center , z+center }; + stencil.values[x+2][y+2][z+2] = GetLaplacian( integrator , depth , offset , _offset , false ); + } +} +template< class Real > +void Octree< Real >::SetLaplacianStencils( int depth , const typename BSplineData< 2 >::Integrator& integrator , Stencil< double , 5 > stencils[2][2][2] ) const +{ + if( depth<2 ) return; + int center = 1<<(depth-1); + for( int i=0 ; i<2 ; i++ ) for( int j=0 ; j<2 ; j++ ) for( int k=0 ; k<2 ; k++ ) + { + int offset[] = { center+i , center+j , center+k }; + for( int x=-2 ; x<=2 ; x++ ) for( int y=-2 ; y<=2 ; y++ ) for( int z=-2 ; z<=2 ; z++ ) + { + int _offset[] = { x+center/2 , y+center/2 , z+center/2 }; + stencils[i][j][k].values[x+2][y+2][z+2] = GetLaplacian( integrator , depth , offset , _offset , true ); + } + } +} +template< class Real > +void Octree< Real >::SetCenterEvaluationStencil( const typename BSplineData< 2 >::template CenterEvaluator< 1 >& evaluator , int depth , Stencil< double , 3 >& stencil ) const +{ + if( depth<2 ) return; + int center = 1<<(depth-1); + for( int x=0 ; x<3 ; x++ ) for( int y=0 ; y<3 ; y++ ) for( int z=0 ; z<3 ; z++ ) + { + int off[] = { center+x-1 , center+y-1 , center+z-1 }; + stencil.values[x][y][z] = Real( evaluator.value( depth , center , off[0] , false , false ) * evaluator.value( depth , center , off[1] , false , false ) * evaluator.value( depth , center , off[2] , false , false ) ); + } +} +template< class Real > +void Octree< Real >::SetCenterEvaluationStencils( const typename BSplineData< 2 >::template CenterEvaluator< 1 >& evaluator , int depth , Stencil< double , 3 > stencils[8] ) const +{ + if( depth<3 ) return; + int center = 1<<(depth-1); + for( int cx=0 ; cx<2 ; cx++ ) for( int cy=0 ; cy<2 ; cy++ ) for( int cz=0 ; cz<2 ; cz++ ) + { + int idx[] = { center+cx , center+cy , center+cz }; + for( int x=0 ; x<3 ; x++ ) for( int y=0 ; y<3 ; y++ ) for( int z=0 ; z<3 ; z++ ) + { + int off[] = { center/2+x-1 , center/2+y-1 , center/2+z-1 }; + stencils[Cube::CornerIndex( cx , cy , cz ) ].values[x][y][z] = Real( evaluator.value( depth , idx[0] , off[0] , false , true ) * evaluator.value( depth , idx[1] , off[1] , false , true ) * evaluator.value( depth , idx[2] , off[2] , false , true ) ); + } + } +} +template< class Real > +void Octree< Real >::SetCornerEvaluationStencil( const typename BSplineData< 2 >::template CornerEvaluator< 2 >& evaluator , int depth , Stencil< double , 3 > stencil[8] ) const +{ + if( depth<2 ) return; + int center = 1<<(depth-1); + for( int cx=0 ; cx<2 ; cx++ ) for( int cy=0 ; cy<2 ; cy++ ) for( int cz=0 ; cz<2 ; cz++ ) + { + int c = Cube::CornerIndex( cx , cy , cz ); + for( int x=0 ; x<3 ; x++ ) for( int y=0 ; y<3 ; y++ ) for( int z=0 ; z<3 ; z++ ) + { + int off[] = { center+x-1 , center+y-1 , center+z-1 }; + stencil[c].values[x][y][z] = evaluator.value( depth , center , cx , off[0] , false , false ) * evaluator.value( depth , center , cy , off[1] , false , false ) * evaluator.value( depth , center , cz , off[2] , false , false ); + } + } +} +template< class Real > +void Octree< Real >::SetCornerEvaluationStencils( const typename BSplineData< 2 >::template CornerEvaluator< 2 >& evaluator , int depth , Stencil< double , 3 > stencils[8][8] ) const +{ + if( depth<3 ) return; + int center = 1<<(depth-1); + for( int cx=0 ; cx<2 ; cx++ ) for( int cy=0 ; cy<2 ; cy++ ) for( int cz=0 ; cz<2 ; cz++ ) + { + int c = Cube::CornerIndex( cx , cy , cz ); + for( int _cx=0 ; _cx<2 ; _cx++ ) for( int _cy=0 ; _cy<2 ; _cy++ ) for( int _cz=0 ; _cz<2 ; _cz++ ) + { + int _c = Cube::CornerIndex( _cx , _cy , _cz ); + int idx[] = { center+_cx , center+_cy , center+_cz }; + for( int x=0 ; x<3 ; x++ ) for( int y=0 ; y<3 ; y++ ) for( int z=0 ; z<3 ; z++ ) + { + int off[] = { center/2+x-1 , center/2+y-1 , center/2+z-1 }; + stencils[c][_c].values[x][y][z] = evaluator.value( depth , idx[0] , cx , off[0] , false , true ) * evaluator.value( depth , idx[1] , cy , off[1] , false , true ) * evaluator.value( depth , idx[2] , cz , off[2] , false , true ); + } + } + } +} +template< class Real > +void Octree< Real >::SetCornerNormalEvaluationStencil( const typename BSplineData< 2 >::template CornerEvaluator< 2 >& evaluator , int depth , Stencil< Point3D< double > , 3 > stencil[8] ) const +{ + if( depth<2 ) return; + int center = 1<<(depth-1); + for( int cx=0 ; cx<2 ; cx++ ) for( int cy=0 ; cy<2 ; cy++ ) for( int cz=0 ; cz<2 ; cz++ ) + { + int c = Cube::CornerIndex( cx , cy , cz ); + for( int x=0 ; x<3 ; x++ ) for( int y=0 ; y<3 ; y++ ) for( int z=0 ; z<3 ; z++ ) + { + int off[] = { center+x-1 , center+y-1 , center+z-1 }; + double v [] = { evaluator.value( depth , center , cx , off[0] , false , false ) , evaluator.value( depth , center , cy , off[1] , false , false ) , evaluator.value( depth , center , cz , off[2] , false , false ) }; + double dv[] = { evaluator.value( depth , center , cx , off[0] , true , false ) , evaluator.value( depth , center , cy , off[1] , true , false ) , evaluator.value( depth , center , cz , off[2] , true , false ) }; + stencil[c].values[x][y][z] = Point3D< double >( dv[0]*v[1]*v[2] , v[0]*dv[1]*v[2] , v[0]*v[1]*dv[2] ); + } + } +} +template< class Real > +void Octree< Real >::SetCornerNormalEvaluationStencils( const typename BSplineData< 2 >::template CornerEvaluator< 2 >& evaluator , int depth , Stencil< Point3D< double > , 3 > stencils[8][8] ) const +{ + if( depth<3 ) return; + int center = 1<<(depth-1); + for( int cx=0 ; cx<2 ; cx++ ) for( int cy=0 ; cy<2 ; cy++ ) for( int cz=0 ; cz<2 ; cz++ ) + { + int c = Cube::CornerIndex( cx , cy , cz ); // Which corner of the finer cube + for( int _cx=0 ; _cx<2 ; _cx++ ) for( int _cy=0 ; _cy<2 ; _cy++ ) for( int _cz=0 ; _cz<2 ; _cz++ ) + { + int _c = Cube::CornerIndex( _cx , _cy , _cz ); // Which child node + int idx[] = { center+_cx , center+_cy , center+_cz }; + for( int x=0 ; x<3 ; x++ ) for( int y=0 ; y<3 ; y++ ) for( int z=0 ; z<3 ; z++ ) + { + int off[] = { center/2+x-1 , center/2+y-1 , center/2+z-1 }; + double v [] = { evaluator.value( depth , idx[0] , cx , off[0] , false , true ) , evaluator.value( depth , idx[1] , cy , off[1] , false , true ) , evaluator.value( depth , idx[2] , cz , off[2] , false , true ) }; + double dv[] = { evaluator.value( depth , idx[0] , cx , off[0] , true , true ) , evaluator.value( depth , idx[1] , cy , off[1] , true , true ) , evaluator.value( depth , idx[2] , cz , off[2] , true , true ) }; + stencils[c][_c].values[x][y][z] = Point3D< double >( dv[0]*v[1]*v[2] , v[0]*dv[1]*v[2] , v[0]*v[1]*dv[2] ); + } + } + } +} +template< class Real > +void Octree< Real >::SetCornerNormalEvaluationStencil( const typename BSplineData< 2 >::template CornerEvaluator< 2 >& evaluator , int depth , Stencil< Point3D< double > , 5 > stencil[8] ) const +{ + if( depth<2 ) return; + int center = 1<<(depth-1); + for( int cx=0 ; cx<2 ; cx++ ) for( int cy=0 ; cy<2 ; cy++ ) for( int cz=0 ; cz<2 ; cz++ ) + { + int c = Cube::CornerIndex( cx , cy , cz ); + for( int x=0 ; x<5 ; x++ ) for( int y=0 ; y<5 ; y++ ) for( int z=0 ; z<5 ; z++ ) + { + int off[] = { center+x-2 , center+y-2 , center+z-2 }; + double v [] = { evaluator.value( depth , center , cx , off[0] , false , false ) , evaluator.value( depth , center , cy , off[1] , false , false ) , evaluator.value( depth , center , cz , off[2] , false , false ) }; + double dv[] = { evaluator.value( depth , center , cx , off[0] , true , false ) , evaluator.value( depth , center , cy , off[1] , true , false ) , evaluator.value( depth , center , cz , off[2] , true , false ) }; + stencil[c].values[x][y][z] = Point3D< double >( dv[0]*v[1]*v[2] , v[0]*dv[1]*v[2] , v[0]*v[1]*dv[2] ); + } + } +} +template< class Real > +void Octree< Real >::SetCornerNormalEvaluationStencils( const typename BSplineData< 2 >::template CornerEvaluator< 2 >& evaluator , int depth , Stencil< Point3D< double > , 5 > stencils[8][8] ) const +{ + if( depth<3 ) return; + int center = 1<<(depth-1); + for( int cx=0 ; cx<2 ; cx++ ) for( int cy=0 ; cy<2 ; cy++ ) for( int cz=0 ; cz<2 ; cz++ ) + { + int c = Cube::CornerIndex( cx , cy , cz ); // Which corner of the finer cube + for( int _cx=0 ; _cx<2 ; _cx++ ) for( int _cy=0 ; _cy<2 ; _cy++ ) for( int _cz=0 ; _cz<2 ; _cz++ ) + { + int _c = Cube::CornerIndex( _cx , _cy , _cz ); // Which child node + int idx[] = { center+_cx , center+_cy , center+_cz }; + for( int x=0 ; x<5 ; x++ ) for( int y=0 ; y<5 ; y++ ) for( int z=0 ; z<5 ; z++ ) + { + int off[] = { center/2+x-2 , center/2+y-2 , center/2+z-2 }; + double v [] = { evaluator.value( depth , idx[0] , cx , off[0] , false , true ) , evaluator.value( depth , idx[1] , cy , off[1] , false , true ) , evaluator.value( depth , idx[2] , cz , off[2] , false , true ) }; + double dv[] = { evaluator.value( depth , idx[0] , cx , off[0] , true , true ) , evaluator.value( depth , idx[1] , cy , off[1] , true , true ) , evaluator.value( depth , idx[2] , cz , off[2] , true , true ) }; + stencils[c][_c].values[x][y][z] = Point3D< double >( dv[0]*v[1]*v[2] , v[0]*dv[1]*v[2] , v[0]*v[1]*dv[2] ); + } + } + } +} + +template< class Real > +void Octree< Real >::UpdateCoarserSupportBounds( const TreeOctNode* node , int& startX , int& endX , int& startY , int& endY , int& startZ , int& endZ ) +{ + if( node->parent ) + { + int x , y , z , c = int( node - node->parent->children ); + Cube::FactorCornerIndex( c , x , y , z ); + if( x==0 ) endX = 4; + else startX = 1; + if( y==0 ) endY = 4; + else startY = 1; + if( z==0 ) endZ = 4; + else startZ = 1; + } +} +// Given the solution @( depth ) add to the met constraints @( depth-1 ) +template< class Real > +void Octree< Real >::UpdateConstraintsFromFiner( const typename BSplineData< 2 >::Integrator& integrator , int depth , const SortedTreeNodes& sNodes , ConstPointer( Real ) fineSolution , Pointer( Real ) coarseConstraints ) const +{ + if( depth<=_minDepth ) return; + Stencil< double , 5 > stencils[2][2][2]; + // Get the stencil describing the Laplacian relating coefficients @(depth) with coefficients @(depth-1) + SetLaplacianStencils( depth , integrator , stencils ); + size_t start = sNodes.nodeCount[depth] , end = sNodes.nodeCount[depth+1] , range = end-start; + int lStart = sNodes.nodeCount[depth-1]; + memset( coarseConstraints , 0 , sizeof(Real)*(sNodes.nodeCount[depth]-sNodes.nodeCount[depth-1]) ); + + // Iterate over the nodes @( depth ) + std::vector< typename TreeOctNode::NeighborKey3 > neighborKeys( std::max< int >( 1 , threads ) ); + for( int i=0 ; iparent->children ); + Cube::FactorCornerIndex( c , x , y , z ); + if( insetSupported ) + { + typename TreeOctNode::Neighbors5 pNeighbors5; + neighborKey.getNeighbors( node->parent , pNeighbors5 ); + const Stencil< double , 5 >& lapStencil = stencils[x][y][z]; + + Pointer( Real ) __coarseConstraints = coarseConstraints-lStart; + bool isInterior; + int d , off[3]; + { + node->depthAndOffset( d , off ); + int o = _boundaryType==0 ? (1<<(d-2) ) : 0; + int mn = 4+o , mx = (1<=mn && off[0]=mn && off[1]=mn && off[2]nodeData.nodeIndex-start ]; + for( int x=startX ; xnodeData.nodeIndex>=0 ) + { + const TreeOctNode* _node = pNeighbors5.neighbors[x][y][z]; + if( isInterior ) +#pragma omp atomic + __coarseConstraints[ _node->nodeData.nodeIndex ] += Real( lapStencil.values[x][y][z] * solution ); + else + { + int _d , _off[3]; + _node->depthAndOffset( _d , _off ); +#pragma omp atomic + __coarseConstraints[ _node->nodeData.nodeIndex ] += Real( GetLaplacian( integrator , d , off , _off , true ) * solution ); + } + } + } + } +} + +template< class Real > +void Octree< Real >::UpdateConstraintsFromCoarser( const PointInfo& pointInfo , const typename TreeOctNode::Neighbors5& neighbors5 , const typename TreeOctNode::Neighbors5& pNeighbors5 , TreeOctNode* node , Pointer( Real ) constraints , ConstPointer( Real ) metSolution , const typename BSplineData< 2 >::Integrator& integrator , const Stencil< double , 5 >& lapStencil ) const +{ + const std::vector< _PointData >& points = pointInfo.points; + if( node->depth()<=_minDepth ) return; + bool isInterior; + int d , off[3]; + { + node->depthAndOffset( d , off ); + int o = _boundaryType==0 ? (1<<(d-2) ) : 0; + int mn = 4+o , mx = (1<=mn && off[0]=mn && off[1]=mn && off[2]nodeData.nodeIndex>=0 ) + { + const TreeOctNode* _node = pNeighbors5.neighbors[x][y][z]; + Real _solution = metSolution[ _node->nodeData.nodeIndex ]; + { + if( isInterior ) constraints[ node->nodeData.nodeIndex ] -= Real( lapStencil.values[x][y][z] * _solution ); + else + { + int _d , _off[3]; + _node->depthAndOffset( _d , _off ); + constraints[ node->nodeData.nodeIndex ] -= Real( GetLaplacian( integrator , d , off , _off , true ) * _solution ); + } + } + } + if( _constrainValues ) + { + double constraint = 0; + int idx[3] ; + node->centerIndex( idx ); + // Evaluate the current node's basis function at adjacent points + for( int x=1 ; x<4 ; x++ ) for( int y=1 ; y<4 ; y++ ) for( int z=1 ; z<4 ; z++ ) + if( neighbors5.neighbors[x][y][z] && pointInfo.pointIndex( neighbors5.neighbors[x][y][z] )!=-1 ) + { + const _PointData& pData = points[ pointInfo.pointIndex( neighbors5.neighbors[x][y][z] ) ]; + Real weightedPointValue = pData.weightedCoarserValue; + Point3D< Real > p = pData.position; + constraint += + _fData.baseBSplines[idx[0]][x-1]( p[0] ) * + _fData.baseBSplines[idx[1]][y-1]( p[1] ) * + _fData.baseBSplines[idx[2]][z-1]( p[2] ) * + weightedPointValue; + } + constraints[ node->nodeData.nodeIndex ] -= Real( constraint ); + } +} +struct UpSampleData +{ + int start; + double v[2]; + UpSampleData( void ) { start = 0 , v[0] = v[1] = 0.; } + UpSampleData( int s , double v1 , double v2 ) { start = s , v[0] = v1 , v[1] = v2; } +}; +template< class Real > +template< class C > +void Octree< Real >::DownSample( int depth , const SortedTreeNodes& sNodes , ConstPointer( C ) fineConstraints , Pointer( C ) coarseConstraints ) const +{ + if( depth==0 ) return; + double cornerValue; + if ( _boundaryType==-1 ) cornerValue = 0.50; + else if( _boundaryType== 1 ) cornerValue = 1.00; + else cornerValue = 0.75; + std::vector< typename TreeOctNode::NeighborKey3 > neighborKeys( std::max< int >( 1 , threads ) ); + for( int i=0 ; idepthAndOffset( d , off ); + for( int dd=0 ; dd<3 ; dd++ ) + { + if ( off[dd] ==0 ) usData[dd] = UpSampleData( 1 , cornerValue , 0.00 ); + else if( off[dd]+1==(1<parent ); + C c = fineConstraints[ i-sNodes.nodeCount[depth] ]; + for( int ii=0 ; ii<2 ; ii++ ) + { + int _ii = ii + usData[0].start; + C cx = C( c*usData[0].v[ii] ); + for( int jj=0 ; jj<2 ; jj++ ) + { + int _jj = jj + usData[1].start; + C cxy = C( cx*usData[1].v[jj] ); + for( int kk=0 ; kk<2 ; kk++ ) + { + int _kk = kk + usData[2].start; + TreeOctNode* pNode = neighbors.neighbors[_ii][_jj][_kk]; + if( pNode ) +#pragma omp atomic + coarseConstraints[ pNode->nodeData.nodeIndex-sNodes.nodeCount[depth-1] ] += C( cxy*usData[2].v[kk] ); + } + } + } + } +} +template< class Real > +template< class C > +void Octree< Real >::UpSample( int depth , const SortedTreeNodes& sNodes , ConstPointer( C ) coarseCoefficients , Pointer( C ) fineCoefficients ) const +{ + double cornerValue; + if ( _boundaryType==-1 ) cornerValue = 0.50; + else if( _boundaryType== 1 ) cornerValue = 1.00; + else cornerValue = 0.75; + if( depth<=_minDepth ) return; + + std::vector< typename TreeOctNode::NeighborKey3 > neighborKeys( std::max< int >( 1 , threads ) ); + for( int i=0 ; idepthAndOffset( d , off ); + for( int d=0 ; d<3 ; d++ ) + { + if ( off[d] ==0 ) usData[d] = UpSampleData( 1 , cornerValue , 0.00 ) , isInterior = false; + else if( off[d]+1==(1<parent ); + for( int ii=0 ; ii<2 ; ii++ ) + { + int _ii = ii + usData[0].start; + double dx = usData[0].v[ii]; + for( int jj=0 ; jj<2 ; jj++ ) + { + int _jj = jj + usData[1].start; + double dxy = dx * usData[1].v[jj]; + for( int kk=0 ; kk<2 ; kk++ ) + { + int _kk = kk + usData[2].start; + TreeOctNode* node = neighbors.neighbors[_ii][_jj][_kk]; + if( node ) + { + double dxyz = dxy * usData[2].v[kk]; + int _i = node->nodeData.nodeIndex; + fineCoefficients[ i-sNodes.nodeCount[depth] ] += coarseCoefficients[ _i-sNodes.nodeCount[depth-1] ] * Real( dxyz ); + } + } + } + } + } +} +// At each point @( depth ), evaluate the met solution @( depth-1 ) +template< class Real > +void Octree< Real >::SetPointValuesFromCoarser( PointInfo& pointInfo , int depth , const SortedTreeNodes& sNodes , ConstPointer( Real ) coarseCoefficients ) +{ + std::vector< _PointData >& points = pointInfo.points; + // For every node at the current depth + std::vector< typename TreeOctNode::NeighborKey3 > neighborKeys( std::max< int >( 1 , threads ) ); + for( int i=0 ; i +Real Octree< Real >::_WeightedCoarserFunctionValue( const _PointData& pointData , const typename TreeOctNode::NeighborKey3& neighborKey , const TreeOctNode* pointNode , ConstPointer( Real ) coarseCoefficients ) const +{ + double pointValue = 0; + int depth = pointNode->depth(); + if( _boundaryType==-1 && depth==0 ) return Real(-0.5) * pointData.weight; + + if( depth<=_minDepth ) return Real(0.); + + Real weight = pointData.weight; + Point3D< Real > p = pointData.position; + + // Iterate over all basis functions that overlap the point at the coarser resolutions + { + int d , _idx[3]; + const typename TreeOctNode::Neighbors3& neighbors = neighborKey.neighbors[depth-1]; + neighbors.neighbors[1][1][1]->depthAndOffset( d , _idx ); + _idx[0] = BinaryNode::CenterIndex( d , _idx[0]-1 ); + _idx[1] = BinaryNode::CenterIndex( d , _idx[1]-1 ); + _idx[2] = BinaryNode::CenterIndex( d , _idx[2]-1 ); + + for( int j=0 ; j<3 ; j++ ) + { +#if ROBERTO_TOLDO_FIX + double xValue = 0; + if( _idx[0]+j>=0 && _idx[0]+j<((1<=0 && _idx[1]+k<((1<nodeData.nodeIndex>=0 && _idx[2]+l>=0 && _idx[2]+l<((1<nodeData.nodeIndex] ); +#else // !ROBERTO_TOLDO_FIX + if( basisNode && basisNode->nodeData.nodeIndex>=0 ) + _pointValue += _fData.baseBSplines[ _idx[2]+l ][2-l]( p[2] ) * double( coarseCoefficients[basisNode->nodeData.nodeIndex] ); +#endif // ROBERTO_TOLDO_FIX + } + pointValue += _pointValue * xyValue; + } + } + } + if( _boundaryType==-1 ) pointValue -= 0.5; + return Real( pointValue * weight ); +} +template< class Real > +void Octree< Real >::SetPointConstraintsFromFiner( const PointInfo& pointInfo , int depth , const SortedTreeNodes& sNodes , ConstPointer( Real ) finerCoefficients , Pointer( Real ) coarserConstraints ) const +{ + const std::vector< _PointData >& points = pointInfo.points; + // Note: We can't iterate over the finer point nodes as the point weights might be + // scaled incorrectly, due to the adaptive exponent. So instead, we will iterate + // over the coarser nodes and evaluate the finer solution at the associated points. + if( !depth ) return; + size_t start = sNodes.nodeCount[depth-1] , end = sNodes.nodeCount[depth] , range = end-start; + memset( coarserConstraints , 0 , sizeof( Real ) * ( sNodes.nodeCount[depth]-sNodes.nodeCount[depth-1] ) ); + std::vector< typename TreeOctNode::NeighborKey3 > neighborKeys( std::max< int >( 1 , threads ) ); + for( int i=0 ; i p = points[ pIdx ].position; + // Update constraints for all nodes @( depth-1 ) that overlap the point + int d , idx[3]; + neighbors.neighbors[1][1][1]->depthAndOffset( d, idx ); + // Set the (offset) index to the top-left-front corner of the 3x3x3 block of b-splines + // overlapping the point. + idx[0] = BinaryNode::CenterIndex( d , idx[0]-1 ); + idx[1] = BinaryNode::CenterIndex( d , idx[1]-1 ); + idx[2] = BinaryNode::CenterIndex( d , idx[2]-1 ); + for( int x=0 ; x<3 ; x++ ) for( int y=0 ; y<3 ; y++ ) for( int z=0 ; z<3 ; z++ ) + if( neighbors.neighbors[x][y][z] ) + { +#pragma omp atomic + coarserConstraints[ neighbors.neighbors[x][y][z]->nodeData.nodeIndex - sNodes.nodeCount[depth-1] ] += + Real( + _fData.baseBSplines[idx[0]+x][2-x]( p[0] ) * + _fData.baseBSplines[idx[1]+y][2-y]( p[1] ) * + _fData.baseBSplines[idx[2]+z][2-z]( p[2] ) * + finerPointValue + ); + } + } + } + } +} +template< class Real > +Real Octree< Real >::_WeightedFinerFunctionValue( const _PointData& pointData , const typename TreeOctNode::NeighborKey3& neighborKey , const TreeOctNode* pointNode , ConstPointer( Real ) finerCoefficients ) const +{ + typename TreeOctNode::Neighbors3 childNeighbors; + double pointValue = 0; + int depth = pointNode->depth(); + Real weight = pointData.weight; + Point3D< Real > p = pointData.position; + neighborKey.getChildNeighbors( p , depth , childNeighbors ); + // Iterate over all finer basis functions that overlap the point at the coarser resolutions + int d , idx[3]; + { + Point3D< Real > c; + Real w; + neighborKey.neighbors[depth].neighbors[1][1][1]->depthAndOffset( d , idx ); + neighborKey.neighbors[depth].neighbors[1][1][1]->centerAndWidth( c , w ); + d++; + idx[0] *= 2 , idx[1] *= 2 , idx[2] *= 2; + int cIndex=TreeOctNode::CornerIndex( c , p ); + if( cIndex&1 ) idx[0]++; + if( cIndex&2 ) idx[1]++; + if( cIndex&4 ) idx[2]++; + } + // Center the indexing at the top-left-front corner + idx[0] = BinaryNode::CenterIndex( d , idx[0]-1 ); + idx[1] = BinaryNode::CenterIndex( d , idx[1]-1 ); + idx[2] = BinaryNode::CenterIndex( d , idx[2]-1 ); + + for( int j=0 ; j<3 ; j++ ) for( int k=0 ; k<3 ; k++ ) for( int l=0 ; l<3 ; l++ ) + { + const TreeOctNode* basisNode = childNeighbors.neighbors[j][k][l]; + if( basisNode && basisNode->nodeData.nodeIndex>=0 ) + pointValue += + _fData.baseBSplines[ idx[0]+j ][2-j]( p[0] ) * + _fData.baseBSplines[ idx[1]+k ][2-k]( p[1] ) * + _fData.baseBSplines[ idx[2]+l ][2-l]( p[2] ) * + double( finerCoefficients[ basisNode->nodeData.nodeIndex ] ); + } + if( _boundaryType==-1 ) pointValue -= Real(0.5); + return Real( pointValue * weight ); +} +template< class Real > +int Octree< Real >::GetSliceMatrixAndUpdateConstraints( const PointInfo& pointInfo , SparseMatrix< Real >& matrix , Pointer( Real ) constraints , const typename BSplineData< 2 >::Integrator& integrator , int depth , const SortedTreeNodes& sNodes , ConstPointer( Real ) metSolution , bool coarseToFine , int nStart , int nEnd ) +{ + size_t range = nEnd-nStart; + Stencil< double , 5 > stencil , stencils[2][2][2]; + SetLaplacianStencil ( depth , integrator , stencil ); + SetLaplacianStencils( depth , integrator , stencils ); + matrix.Resize( (int)range ); + std::vector< typename TreeOctNode::NeighborKey3 > neighborKeys( std::max< int >( 1 , threads ) ); + for( int i=0 ; i( i , Real(1) ); + matrix.rowSizes[i] = 1; + } + + if( depth>_minDepth ) + { + // Offset the constraints using the solution from lower resolutions. + int x , y , z , c; + if( node->parent ) + { + c = int( node - node->parent->children ); + Cube::FactorCornerIndex( c , x , y , z ); + } + else x = y = z = 0; + if( insetSupported && coarseToFine ) + { + typename TreeOctNode::Neighbors5 pNeighbors5; + neighborKey.getNeighbors( node->parent , pNeighbors5 ); + UpdateConstraintsFromCoarser( pointInfo , neighbors5 , pNeighbors5 , node , constraints , metSolution , integrator , stencils[x][y][z] ); + } + } + } + return 1; +} +template< class Real > +int Octree< Real >::GetMatrixAndUpdateConstraints( const PointInfo& pointInfo , SparseSymmetricMatrix< Real >& matrix , Pointer( Real ) constraints , const typename BSplineData< 2 >::Integrator& integrator , int depth , const SortedTreeNodes& sNodes , ConstPointer( Real ) metSolution , bool coarseToFine ) +{ + size_t start = sNodes.nodeCount[depth] , end = sNodes.nodeCount[depth+1] , range = end-start; + Stencil< double , 5 > stencil , stencils[2][2][2]; + SetLaplacianStencil ( depth , integrator , stencil ); + SetLaplacianStencils( depth , integrator , stencils ); + matrix.Resize( (int)range ); + std::vector< typename TreeOctNode::NeighborKey3 > neighborKeys( std::max< int >( 1 , threads ) ); + for( int i=0 ; i( i , Real(1) ); + matrix.rowSizes[i] = 1; + } + if( depth>_minDepth ) + { + // Offset the constraints using the solution from lower resolutions. + int x , y , z , c; + if( node->parent ) + { + c = int( node - node->parent->children ); + Cube::FactorCornerIndex( c , x , y , z ); + } + else x = y = z = 0; + if( insetSupported && coarseToFine ) + { + typename TreeOctNode::Neighbors5 pNeighbors5; + neighborKey.getNeighbors( node->parent , pNeighbors5 ); + UpdateConstraintsFromCoarser( pointInfo , neighbors5 , pNeighbors5 , node , constraints , metSolution , integrator , stencils[x][y][z] ); + } + } + } + return 1; +} + +template< class Real > +Pointer( Real ) Octree< Real >::SolveSystem( PointInfo& pointInfo , Pointer( Real ) constraints , bool showResidual , int iters , int maxSolveDepth , int cgDepth , double accuracy ) +{ + int iter=0; + typename BSplineData< 2 >::Integrator integrator; + _fData.setIntegrator( integrator , _boundaryType==0 ); + iters = std::max< int >( 0 , iters ); + if( _boundaryType==0 ) maxSolveDepth++ , cgDepth++; + + Pointer( Real ) solution = AllocPointer< Real >( _sNodes.nodeCount[_sNodes.maxDepth] ); + memset( solution , 0 , sizeof(Real)*_sNodes.nodeCount[_sNodes.maxDepth] ); + + solution[0] = 0; + + std::vector< Real > metSolution( _sNodes.nodeCount[ _sNodes.maxDepth-1 ] , 0 ); + for( int d=_minDepth ; d<_sNodes.maxDepth ; d++ ) + { + DumpOutput( "Depth[%d/%d]: %d\n" , _boundaryType==0 ? d-1 : d , _boundaryType==0 ? _sNodes.maxDepth-2 : _sNodes.maxDepth-1 , _sNodes.nodeCount[d+1]-_sNodes.nodeCount[d] ); + if( d==_minDepth ) + _SolveSystemCG( pointInfo , d , integrator , _sNodes , solution , constraints , GetPointer( metSolution ) , _sNodes.nodeCount[_minDepth+1]-_sNodes.nodeCount[_minDepth] , true , showResidual, NULL , NULL , NULL ); + else + { + if( d>cgDepth ) iter += _SolveSystemGS( pointInfo , d , integrator , _sNodes , solution , constraints , GetPointer( metSolution ) , d>maxSolveDepth ? 0 : iters , true , showResidual , NULL , NULL , NULL ); + else iter += _SolveSystemCG( pointInfo , d , integrator , _sNodes , solution , constraints , GetPointer( metSolution ) , d>maxSolveDepth ? 0 : iters , true , showResidual , NULL , NULL , NULL , accuracy ); + } + } + + return solution; +} +template< class Real > +void Octree< Real >::_setMultiColorIndices( int start , int end , std::vector< std::vector< int > >& indices ) const +{ + const int modulus = 3; + indices.resize( modulus*modulus*modulus ); + int count[modulus*modulus*modulus]; + memset( count , 0 , sizeof(int)*modulus*modulus*modulus ); +#pragma omp parallel for num_threads( threads ) + for( int i=start ; idepthAndOffset( d , off ); + int idx = (modulus*modulus) * ( off[2]%modulus ) + modulus * ( off[1]%modulus ) + ( off[0]%modulus ); +#pragma omp atomic + count[idx]++; + } + + for( int i=0 ; idepthAndOffset( d , off ); + int idx = (modulus*modulus) * ( off[2]%modulus ) + modulus * ( off[1]%modulus ) + ( off[0]%modulus ); + indices[idx].push_back( _sNodes.treeNodes[i]->nodeData.nodeIndex - start ); + } +} +template< class Real > +int Octree< Real >::_SolveSystemGS( PointInfo& pointInfo , int depth , const typename BSplineData< 2 >::Integrator& integrator , const SortedTreeNodes& sNodes , Pointer( Real ) solution , Pointer( Real ) constraints , Pointer( Real ) metSolutionConstraints , int iters , bool coarseToFine , bool showResidual , double* bNorm2 , double* inRNorm2 , double* outRNorm2 , bool forceSilent ) +{ + Pointer( Real ) metSolution = NullPointer< Real >(); + Pointer( Real ) metConstraints = NullPointer< Real >(); + if( coarseToFine ) metSolution = metSolutionConstraints; // This stores the up-sampled solution up to depth-2 + else metConstraints = metSolutionConstraints; // This stores the down-sampled constraints up to depth + + double _maxMemoryUsage = maxMemoryUsage; + maxMemoryUsage = 0; + Vector< Real > X , B; + int slices = 1< offsets( slices+1 , 0 ); + for( int i=sNodes.nodeCount[depth] ; idepthAndOffset( d , off ); + offsets[ off[2] ]++; + } + for( int i=1 ; i=1 ; i-- ) offsets[i] = offsets[i-1]; + offsets[0] = 0; + + X.Resize( sNodes.nodeCount[depth+1]-sNodes.nodeCount[depth] ); + B.Resize( sNodes.nodeCount[depth+1]-sNodes.nodeCount[depth] ); + if( coarseToFine ) + { + if( depth>_minDepth ) + { + // Up-sample the cumulative change in solution @(depth-2) into the cumulative change in solution @(depth-1) + if( depth-2>=_minDepth ) UpSample( depth-1 , sNodes , ( ConstPointer( Real ) )metSolution+_sNodes.nodeCount[depth-2] , metSolution+_sNodes.nodeCount[depth-1] ); + // Add in the change in solution @(depth-1) +#pragma omp parallel for num_threads( threads ) + for( int i=_sNodes.nodeCount[depth-1] ; i<_sNodes.nodeCount[depth] ; i++ ) metSolution[i] += solution[i]; + // Evaluate the points @(depth) using the cumulative change in solution @(depth-1) + if( _constrainValues ) + { + evaluateTime = Time(); + SetPointValuesFromCoarser( pointInfo , depth , sNodes , metSolution+_sNodes.nodeCount[depth-1] ); + evaluateTime = Time() - evaluateTime; + } + } + } + else if( depth<_sNodes.maxDepth-1 ) + for( int i=_sNodes.nodeCount[depth] ; i<_sNodes.nodeCount[depth+1] ; i++ ) constraints[i] -= metConstraints[i]; + // Initialize with the previously computed solution +#pragma omp parallel for num_threads( threads ) + for( int i=_sNodes.nodeCount[depth] ; i<_sNodes.nodeCount[depth+1] ; i++ ) X[ i-_sNodes.nodeCount[depth] ] = solution[i]; + double bNorm=0 , inRNorm=0 , outRNorm=0; + if( depth>=_minDepth ) + { + int frontOffset = ( showResidual || inRNorm2 ) ? 2 : 0; + int backOffset = ( showResidual || outRNorm2 ) ? 2 : 0; + int solveSlices = std::min< int >( 2*iters-1 , slices ) , matrixSlices = std::max< int >( 1 , std::min< int >( solveSlices+frontOffset+backOffset , slices ) ); + std::vector< SparseMatrix< Real > > _M( matrixSlices ); + std::vector< std::vector< std::vector< int > > > __mcIndices( std::max< int >( 0 , solveSlices ) ); + + int dir = coarseToFine ? -1 : 1 , start = coarseToFine ? slices-1 : 0 , end = coarseToFine ? -1 : slices; + for( int frontSlice=start-frontOffset*dir , backSlice = frontSlice-2*(iters-1)*dir ; backSlice!=end+backOffset*dir ; frontSlice+=dir , backSlice+=dir ) + { + double t; + if( frontSlice+frontOffset*dir>=0 && frontSlice+frontOffset*dirnodeData.nodeIndex ]; + else B[i] = Real(0); + } + if( showResidual || inRNorm2 ) +#pragma omp parallel for num_threads( threads ) reduction( + : bNorm , inRNorm ) + for( int j=0 ; j<_M[_s].rows ; j++ ) + { + Real temp = Real(0); + ConstPointer( MatrixEntry< Real > ) start = _M[_s][j]; + ConstPointer( MatrixEntry< Real > ) end = start + _M[_s].rowSizes[j]; + ConstPointer( MatrixEntry< Real > ) e; + for( e=start ; e!=end ; e++ ) temp += X[ e->N ] * e->Value; + Real b = B[ j + offsets[s] ] ; + bNorm += b*b; + inRNorm += (temp-b) * (temp-b); + } + else if( bNorm2 ) +#pragma omp parallel for num_threads( threads ) reduction( + : bNorm ) + for( int j=0 ; j<_M[_s].rows ; j++ ) + { + Real b = B[ j + offsets[s] ] ; + bNorm += b*b; + } + } + t = Time(); + if( iters && frontSlice>=0 && frontSlice=backSlice*dir ; slice-=2*dir ) + if( slice>=0 && slice::SolveGS( __mcIndices[__s] , _M[_s] , B , X , !coarseToFine , threads , offsets[s] ); + } + solveTime += Time() - t; + if( (showResidual || outRNorm2) && backSlice-backOffset*dir>=0 && backSlice-backOffset*dir ) start = _M[_s][j]; + ConstPointer( MatrixEntry< Real > ) end = start + _M[_s].rowSizes[j]; + ConstPointer( MatrixEntry< Real > ) e; + for( e=start ; e!=end ; e++ ) temp += X[ e->N ] * e->Value; + Real b = B[ j + offsets[s] ]; + outRNorm += (temp-b) * (temp-b); + } + } + } + } + + if( bNorm2 ) bNorm2[depth] = bNorm; + if( inRNorm2 ) inRNorm2[depth] = inRNorm; + if( outRNorm2 ) outRNorm2[depth] = outRNorm; + if( showResidual && iters ) + { + for( int i=0 ; i %.4e -> %.4e (%.2e) [%d]\n" , sqrt( bNorm ) , sqrt( inRNorm ) , sqrt( outRNorm ) , sqrt( outRNorm/bNorm ) , iters ); + } + + // Copy the old solution into the buffer, write in the new solution, compute the change, and update the met constraints +#pragma omp parallel for num_threads( threads ) + for( int i=sNodes.nodeCount[depth] ; i_minDepth ) + { + // Explicitly compute the restriction of the met solution onto the coarser nodes + // and down-sample the previous accumulation + { + UpdateConstraintsFromFiner( integrator , depth , sNodes , GetPointer( X ) , metConstraints+sNodes.nodeCount[depth-1] ); + if( _constrainValues ) SetPointConstraintsFromFiner( pointInfo , depth , sNodes , GetPointer( X ) , metConstraints+sNodes.nodeCount[depth-1] ); + if( depth( maxMemoryUsage , _maxMemoryUsage ); + + return iters; +} +template< class Real > +int Octree< Real >::_SolveSystemCG( PointInfo& pointInfo , int depth , const typename BSplineData< 2 >::Integrator& integrator , const SortedTreeNodes& sNodes , Pointer( Real ) solution , Pointer( Real ) constraints , Pointer( Real ) metSolutionConstraints , int iters , bool coarseToFine , bool showResidual , double* bNorm2 , double* inRNorm2 , double* outRNorm2 , double accuracy ) +{ + Pointer( Real ) metSolution = NullPointer< Real >(); + Pointer( Real ) metConstraints = NullPointer< Real >(); + if( coarseToFine ) metSolution = metSolutionConstraints; // This stores the up-sampled solution up to depth-2 + else metConstraints = metSolutionConstraints; // This stores the down-sampled constraints up to depth + double _maxMemoryUsage = maxMemoryUsage; + maxMemoryUsage = 0; + int iter = 0; + Vector< Real > X , B; + SparseSymmetricMatrix< Real > M; + double systemTime=0. , solveTime=0. , updateTime=0. , evaluateTime = 0.; + X.Resize( sNodes.nodeCount[depth+1]-sNodes.nodeCount[depth] ); + if( coarseToFine ) + { + if( depth>_minDepth ) + { + // Up-sample the cumulative change in solution @(depth-2) into the cumulative change in solution @(depth-1) + if( depth-2>=_minDepth ) UpSample( depth-1 , sNodes , ( ConstPointer( Real ) )metSolution+_sNodes.nodeCount[depth-2] , metSolution+_sNodes.nodeCount[depth-1] ); + // Add in the change in solution @(depth-1) +#pragma omp parallel for num_threads( threads ) + for( int i=_sNodes.nodeCount[depth-1] ; i<_sNodes.nodeCount[depth] ; i++ ) metSolution[i] += solution[i]; + // Evaluate the points @(depth) using the cumulative change in solution @(depth-1) + if( _constrainValues ) + { + evaluateTime = Time(); + SetPointValuesFromCoarser( pointInfo , depth , sNodes , metSolution+_sNodes.nodeCount[depth-1] ); + evaluateTime = Time() - evaluateTime; + } + } + } + else if( depth<_sNodes.maxDepth-1 ) + for( int i=_sNodes.nodeCount[depth] ; i<_sNodes.nodeCount[depth+1] ; i++ ) constraints[i] -= metConstraints[i]; + // Initialize with the previously computed solution +#pragma omp parallel for num_threads( threads ) + for( int i=_sNodes.nodeCount[depth] ; i<_sNodes.nodeCount[depth+1] ; i++ ) X[ i-_sNodes.nodeCount[depth] ] = solution[i]; + systemTime = Time(); + { + // Get the system matrix (and adjust the right-hand-side based on the coarser solution if prolonging) + if( coarseToFine ) GetMatrixAndUpdateConstraints( pointInfo , M , constraints , integrator , depth , sNodes , metSolution , true ); + else GetMatrixAndUpdateConstraints( pointInfo , M , constraints , integrator , depth , sNodes , NullPointer< Real >() , false ); + // Set the constraint vector + B.Resize( sNodes.nodeCount[depth+1]-sNodes.nodeCount[depth] ); + for( int i=sNodes.nodeCount[depth] ; i mrVector; + mrVector.resize( threads , M.rows ); + bool addDCTerm = (M.rows==res*res*res && !_constrainValues && _boundaryType!=-1); + double bNorm , inRNorm , outRNorm; + if( showResidual || bNorm2 ) bNorm = B.Norm( 2 ); + if( showResidual || inRNorm2 ) inRNorm = ( addDCTerm ? ( B - M * X - X.Average() ) : ( B - M * X ) ).Norm( 2 ); + + if( _boundaryType==0 && depth>3 ) res -= 1<<(depth-2); + if( iters ) iter += SparseSymmetricMatrix< Real >::SolveCG( M , B , iters , X , mrVector , Real( accuracy ) , 0 , addDCTerm ); + solveTime = Time()-solveTime; + if( showResidual || outRNorm2 ) outRNorm = ( addDCTerm ? ( B - M * X - X.Average() ) : ( B - M * X ) ).Norm( 2 ); + if( bNorm2 ) bNorm2[depth] = bNorm * bNorm; + if( inRNorm2 ) inRNorm2[depth] = inRNorm * inRNorm; + if( outRNorm2 ) outRNorm2[depth] = outRNorm * outRNorm; + if( showResidual && iters ) + { + for( int i=0 ; i %.4e -> %.4e (%.2e) [%d]\n" , bNorm , inRNorm , outRNorm , outRNorm/bNorm , iter ); + } + + // Copy the old solution into the buffer, write in the new solution, compute the change, and update the met solution + { +#pragma omp parallel for num_threads( threads ) + for( int i=sNodes.nodeCount[depth] ; i_minDepth ) + { + // Explicitly compute the restriction of the met solution onto the coarser nodes + // and down-sample the previous accumulation + { + UpdateConstraintsFromFiner( integrator , depth , sNodes , GetPointer( X ) , metConstraints + sNodes.nodeCount[depth-1] ); + if( _constrainValues ) SetPointConstraintsFromFiner( pointInfo , depth , sNodes , GetPointer( X ) , metConstraints+sNodes.nodeCount[depth-1] ); + if( depth( maxMemoryUsage , _maxMemoryUsage ); + return iter; +} +template< class Real > +int Octree< Real >::HasNormals( TreeOctNode* node , const NormalInfo& normalInfo ) +{ + int idx = normalInfo.normalIndex( node ); + if( idx>=0 ) + { + const Point3D< Real >& normal = normalInfo.normals[ idx ]; + if( normal[0]!=0 || normal[1]!=0 || normal[2]!=0 ) return 1; + } + if( node->children ) for( int i=0 ; ichildren[i] , normalInfo ) ) return 1; + return 0; +} +template< class Real > +Pointer( Real ) Octree< Real >::SetLaplacianConstraints( const NormalInfo& normalInfo ) +{ + // To set the Laplacian constraints, we iterate over the + // splatted normals and compute the dot-product of the + // divergence of the normal field with all the basis functions. + // Within the same depth: set directly as a gather + // Coarser depths + typename BSplineData< 2 >::Integrator integrator; + _fData.setIntegrator( integrator , _boundaryType==0 ); + int maxDepth = _sNodes.maxDepth-1; + Point3D< Real > zeroPoint; + zeroPoint[0] = zeroPoint[1] = zeroPoint[2] = 0; + Pointer( Real ) constraints = AllocPointer< Real >( _sNodes.nodeCount[_sNodes.maxDepth] ); + if( !constraints ) fprintf( stderr , "[ERROR] Failed to allocate constraints: %d * %zu\n" , _sNodes.nodeCount[_sNodes.maxDepth] , sizeof( Real ) ) , exit( 0 ); + memset( constraints , 0 , sizeof(Real)*_sNodes.nodeCount[_sNodes.maxDepth] ); + Pointer( Real ) _constraints = AllocPointer< Real >( _sNodes.nodeCount[maxDepth] ); + if( !_constraints ) fprintf( stderr , "[ERROR] Failed to allocate _constraints: %d * %zu\n" , _sNodes.nodeCount[maxDepth] , sizeof( Real ) ) , exit( 0 ); + memset( _constraints , 0 , sizeof(Real)*_sNodes.nodeCount[maxDepth] ); + MemoryUsage(); + + for( int d=maxDepth ; d>=(_boundaryType==0?2:0) ; d-- ) + { + int offset = d>0 ? _sNodes.treeNodes[ _sNodes.nodeCount[d-1] ]->nodeData.nodeIndex : 0; + Stencil< Point3D< double > , 5 > stencil , stencils[2][2][2]; + SetDivergenceStencil ( d , integrator , stencil , false ); + SetDivergenceStencils( d , integrator , stencils , true ); + + std::vector< typename TreeOctNode::NeighborKey3 > neighborKeys( std::max< int >( 1 , threads ) ); + for( int i=0 ; idepth(); + typename TreeOctNode::Neighbors5 neighbors5; + neighborKey.getNeighbors( node , neighbors5 ); + + bool isInterior , isInterior2; + { + int d , off[3]; + node->depthAndOffset( d , off ); + int o = _boundaryType==0 ? (1<<(d-2)) : 0; + int mn = 2+o , mx = (1<=mn && off[0]=mn && off[1]=mn && off[2]=mn && off[0]=mn && off[1]=mn && off[2]parent->children ); + Cube::FactorCornerIndex( c , cx , cy , cz ); + } + else cx = cy = cz = 0; + Stencil< Point3D< double > , 5 >& _stencil = stencils[cx][cy][cz]; + int d , off[3]; + node->depthAndOffset( d , off ); + // Set constraints from current depth + // Gather the constraints from the vector-field at _node into the constraint stored with node + { + + if( isInterior ) + for( int x=startX ; x=0 ) constraints[ node->nodeData.nodeIndex ] += Point3D< Real >::Dot( stencil.values[x][y][z] , normalInfo.normals[ _idx ] ); + } + } + else + for( int x=startX ; x=0 ) + { + int _d , _off[3]; + _node->depthAndOffset( _d , _off ); + constraints[ node->nodeData.nodeIndex ] += Real( GetDivergence2( integrator , d , off , _off , false , normalInfo.normals[ _idx ] ) ); + } + } + } + UpdateCoarserSupportBounds( neighbors5.neighbors[2][2][2] , startX , endX , startY , endY , startZ , endZ ); + } + int idx = normalInfo.normalIndex( node ); + if( idx<0 ) continue; + const Point3D< Real >& normal = normalInfo.normals[ idx ]; + if( normal[0]==0 && normal[1]==0 && normal[2]==0 ) continue; + + // Set the constraints for the parents + if( depth>_minDepth ) + { + neighborKey.getNeighbors( node->parent , neighbors5 ); + + for( int x=startX ; x& div = _stencil.values[x][y][z]; + c = Real( div[0] * normal[0] + div[1] * normal[1] + div[2] * normal[2] ); + } + else + { + int _d , _off[3]; + _node->depthAndOffset( _d , _off ); + c = Real( GetDivergence1( integrator , d , off , _off , true , normal ) ); + } +#pragma omp atomic + _constraints[ _node->nodeData.nodeIndex ] += c; + } + } + } + MemoryUsage(); + } + + // Fine-to-coarse down-sampling of constraints + for( int d=maxDepth-1 ; d>=(_boundaryType==0?2:0) ; d-- ) DownSample( d , _sNodes , ( ConstPointer( Real ) )_constraints + _sNodes.nodeCount[d] , _constraints+_sNodes.nodeCount[d-1] ); + + // Add the accumulated constraints from all finer depths +#pragma omp parallel for num_threads( threads ) + for( int i=0 ; i<_sNodes.nodeCount[maxDepth] ; i++ ) constraints[i] += _constraints[i]; + + FreePointer( _constraints ); + + + std::vector< Point3D< Real > > coefficients( _sNodes.nodeCount[maxDepth] , zeroPoint ); + for( int d=maxDepth-1 ; d>=0 ; d-- ) + { +#pragma omp parallel for num_threads( threads ) + for( int i=_sNodes.nodeCount[d] ; i<_sNodes.nodeCount[d+1] ; i++ ) + { + int idx = normalInfo.normalIndex( _sNodes.treeNodes[i] ); + if( idx<0 ) continue; + coefficients[i] = normalInfo.normals[ idx ]; + } + } + + // Coarse-to-fine up-sampling of coefficients + for( int d=(_boundaryType==0?2:0) ; d ) ) GetPointer( coefficients ) + _sNodes.nodeCount[d-1] , GetPointer( coefficients ) + _sNodes.nodeCount[d] ); + + // Compute the contribution from all coarser depths + for( int d=0 ; d<=maxDepth ; d++ ) + { + size_t start = _sNodes.nodeCount[d] , end = _sNodes.nodeCount[d+1] , range = end - start; + Stencil< Point3D< double > , 5 > stencils[2][2][2]; + SetDivergenceStencils( d , integrator , stencils , false ); + std::vector< typename TreeOctNode::NeighborKey3 > neighborKeys( std::max< int >( 1 , threads ) ); + for( int i=0 ; idepth(); + if( !depth ) continue; + int startX=0 , endX=5 , startY=0 , endY=5 , startZ=0 , endZ=5; + UpdateCoarserSupportBounds( node , startX , endX , startY , endY , startZ , endZ ); + typename TreeOctNode::Neighbors5 neighbors5; + neighborKey.getNeighbors( node->parent , neighbors5 ); + + bool isInterior; + { + int d , off[3]; + node->depthAndOffset( d , off ); + int o = _boundaryType==0 ? (1<<(d-2)) : 0; + int mn = 4+o , mx = (1<=mn && off[0]=mn && off[1]=mn && off[2]parent->children ); + Cube::FactorCornerIndex( c , cx , cy , cz ); + } + else cx = cy = cz = 0; + Stencil< Point3D< double > , 5 >& _stencil = stencils[cx][cy][cz]; + + Real constraint = Real(0); + int d , off[3]; + node->depthAndOffset( d , off ); + for( int x=startX ; xnodeData.nodeIndex; + if( isInterior ) + { + Point3D< double >& div = _stencil.values[x][y][z]; + Point3D< Real >& normal = coefficients[_i]; + constraint += Real( div[0] * normal[0] + div[1] * normal[1] + div[2] * normal[2] ); + } + else + { + int _d , _off[3]; + _node->depthAndOffset( _d , _off ); + constraint += Real( GetDivergence2( integrator , d , off , _off , true , coefficients[_i] ) ); + } + } + constraints[ node->nodeData.nodeIndex ] += constraint; + } + } + MemoryUsage(); + return constraints; +} +template< class Real > +void Octree< Real >::refineBoundary( std::vector< int >* map ){ _sNodes.set( tree , map ); } + + + +template< class Real > +Real Octree< Real >::getCenterValue( const typename TreeOctNode::ConstNeighborKey3& neighborKey , const TreeOctNode* node , ConstPointer( Real ) solution , ConstPointer( Real ) metSolution , const typename BSplineData< 2 >::template CenterEvaluator< 1 >& evaluator , const Stencil< double , 3 >& stencil , const Stencil< double , 3 >& pStencil , bool isInterior ) const +{ + if( node->children ) fprintf( stderr , "[WARNING] getCenterValue assumes leaf node\n" ); + Real value=0; + + int d , off[3]; + node->depthAndOffset( d , off ); + + if( isInterior ) + { + for( int i=0 ; i<3 ; i++ ) for( int j=0 ; j<3 ; j++ ) for( int k=0 ; k<3 ; k++ ) + { + const TreeOctNode* n = neighborKey.neighbors[d].neighbors[i][j][k]; + if( n ) value += solution[ n->nodeData.nodeIndex ] * Real( stencil.values[i][j][k] ); + } + if( d>_minDepth ) + for( int i=0 ; i<3 ; i++ ) for( int j=0 ; j<3 ; j++ ) for( int k=0 ; k<3 ; k++ ) + { + const TreeOctNode* n = neighborKey.neighbors[d-1].neighbors[i][j][k]; + if( n ) value += metSolution[n->nodeData.nodeIndex] * Real( pStencil.values[i][j][k] ); + } + } + else + { + for( int i=0 ; i<3 ; i++ ) for( int j=0 ; j<3 ; j++ ) for( int k=0 ; k<3 ; k++ ) + { + const TreeOctNode* n = neighborKey.neighbors[d].neighbors[i][j][k]; + if( n ) + { + int _d , _off[3]; + n->depthAndOffset( _d , _off ); + value += + solution[ n->nodeData.nodeIndex ] * Real( + evaluator.value( d , off[0] , _off[0] , false , false ) * evaluator.value( d , off[1] , _off[1] , false , false ) * evaluator.value( d , off[1] , _off[1] , false , false ) ); + } + } + if( d>_minDepth ) + for( int i=0 ; i<3 ; i++ ) for( int j=0 ; j<3 ; j++ ) for( int k=0 ; k<3 ; k++ ) + { + const TreeOctNode* n = neighborKey.neighbors[d-1].neighbors[i][j][k]; + if( n ) + { + int _d , _off[3]; + n->depthAndOffset( _d , _off ); + value += + solution[ n->nodeData.nodeIndex ] * Real( + evaluator.value( d , off[0] , _off[0] , false , false ) * evaluator.value( d , off[1] , _off[1] , false , false ) * evaluator.value( d , off[1] , _off[1] , false , false ) ); + } + } + } + return value; +} +template< class Real > +Real Octree< Real >::getCornerValue( const typename TreeOctNode::ConstNeighborKey3& neighborKey , const TreeOctNode* node , int corner , ConstPointer( Real ) solution , ConstPointer( Real ) metSolution , const typename BSplineData< 2 >::template CornerEvaluator< 2 >& evaluator , const Stencil< double , 3 >& stencil , const Stencil< double , 3 > stencils[8] , bool isInterior ) const +{ + double value = 0; + if( _boundaryType==-1 ) value = -0.5; + int d , off[3]; + node->depthAndOffset( d , off ); + + int cx , cy , cz; + int startX = 0 , endX = 3 , startY = 0 , endY = 3 , startZ = 0 , endZ = 3; + Cube::FactorCornerIndex( corner , cx , cy , cz ); + { + typename TreeOctNode::ConstNeighbors3& neighbors = neighborKey.neighbors[d]; + if( cx==0 ) endX = 2; + else startX = 1; + if( cy==0 ) endY = 2; + else startY = 1; + if( cz==0 ) endZ = 2; + else startZ = 1; + if( isInterior ) + for( int x=startX ; xnodeData.nodeIndex ] * stencil.values[x][y][z]; + } + else + for( int x=startX ; xdepthAndOffset( _d , _off ); + value += solution[ _node->nodeData.nodeIndex ] * evaluator.value( d , off[0] , cx , _off[0] , false , false ) * evaluator.value( d , off[1] , cy , _off[1] , false , false ) * evaluator.value( d , off[2] , cz , _off[2] , false , false ); + } + } + } + if( d>_minDepth ) + { + int _corner = int( node - node->parent->children ); + int _cx , _cy , _cz; + Cube::FactorCornerIndex( _corner , _cx , _cy , _cz ); + if( cx!=_cx ) startX = 0 , endX = 3; + if( cy!=_cy ) startY = 0 , endY = 3; + if( cz!=_cz ) startZ = 0 , endZ = 3; + typename TreeOctNode::ConstNeighbors3& neighbors = neighborKey.neighbors[d-1]; + if( isInterior ) + for( int x=startX ; xnodeData.nodeIndex ] * stencils[_corner].values[x][y][z]; + } + else + for( int x=startX ; xdepthAndOffset( _d , _off ); + value += metSolution[ _node->nodeData.nodeIndex ] * evaluator.value( d , off[0] , cx , _off[0] , false , true ) * evaluator.value( d , off[1] , cy , _off[1] , false , true ) * evaluator.value( d , off[2] , cz , _off[2] , false , true ); + } + } + } + return Real( value ); +} +template< class Real > +std::pair< Real , Point3D< Real > > Octree< Real >::getCornerValueAndNormal( const typename TreeOctNode::ConstNeighborKey3& neighborKey , const TreeOctNode* node , int corner , ConstPointer( Real ) solution , ConstPointer( Real ) metSolution , const typename BSplineData< 2 >::template CornerEvaluator< 2 >& evaluator , const Stencil< double , 3 >& vStencil , const Stencil< double , 3 > vStencils[8] , const Stencil< Point3D< double > , 3 >& nStencil , const Stencil< Point3D< double > , 3 > nStencils[8] , bool isInterior ) const +{ + double value = 0; + Point3D< double > normal; + if( _boundaryType==-1 ) value = -0.5; + int d , off[3]; + node->depthAndOffset( d , off ); + + int cx , cy , cz; + int startX = 0 , endX = 3 , startY = 0 , endY = 3 , startZ = 0 , endZ = 3; + Cube::FactorCornerIndex( corner , cx , cy , cz ); + { + typename TreeOctNode::ConstNeighbors3& neighbors = neighborKey.neighbors[d]; + if( cx==0 ) endX = 2; + else startX = 1; + if( cy==0 ) endY = 2; + else startY = 1; + if( cz==0 ) endZ = 2; + else startZ = 1; + if( isInterior ) + for( int x=startX ; xnodeData.nodeIndex ] * vStencil.values[x][y][z] , normal += nStencil.values[x][y][z] * solution[ _node->nodeData.nodeIndex ]; + } + else + for( int x=startX ; xdepthAndOffset( _d , _off ); + double v [] = { evaluator.value( d , off[0] , cx , _off[0] , false , false ) , evaluator.value( d , off[1] , cy , _off[1] , false , false ) , evaluator.value( d , off[2] , cz , _off[2] , false , false ) }; + double dv[] = { evaluator.value( d , off[0] , cx , _off[0] , true , false ) , evaluator.value( d , off[1] , cy , _off[1] , true , false ) , evaluator.value( d , off[2] , cz , _off[2] , true , false ) }; + value += solution[ _node->nodeData.nodeIndex ] * evaluator.value( d , off[0] , cx , _off[0] , false , false ) * evaluator.value( d , off[1] , cy , _off[1] , false , false ) * evaluator.value( d , off[2] , cz , _off[2] , false , false ); + normal += Point3D< double >( dv[0]*v[1]*v[2] , v[0]*dv[1]*v[2] , v[0]*v[1]*dv[2] ) * solution[ _node->nodeData.nodeIndex ]; + } + } + } + if( d>_minDepth ) + { + int _corner = int( node - node->parent->children ); + int _cx , _cy , _cz; + Cube::FactorCornerIndex( _corner , _cx , _cy , _cz ); + if( cx!=_cx ) startX = 0 , endX = 3; + if( cy!=_cy ) startY = 0 , endY = 3; + if( cz!=_cz ) startZ = 0 , endZ = 3; + typename TreeOctNode::ConstNeighbors3& neighbors = neighborKey.neighbors[d-1]; + if( isInterior ) + for( int x=startX ; xnodeData.nodeIndex ] * vStencils[_corner].values[x][y][z] , normal += nStencils[_corner].values[x][y][z] * metSolution[ _node->nodeData.nodeIndex ]; + } + else + for( int x=startX ; xdepthAndOffset( _d , _off ); + double v [] = { evaluator.value( d , off[0] , cx , _off[0] , false , true ) , evaluator.value( d , off[1] , cy , _off[1] , false , true ) , evaluator.value( d , off[2] , cz , _off[2] , false , true ) }; + double dv[] = { evaluator.value( d , off[0] , cx , _off[0] , true , true ) , evaluator.value( d , off[1] , cy , _off[1] , true , true ) , evaluator.value( d , off[2] , cz , _off[2] , true , true ) }; + value += metSolution[ _node->nodeData.nodeIndex ] * evaluator.value( d , off[0] , cx , _off[0] , false , true ) * evaluator.value( d , off[1] , cy , _off[1] , false , true ) * evaluator.value( d , off[2] , cz , _off[2] , false , true ); + normal += Point3D< double >( dv[0]*v[1]*v[2] , v[0]*dv[1]*v[2] , v[0]*v[1]*dv[2] ) * metSolution[ _node->nodeData.nodeIndex ]; + } + } + } + return std::pair< Real , Point3D< Real > >( Real( value ) , Point3D< Real >( normal ) ); +} +template< class Real > +Point3D< Real > Octree< Real >::getCornerNormal( const typename TreeOctNode::ConstNeighbors5& neighbors5 , const typename TreeOctNode::ConstNeighbors5& pNeighbors5 , const TreeOctNode* node , int corner , ConstPointer( Real ) solution , ConstPointer( Real ) metSolution , const typename BSplineData< 2 >::template CornerEvaluator< 2 >& evaluator , const Stencil< Point3D< double > , 5 >& nStencil , const Stencil< Point3D< double > , 5 > nStencils[8] , bool isInterior ) const +{ + Point3D< double > normal; + normal[0] = normal[1] = normal[2] = 0.; + + int d , off[3]; + node->depthAndOffset( d , off ); + + int cx , cy , cz; + int startX = 0 , endX = 5 , startY = 0 , endY = 5 , startZ = 0 , endZ = 5; + Cube::FactorCornerIndex( corner , cx , cy , cz ); + { + if( cx==0 ) endX = 4; + else startX = 1; + if( cy==0 ) endY = 4; + else startY = 1; + if( cz==0 ) endZ = 4; + else startZ = 1; + if( isInterior ) + for( int x=startX ; xnodeData.nodeIndex ]; + } + else + for( int x=startX ; xdepthAndOffset( _d , _off ); + double v [] = { evaluator.value( d , off[0] , cx , _off[0] , false , false ) , evaluator.value( d , off[1] , cy , _off[1] , false , false ) , evaluator.value( d , off[2] , cz , _off[2] , false , false ) }; + double dv[] = { evaluator.value( d , off[0] , cx , _off[0] , true , false ) , evaluator.value( d , off[1] , cy , _off[1] , true , false ) , evaluator.value( d , off[2] , cz , _off[2] , true , false ) }; + normal += Point3D< double >( dv[0]*v[1]*v[2] , v[0]*dv[1]*v[2] , v[0]*v[1]*dv[2] ) * solution[ _node->nodeData.nodeIndex ]; + } + } + } + if( d>_minDepth ) + { + int _cx , _cy , _cz , _corner = int( node - node->parent->children ); + Cube::FactorCornerIndex( _corner , _cx , _cy , _cz ); + if( cx!=_cx ) startX = 0 , endX = 5; + if( cy!=_cy ) startY = 0 , endY = 5; + if( cz!=_cz ) startZ = 0 , endZ = 5; + if( isInterior ) + for( int x=startX ; xnodeData.nodeIndex ]; + } + else + for( int x=startX ; xdepthAndOffset( _d , _off ); + double v [] = { evaluator.value( d , off[0] , cx , _off[0] , false , true ) , evaluator.value( d , off[1] , cy , _off[1] , false , true ) , evaluator.value( d , off[2] , cz , _off[2] , false , true ) }; + double dv[] = { evaluator.value( d , off[0] , cx , _off[0] , true , true ) , evaluator.value( d , off[1] , cy , _off[1] , true , true ) , evaluator.value( d , off[2] , cz , _off[2] , true , true ) }; + normal += Point3D< double >( dv[0]*v[1]*v[2] , v[0]*dv[1]*v[2] , v[0]*v[1]*dv[2] ) * metSolution[ _node->nodeData.nodeIndex ]; + } + } + } + return Point3D< Real >( Real(normal[0]) , Real(normal[1]) , Real(normal[2]) ); +} + +template< class Real > +Real Octree< Real >::Evaluate( ConstPointer( Real ) coefficients , Point3D< Real > p , const BSplineData< 2 >* fData ) const +{ + Real value = Real(0); + BSplineData< 2 > _fData; + if( !fData ) _fData.set( tree.maxDepth() , _boundaryType ) , fData = &_fData; + const TreeOctNode* n = tree.nextNode(); + while( n ) + { + Point3D< Real > c; + Real w; + n->centerAndWidth( c , w ); + c -= p , w *= Real(1.5); + if( fabs(c[0])>w || fabs(c[1])>w || fabs(c[2])>w ) + { + n = tree.nextBranch( n ); + continue; + } + int d , off[3]; + n->depthAndOffset( d , off ); + value += (Real) + ( + coefficients[ n->nodeData.nodeIndex ] * + fData->baseFunctions[ BinaryNode::CenterIndex( d , off[0] ) ]( p[0] ) * + fData->baseFunctions[ BinaryNode::CenterIndex( d , off[1] ) ]( p[1] ) * + fData->baseFunctions[ BinaryNode::CenterIndex( d , off[2] ) ]( p[2] ) + ); + n = tree.nextNode( n ); + } + if( _boundaryType==-1 ) value -= Real(0.5); + return value; +} +template< class Real > +Pointer( Real ) Octree< Real >::Evaluate( ConstPointer( Real ) coefficients , int& res , Real isoValue , int depth ) +{ + int maxDepth = _boundaryType==0 ? tree.maxDepth()-1 : tree.maxDepth(); + if( depth<=0 || depth>maxDepth ) depth = maxDepth; + res = 1<::template ValueTables< Real > vTables = _fData.template getValueTables< Real >( _fData.VALUE_FLAG ); + Pointer( Real ) values = NewPointer< Real >( res * res * res ); + memset( values , 0 , sizeof( Real ) * res * res * res ); + + for( TreeOctNode* n=tree.nextNode() ; n ; n=tree.nextNode( n ) ) + { + if( n->depth()>(_boundaryType==0?depth+1:depth) ) continue; + if( n->depth()<_minDepth ) continue; + int d , idx[3] , start[3] , end[3]; + n->depthAndOffset( d , idx ); + bool skip=false; + for( int i=0 ; i<3 ; i++ ) + { + // Get the index of the functions + idx[i] = BinaryNode::CenterIndex( d , idx[i] ); + // Figure out which samples fall into the range + vTables.setSampleSpan( idx[i] , start[i] , end[i] ); + // We only care about the odd indices + if( !(start[i]&1) ) start[i]++; + if( !( end[i]&1) ) end[i]--; + if( _boundaryType==0 ) + { + // (start[i]-1)>>1 >= res/2 + // ( end[i]-1)<<1 < 3*res/2 + start[i] = std::max< int >( start[i] , res+1 ); + end [i] = std::min< int >( end [i] , 3*res-1 ); + } + } + if( skip ) continue; + Real coefficient = coefficients[ n->nodeData.nodeIndex ]; + for( int x=start[0] ; x<=end[0] ; x+=2 ) + for( int y=start[1] ; y<=end[1] ; y+=2 ) + for( int z=start[2] ; z<=end[2] ; z+=2 ) + { + int xx = (x-1)>>1 , yy=(y-1)>>1 , zz = (z-1)>>1; + if( _boundaryType==0 ) xx -= res/2 , yy -= res/2 , zz -= res/2; + values[ zz*res*res + yy*res + xx ] += + coefficient * + vTables.valueTable[ idx[0] + x*vTables.functionCount ] * + vTables.valueTable[ idx[1] + y*vTables.functionCount ] * + vTables.valueTable[ idx[2] + z*vTables.functionCount ]; + } + } + if( _boundaryType==-1 ) for( int i=0 ; idepthAndOffset(d,o); + for(int i=0;idepthAndOffset( d , o ); + for( int i=0 ; idepthAndOffset(d,o); + for(int i=0;idepthAndOffset( d ,off ); + Cube::FactorEdgeIndex( eIndex , o , i1 , i2 ); + for( int i=0 ; i +#include +#ifndef WIN32 +#include +#endif // WIN32 + +inline double Time( void ) +{ +#ifdef WIN32 + struct _timeb t; + _ftime( &t ); + return double( t.time ) + double( t.millitm ) / 1000.0; +#else // WIN32 + struct timeval t; + gettimeofday( &t , NULL ); + return t.tv_sec + double( t.tv_usec ) / 1000000; +#endif // WIN32 +} + +#endif // MY_TIME_INCLUDED diff --git a/Src/Octree.h b/Src/Octree.h new file mode 100644 index 0000000..407a2e0 --- /dev/null +++ b/Src/Octree.h @@ -0,0 +1,292 @@ +/* +Copyright (c) 2006, Michael Kazhdan and Matthew Bolitho +All rights reserved. + +Redistribution and use in source and binary forms, with or without modification, +are permitted provided that the following conditions are met: + +Redistributions of source code must retain the above copyright notice, this list of +conditions and the following disclaimer. Redistributions in binary form must reproduce +the above copyright notice, this list of conditions and the following disclaimer +in the documentation and/or other materials provided with the distribution. + +Neither the name of the Johns Hopkins University nor the names of its contributors +may be used to endorse or promote products derived from this software without specific +prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY +EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO THE IMPLIED WARRANTIES +OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT +SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED +TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR +BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN +ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH +DAMAGE. +*/ + +#ifndef OCT_NODE_INCLUDED +#define OCT_NODE_INCLUDED + +#include "Allocator.h" +#include "BinaryNode.h" +#include "MarchingCubes.h" + +#define DIMENSION 3 + + +template< class NodeData > +class OctNode +{ +private: + static int UseAlloc; + unsigned long long _depthAndOffset; + + class AdjacencyCountFunction + { + public: + int count; + void Function( const OctNode< NodeData >* node1 , const OctNode< NodeData >* node2 ); + }; + template + void __processNodeFaces(OctNode* node,NodeAdjacencyFunction* F,int cIndex1,int cIndex2,int cIndex3,int cIndex4); + template< class NodeAdjacencyFunction > + void __processNodeFaces( const OctNode* node , NodeAdjacencyFunction* F , int cIndex1 , int cIndex2 , int cIndex3 , int cIndex4 ) const; + template + void __processNodeEdges(OctNode* node,NodeAdjacencyFunction* F,int cIndex1,int cIndex2); + template + void __processNodeNodes(OctNode* node,NodeAdjacencyFunction* F); + template + static void __ProcessNodeAdjacentNodes(int dx,int dy,int dz,OctNode* node1,int radius1,OctNode* node2,int radius2,int cWidth2,NodeAdjacencyFunction* F); + template + static void __ProcessTerminatingNodeAdjacentNodes(int dx,int dy,int dz,OctNode* node1,int radius1,OctNode* node2,int radius2,int cWidth2,TerminatingNodeAdjacencyFunction* F); + template + static void __ProcessPointAdjacentNodes(int dx,int dy,int dz,OctNode* node2,int radius2,int cWidth2,PointAdjacencyFunction* F); + template + static void __ProcessFixedDepthNodeAdjacentNodes(int dx,int dy,int dz,OctNode* node1,int radius1,OctNode* node2,int radius2,int cWidth2,int depth,NodeAdjacencyFunction* F); + template + static void __ProcessMaxDepthNodeAdjacentNodes(int dx,int dy,int dz,OctNode* node1,int radius1,OctNode* node2,int radius2,int cWidth2,int depth,NodeAdjacencyFunction* F); + + // This is made private because the division by two has been pulled out. + static inline int Overlap(int c1,int c2,int c3,int dWidth); + inline static int ChildOverlap(int dx,int dy,int dz,int d,int cRadius2); + + const OctNode* __faceNeighbor(int dir,int off) const; + const OctNode* __edgeNeighbor(int o,const int i[2],const int idx[2]) const; + OctNode* __faceNeighbor(int dir,int off,int forceChildren); + OctNode* __edgeNeighbor(int o,const int i[2],const int idx[2],int forceChildren); +public: + static const int DepthShift,OffsetShift,OffsetShift1,OffsetShift2,OffsetShift3; + static const int DepthMask,OffsetMask; + + static Allocator< OctNode > NodeAllocator; + static int UseAllocator( void ); + static void SetAllocator( int blockSize ); + + OctNode* parent; + OctNode* children; + NodeData nodeData; + + OctNode(void); + ~OctNode(void); + int initChildren( void ); + + void depthAndOffset( int& depth , int offset[DIMENSION] ) const; + void centerIndex( int index[DIMENSION] ) const; + int depth( void ) const; + static inline void DepthAndOffset( const long long& index , int& depth , int offset[DIMENSION] ); + template< class Real > static inline void CenterAndWidth( const long long& index , Point3D< Real >& center , Real& width ); + static inline int Depth( const long long& index ); + static inline void Index( int depth , const int offset[3] , short& d , short off[DIMENSION] ); + static inline unsigned long long Index( int depth , const int offset[3] ); + template< class Real > void centerAndWidth( Point3D& center , Real& width ) const; + template< class Real > bool isInside( Point3D< Real > p ) const; + + size_t leaves( void ) const; + size_t maxDepthLeaves( int maxDepth ) const; + size_t nodes( void ) const; + int maxDepth( void ) const; + + const OctNode* root( void ) const; + + const OctNode* nextLeaf(const OctNode* currentLeaf=NULL) const; + OctNode* nextLeaf(OctNode* currentLeaf=NULL); + const OctNode* nextNode(const OctNode* currentNode=NULL) const; + OctNode* nextNode(OctNode* currentNode=NULL); + const OctNode* nextBranch(const OctNode* current) const; + OctNode* nextBranch(OctNode* current); + const OctNode* prevBranch(const OctNode* current) const; + OctNode* prevBranch(OctNode* current); + + void setFullDepth(int maxDepth); + + void printLeaves(void) const; + void printRange(void) const; + + template + void processNodeFaces(OctNode* node,NodeAdjacencyFunction* F,int fIndex,int processCurrent=1); + template< class NodeAdjacencyFunction > + void processNodeFaces( const OctNode* node , NodeAdjacencyFunction* F , int fIndex , int processCurrent=1 ) const; + template + void processNodeEdges(OctNode* node,NodeAdjacencyFunction* F,int eIndex,int processCurrent=1); + template + void processNodeCorners(OctNode* node,NodeAdjacencyFunction* F,int cIndex,int processCurrent=1); + template + void processNodeNodes(OctNode* node,NodeAdjacencyFunction* F,int processCurrent=1); + + template + static void ProcessNodeAdjacentNodes(int maxDepth,OctNode* node1,int width1,OctNode* node2,int width2,NodeAdjacencyFunction* F,int processCurrent=1); + template + static void ProcessNodeAdjacentNodes(int dx,int dy,int dz,OctNode* node1,int radius1,OctNode* node2,int radius2,int width2,NodeAdjacencyFunction* F,int processCurrent=1); + template + static void ProcessTerminatingNodeAdjacentNodes(int maxDepth,OctNode* node1,int width1,OctNode* node2,int width2,TerminatingNodeAdjacencyFunction* F,int processCurrent=1); + template + static void ProcessTerminatingNodeAdjacentNodes(int dx,int dy,int dz,OctNode* node1,int radius1,OctNode* node2,int radius2,int width2,TerminatingNodeAdjacencyFunction* F,int processCurrent=1); + template + static void ProcessPointAdjacentNodes(int maxDepth,const int center1[3],OctNode* node2,int width2,PointAdjacencyFunction* F,int processCurrent=1); + template + static void ProcessPointAdjacentNodes(int dx,int dy,int dz,OctNode* node2,int radius2,int width2,PointAdjacencyFunction* F,int processCurrent=1); + template + static void ProcessFixedDepthNodeAdjacentNodes(int maxDepth,OctNode* node1,int width1,OctNode* node2,int width2,int depth,NodeAdjacencyFunction* F,int processCurrent=1); + template + static void ProcessFixedDepthNodeAdjacentNodes(int dx,int dy,int dz,OctNode* node1,int radius1,OctNode* node2,int radius2,int width2,int depth,NodeAdjacencyFunction* F,int processCurrent=1); + template + static void ProcessMaxDepthNodeAdjacentNodes(int maxDepth,OctNode* node1,int width1,OctNode* node2,int width2,int depth,NodeAdjacencyFunction* F,int processCurrent=1); + template + static void ProcessMaxDepthNodeAdjacentNodes(int dx,int dy,int dz,OctNode* node1,int radius1,OctNode* node2,int radius2,int width2,int depth,NodeAdjacencyFunction* F,int processCurrent=1); + + template< class Real > static int CornerIndex( const Point3D& center , const Point3D &p ); + + OctNode* faceNeighbor(int faceIndex,int forceChildren=0); + const OctNode* faceNeighbor(int faceIndex) const; + OctNode* edgeNeighbor(int edgeIndex,int forceChildren=0); + const OctNode* edgeNeighbor(int edgeIndex) const; + OctNode* cornerNeighbor(int cornerIndex,int forceChildren=0); + const OctNode* cornerNeighbor(int cornerIndex) const; + + template< class Real > OctNode* getNearestLeaf(const Point3D& p); + template< class Real > const OctNode* getNearestLeaf(const Point3D& p) const; + + static int CommonEdge(const OctNode* node1,int eIndex1,const OctNode* node2,int eIndex2); + static int CompareForwardDepths(const void* v1,const void* v2); + static int CompareByDepthAndXYZ( const void* v1 , const void* v2 ); + static int CompareByDepthAndZIndex( const void* v1 , const void* v2 ); + static int CompareForwardPointerDepths(const void* v1,const void* v2); + static int CompareBackwardDepths(const void* v1,const void* v2); + static int CompareBackwardPointerDepths(const void* v1,const void* v2); + + + template + OctNode& operator = ( const OctNode< NodeData2 >& node ); + + template< class Real > + static inline int Overlap2(const int &depth1,const int offSet1[DIMENSION],const Real& multiplier1,const int &depth2,const int offSet2[DIMENSION],const Real& multiplier2); + + + int write(const char* fileName) const; + int write(FILE* fp) const; + int read(const char* fileName); + int read(FILE* fp); + + class Neighbors5 + { + public: + OctNode* neighbors[5][5][5]; + Neighbors5( void ); + void clear( void ); + }; + class ConstNeighbors5 + { + public: + const OctNode* neighbors[5][5][5]; + ConstNeighbors5( void ); + void clear( void ); + }; + + class NeighborKey5 + { + int _depth; + public: + Neighbors5* neighbors; + + NeighborKey5( void ); + ~NeighborKey5( void ); + + void set( int depth ); + Neighbors5& getNeighbors( OctNode* node ); + Neighbors5& setNeighbors( OctNode* node , int xStart=0 , int xEnd=5 , int yStart=0 , int yEnd=5 , int zStart=0 , int zEnd=5 ); + }; + class ConstNeighborKey5 + { + int _depth; + public: + ConstNeighbors5* neighbors; + + ConstNeighborKey5( void ); + ~ConstNeighborKey5( void ); + + void set( int depth ); + ConstNeighbors5& getNeighbors( const OctNode* node ); + }; + + class Neighbors3 + { + public: + OctNode* neighbors[3][3][3]; + Neighbors3( void ); + void clear( void ); + }; + class ConstNeighbors3 + { + public: + const OctNode* neighbors[3][3][3]; + ConstNeighbors3( void ); + void clear( void ); + }; + class NeighborKey3 + { + int _depth; + public: + Neighbors3* neighbors; + + NeighborKey3( void ); + NeighborKey3( const NeighborKey3& key3 ); + ~NeighborKey3( void ); + + void set( int depth ); + template< class Real > Neighbors3& setNeighbors( OctNode* root , Point3D< Real > p , int d ); + template< class Real > Neighbors3& getNeighbors( OctNode* root , Point3D< Real > p , int d ); + Neighbors3& setNeighbors( OctNode* node , bool flags[3][3][3] ); + Neighbors3& setNeighbors( OctNode* node ); + Neighbors3& getNeighbors( OctNode* node ); + void setNeighbors( OctNode* node , typename OctNode< NodeData >::Neighbors5& neighbors ); + void getNeighbors( OctNode* node , typename OctNode< NodeData >::Neighbors5& neighbors ); + + template< class Real > bool setChildNeighbors( Point3D< Real > p , int d , Neighbors3& childNeighbors ) const; + template< class Real > bool getChildNeighbors( Point3D< Real > p , int d , Neighbors3& childNeighbors ) const; + }; + class ConstNeighborKey3 + { + int _depth; + public: + ConstNeighbors3* neighbors; + + ConstNeighborKey3( void ); + ConstNeighborKey3( const ConstNeighborKey3& key3 ); + ~ConstNeighborKey3( void ); + + void set(int depth); + ConstNeighbors3& getNeighbors( const OctNode* node ); + ConstNeighbors3& getNeighbors( const OctNode* node , int minDepth ); + void getNeighbors( const OctNode* node , typename OctNode< NodeData >::ConstNeighbors5& neighbors ); + }; + + void centerIndex(int maxDepth,int index[DIMENSION]) const; + int width(int maxDepth) const; +}; + + +#include "Octree.inl" + +#endif // OCT_NODE diff --git a/Src/Octree.inl b/Src/Octree.inl new file mode 100644 index 0000000..2bfc5d1 --- /dev/null +++ b/Src/Octree.inl @@ -0,0 +1,2349 @@ +/* +Copyright (c) 2006, Michael Kazhdan and Matthew Bolitho +All rights reserved. + +Redistribution and use in source and binary forms, with or without modification, +are permitted provided that the following conditions are met: + +Redistributions of source code must retain the above copyright notice, this list of +conditions and the following disclaimer. Redistributions in binary form must reproduce +the above copyright notice, this list of conditions and the following disclaimer +in the documentation and/or other materials provided with the distribution. + +Neither the name of the Johns Hopkins University nor the names of its contributors +may be used to endorse or promote products derived from this software without specific +prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY +EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO THE IMPLIED WARRANTIES +OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT +SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED +TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR +BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN +ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH +DAMAGE. +*/ + +#include +#include +#include + +///////////// +// OctNode // +///////////// +template< class NodeData > const int OctNode< NodeData >::DepthShift=5; +template< class NodeData > const int OctNode< NodeData >::OffsetShift = ( sizeof(long long)*8 - DepthShift ) / 3; +template< class NodeData > const int OctNode< NodeData >::DepthMask=(1< const int OctNode< NodeData >::OffsetMask=(1< const int OctNode< NodeData >::OffsetShift1=DepthShift; +template< class NodeData > const int OctNode< NodeData >::OffsetShift2=OffsetShift1+OffsetShift; +template< class NodeData > const int OctNode< NodeData >::OffsetShift3=OffsetShift2+OffsetShift; + +template< class NodeData > int OctNode< NodeData >::UseAlloc=0; +template< class NodeData > Allocator > OctNode< NodeData >::NodeAllocator; + +template< class NodeData > +void OctNode< NodeData >::SetAllocator(int blockSize) +{ + if(blockSize>0) + { + UseAlloc=1; + NodeAllocator.set(blockSize); + } + else{UseAlloc=0;} +} +template< class NodeData > +int OctNode< NodeData >::UseAllocator(void){return UseAlloc;} + +template< class NodeData > +OctNode< NodeData >::OctNode(void){ + parent=children=NULL; + _depthAndOffset = 0; +} + +template< class NodeData > +OctNode< NodeData >::~OctNode(void){ + if(!UseAlloc){if(children){delete[] children;}} + parent=children=NULL; +} +template< class NodeData > +void OctNode< NodeData >::setFullDepth( int maxDepth ) +{ + if( maxDepth ) + { + if( !children ) initChildren(); + for( int i=0 ; i<8 ; i++ ) children[i].setFullDepth( maxDepth-1 ); + } +} + +template< class NodeData > +int OctNode< NodeData >::initChildren( void ) +{ + if( UseAlloc ) children=NodeAllocator.newElements(8); + else + { + if( children ) delete[] children; + children = NULL; + children = new OctNode[Cube::CORNERS]; + } + if( !children ) + { + fprintf(stderr,"Failed to initialize children in OctNode::initChildren\n"); + exit(0); + return 0; + } + int d , off[3]; + depthAndOffset( d , off ); + for( int i=0 ; i<2 ; i++ ) for( int j=0 ; j<2 ; j++ ) for( int k=0 ; k<2 ; k++ ) + { + int idx=Cube::CornerIndex(i,j,k); + children[idx].parent = this; + children[idx].children = NULL; + int off2[3]; + off2[0] = (off[0]<<1)+i; + off2[1] = (off[1]<<1)+j; + off2[2] = (off[2]<<1)+k; + children[idx]._depthAndOffset = Index( d+1 , off2 ); + } + return 1; +} +template< class NodeData > +inline void OctNode< NodeData >::Index(int depth,const int offset[3],short& d,short off[3]){ + d=short(depth); + off[0]=short((1< +inline void OctNode< NodeData >::depthAndOffset( int& depth , int offset[DIMENSION] ) const +{ + depth = int( _depthAndOffset & DepthMask ); + offset[0] = int( (_depthAndOffset>>OffsetShift1) & OffsetMask ); + offset[1] = int( (_depthAndOffset>>OffsetShift2) & OffsetMask ); + offset[2] = int( (_depthAndOffset>>OffsetShift3) & OffsetMask ); +} +template< class NodeData > +inline void OctNode< NodeData >::centerIndex( int index[DIMENSION] ) const +{ + int d , off[DIMENSION]; + depthAndOffset( d , off ); + for( int i=0 ; i +inline unsigned long long OctNode< NodeData >::Index( int depth , const int offset[3] ) +{ + unsigned long long idx=0; + idx |= ( ( (unsigned long long)(depth ) ) & DepthMask ); + idx |= ( ( (unsigned long long)(offset[0]) ) & OffsetMask ) << OffsetShift1; + idx |= ( ( (unsigned long long)(offset[1]) ) & OffsetMask ) << OffsetShift2; + idx |= ( ( (unsigned long long)(offset[2]) ) & OffsetMask ) << OffsetShift3; + return idx; +} +template< class NodeData > +inline int OctNode< NodeData >::depth( void ) const {return int( _depthAndOffset & DepthMask );} +template< class NodeData > +inline void OctNode< NodeData >::DepthAndOffset(const long long& index,int& depth,int offset[3]){ + depth=int(index&DepthMask); + offset[0]=(int((index>>OffsetShift1)&OffsetMask)+1)&(~(1<>OffsetShift2)&OffsetMask)+1)&(~(1<>OffsetShift3)&OffsetMask)+1)&(~(1< +inline int OctNode< NodeData >::Depth(const long long& index){return int(index&DepthMask);} +template< class NodeData > +template< class Real > +void OctNode< NodeData >::centerAndWidth( Point3D& center , Real& width ) const +{ + int depth , offset[3]; + depthAndOffset( depth , offset ); + width = Real( 1.0 / (1< +template< class Real > +bool OctNode< NodeData >::isInside( Point3D< Real > p ) const +{ + Point3D< Real > c; + Real w; + centerAndWidth( c , w ); + w /= 2; + return (c[0]-w) +template< class Real > +inline void OctNode< NodeData >::CenterAndWidth(const long long& index,Point3D& center,Real& width){ + int depth,offset[3]; + depth=index&DepthMask; + offset[0]=(int((index>>OffsetShift1)&OffsetMask)+1)&(~(1<>OffsetShift2)&OffsetMask)+1)&(~(1<>OffsetShift3)&OffsetMask)+1)&(~(1< +int OctNode< NodeData >::maxDepth(void) const{ + if(!children){return 0;} + else{ + int c,d; + for(int i=0;ic){c=d;} + } + return c+1; + } +} +template< class NodeData > +size_t OctNode< NodeData >::nodes( void ) const +{ + if( !children ) return 1; + else + { + size_t c=0; + for( int i=0 ; i +size_t OctNode< NodeData >::leaves( void ) const +{ + if( !children ) return 1; + else + { + size_t c=0; + for( int i=0 ; i +size_t OctNode< NodeData >::maxDepthLeaves( int maxDepth ) const +{ + if( depth()>maxDepth ) return 0; + if( !children ) return 1; + else + { + size_t c=0; + for( int i=0 ; i +const OctNode< NodeData >* OctNode< NodeData >::root(void) const{ + const OctNode* temp=this; + while(temp->parent){temp=temp->parent;} + return temp; +} + + +template< class NodeData > +const OctNode< NodeData >* OctNode< NodeData >::nextBranch( const OctNode* current ) const +{ + if( !current->parent || current==this ) return NULL; + if(current-current->parent->children==Cube::CORNERS-1) return nextBranch( current->parent ); + else return current+1; +} +template< class NodeData > +OctNode< NodeData >* OctNode< NodeData >::nextBranch(OctNode* current){ + if(!current->parent || current==this){return NULL;} + if(current-current->parent->children==Cube::CORNERS-1){return nextBranch(current->parent);} + else{return current+1;} +} +template< class NodeData > +const OctNode< NodeData >* OctNode< NodeData >::prevBranch( const OctNode* current ) const +{ + if( !current->parent || current==this ) return NULL; + if( current-current->parent->children==0 ) return prevBranch( current->parent ); + else return current-1; +} +template< class NodeData > +OctNode< NodeData >* OctNode< NodeData >::prevBranch( OctNode* current ) +{ + if( !current->parent || current==this ) return NULL; + if( current-current->parent->children==0 ) return prevBranch( current->parent ); + else return current-1; +} +template< class NodeData > +const OctNode< NodeData >* OctNode< NodeData >::nextLeaf(const OctNode* current) const{ + if(!current){ + const OctNode< NodeData >* temp=this; + while(temp->children){temp=&temp->children[0];} + return temp; + } + if(current->children){return current->nextLeaf();} + const OctNode* temp=nextBranch(current); + if(!temp){return NULL;} + else{return temp->nextLeaf();} +} +template< class NodeData > +OctNode< NodeData >* OctNode< NodeData >::nextLeaf(OctNode* current){ + if(!current){ + OctNode< NodeData >* temp=this; + while(temp->children){temp=&temp->children[0];} + return temp; + } + if(current->children){return current->nextLeaf();} + OctNode* temp=nextBranch(current); + if(!temp){return NULL;} + else{return temp->nextLeaf();} +} + +template< class NodeData > +const OctNode< NodeData >* OctNode< NodeData >::nextNode( const OctNode* current ) const +{ + if( !current ) return this; + else if( current->children ) return ¤t->children[0]; + else return nextBranch(current); +} +template< class NodeData > +OctNode< NodeData >* OctNode< NodeData >::nextNode( OctNode* current ) +{ + if( !current ) return this; + else if( current->children ) return ¤t->children[0]; + else return nextBranch( current ); +} + +template< class NodeData > +void OctNode< NodeData >::printRange(void) const +{ + Point3D< float > center; + float width; + centerAndWidth(center,width); + for(int dim=0;dim +void OctNode< NodeData >::AdjacencyCountFunction::Function(const OctNode< NodeData >* node1,const OctNode< NodeData >* node2){count++;} + +template< class NodeData > +template +void OctNode< NodeData >::processNodeNodes(OctNode* node,NodeAdjacencyFunction* F,int processCurrent){ + if(processCurrent){F->Function(this,node);} + if(children){__processNodeNodes(node,F);} +} +template< class NodeData > +template +void OctNode< NodeData >::processNodeFaces(OctNode* node,NodeAdjacencyFunction* F,int fIndex,int processCurrent){ + if(processCurrent){F->Function(this,node);} + if(children){ + int c1,c2,c3,c4; + Cube::FaceCorners(fIndex,c1,c2,c3,c4); + __processNodeFaces(node,F,c1,c2,c3,c4); + } +} +template< class NodeData > +template< class NodeAdjacencyFunction > +void OctNode< NodeData >::processNodeFaces( const OctNode* node , NodeAdjacencyFunction* F , int fIndex , int processCurrent ) const +{ + if( processCurrent ) F->Function( this , node ); + if(children) + { + int c1 , c2 , c3 , c4; + Cube::FaceCorners( fIndex , c1 , c2 , c3 , c4 ); + __processNodeFaces( node , F , c1 , c2 , c3 , c4 ); + } +} +template< class NodeData > +template +void OctNode< NodeData >::processNodeEdges(OctNode* node,NodeAdjacencyFunction* F,int eIndex,int processCurrent){ + if(processCurrent){F->Function(this,node);} + if(children){ + int c1,c2; + Cube::EdgeCorners(eIndex,c1,c2); + __processNodeEdges(node,F,c1,c2); + } +} +template< class NodeData > +template +void OctNode< NodeData >::processNodeCorners(OctNode* node,NodeAdjacencyFunction* F,int cIndex,int processCurrent){ + if(processCurrent){F->Function(this,node);} + OctNode< NodeData >* temp=this; + while(temp->children){ + temp=&temp->children[cIndex]; + F->Function(temp,node); + } +} +template< class NodeData > +template +void OctNode< NodeData >::__processNodeNodes(OctNode* node,NodeAdjacencyFunction* F){ + F->Function(&children[0],node); + F->Function(&children[1],node); + F->Function(&children[2],node); + F->Function(&children[3],node); + F->Function(&children[4],node); + F->Function(&children[5],node); + F->Function(&children[6],node); + F->Function(&children[7],node); + if(children[0].children){children[0].__processNodeNodes(node,F);} + if(children[1].children){children[1].__processNodeNodes(node,F);} + if(children[2].children){children[2].__processNodeNodes(node,F);} + if(children[3].children){children[3].__processNodeNodes(node,F);} + if(children[4].children){children[4].__processNodeNodes(node,F);} + if(children[5].children){children[5].__processNodeNodes(node,F);} + if(children[6].children){children[6].__processNodeNodes(node,F);} + if(children[7].children){children[7].__processNodeNodes(node,F);} +} +template< class NodeData > +template +void OctNode< NodeData >::__processNodeEdges(OctNode* node,NodeAdjacencyFunction* F,int cIndex1,int cIndex2){ + F->Function(&children[cIndex1],node); + F->Function(&children[cIndex2],node); + if(children[cIndex1].children){children[cIndex1].__processNodeEdges(node,F,cIndex1,cIndex2);} + if(children[cIndex2].children){children[cIndex2].__processNodeEdges(node,F,cIndex1,cIndex2);} +} +template< class NodeData > +template +void OctNode< NodeData >::__processNodeFaces(OctNode* node,NodeAdjacencyFunction* F,int cIndex1,int cIndex2,int cIndex3,int cIndex4){ + F->Function(&children[cIndex1],node); + F->Function(&children[cIndex2],node); + F->Function(&children[cIndex3],node); + F->Function(&children[cIndex4],node); + if(children[cIndex1].children){children[cIndex1].__processNodeFaces(node,F,cIndex1,cIndex2,cIndex3,cIndex4);} + if(children[cIndex2].children){children[cIndex2].__processNodeFaces(node,F,cIndex1,cIndex2,cIndex3,cIndex4);} + if(children[cIndex3].children){children[cIndex3].__processNodeFaces(node,F,cIndex1,cIndex2,cIndex3,cIndex4);} + if(children[cIndex4].children){children[cIndex4].__processNodeFaces(node,F,cIndex1,cIndex2,cIndex3,cIndex4);} +} +template< class NodeData > +template< class NodeAdjacencyFunction > +void OctNode< NodeData >::__processNodeFaces( const OctNode* node , NodeAdjacencyFunction* F , int cIndex1 , int cIndex2 , int cIndex3 , int cIndex4 ) const +{ + F->Function( &children[cIndex1] , node ); + F->Function( &children[cIndex2] , node ); + F->Function( &children[cIndex3] , node ); + F->Function( &children[cIndex4] , node ); + if( children[cIndex1].children ) children[cIndex1].__processNodeFaces( node , F , cIndex1 , cIndex2 , cIndex3 , cIndex4 ); + if( children[cIndex2].children ) children[cIndex2].__processNodeFaces( node , F , cIndex1 , cIndex2 , cIndex3 , cIndex4 ); + if( children[cIndex3].children ) children[cIndex3].__processNodeFaces( node , F , cIndex1 , cIndex2 , cIndex3 , cIndex4 ); + if( children[cIndex4].children ) children[cIndex4].__processNodeFaces( node , F , cIndex1 , cIndex2 , cIndex3 , cIndex4 ); +} +template< class NodeData > +template +void OctNode< NodeData >::ProcessNodeAdjacentNodes(int maxDepth,OctNode* node1,int width1,OctNode* node2,int width2,NodeAdjacencyFunction* F,int processCurrent){ + int c1[3],c2[3],w1,w2; + node1->centerIndex(maxDepth+1,c1); + node2->centerIndex(maxDepth+1,c2); + w1=node1->width(maxDepth+1); + w2=node2->width(maxDepth+1); + + ProcessNodeAdjacentNodes(c1[0]-c2[0],c1[1]-c2[1],c1[2]-c2[2],node1,(width1*w1)>>1,node2,(width2*w2)>>1,w2,F,processCurrent); +} +template< class NodeData > +template +void OctNode< NodeData >::ProcessNodeAdjacentNodes(int dx,int dy,int dz, + OctNode< NodeData >* node1,int radius1, + OctNode< NodeData >* node2,int radius2,int width2, + NodeAdjacencyFunction* F,int processCurrent){ + if(!Overlap(dx,dy,dz,radius1+radius2)){return;} + if(processCurrent){F->Function(node2,node1);} + if(!node2->children){return;} + __ProcessNodeAdjacentNodes(-dx,-dy,-dz,node1,radius1,node2,radius2,width2/2,F); +} +template< class NodeData > +template +void OctNode< NodeData >::ProcessTerminatingNodeAdjacentNodes(int maxDepth,OctNode* node1,int width1,OctNode* node2,int width2,TerminatingNodeAdjacencyFunction* F,int processCurrent){ + int c1[3],c2[3],w1,w2; + node1->centerIndex(maxDepth+1,c1); + node2->centerIndex(maxDepth+1,c2); + w1=node1->width(maxDepth+1); + w2=node2->width(maxDepth+1); + + ProcessTerminatingNodeAdjacentNodes(c1[0]-c2[0],c1[1]-c2[1],c1[2]-c2[2],node1,(width1*w1)>>1,node2,(width2*w2)>>1,w2,F,processCurrent); +} +template< class NodeData > +template +void OctNode< NodeData >::ProcessTerminatingNodeAdjacentNodes(int dx,int dy,int dz, + OctNode< NodeData >* node1,int radius1, + OctNode< NodeData >* node2,int radius2,int width2, + TerminatingNodeAdjacencyFunction* F,int processCurrent) +{ + if(!Overlap(dx,dy,dz,radius1+radius2)){return;} + if(processCurrent){F->Function(node2,node1);} + if(!node2->children){return;} + __ProcessTerminatingNodeAdjacentNodes(-dx,-dy,-dz,node1,radius1,node2,radius2,width2/2,F); +} +template< class NodeData > +template +void OctNode< NodeData >::ProcessPointAdjacentNodes( int maxDepth , const int c1[3] , OctNode* node2 , int width2 , PointAdjacencyFunction* F , int processCurrent ) +{ + int c2[3] , w2; + node2->centerIndex( maxDepth+1 , c2 ); + w2 = node2->width( maxDepth+1 ); + ProcessPointAdjacentNodes( c1[0]-c2[0] , c1[1]-c2[1] , c1[2]-c2[2] , node2 , (width2*w2)>>1 , w2 , F , processCurrent ); +} +template< class NodeData > +template +void OctNode< NodeData >::ProcessPointAdjacentNodes(int dx,int dy,int dz, + OctNode< NodeData >* node2,int radius2,int width2, + PointAdjacencyFunction* F,int processCurrent) +{ + if( !Overlap(dx,dy,dz,radius2) ) return; + if( processCurrent ) F->Function(node2); + if( !node2->children ) return; + __ProcessPointAdjacentNodes( -dx , -dy , -dz , node2 , radius2 , width2>>1 , F ); +} +template< class NodeData > +template +void OctNode< NodeData >::ProcessFixedDepthNodeAdjacentNodes(int maxDepth, + OctNode< NodeData >* node1,int width1, + OctNode< NodeData >* node2,int width2, + int depth,NodeAdjacencyFunction* F,int processCurrent) +{ + int c1[3],c2[3],w1,w2; + node1->centerIndex(maxDepth+1,c1); + node2->centerIndex(maxDepth+1,c2); + w1=node1->width(maxDepth+1); + w2=node2->width(maxDepth+1); + + ProcessFixedDepthNodeAdjacentNodes(c1[0]-c2[0],c1[1]-c2[1],c1[2]-c2[2],node1,(width1*w1)>>1,node2,(width2*w2)>>1,w2,depth,F,processCurrent); +} +template< class NodeData > +template +void OctNode< NodeData >::ProcessFixedDepthNodeAdjacentNodes(int dx,int dy,int dz, + OctNode< NodeData >* node1,int radius1, + OctNode< NodeData >* node2,int radius2,int width2, + int depth,NodeAdjacencyFunction* F,int processCurrent) +{ + int d=node2->depth(); + if(d>depth){return;} + if(!Overlap(dx,dy,dz,radius1+radius2)){return;} + if(d==depth){if(processCurrent){F->Function(node2,node1);}} + else{ + if(!node2->children){return;} + __ProcessFixedDepthNodeAdjacentNodes(-dx,-dy,-dz,node1,radius1,node2,radius2,width2/2,depth-1,F); + } +} +template< class NodeData > +template +void OctNode< NodeData >::ProcessMaxDepthNodeAdjacentNodes(int maxDepth, + OctNode< NodeData >* node1,int width1, + OctNode< NodeData >* node2,int width2, + int depth,NodeAdjacencyFunction* F,int processCurrent) +{ + int c1[3],c2[3],w1,w2; + node1->centerIndex(maxDepth+1,c1); + node2->centerIndex(maxDepth+1,c2); + w1=node1->width(maxDepth+1); + w2=node2->width(maxDepth+1); + ProcessMaxDepthNodeAdjacentNodes(c1[0]-c2[0],c1[1]-c2[1],c1[2]-c2[2],node1,(width1*w1)>>1,node2,(width2*w2)>>1,w2,depth,F,processCurrent); +} +template< class NodeData > +template +void OctNode< NodeData >::ProcessMaxDepthNodeAdjacentNodes(int dx,int dy,int dz, + OctNode< NodeData >* node1,int radius1, + OctNode< NodeData >* node2,int radius2,int width2, + int depth,NodeAdjacencyFunction* F,int processCurrent) +{ + int d=node2->depth(); + if(d>depth){return;} + if(!Overlap(dx,dy,dz,radius1+radius2)){return;} + if(processCurrent){F->Function(node2,node1);} + if(dchildren){__ProcessMaxDepthNodeAdjacentNodes(-dx,-dy,-dz,node1,radius1,node2,radius2,width2>>1,depth-1,F);} +} +template< class NodeData > +template +void OctNode< NodeData >::__ProcessNodeAdjacentNodes(int dx,int dy,int dz, + OctNode* node1,int radius1, + OctNode* node2,int radius2,int cWidth2, + NodeAdjacencyFunction* F) +{ + int cWidth=cWidth2>>1; + int radius=radius2>>1; + int o=ChildOverlap(dx,dy,dz,radius1+radius,cWidth); + if(o){ + int dx1=dx-cWidth; + int dx2=dx+cWidth; + int dy1=dy-cWidth; + int dy2=dy+cWidth; + int dz1=dz-cWidth; + int dz2=dz+cWidth; + if(o& 1){F->Function(&node2->children[0],node1);if(node2->children[0].children){__ProcessNodeAdjacentNodes(dx1,dy1,dz1,node1,radius1,&node2->children[0],radius,cWidth,F);}} + if(o& 2){F->Function(&node2->children[1],node1);if(node2->children[1].children){__ProcessNodeAdjacentNodes(dx2,dy1,dz1,node1,radius1,&node2->children[1],radius,cWidth,F);}} + if(o& 4){F->Function(&node2->children[2],node1);if(node2->children[2].children){__ProcessNodeAdjacentNodes(dx1,dy2,dz1,node1,radius1,&node2->children[2],radius,cWidth,F);}} + if(o& 8){F->Function(&node2->children[3],node1);if(node2->children[3].children){__ProcessNodeAdjacentNodes(dx2,dy2,dz1,node1,radius1,&node2->children[3],radius,cWidth,F);}} + if(o& 16){F->Function(&node2->children[4],node1);if(node2->children[4].children){__ProcessNodeAdjacentNodes(dx1,dy1,dz2,node1,radius1,&node2->children[4],radius,cWidth,F);}} + if(o& 32){F->Function(&node2->children[5],node1);if(node2->children[5].children){__ProcessNodeAdjacentNodes(dx2,dy1,dz2,node1,radius1,&node2->children[5],radius,cWidth,F);}} + if(o& 64){F->Function(&node2->children[6],node1);if(node2->children[6].children){__ProcessNodeAdjacentNodes(dx1,dy2,dz2,node1,radius1,&node2->children[6],radius,cWidth,F);}} + if(o&128){F->Function(&node2->children[7],node1);if(node2->children[7].children){__ProcessNodeAdjacentNodes(dx2,dy2,dz2,node1,radius1,&node2->children[7],radius,cWidth,F);}} + } +} +template< class NodeData > +template +void OctNode< NodeData >::__ProcessTerminatingNodeAdjacentNodes(int dx,int dy,int dz, + OctNode* node1,int radius1, + OctNode* node2,int radius2,int cWidth2, + TerminatingNodeAdjacencyFunction* F) +{ + int cWidth=cWidth2>>1; + int radius=radius2>>1; + int o=ChildOverlap(dx,dy,dz,radius1+radius,cWidth); + if(o){ + int dx1=dx-cWidth; + int dx2=dx+cWidth; + int dy1=dy-cWidth; + int dy2=dy+cWidth; + int dz1=dz-cWidth; + int dz2=dz+cWidth; + if(o& 1){if(F->Function(&node2->children[0],node1) && node2->children[0].children){__ProcessTerminatingNodeAdjacentNodes(dx1,dy1,dz1,node1,radius1,&node2->children[0],radius,cWidth,F);}} + if(o& 2){if(F->Function(&node2->children[1],node1) && node2->children[1].children){__ProcessTerminatingNodeAdjacentNodes(dx2,dy1,dz1,node1,radius1,&node2->children[1],radius,cWidth,F);}} + if(o& 4){if(F->Function(&node2->children[2],node1) && node2->children[2].children){__ProcessTerminatingNodeAdjacentNodes(dx1,dy2,dz1,node1,radius1,&node2->children[2],radius,cWidth,F);}} + if(o& 8){if(F->Function(&node2->children[3],node1) && node2->children[3].children){__ProcessTerminatingNodeAdjacentNodes(dx2,dy2,dz1,node1,radius1,&node2->children[3],radius,cWidth,F);}} + if(o& 16){if(F->Function(&node2->children[4],node1) && node2->children[4].children){__ProcessTerminatingNodeAdjacentNodes(dx1,dy1,dz2,node1,radius1,&node2->children[4],radius,cWidth,F);}} + if(o& 32){if(F->Function(&node2->children[5],node1) && node2->children[5].children){__ProcessTerminatingNodeAdjacentNodes(dx2,dy1,dz2,node1,radius1,&node2->children[5],radius,cWidth,F);}} + if(o& 64){if(F->Function(&node2->children[6],node1) && node2->children[6].children){__ProcessTerminatingNodeAdjacentNodes(dx1,dy2,dz2,node1,radius1,&node2->children[6],radius,cWidth,F);}} + if(o&128){if(F->Function(&node2->children[7],node1) && node2->children[7].children){__ProcessTerminatingNodeAdjacentNodes(dx2,dy2,dz2,node1,radius1,&node2->children[7],radius,cWidth,F);}} + } +} +template< class NodeData > +template +void OctNode< NodeData >::__ProcessPointAdjacentNodes(int dx,int dy,int dz, + OctNode* node2,int radius2,int cWidth2, + PointAdjacencyFunction* F) +{ + int cWidth=cWidth2>>1; + int radius=radius2>>1; + int o=ChildOverlap(dx,dy,dz,radius,cWidth); + if( o ) + { + int dx1=dx-cWidth; + int dx2=dx+cWidth; + int dy1=dy-cWidth; + int dy2=dy+cWidth; + int dz1=dz-cWidth; + int dz2=dz+cWidth; + if(o& 1){F->Function(&node2->children[0]);if(node2->children[0].children){__ProcessPointAdjacentNodes(dx1,dy1,dz1,&node2->children[0],radius,cWidth,F);}} + if(o& 2){F->Function(&node2->children[1]);if(node2->children[1].children){__ProcessPointAdjacentNodes(dx2,dy1,dz1,&node2->children[1],radius,cWidth,F);}} + if(o& 4){F->Function(&node2->children[2]);if(node2->children[2].children){__ProcessPointAdjacentNodes(dx1,dy2,dz1,&node2->children[2],radius,cWidth,F);}} + if(o& 8){F->Function(&node2->children[3]);if(node2->children[3].children){__ProcessPointAdjacentNodes(dx2,dy2,dz1,&node2->children[3],radius,cWidth,F);}} + if(o& 16){F->Function(&node2->children[4]);if(node2->children[4].children){__ProcessPointAdjacentNodes(dx1,dy1,dz2,&node2->children[4],radius,cWidth,F);}} + if(o& 32){F->Function(&node2->children[5]);if(node2->children[5].children){__ProcessPointAdjacentNodes(dx2,dy1,dz2,&node2->children[5],radius,cWidth,F);}} + if(o& 64){F->Function(&node2->children[6]);if(node2->children[6].children){__ProcessPointAdjacentNodes(dx1,dy2,dz2,&node2->children[6],radius,cWidth,F);}} + if(o&128){F->Function(&node2->children[7]);if(node2->children[7].children){__ProcessPointAdjacentNodes(dx2,dy2,dz2,&node2->children[7],radius,cWidth,F);}} + } +} +template< class NodeData > +template +void OctNode< NodeData >::__ProcessFixedDepthNodeAdjacentNodes(int dx,int dy,int dz, + OctNode* node1,int radius1, + OctNode* node2,int radius2,int cWidth2, + int depth,NodeAdjacencyFunction* F) +{ + int cWidth=cWidth2>>1; + int radius=radius2>>1; + int o=ChildOverlap(dx,dy,dz,radius1+radius,cWidth); + if(o){ + int dx1=dx-cWidth; + int dx2=dx+cWidth; + int dy1=dy-cWidth; + int dy2=dy+cWidth; + int dz1=dz-cWidth; + int dz2=dz+cWidth; + if(node2->depth()==depth){ + if(o& 1){F->Function(&node2->children[0],node1);} + if(o& 2){F->Function(&node2->children[1],node1);} + if(o& 4){F->Function(&node2->children[2],node1);} + if(o& 8){F->Function(&node2->children[3],node1);} + if(o& 16){F->Function(&node2->children[4],node1);} + if(o& 32){F->Function(&node2->children[5],node1);} + if(o& 64){F->Function(&node2->children[6],node1);} + if(o&128){F->Function(&node2->children[7],node1);} + } + else{ + if(o& 1){if(node2->children[0].children){__ProcessFixedDepthNodeAdjacentNodes(dx1,dy1,dz1,node1,radius1,&node2->children[0],radius,cWidth,depth,F);}} + if(o& 2){if(node2->children[1].children){__ProcessFixedDepthNodeAdjacentNodes(dx2,dy1,dz1,node1,radius1,&node2->children[1],radius,cWidth,depth,F);}} + if(o& 4){if(node2->children[2].children){__ProcessFixedDepthNodeAdjacentNodes(dx1,dy2,dz1,node1,radius1,&node2->children[2],radius,cWidth,depth,F);}} + if(o& 8){if(node2->children[3].children){__ProcessFixedDepthNodeAdjacentNodes(dx2,dy2,dz1,node1,radius1,&node2->children[3],radius,cWidth,depth,F);}} + if(o& 16){if(node2->children[4].children){__ProcessFixedDepthNodeAdjacentNodes(dx1,dy1,dz2,node1,radius1,&node2->children[4],radius,cWidth,depth,F);}} + if(o& 32){if(node2->children[5].children){__ProcessFixedDepthNodeAdjacentNodes(dx2,dy1,dz2,node1,radius1,&node2->children[5],radius,cWidth,depth,F);}} + if(o& 64){if(node2->children[6].children){__ProcessFixedDepthNodeAdjacentNodes(dx1,dy2,dz2,node1,radius1,&node2->children[6],radius,cWidth,depth,F);}} + if(o&128){if(node2->children[7].children){__ProcessFixedDepthNodeAdjacentNodes(dx2,dy2,dz2,node1,radius1,&node2->children[7],radius,cWidth,depth,F);}} + } + } +} +template< class NodeData > +template +void OctNode< NodeData >::__ProcessMaxDepthNodeAdjacentNodes(int dx,int dy,int dz, + OctNode* node1,int radius1, + OctNode* node2,int radius2,int cWidth2, + int depth,NodeAdjacencyFunction* F) +{ + int cWidth=cWidth2>>1; + int radius=radius2>>1; + int o=ChildOverlap(dx,dy,dz,radius1+radius,cWidth); + if(o){ + int dx1=dx-cWidth; + int dx2=dx+cWidth; + int dy1=dy-cWidth; + int dy2=dy+cWidth; + int dz1=dz-cWidth; + int dz2=dz+cWidth; + if(node2->depth()<=depth){ + if(o& 1){F->Function(&node2->children[0],node1);} + if(o& 2){F->Function(&node2->children[1],node1);} + if(o& 4){F->Function(&node2->children[2],node1);} + if(o& 8){F->Function(&node2->children[3],node1);} + if(o& 16){F->Function(&node2->children[4],node1);} + if(o& 32){F->Function(&node2->children[5],node1);} + if(o& 64){F->Function(&node2->children[6],node1);} + if(o&128){F->Function(&node2->children[7],node1);} + } + if(node2->depth()children[0].children){__ProcessMaxDepthNodeAdjacentNodes(dx1,dy1,dz1,node1,radius1,&node2->children[0],radius,cWidth,depth,F);}} + if(o& 2){if(node2->children[1].children){__ProcessMaxDepthNodeAdjacentNodes(dx2,dy1,dz1,node1,radius1,&node2->children[1],radius,cWidth,depth,F);}} + if(o& 4){if(node2->children[2].children){__ProcessMaxDepthNodeAdjacentNodes(dx1,dy2,dz1,node1,radius1,&node2->children[2],radius,cWidth,depth,F);}} + if(o& 8){if(node2->children[3].children){__ProcessMaxDepthNodeAdjacentNodes(dx2,dy2,dz1,node1,radius1,&node2->children[3],radius,cWidth,depth,F);}} + if(o& 16){if(node2->children[4].children){__ProcessMaxDepthNodeAdjacentNodes(dx1,dy1,dz2,node1,radius1,&node2->children[4],radius,cWidth,depth,F);}} + if(o& 32){if(node2->children[5].children){__ProcessMaxDepthNodeAdjacentNodes(dx2,dy1,dz2,node1,radius1,&node2->children[5],radius,cWidth,depth,F);}} + if(o& 64){if(node2->children[6].children){__ProcessMaxDepthNodeAdjacentNodes(dx1,dy2,dz2,node1,radius1,&node2->children[6],radius,cWidth,depth,F);}} + if(o&128){if(node2->children[7].children){__ProcessMaxDepthNodeAdjacentNodes(dx2,dy2,dz2,node1,radius1,&node2->children[7],radius,cWidth,depth,F);}} + } + } +} +template< class NodeData > +inline int OctNode< NodeData >::ChildOverlap(int dx,int dy,int dz,int d,int cRadius2) +{ + int w1=d-cRadius2; + int w2=d+cRadius2; + int overlap=0; + + int test=0,test1=0; + if(dx-w1){test =1;} + if(dx-w2){test|=2;} + + if(!test){return 0;} + if(dz-w1){test1 =test;} + if(dz-w2){test1|=test<<4;} + + if(!test1){return 0;} + if(dy-w1){overlap =test1;} + if(dy-w2){overlap|=test1<<2;} + return overlap; +} + +template< class NodeData > +template< class Real > +OctNode< NodeData >* OctNode< NodeData >::getNearestLeaf(const Point3D& p){ + Point3D center; + Real width; + OctNode< NodeData >* temp; + int cIndex; + if(!children){return this;} + centerAndWidth(center,width); + temp=this; + while(temp->children){ + cIndex=CornerIndex(center,p); + temp=&temp->children[cIndex]; + width/=2; + if(cIndex&1){center.coords[0]+=width/2;} + else {center.coords[0]-=width/2;} + if(cIndex&2){center.coords[1]+=width/2;} + else {center.coords[1]-=width/2;} + if(cIndex&4){center.coords[2]+=width/2;} + else {center.coords[2]-=width/2;} + } + return temp; +} +template< class NodeData > +template< class Real > +const OctNode< NodeData >* OctNode< NodeData >::getNearestLeaf(const Point3D& p) const{ + int nearest; + Real temp,dist2; + if(!children){return this;} + for(int i=0;i +int OctNode< NodeData >::CommonEdge(const OctNode< NodeData >* node1,int eIndex1,const OctNode< NodeData >* node2,int eIndex2){ + int o1,o2,i1,i2,j1,j2; + + Cube::FactorEdgeIndex(eIndex1,o1,i1,j1); + Cube::FactorEdgeIndex(eIndex2,o2,i2,j2); + if(o1!=o2){return 0;} + + int dir[2]; + int idx1[2]; + int idx2[2]; + switch(o1){ + case 0: dir[0]=1; dir[1]=2; break; + case 1: dir[0]=0; dir[1]=2; break; + case 2: dir[0]=0; dir[1]=1; break; + }; + int d1,d2,off1[3],off2[3]; + node1->depthAndOffset(d1,off1); + node2->depthAndOffset(d2,off2); + idx1[0]=off1[dir[0]]+(1<d2){ + idx2[0]<<=(d1-d2); + idx2[1]<<=(d1-d2); + } + else{ + idx1[0]<<=(d2-d1); + idx1[1]<<=(d2-d1); + } + if(idx1[0]==idx2[0] && idx1[1]==idx2[1]){return 1;} + else {return 0;} +} +template< class NodeData > +template< class Real > +int OctNode< NodeData >::CornerIndex(const Point3D& center,const Point3D& p){ + int cIndex=0; + if(p.coords[0]>center.coords[0]){cIndex|=1;} + if(p.coords[1]>center.coords[1]){cIndex|=2;} + if(p.coords[2]>center.coords[2]){cIndex|=4;} + return cIndex; +} +template< class NodeData > +template< class NodeData2 > +OctNode< NodeData >& OctNode< NodeData >::operator = ( const OctNode< NodeData2 >& node ) +{ + int i; + if(children){delete[] children;} + children=NULL; + + this->depth = node.depth; + for(i=0;ioffset[i] = node.offset[i];} + if(node.children){ + initChildren(); + for(i=0;i +int OctNode< NodeData >::CompareForwardDepths(const void* v1,const void* v2){ + return ((const OctNode< NodeData >*)v1)->depth-((const OctNode< NodeData >*)v2)->depth; +} +template< class NodeData > +int OctNode< NodeData >::CompareByDepthAndXYZ( const void* v1 , const void* v2 ) +{ + const OctNode< NodeData > *n1 = (*(const OctNode< NodeData >**)v1); + const OctNode< NodeData > *n2 = (*(const OctNode< NodeData >**)v2); + if( n1->d!=n2->d ) return int(n1->d)-int(n2->d); + else if( n1->off[0]!=n2->off[0] ) return int(n1->off[0]) - int(n2->off[0]); + else if( n1->off[1]!=n2->off[1] ) return int(n1->off[1]) - int(n2->off[1]); + else if( n1->off[2]!=n2->off[2] ) return int(n1->off[2]) - int(n2->off[2]); + return 0; +} + +long long _InterleaveBits( int p[3] ) +{ + long long key = 0; + for( int i=0 ; i<32 ; i++ ) key |= ( ( p[0] & (1< +int OctNode< NodeData >::CompareByDepthAndZIndex( const void* v1 , const void* v2 ) +{ + const OctNode< NodeData >* n1 = (*(const OctNode< NodeData >**)v1); + const OctNode< NodeData >* n2 = (*(const OctNode< NodeData >**)v2); + int d1 , off1[3] , d2 , off2[3]; + n1->depthAndOffset( d1 , off1 ) , n2->depthAndOffset( d2 , off2 ); + if ( d1>d2 ) return 1; + else if( d1k2 ) return 1; + else if( k1 +int OctNode< NodeData >::CompareForwardPointerDepths( const void* v1 , const void* v2 ) +{ + const OctNode< NodeData >* n1 = (*(const OctNode< NodeData >**)v1); + const OctNode< NodeData >* n2 = (*(const OctNode< NodeData >**)v2); + if(n1->d!=n2->d){return int(n1->d)-int(n2->d);} + while( n1->parent!=n2->parent ) + { + n1=n1->parent; + n2=n2->parent; + } + if(n1->off[0]!=n2->off[0]){return int(n1->off[0])-int(n2->off[0]);} + if(n1->off[1]!=n2->off[1]){return int(n1->off[1])-int(n2->off[1]);} + return int(n1->off[2])-int(n2->off[2]); + return 0; +} +template< class NodeData > +int OctNode< NodeData >::CompareBackwardDepths(const void* v1,const void* v2){ + return ((const OctNode< NodeData >*)v2)->depth-((const OctNode< NodeData >*)v1)->depth; +} +template< class NodeData > +int OctNode< NodeData >::CompareBackwardPointerDepths(const void* v1,const void* v2){ + return (*(const OctNode< NodeData >**)v2)->depth()-(*(const OctNode< NodeData >**)v1)->depth(); +} +template< class NodeData > +template< class Real > +inline int OctNode< NodeData >::Overlap2(const int &depth1,const int offSet1[DIMENSION],const Real& multiplier1,const int &depth2,const int offSet2[DIMENSION],const Real& multiplier2){ + int d=depth2-depth1; + Real w=multiplier2+multiplier1*(1<=w || + fabs(Real(offSet2[1]-(offSet1[1]<=w || + fabs(Real(offSet2[2]-(offSet1[2]<=w + ){return 0;} + return 1; +} +template< class NodeData > +inline int OctNode< NodeData >::Overlap(int c1,int c2,int c3,int dWidth){ + if(c1>=dWidth || c1<=-dWidth || c2>=dWidth || c2<=-dWidth || c3>=dWidth || c3<=-dWidth){return 0;} + else{return 1;} +} +template< class NodeData > +OctNode< NodeData >* OctNode< NodeData >::faceNeighbor(int faceIndex,int forceChildren){return __faceNeighbor(faceIndex>>1,faceIndex&1,forceChildren);} +template< class NodeData > +const OctNode< NodeData >* OctNode< NodeData >::faceNeighbor(int faceIndex) const {return __faceNeighbor(faceIndex>>1,faceIndex&1);} +template< class NodeData > +OctNode< NodeData >* OctNode< NodeData >::__faceNeighbor(int dir,int off,int forceChildren){ + if(!parent){return NULL;} + int pIndex=int(this-parent->children); + pIndex^=(1<children[pIndex];} + else{ + OctNode* temp=parent->__faceNeighbor(dir,off,forceChildren); + if(!temp){return NULL;} + if(!temp->children){ + if(forceChildren){temp->initChildren();} + else{return temp;} + } + return &temp->children[pIndex]; + } +} +template< class NodeData > +const OctNode< NodeData >* OctNode< NodeData >::__faceNeighbor(int dir,int off) const { + if(!parent){return NULL;} + int pIndex=int(this-parent->children); + pIndex^=(1<children[pIndex];} + else{ + const OctNode* temp=parent->__faceNeighbor(dir,off); + if(!temp || !temp->children){return temp;} + else{return &temp->children[pIndex];} + } +} + +template< class NodeData > +OctNode< NodeData >* OctNode< NodeData >::edgeNeighbor(int edgeIndex,int forceChildren){ + int idx[2],o,i[2]; + Cube::FactorEdgeIndex(edgeIndex,o,i[0],i[1]); + switch(o){ + case 0: idx[0]=1; idx[1]=2; break; + case 1: idx[0]=0; idx[1]=2; break; + case 2: idx[0]=0; idx[1]=1; break; + }; + return __edgeNeighbor(o,i,idx,forceChildren); +} +template< class NodeData > +const OctNode< NodeData >* OctNode< NodeData >::edgeNeighbor(int edgeIndex) const { + int idx[2],o,i[2]; + Cube::FactorEdgeIndex(edgeIndex,o,i[0],i[1]); + switch(o){ + case 0: idx[0]=1; idx[1]=2; break; + case 1: idx[0]=0; idx[1]=2; break; + case 2: idx[0]=0; idx[1]=1; break; + }; + return __edgeNeighbor(o,i,idx); +} +template< class NodeData > +const OctNode< NodeData >* OctNode< NodeData >::__edgeNeighbor(int o,const int i[2],const int idx[2]) const{ + if(!parent){return NULL;} + int pIndex=int(this-parent->children); + int aIndex,x[DIMENSION]; + + Cube::FactorCornerIndex(pIndex,x[0],x[1],x[2]); + aIndex=(~((i[0] ^ x[idx[0]]) | ((i[1] ^ x[idx[1]])<<1))) & 3; + pIndex^=(7 ^ (1<__faceNeighbor(idx[0],i[0]); + if(!temp || !temp->children){return NULL;} + else{return &temp->children[pIndex];} + } + else if(aIndex==2) { // I can get the neighbor from the parent's face adjacent neighbor + const OctNode* temp=parent->__faceNeighbor(idx[1],i[1]); + if(!temp || !temp->children){return NULL;} + else{return &temp->children[pIndex];} + } + else if(aIndex==0) { // I can get the neighbor from the parent + return &parent->children[pIndex]; + } + else if(aIndex==3) { // I can get the neighbor from the parent's edge adjacent neighbor + const OctNode* temp=parent->__edgeNeighbor(o,i,idx); + if(!temp || !temp->children){return temp;} + else{return &temp->children[pIndex];} + } + else{return NULL;} +} +template< class NodeData > +OctNode< NodeData >* OctNode< NodeData >::__edgeNeighbor(int o,const int i[2],const int idx[2],int forceChildren){ + if(!parent){return NULL;} + int pIndex=int(this-parent->children); + int aIndex,x[DIMENSION]; + + Cube::FactorCornerIndex(pIndex,x[0],x[1],x[2]); + aIndex=(~((i[0] ^ x[idx[0]]) | ((i[1] ^ x[idx[1]])<<1))) & 3; + pIndex^=(7 ^ (1<__faceNeighbor(idx[0],i[0],0); + if(!temp || !temp->children){return NULL;} + else{return &temp->children[pIndex];} + } + else if(aIndex==2) { // I can get the neighbor from the parent's face adjacent neighbor + OctNode* temp=parent->__faceNeighbor(idx[1],i[1],0); + if(!temp || !temp->children){return NULL;} + else{return &temp->children[pIndex];} + } + else if(aIndex==0) { // I can get the neighbor from the parent + return &parent->children[pIndex]; + } + else if(aIndex==3) { // I can get the neighbor from the parent's edge adjacent neighbor + OctNode* temp=parent->__edgeNeighbor(o,i,idx,forceChildren); + if(!temp){return NULL;} + if(!temp->children){ + if(forceChildren){temp->initChildren();} + else{return temp;} + } + return &temp->children[pIndex]; + } + else{return NULL;} +} + +template< class NodeData > +const OctNode< NodeData >* OctNode< NodeData >::cornerNeighbor(int cornerIndex) const { + int pIndex,aIndex=0; + if(!parent){return NULL;} + + pIndex=int(this-parent->children); + aIndex=(cornerIndex ^ pIndex); // The disagreement bits + pIndex=(~pIndex)&7; // The antipodal point + if(aIndex==7){ // Agree on no bits + return &parent->children[pIndex]; + } + else if(aIndex==0){ // Agree on all bits + const OctNode* temp=((const OctNode*)parent)->cornerNeighbor(cornerIndex); + if(!temp || !temp->children){return temp;} + else{return &temp->children[pIndex];} + } + else if(aIndex==6){ // Agree on face 0 + const OctNode* temp=((const OctNode*)parent)->__faceNeighbor(0,cornerIndex & 1); + if(!temp || !temp->children){return NULL;} + else{return & temp->children[pIndex];} + } + else if(aIndex==5){ // Agree on face 1 + const OctNode* temp=((const OctNode*)parent)->__faceNeighbor(1,(cornerIndex & 2)>>1); + if(!temp || !temp->children){return NULL;} + else{return & temp->children[pIndex];} + } + else if(aIndex==3){ // Agree on face 2 + const OctNode* temp=((const OctNode*)parent)->__faceNeighbor(2,(cornerIndex & 4)>>2); + if(!temp || !temp->children){return NULL;} + else{return & temp->children[pIndex];} + } + else if(aIndex==4){ // Agree on edge 2 + const OctNode* temp=((const OctNode*)parent)->edgeNeighbor(8 | (cornerIndex & 1) | (cornerIndex & 2) ); + if(!temp || !temp->children){return NULL;} + else{return & temp->children[pIndex];} + } + else if(aIndex==2){ // Agree on edge 1 + const OctNode* temp=((const OctNode*)parent)->edgeNeighbor(4 | (cornerIndex & 1) | ((cornerIndex & 4)>>1) ); + if(!temp || !temp->children){return NULL;} + else{return & temp->children[pIndex];} + } + else if(aIndex==1){ // Agree on edge 0 + const OctNode* temp=((const OctNode*)parent)->edgeNeighbor(((cornerIndex & 2) | (cornerIndex & 4))>>1 ); + if(!temp || !temp->children){return NULL;} + else{return & temp->children[pIndex];} + } + else{return NULL;} +} +template< class NodeData > +OctNode< NodeData >* OctNode< NodeData >::cornerNeighbor(int cornerIndex,int forceChildren){ + int pIndex,aIndex=0; + if(!parent){return NULL;} + + pIndex=int(this-parent->children); + aIndex=(cornerIndex ^ pIndex); // The disagreement bits + pIndex=(~pIndex)&7; // The antipodal point + if(aIndex==7){ // Agree on no bits + return &parent->children[pIndex]; + } + else if(aIndex==0){ // Agree on all bits + OctNode* temp=((OctNode*)parent)->cornerNeighbor(cornerIndex,forceChildren); + if(!temp){return NULL;} + if(!temp->children){ + if(forceChildren){temp->initChildren();} + else{return temp;} + } + return &temp->children[pIndex]; + } + else if(aIndex==6){ // Agree on face 0 + OctNode* temp=((OctNode*)parent)->__faceNeighbor(0,cornerIndex & 1,0); + if(!temp || !temp->children){return NULL;} + else{return & temp->children[pIndex];} + } + else if(aIndex==5){ // Agree on face 1 + OctNode* temp=((OctNode*)parent)->__faceNeighbor(1,(cornerIndex & 2)>>1,0); + if(!temp || !temp->children){return NULL;} + else{return & temp->children[pIndex];} + } + else if(aIndex==3){ // Agree on face 2 + OctNode* temp=((OctNode*)parent)->__faceNeighbor(2,(cornerIndex & 4)>>2,0); + if(!temp || !temp->children){return NULL;} + else{return & temp->children[pIndex];} + } + else if(aIndex==4){ // Agree on edge 2 + OctNode* temp=((OctNode*)parent)->edgeNeighbor(8 | (cornerIndex & 1) | (cornerIndex & 2) ); + if(!temp || !temp->children){return NULL;} + else{return & temp->children[pIndex];} + } + else if(aIndex==2){ // Agree on edge 1 + OctNode* temp=((OctNode*)parent)->edgeNeighbor(4 | (cornerIndex & 1) | ((cornerIndex & 4)>>1) ); + if(!temp || !temp->children){return NULL;} + else{return & temp->children[pIndex];} + } + else if(aIndex==1){ // Agree on edge 0 + OctNode* temp=((OctNode*)parent)->edgeNeighbor(((cornerIndex & 2) | (cornerIndex & 4))>>1 ); + if(!temp || !temp->children){return NULL;} + else{return & temp->children[pIndex];} + } + else{return NULL;} +} +//////////////////////// +// OctNodeNeighborKey // +//////////////////////// +template< class NodeData > +OctNode< NodeData >::Neighbors3::Neighbors3(void){clear();} +template< class NodeData > +void OctNode< NodeData >::Neighbors3::clear(void){ + for(int i=0;i<3;i++){for(int j=0;j<3;j++){for(int k=0;k<3;k++){neighbors[i][j][k]=NULL;}}} +} +template< class NodeData > +OctNode< NodeData >::NeighborKey3::NeighborKey3( void ){ _depth=-1 , neighbors=NULL; } +template< class NodeData > +OctNode< NodeData >::NeighborKey3::NeighborKey3( const NeighborKey3& nKey3 ) +{ + _depth = 0 , neighbors = NULL; + set( nKey3._depth ); + for( int d=0 ; d<=_depth ; d++ ) memcpy( &neighbors[d] , &nKey3.neighbors[d] , sizeof(Neighbors3) ); +} +template< class NodeData > +OctNode< NodeData >::NeighborKey3::~NeighborKey3(void) +{ + if( neighbors ) delete[] neighbors; + neighbors = NULL; +} + +template< class NodeData > +void OctNode< NodeData >::NeighborKey3::set( int d ) +{ + if( neighbors ) delete[] neighbors; + neighbors = NULL; + _depth = d; + if( d<0 ) return; + neighbors = new Neighbors3[d+1]; +} +template< class NodeData > +template< class Real > +bool OctNode< NodeData >::NeighborKey3::setChildNeighbors( Point3D< Real > p , int d , typename OctNode< NodeData >::Neighbors3& childNeighbors ) const +{ + if( !neighbors[d].neighbors[1][1][1] ) return false; + int i , j , k , x1 , y1 , z1 , x2 , y2 , z2; + Point3D< Real > c; + Real w; + neighbors[d].neighbors[1][1][1]->centerAndWidth( c , w ); + int idx = CornerIndex( c , p ); + Cube::FactorCornerIndex( idx , x1 , y1 , z1 ); + Cube::FactorCornerIndex( (~idx)&7 , x2 , y2 , z2 ); + + if( !neighbors[d].neighbors[1][1][1]->children ) neighbors[d].neighbors[1][1][1]->initChildren(); + for( i=0 ; i<2 ; i++ ) for( j=0 ; j<2 ; j++ ) for( k=0 ; k<2 ; k++ ) + childNeighbors.neighbors[x2+i][y2+j][z2+k] = &neighbors[d].neighbors[1][1][1]->children[Cube::CornerIndex(i,j,k)]; + + // Set the neighbors from across the faces + i=x1<<1; + if( neighbors[d].neighbors[i][1][1] ) + { + if( !neighbors[d].neighbors[i][1][1]->children ) neighbors[d].neighbors[i][1][1]->initChildren(); + for( j=0 ; j<2 ; j++ ) for( k=0 ; k<2 ; k++ ) childNeighbors.neighbors[i][y2+j][z2+k] = &neighbors[d].neighbors[i][1][1]->children[Cube::CornerIndex(x2,j,k)]; + } + j=y1<<1; + if( neighbors[d].neighbors[1][j][1] ) + { + if( !neighbors[d].neighbors[1][j][1]->children ) neighbors[d].neighbors[1][j][1]->initChildren(); + for( i=0 ; i<2 ; i++ ) for( k=0 ; k<2 ; k++ ) childNeighbors.neighbors[x2+i][j][z2+k] = &neighbors[d].neighbors[1][j][1]->children[Cube::CornerIndex(i,y2,k)]; + } + k=z1<<1; + if( neighbors[d].neighbors[1][1][k] ) + { + if( !neighbors[d].neighbors[1][1][k]->children ) neighbors[d].neighbors[1][1][k]->initChildren(); + for( i=0 ; i<2 ; i++ ) for( j=0 ; j<2 ; j++ ) childNeighbors.neighbors[x2+i][y2+j][k] = &neighbors[d].neighbors[1][1][k]->children[Cube::CornerIndex(i,j,z2)]; + } + + // Set the neighbors from across the edges + i=x1<<1 , j=y1<<1; + if( neighbors[d].neighbors[i][j][1] ) + { + if( !neighbors[d].neighbors[i][j][1]->children ) neighbors[d].neighbors[i][j][1]->initChildren(); + for( k=0 ; k<2 ; k++ ) childNeighbors.neighbors[i][j][z2+k] = &neighbors[d].neighbors[i][j][1]->children[Cube::CornerIndex(x2,y2,k)]; + } + i=x1<<1 , k=z1<<1; + if( neighbors[d].neighbors[i][1][k] ) + { + if( !neighbors[d].neighbors[i][1][k]->children ) neighbors[d].neighbors[i][1][k]->initChildren(); + for( j=0 ; j<2 ; j++ ) childNeighbors.neighbors[i][y2+j][k] = &neighbors[d].neighbors[i][1][k]->children[Cube::CornerIndex(x2,j,z2)]; + } + j=y1<<1 , k=z1<<1; + if( neighbors[d].neighbors[1][j][k] ) + { + if( !neighbors[d].neighbors[1][j][k]->children ) neighbors[d].neighbors[1][j][k]->initChildren(); + for( i=0 ; i<2 ; i++ ) childNeighbors.neighbors[x2+i][j][k] = &neighbors[d].neighbors[1][j][k]->children[Cube::CornerIndex(i,y2,z2)]; + } + + // Set the neighbor from across the corner + i=x1<<1 , j=y1<<1 , k=z1<<1; + if( neighbors[d].neighbors[i][j][k] ) + { + if( !neighbors[d].neighbors[i][j][k]->children ) neighbors[d].neighbors[i][j][k]->initChildren(); + childNeighbors.neighbors[i][j][k] = &neighbors[d].neighbors[i][j][k]->children[Cube::CornerIndex(x2,y2,z2)]; + } + return true; +} +template< class NodeData > +template< class Real > +bool OctNode< NodeData >::NeighborKey3::getChildNeighbors( Point3D< Real > p , int d , typename OctNode< NodeData >::Neighbors3& childNeighbors ) const +{ + if( !neighbors[d].neighbors[1][1][1] ) return false; + int i , j , k , x1 , y1 , z1 , x2 , y2 , z2; + Point3D< Real > c; + Real w; + neighbors[d].neighbors[1][1][1]->centerAndWidth( c , w ); + int idx = CornerIndex( c , p ); + Cube::FactorCornerIndex( idx , x1 , y1 , z1 ); + Cube::FactorCornerIndex( (~idx)&7 , x2 , y2 , z2 ); + + // Set the neighbors of the center cell + if( neighbors[d].neighbors[1][1][1] && neighbors[d].neighbors[1][1][1]->children ) + for( i=0 ; i<2 ; i++ ) for( j=0 ; j<2 ; j++ ) for( k=0 ; k<2 ; k++ ) + childNeighbors.neighbors[x2+i][y2+j][z2+k] = &neighbors[d].neighbors[1][1][1]->children[Cube::CornerIndex(i,j,k)]; + + // Set the neighbors from across the faces + i=x1<<1; + if( neighbors[d].neighbors[i][1][1] && neighbors[d].neighbors[i][1][1]->children ) + for( j=0 ; j<2 ; j++ ) for( k=0 ; k<2 ; k++ ) childNeighbors.neighbors[i][y2+j][z2+k] = &neighbors[d].neighbors[i][1][1]->children[Cube::CornerIndex(x2,j,k)]; + else + for( j=0 ; j<2 ; j++ ) for( k=0 ; k<2 ; k++ ) childNeighbors.neighbors[i][y2+j][z2+k] = NULL; + j=y1<<1; + if( neighbors[d].neighbors[1][j][1] && neighbors[d].neighbors[1][j][1]->children ) + for( i=0 ; i<2 ; i++ ) for( k=0 ; k<2 ; k++ ) childNeighbors.neighbors[x2+i][j][z2+k] = &neighbors[d].neighbors[1][j][1]->children[Cube::CornerIndex(i,y2,k)]; + else + for( i=0 ; i<2 ; i++ ) for( k=0 ; k<2 ; k++ ) childNeighbors.neighbors[x2+i][j][z2+k] = NULL; + k=z1<<1; + if( neighbors[d].neighbors[1][1][k] && neighbors[d].neighbors[1][1][k]->children ) + for( i=0 ; i<2 ; i++ ) for( j=0 ; j<2 ; j++ ) childNeighbors.neighbors[x2+i][y2+j][k] = &neighbors[d].neighbors[1][1][k]->children[Cube::CornerIndex(i,j,z2)]; + else + for( i=0 ; i<2 ; i++ ) for( j=0 ; j<2 ; j++ ) childNeighbors.neighbors[x2+i][y2+j][k] = NULL; + + // Set the neighbors from across the edges + i=x1<<1 , j=y1<<1; + if( neighbors[d].neighbors[i][j][1] && neighbors[d].neighbors[i][j][1]->children ) + for( k=0 ; k<2 ; k++ ) childNeighbors.neighbors[i][j][z2+k] = &neighbors[d].neighbors[i][j][1]->children[Cube::CornerIndex(x2,y2,k)]; + else + for( k=0 ; k<2 ; k++ ) childNeighbors.neighbors[i][j][z2+k] = NULL; + i=x1<<1 , k=z1<<1; + if( neighbors[d].neighbors[i][1][k] && neighbors[d].neighbors[i][1][k]->children ) + for( j=0 ; j<2 ; j++ ) childNeighbors.neighbors[i][y2+j][k] = &neighbors[d].neighbors[i][1][k]->children[Cube::CornerIndex(x2,j,z2)]; + else + for( j=0 ; j<2 ; j++ ) childNeighbors.neighbors[i][y2+j][k] = NULL; + j=y1<<1 , k=z1<<1; + if( neighbors[d].neighbors[1][j][k] && neighbors[d].neighbors[1][j][k]->children ) + for( i=0 ; i<2 ; i++ ) childNeighbors.neighbors[x2+i][j][k] = &neighbors[d].neighbors[1][j][k]->children[Cube::CornerIndex(i,y2,z2)]; + else + for( i=0 ; i<2 ; i++ ) childNeighbors.neighbors[x2+i][j][k] = NULL; + + // Set the neighbor from across the corner + i=x1<<1 , j=y1<<1 , k=z1<<1; + if( neighbors[d].neighbors[i][j][k] && neighbors[d].neighbors[i][j][k]->children ) + childNeighbors.neighbors[i][j][k] = &neighbors[d].neighbors[i][j][k]->children[Cube::CornerIndex(x2,y2,z2)]; + else + childNeighbors.neighbors[i][j][k] = NULL; + + return true; +} +template< class NodeData > +template< class Real > +typename OctNode< NodeData >::Neighbors3& OctNode< NodeData >::NeighborKey3::setNeighbors( OctNode< NodeData >* root , Point3D< Real > p , int d ) +{ + if( !neighbors[d].neighbors[1][1][1] || !neighbors[d].neighbors[1][1][1]->isInside( p ) ) + { + neighbors[d].clear(); + + if( !d ) neighbors[d].neighbors[1][1][1] = root; + else + { + Neighbors3& temp = setNeighbors( root , p , d-1 ); + + int i , j , k , x1 , y1 , z1 , x2 , y2 , z2; + Point3D< Real > c; + Real w; + temp.neighbors[1][1][1]->centerAndWidth( c , w ); + int idx = CornerIndex( c , p ); + Cube::FactorCornerIndex( idx , x1 , y1 , z1 ); + Cube::FactorCornerIndex( (~idx)&7 , x2 , y2 , z2 ); + + if( !temp.neighbors[1][1][1]->children ) temp.neighbors[1][1][1]->initChildren(); + for( i=0 ; i<2 ; i++ ) for( j=0 ; j<2 ; j++ ) for( k=0 ; k<2 ; k++ ) + neighbors[d].neighbors[x2+i][y2+j][z2+k] = &temp.neighbors[1][1][1]->children[Cube::CornerIndex(i,j,k)]; + + + // Set the neighbors from across the faces + i=x1<<1; + if( temp.neighbors[i][1][1] ) + { + if( !temp.neighbors[i][1][1]->children ) temp.neighbors[i][1][1]->initChildren(); + for( j=0 ; j<2 ; j++ ) for( k=0 ; k<2 ; k++ ) neighbors[d].neighbors[i][y2+j][z2+k] = &temp.neighbors[i][1][1]->children[Cube::CornerIndex(x2,j,k)]; + } + j=y1<<1; + if( temp.neighbors[1][j][1] ) + { + if( !temp.neighbors[1][j][1]->children ) temp.neighbors[1][j][1]->initChildren(); + for( i=0 ; i<2 ; i++ ) for( k=0 ; k<2 ; k++ ) neighbors[d].neighbors[x2+i][j][z2+k] = &temp.neighbors[1][j][1]->children[Cube::CornerIndex(i,y2,k)]; + } + k=z1<<1; + if( temp.neighbors[1][1][k] ) + { + if( !temp.neighbors[1][1][k]->children ) temp.neighbors[1][1][k]->initChildren(); + for( i=0 ; i<2 ; i++ ) for( j=0 ; j<2 ; j++ ) neighbors[d].neighbors[x2+i][y2+j][k] = &temp.neighbors[1][1][k]->children[Cube::CornerIndex(i,j,z2)]; + } + + // Set the neighbors from across the edges + i=x1<<1 , j=y1<<1; + if( temp.neighbors[i][j][1] ) + { + if( !temp.neighbors[i][j][1]->children ) temp.neighbors[i][j][1]->initChildren(); + for( k=0 ; k<2 ; k++ ) neighbors[d].neighbors[i][j][z2+k] = &temp.neighbors[i][j][1]->children[Cube::CornerIndex(x2,y2,k)]; + } + i=x1<<1 , k=z1<<1; + if( temp.neighbors[i][1][k] ) + { + if( !temp.neighbors[i][1][k]->children ) temp.neighbors[i][1][k]->initChildren(); + for( j=0 ; j<2 ; j++ ) neighbors[d].neighbors[i][y2+j][k] = &temp.neighbors[i][1][k]->children[Cube::CornerIndex(x2,j,z2)]; + } + j=y1<<1 , k=z1<<1; + if( temp.neighbors[1][j][k] ) + { + if( !temp.neighbors[1][j][k]->children ) temp.neighbors[1][j][k]->initChildren(); + for( i=0 ; i<2 ; i++ ) neighbors[d].neighbors[x2+i][j][k] = &temp.neighbors[1][j][k]->children[Cube::CornerIndex(i,y2,z2)]; + } + + // Set the neighbor from across the corner + i=x1<<1 , j=y1<<1 , k=z1<<1; + if( temp.neighbors[i][j][k] ) + { + if( !temp.neighbors[i][j][k]->children ) temp.neighbors[i][j][k]->initChildren(); + neighbors[d].neighbors[i][j][k] = &temp.neighbors[i][j][k]->children[Cube::CornerIndex(x2,y2,z2)]; + } + } + } + return neighbors[d]; +} +template< class NodeData > +template< class Real > +typename OctNode< NodeData >::Neighbors3& OctNode< NodeData >::NeighborKey3::getNeighbors( OctNode< NodeData >* root , Point3D< Real > p , int d ) +{ + if( !neighbors[d].neighbors[1][1][1] || !neighbors[d].neighbors[1][1][1]->isInside( p ) ) + { + neighbors[d].clear(); + + if( !d ) neighbors[d].neighbors[1][1][1] = root; + else + { + Neighbors3& temp = getNeighbors( root , p , d-1 ); + + int i , j , k , x1 , y1 , z1 , x2 , y2 , z2; + Point3D< Real > c; + Real w; + temp.neighbors[1][1][1]->centerAndWidth( c , w ); + int idx = CornerIndex( c , p ); + Cube::FactorCornerIndex( idx , x1 , y1 , z1 ); + Cube::FactorCornerIndex( (~idx)&7 , x2 , y2 , z2 ); + + if( !temp.neighbors[1][1][1] || !temp.neighbors[1][1][1]->children ) + { + fprintf( stderr , "[ERROR] Couldn't find node at appropriate depth\n" ); + exit( 0 ); + } + for( i=0 ; i<2 ; i++ ) for( j=0 ; j<2 ; j++ ) for( k=0 ; k<2 ; k++ ) + neighbors[d].neighbors[x2+i][y2+j][z2+k] = &temp.neighbors[1][1][1]->children[Cube::CornerIndex(i,j,k)]; + + + // Set the neighbors from across the faces + i=x1<<1; + if( temp.neighbors[i][1][1] && temp.neighbors[i][1][1]->children ) + for( j=0 ; j<2 ; j++ ) for( k=0 ; k<2 ; k++ ) neighbors[d].neighbors[i][y2+j][z2+k] = &temp.neighbors[i][1][1]->children[Cube::CornerIndex(x2,j,k)]; + j=y1<<1; + if( temp.neighbors[1][j][1] && temp.neighbors[1][j][1]->children ) + for( i=0 ; i<2 ; i++ ) for( k=0 ; k<2 ; k++ ) neighbors[d].neighbors[x2+i][j][z2+k] = &temp.neighbors[1][j][1]->children[Cube::CornerIndex(i,y2,k)]; + k=z1<<1; + if( temp.neighbors[1][1][k] && temp.neighbors[1][1][k]->children ) + for( i=0 ; i<2 ; i++ ) for( j=0 ; j<2 ; j++ ) neighbors[d].neighbors[x2+i][y2+j][k] = &temp.neighbors[1][1][k]->children[Cube::CornerIndex(i,j,z2)]; + + // Set the neighbors from across the edges + i=x1<<1 , j=y1<<1; + if( temp.neighbors[i][j][1] && temp.neighbors[i][j][1]->children ) + for( k=0 ; k<2 ; k++ ) neighbors[d].neighbors[i][j][z2+k] = &temp.neighbors[i][j][1]->children[Cube::CornerIndex(x2,y2,k)]; + i=x1<<1 , k=z1<<1; + if( temp.neighbors[i][1][k] && temp.neighbors[i][1][k]->children ) + for( j=0 ; j<2 ; j++ ) neighbors[d].neighbors[i][y2+j][k] = &temp.neighbors[i][1][k]->children[Cube::CornerIndex(x2,j,z2)]; + j=y1<<1 , k=z1<<1; + if( temp.neighbors[1][j][k] && temp.neighbors[1][j][k]->children ) + for( i=0 ; i<2 ; i++ ) neighbors[d].neighbors[x2+i][j][k] = &temp.neighbors[1][j][k]->children[Cube::CornerIndex(i,y2,z2)]; + + // Set the neighbor from across the corner + i=x1<<1 , j=y1<<1 , k=z1<<1; + if( temp.neighbors[i][j][k] && temp.neighbors[i][j][k]->children ) + neighbors[d].neighbors[i][j][k] = &temp.neighbors[i][j][k]->children[Cube::CornerIndex(x2,y2,z2)]; + } + } + return neighbors[d]; +} + +template< class NodeData > +typename OctNode< NodeData >::Neighbors3& OctNode< NodeData >::NeighborKey3::setNeighbors( OctNode< NodeData >* node ) +{ + int d = node->depth(); + if( node==neighbors[d].neighbors[1][1][1] ) + { + bool reset = false; + for( int i=0 ; i<3 ; i++ ) for( int j=0 ; j<3 ; j++ ) for( int k=0 ; k<3 ; k++ ) if( !neighbors[d].neighbors[i][j][k] ) reset = true; + if( reset ) neighbors[d].neighbors[1][1][1] = NULL; + } + if( node!=neighbors[d].neighbors[1][1][1] ) + { + neighbors[d].clear(); + + if( !node->parent ) neighbors[d].neighbors[1][1][1] = node; + else + { + int i,j,k,x1,y1,z1,x2,y2,z2; + int idx=int(node-node->parent->children); + Cube::FactorCornerIndex( idx ,x1,y1,z1); + Cube::FactorCornerIndex((~idx)&7,x2,y2,z2); + for(i=0;i<2;i++){ + for(j=0;j<2;j++){ + for(k=0;k<2;k++){ + neighbors[d].neighbors[x2+i][y2+j][z2+k]=&node->parent->children[Cube::CornerIndex(i,j,k)]; + } + } + } + Neighbors3& temp=setNeighbors(node->parent); + + // Set the neighbors from across the faces + i=x1<<1; + if(temp.neighbors[i][1][1]){ + if(!temp.neighbors[i][1][1]->children) temp.neighbors[i][1][1]->initChildren(); + for(j=0;j<2;j++) for(k=0;k<2;k++) neighbors[d].neighbors[i][y2+j][z2+k]=&temp.neighbors[i][1][1]->children[Cube::CornerIndex(x2,j,k)]; + } + j=y1<<1; + if(temp.neighbors[1][j][1]){ + if(!temp.neighbors[1][j][1]->children){temp.neighbors[1][j][1]->initChildren();} + for(i=0;i<2;i++){for(k=0;k<2;k++){neighbors[d].neighbors[x2+i][j][z2+k]=&temp.neighbors[1][j][1]->children[Cube::CornerIndex(i,y2,k)];}} + } + k=z1<<1; + if(temp.neighbors[1][1][k]){ + if(!temp.neighbors[1][1][k]->children){temp.neighbors[1][1][k]->initChildren();} + for(i=0;i<2;i++){for(j=0;j<2;j++){neighbors[d].neighbors[x2+i][y2+j][k]=&temp.neighbors[1][1][k]->children[Cube::CornerIndex(i,j,z2)];}} + } + + // Set the neighbors from across the edges + i=x1<<1; j=y1<<1; + if(temp.neighbors[i][j][1]){ + if(!temp.neighbors[i][j][1]->children){temp.neighbors[i][j][1]->initChildren();} + for(k=0;k<2;k++){neighbors[d].neighbors[i][j][z2+k]=&temp.neighbors[i][j][1]->children[Cube::CornerIndex(x2,y2,k)];} + } + i=x1<<1; k=z1<<1; + if(temp.neighbors[i][1][k]){ + if(!temp.neighbors[i][1][k]->children){temp.neighbors[i][1][k]->initChildren();} + for(j=0;j<2;j++){neighbors[d].neighbors[i][y2+j][k]=&temp.neighbors[i][1][k]->children[Cube::CornerIndex(x2,j,z2)];} + } + j=y1<<1; k=z1<<1; + if(temp.neighbors[1][j][k]){ + if(!temp.neighbors[1][j][k]->children){temp.neighbors[1][j][k]->initChildren();} + for(i=0;i<2;i++){neighbors[d].neighbors[x2+i][j][k]=&temp.neighbors[1][j][k]->children[Cube::CornerIndex(i,y2,z2)];} + } + + // Set the neighbor from across the corner + i=x1<<1; j=y1<<1; k=z1<<1; + if(temp.neighbors[i][j][k]){ + if(!temp.neighbors[i][j][k]->children){temp.neighbors[i][j][k]->initChildren();} + neighbors[d].neighbors[i][j][k]=&temp.neighbors[i][j][k]->children[Cube::CornerIndex(x2,y2,z2)]; + } + } + } + return neighbors[d]; +} +// Note the assumption is that if you enable an edge, you also enable adjacent faces. +// And, if you enable a corner, you enable adjacent edges and faces. +template< class NodeData > +typename OctNode< NodeData >::Neighbors3& OctNode< NodeData >::NeighborKey3::setNeighbors( OctNode< NodeData >* node , bool flags[3][3][3] ) +{ + int d = node->depth(); + if( node==neighbors[d].neighbors[1][1][1] ) + { + bool reset = false; + for( int i=0 ; i<3 ; i++ ) for( int j=0 ; j<3 ; j++ ) for( int k=0 ; k<3 ; k++ ) if( flags[i][j][k] && !neighbors[d].neighbors[i][j][k] ) reset = true; + if( reset ) neighbors[d].neighbors[1][1][1] = NULL; + } + if( node!=neighbors[d].neighbors[1][1][1] ) + { + neighbors[d].clear(); + + if( !node->parent ) neighbors[d].neighbors[1][1][1] = node; + else + { + int x1,y1,z1,x2,y2,z2; + int idx=int(node-node->parent->children); + Cube::FactorCornerIndex( idx ,x1,y1,z1); + Cube::FactorCornerIndex((~idx)&7,x2,y2,z2); + for( int i=0 ; i<2 ; i++ ) + for( int j=0 ; j<2 ; j++ ) + for( int k=0 ; k<2 ; k++ ) + neighbors[d].neighbors[x2+i][y2+j][z2+k]=&node->parent->children[Cube::CornerIndex(i,j,k)]; + + Neighbors3& temp=setNeighbors( node->parent , flags ); + + // Set the neighbors from across the faces + { + int i=x1<<1; + if( temp.neighbors[i][1][1] ) + { + if( flags[i][1][1] && !temp.neighbors[i][1][1]->children ) temp.neighbors[i][1][1]->initChildren(); + if( temp.neighbors[i][1][1]->children ) for( int j=0 ; j<2 ; j++ ) for( int k=0 ; k<2 ; k++ ) neighbors[d].neighbors[i][y2+j][z2+k] = &temp.neighbors[i][1][1]->children[Cube::CornerIndex(x2,j,k)]; + } + } + { + int j = y1<<1; + if( temp.neighbors[1][j][1] ) + { + if( flags[1][j][1] && !temp.neighbors[1][j][1]->children ) temp.neighbors[1][j][1]->initChildren(); + if( temp.neighbors[1][j][1]->children ) for( int i=0 ; i<2 ; i++ ) for( int k=0 ; k<2 ; k++ ) neighbors[d].neighbors[x2+i][j][z2+k] = &temp.neighbors[1][j][1]->children[Cube::CornerIndex(i,y2,k)]; + } + } + { + int k = z1<<1; + if( temp.neighbors[1][1][k] ) + { + if( flags[1][1][k] && !temp.neighbors[1][1][k]->children ) temp.neighbors[1][1][k]->initChildren(); + if( temp.neighbors[1][1][k]->children ) for( int i=0 ; i<2 ; i++ ) for( int j=0 ; j<2 ; j++ ) neighbors[d].neighbors[x2+i][y2+j][k] = &temp.neighbors[1][1][k]->children[Cube::CornerIndex(i,j,z2)]; + } + } + + // Set the neighbors from across the edges + { + int i=x1<<1 , j=y1<<1; + if( temp.neighbors[i][j][1] ) + { + if( flags[i][j][1] && !temp.neighbors[i][j][1]->children ) temp.neighbors[i][j][1]->initChildren(); + if( temp.neighbors[i][j][1]->children ) for( int k=0 ; k<2 ; k++ ) neighbors[d].neighbors[i][j][z2+k] = &temp.neighbors[i][j][1]->children[Cube::CornerIndex(x2,y2,k)]; + } + } + { + int i=x1<<1 , k=z1<<1; + if( temp.neighbors[i][1][k] ) + { + if( flags[i][1][k] && !temp.neighbors[i][1][k]->children ) temp.neighbors[i][1][k]->initChildren(); + if( temp.neighbors[i][1][k]->children ) for( int j=0 ; j<2 ; j++ ) neighbors[d].neighbors[i][y2+j][k] = &temp.neighbors[i][1][k]->children[Cube::CornerIndex(x2,j,z2)]; + } + } + { + int j=y1<<1 , k=z1<<1; + if( temp.neighbors[1][j][k] ) + { + if( flags[1][j][k] && !temp.neighbors[1][j][k]->children ) temp.neighbors[1][j][k]->initChildren(); + if( temp.neighbors[1][j][k]->children ) for( int i=0 ; i<2 ; i++ ) neighbors[d].neighbors[x2+i][j][k] = &temp.neighbors[1][j][k]->children[Cube::CornerIndex(i,y2,z2)]; + } + } + + // Set the neighbor from across the corner + { + int i=x1<<1 , j=y1<<1 , k=z1<<1; + if( temp.neighbors[i][j][k] ) + { + if( flags[i][j][k] && !temp.neighbors[i][j][k]->children ) temp.neighbors[i][j][k]->initChildren(); + if( temp.neighbors[i][j][k]->children ) neighbors[d].neighbors[i][j][k] = &temp.neighbors[i][j][k]->children[Cube::CornerIndex(x2,y2,z2)]; + } + } + } + } + return neighbors[d]; +} + +template< class NodeData > +typename OctNode< NodeData >::Neighbors3& OctNode< NodeData >::NeighborKey3::getNeighbors( OctNode< NodeData >* node ) +{ + int d=node->depth(); + if(node!=neighbors[d].neighbors[1][1][1]) + { + neighbors[d].clear(); + + if( !node->parent ) neighbors[d].neighbors[1][1][1] = node; + else + { + int i,j,k,x1,y1,z1,x2,y2,z2; + int idx=int(node-node->parent->children); + Cube::FactorCornerIndex( idx ,x1,y1,z1); + Cube::FactorCornerIndex((~idx)&7,x2,y2,z2); + for( i=0 ; i<2 ; i++ ) for( j=0 ; j<2 ; j++ ) for( k=0 ; k<2 ; k++ ) + neighbors[d].neighbors[x2+i][y2+j][z2+k] = node->parent->children + Cube::CornerIndex(i,j,k); + + Neighbors3& temp=getNeighbors(node->parent); + + // Set the neighbors from across the faces + i=x1<<1; + if( temp.neighbors[i][1][1] && temp.neighbors[i][1][1]->children ) + for( j=0 ; j<2 ; j++ ) for( k=0 ; k<2 ; k++ ) neighbors[d].neighbors[i][y2+j][z2+k] = temp.neighbors[i][1][1]->children + Cube::CornerIndex(x2,j,k); + j=y1<<1; + if( temp.neighbors[1][j][1] && temp.neighbors[1][j][1]->children ) + for( i=0 ; i<2 ; i++ ) for( k=0 ; k<2 ; k++ ) neighbors[d].neighbors[x2+i][j][z2+k] = temp.neighbors[1][j][1]->children + Cube::CornerIndex(i,y2,k); + k=z1<<1; + if( temp.neighbors[1][1][k] && temp.neighbors[1][1][k]->children ) + for( i=0 ; i<2 ; i++ ) for( j=0 ; j<2 ; j++) neighbors[d].neighbors[x2+i][y2+j][k] = temp.neighbors[1][1][k]->children + Cube::CornerIndex(i,j,z2); + + // Set the neighbors from across the edges + i=x1<<1; j=y1<<1; + if( temp.neighbors[i][j][1] && temp.neighbors[i][j][1]->children ) + for( k=0 ; k<2 ; k++ ) neighbors[d].neighbors[i][j][z2+k] = temp.neighbors[i][j][1]->children + Cube::CornerIndex(x2,y2,k); + i=x1<<1; k=z1<<1; + if( temp.neighbors[i][1][k] && temp.neighbors[i][1][k]->children ) + for( j=0 ; j<2 ; j++ ) neighbors[d].neighbors[i][y2+j][k] = temp.neighbors[i][1][k]->children + Cube::CornerIndex(x2,j,z2); + j=y1<<1; k=z1<<1; + if( temp.neighbors[1][j][k] && temp.neighbors[1][j][k]->children ) + for( i=0 ; i<2 ; i++ ) neighbors[d].neighbors[x2+i][j][k] = temp.neighbors[1][j][k]->children + Cube::CornerIndex(i,y2,z2); + + // Set the neighbor from across the corner + i=x1<<1; j=y1<<1; k=z1<<1; + if( temp.neighbors[i][j][k] && temp.neighbors[i][j][k]->children ) + neighbors[d].neighbors[i][j][k] = temp.neighbors[i][j][k]->children + Cube::CornerIndex(x2,y2,z2); + } + } + return neighbors[node->depth()]; +} +template< class NodeData > +void OctNode< NodeData >::NeighborKey3::setNeighbors( OctNode< NodeData >* node , typename OctNode< NodeData >::Neighbors5& neighbors ) +{ + neighbors.clear(); + if( !node ) return; + if( !node->parent ) neighbors.neighbors[2][2][2] = node; + else + { + int c = int( node - node->parent->children ); + const OctNode< NodeData >::Neighbors3& _neighbors = setNeighbors( node->parent ); + switch( c ) + { + case 0: + for( int i=0 ; i<3 ; i++ ) for( int j=0 ; j<3 ; j++ ) for( int k=0 ; k<3 ; k++ ) + if( _neighbors.neighbors[i][j][k] ) + { + if( !_neighbors.neighbors[i][j][k]->children ) _neighbors.neighbors[i][j][k]->initChildren(); + for( int ii=0 ; ii<( (i==2) ? 1 : 2 ) ; ii++ ) + for( int jj=0 ; jj<( (j==2) ? 1 : 2 ) ; jj++ ) + for( int kk=0 ; kk<( (k==2) ? 1 : 2 ) ; kk++ ) + neighbors.neighbors[2*i+ii][2*j+jj][2*k+kk] = _neighbors.neighbors[i][j][k]->children + Cube::CornerIndex( ii , jj , kk ); + } + break; + case 1: + for( int i=0 ; i<3 ; i++ ) for( int j=0 ; j<3 ; j++ ) for( int k=0 ; k<3 ; k++ ) + if( _neighbors.neighbors[i][j][k] ) + { + if( !_neighbors.neighbors[i][j][k]->children ) _neighbors.neighbors[i][j][k]->initChildren(); + for( int ii=( (i==0) ? 1 : 0 ) ; ii<2 ; ii++ ) + for( int jj=0 ; jj<( (j==2) ? 1 : 2 ) ; jj++ ) + for( int kk=0 ; kk<( (k==2) ? 1 : 2 ) ; kk++ ) + neighbors.neighbors[2*i+ii-1][2*j+jj][2*k+kk] = _neighbors.neighbors[i][j][k]->children + Cube::CornerIndex( ii , jj , kk ); + } + break; + case 2: + for( int i=0 ; i<3 ; i++ ) for( int j=0 ; j<3 ; j++ ) for( int k=0 ; k<3 ; k++ ) + if( _neighbors.neighbors[i][j][k] ) + { + if( !_neighbors.neighbors[i][j][k]->children ) _neighbors.neighbors[i][j][k]->initChildren(); + for( int ii=0 ; ii<( (i==2) ? 1 : 2 ) ; ii++ ) + for( int jj=( (j==0) ? 1 : 0 ) ; jj<2 ; jj++ ) + for( int kk=0 ; kk<( (k==2) ? 1 : 2 ) ; kk++ ) + neighbors.neighbors[2*i+ii][2*j+jj-1][2*k+kk] = _neighbors.neighbors[i][j][k]->children + Cube::CornerIndex( ii , jj , kk ); + } + break; + case 3: + for( int i=0 ; i<3 ; i++ ) for( int j=0 ; j<3 ; j++ ) for( int k=0 ; k<3 ; k++ ) + if( _neighbors.neighbors[i][j][k] ) + { + if( !_neighbors.neighbors[i][j][k]->children ) _neighbors.neighbors[i][j][k]->initChildren(); + for( int ii=( (i==0) ? 1 : 0 ) ; ii<2 ; ii++ ) + for( int jj=( (j==0) ? 1 : 0 ) ; jj<2 ; jj++ ) + for( int kk=0 ; kk<( (k==2) ? 1 : 2 ) ; kk++ ) + neighbors.neighbors[2*i+ii-1][2*j+jj-1][2*k+kk] = _neighbors.neighbors[i][j][k]->children + Cube::CornerIndex( ii , jj , kk ); + } + break; + case 4: + for( int i=0 ; i<3 ; i++ ) for( int j=0 ; j<3 ; j++ ) for( int k=0 ; k<3 ; k++ ) + if( _neighbors.neighbors[i][j][k] ) + { + if( !_neighbors.neighbors[i][j][k]->children ) _neighbors.neighbors[i][j][k]->initChildren(); + for( int ii=0 ; ii<( (i==2) ? 1 : 2 ) ; ii++ ) + for( int jj=0 ; jj<( (j==2) ? 1 : 2 ) ; jj++ ) + for( int kk=( (k==0) ? 1 : 0 ) ; kk<2 ; kk++ ) + neighbors.neighbors[2*i+ii][2*j+jj][2*k+kk-1] = _neighbors.neighbors[i][j][k]->children + Cube::CornerIndex( ii , jj , kk ); + } + break; + case 5: + for( int i=0 ; i<3 ; i++ ) for( int j=0 ; j<3 ; j++ ) for( int k=0 ; k<3 ; k++ ) + if( _neighbors.neighbors[i][j][k] ) + { + if( !_neighbors.neighbors[i][j][k]->children ) _neighbors.neighbors[i][j][k]->initChildren(); + for( int ii=( (i==0) ? 1 : 0 ) ; ii<2 ; ii++ ) + for( int jj=0 ; jj<( (j==2) ? 1 : 2 ) ; jj++ ) + for( int kk=( (k==0) ? 1 : 0 ) ; kk<2 ; kk++ ) + neighbors.neighbors[2*i+ii-1][2*j+jj][2*k+kk-1] = _neighbors.neighbors[i][j][k]->children + Cube::CornerIndex( ii , jj , kk ); + } + break; + case 6: + for( int i=0 ; i<3 ; i++ ) for( int j=0 ; j<3 ; j++ ) for( int k=0 ; k<3 ; k++ ) + if( _neighbors.neighbors[i][j][k] ) + { + if( !_neighbors.neighbors[i][j][k]->children ) _neighbors.neighbors[i][j][k]->initChildren(); + for( int ii=0 ; ii<( (i==2) ? 1 : 2 ) ; ii++ ) + for( int jj=( (j==0) ? 1 : 0 ) ; jj<2 ; jj++ ) + for( int kk=( (k==0) ? 1 : 0 ) ; kk<2 ; kk++ ) + neighbors.neighbors[2*i+ii][2*j+jj-1][2*k+kk-1] = _neighbors.neighbors[i][j][k]->children + Cube::CornerIndex( ii , jj , kk ); + } + break; + case 7: + for( int i=0 ; i<3 ; i++ ) for( int j=0 ; j<3 ; j++ ) for( int k=0 ; k<3 ; k++ ) + if( _neighbors.neighbors[i][j][k] ) + { + if( !_neighbors.neighbors[i][j][k]->children ) _neighbors.neighbors[i][j][k]->initChildren(); + for( int ii=( (i==0) ? 1 : 0 ) ; ii<2 ; ii++ ) + for( int jj=( (j==0) ? 1 : 0 ) ; jj<2 ; jj++ ) + for( int kk=( (k==0) ? 1 : 0 ) ; kk<2 ; kk++ ) + neighbors.neighbors[2*i+ii-1][2*j+jj-1][2*k+kk-1] = _neighbors.neighbors[i][j][k]->children + Cube::CornerIndex( ii , jj , kk ); + } + break; + } + } +} +template< class NodeData > +void OctNode< NodeData >::NeighborKey3::getNeighbors( OctNode< NodeData >* node , typename OctNode< NodeData >::Neighbors5& neighbors ) +{ + neighbors.clear(); + if( !node ) return; + if( !node->parent ) neighbors.neighbors[2][2][2] = node; + else + { + int c = int( node - node->parent->children ); + const OctNode< NodeData >::Neighbors3& _neighbors = getNeighbors( node->parent ); + OctNode< NodeData >* const * _nodes = &_neighbors.neighbors[0][0][0]; + const OctNode< NodeData >* const * _node; + const OctNode< NodeData >* __node; + int iS , iE , jS , jE , kS , kE; +#define _S( i ) ( (i==0) ? 1 : 0 ) +#define _E( i ) ( (i==2) ? 1 : 2 ) + switch( c ) + { + case 0: + _node = _nodes; + for( int i=0 ; i<3 ; i++ ){ iE=_E(i) ; for( int j=0 ; j<3 ; j++ ){ jE=_E(j) ; for( int k=0 ; k<3 ; k++ , _node++ ){ kE=_E(k) , __node = *_node; + if( __node && __node->children ) for( int ii=0 , iii=2*i ; iichildren + Cube::CornerIndex( ii , jj , kk ); + } } } + break; + case 1: + _node = _nodes; + for( int i=0 ; i<3 ; i++ ){ iS=_S(i) ; for( int j=0 ; j<3 ; j++ ){ jE=_E(j) ; for( int k=0 ; k<3 ; k++ , _node++ ){ kE=_E(k) , __node = *_node; + if( __node && __node->children ) for( int ii=iS , iii=2*i+iS-1 ; ii<2 ; ii++ , iii++ ) for( int jj=0 , jjj=2*j ; jjchildren + Cube::CornerIndex( ii , jj , kk ); + } } } + break; + case 2: + _node = _nodes; + for( int i=0 ; i<3 ; i++ ){ iE=_E(i) ; for( int j=0 ; j<3 ; j++ ){ jS=_S(j) ; for( int k=0 ; k<3 ; k++ , _node++ ){ kE=_E(k) , __node = *_node; + if( __node && __node->children ) for( int ii=0 , iii=2*i ; iichildren + Cube::CornerIndex( ii , jj , kk ); + } } } + break; + case 3: + _node = _nodes; + for( int i=0 ; i<3 ; i++ ){ iS=_S(i) ; for( int j=0 ; j<3 ; j++ ){ jS=_S(j) ; for( int k=0 ; k<3 ; k++ , _node++ ){ kE=_E(k) , __node = *_node; + if( __node && __node->children ) for( int ii=iS , iii=2*i+iS-1 ; ii<2 ; ii++ , iii++ ) for( int jj=jS , jjj=2*j+jS-1 ; jj<2 ; jj++ , jjj++ ) for( int kk=0 , kkk=2*k ; kkchildren + Cube::CornerIndex( ii , jj , kk ); + } } } + break; + case 4: + _node = _nodes; + for( int i=0 ; i<3 ; i++ ){ iE=_E(i) ; for( int j=0 ; j<3 ; j++ ){ jE=_E(j) ; for( int k=0 ; k<3 ; k++ , _node++ ){ kS=_S(k) , __node = *_node; + if( __node && __node->children ) for( int ii=0 , iii=2*i ; iichildren + Cube::CornerIndex( ii , jj , kk ); + } } } + break; + case 5: + _node = _nodes; + for( int i=0 ; i<3 ; i++ ){ iS=_S(i) ; for( int j=0 ; j<3 ; j++ ){ jE=_E(j) ; for( int k=0 ; k<3 ; k++ , _node++ ){ kS=_S(k) , __node = *_node; + if( __node && __node->children ) for( int ii=iS , iii=2*i+iS-1 ; ii<2 ; ii++ , iii++ ) for( int jj=0 , jjj=2*j ; jjchildren + Cube::CornerIndex( ii , jj , kk ); + } } } + break; + case 6: + _node = _nodes; + for( int i=0 ; i<3 ; i++ ){ iE=_E(i) ; for( int j=0 ; j<3 ; j++ ){ jS=_S(j) ; for( int k=0 ; k<3 ; k++ , _node++ ){ kS=_S(k) , __node = *_node; + if( __node && __node->children ) for( int ii=0 , iii=2*i ; iichildren + Cube::CornerIndex( ii , jj , kk ); + } } } + break; + case 7: + _node = _nodes; + for( int i=0 ; i<3 ; i++ ){ iS=_S(i) ; for( int j=0 ; j<3 ; j++ ){ jS=_S(j) ; for( int k=0 ; k<3 ; k++ , _node++ ){ kS=_S(k) , __node = *_node; + if( __node && __node->children ) for( int ii=iS , iii=2*i+iS-1 ; ii<2 ; ii++ , iii++ ) for( int jj=jS , jjj=2*j+jS-1 ; jj<2 ; jj++ , jjj++ ) for( int kk=kS , kkk=2*k+kS-1 ; kk<2 ; kk++ , kkk++ ) + neighbors.neighbors[iii][jjj][kkk] = __node->children + Cube::CornerIndex( ii , jj , kk ); + } } } + break; + } +#undef _S +#undef _E + } +} +template< class NodeData > +void OctNode< NodeData >::ConstNeighborKey3::getNeighbors( const OctNode< NodeData >* node , typename OctNode< NodeData >::ConstNeighbors5& neighbors ) +{ + neighbors.clear(); + if( !node ) return; + if( !node->parent ) neighbors.neighbors[2][2][2] = node; + else + { + int c = int( node - node->parent->children ); + const OctNode< NodeData >::ConstNeighbors3& _neighbors = getNeighbors( node->parent ); + switch( c ) + { + case 0: + for( int i=0 ; i<3 ; i++ ) for( int j=0 ; j<3 ; j++ ) for( int k=0 ; k<3 ; k++ ) + if( _neighbors.neighbors[i][j][k] && _neighbors.neighbors[i][j][k]->children ) + for( int ii=0 ; ii<( (i==2) ? 1 : 2 ) ; ii++ ) + for( int jj=0 ; jj<( (j==2) ? 1 : 2 ) ; jj++ ) + for( int kk=0 ; kk<( (k==2) ? 1 : 2 ) ; kk++ ) + neighbors.neighbors[2*i+ii][2*j+jj][2*k+kk] = _neighbors.neighbors[i][j][k]->children + Cube::CornerIndex( ii , jj , kk ); + break; + case 1: + for( int i=0 ; i<3 ; i++ ) for( int j=0 ; j<3 ; j++ ) for( int k=0 ; k<3 ; k++ ) + if( _neighbors.neighbors[i][j][k] && _neighbors.neighbors[i][j][k]->children ) + for( int ii=( (i==0) ? 1 : 0 ) ; ii<2 ; ii++ ) + for( int jj=0 ; jj<( (j==2) ? 1 : 2 ) ; jj++ ) + for( int kk=0 ; kk<( (k==2) ? 1 : 2 ) ; kk++ ) + neighbors.neighbors[2*i+ii-1][2*j+jj][2*k+kk] = _neighbors.neighbors[i][j][k]->children + Cube::CornerIndex( ii , jj , kk ); + break; + case 2: + for( int i=0 ; i<3 ; i++ ) for( int j=0 ; j<3 ; j++ ) for( int k=0 ; k<3 ; k++ ) + if( _neighbors.neighbors[i][j][k] && _neighbors.neighbors[i][j][k]->children ) + for( int ii=0 ; ii<( (i==2) ? 1 : 2 ) ; ii++ ) + for( int jj=( (j==0) ? 1 : 0 ) ; jj<2 ; jj++ ) + for( int kk=0 ; kk<( (k==2) ? 1 : 2 ) ; kk++ ) + neighbors.neighbors[2*i+ii][2*j+jj-1][2*k+kk] = _neighbors.neighbors[i][j][k]->children + Cube::CornerIndex( ii , jj , kk ); + break; + case 3: + for( int i=0 ; i<3 ; i++ ) for( int j=0 ; j<3 ; j++ ) for( int k=0 ; k<3 ; k++ ) + if( _neighbors.neighbors[i][j][k] && _neighbors.neighbors[i][j][k]->children ) + for( int ii=( (i==0) ? 1 : 0 ) ; ii<2 ; ii++ ) + for( int jj=( (j==0) ? 1 : 0 ) ; jj<2 ; jj++ ) + for( int kk=0 ; kk<( (k==2) ? 1 : 2 ) ; kk++ ) + neighbors.neighbors[2*i+ii-1][2*j+jj-1][2*k+kk] = _neighbors.neighbors[i][j][k]->children + Cube::CornerIndex( ii , jj , kk ); + break; + case 4: + for( int i=0 ; i<3 ; i++ ) for( int j=0 ; j<3 ; j++ ) for( int k=0 ; k<3 ; k++ ) + if( _neighbors.neighbors[i][j][k] && _neighbors.neighbors[i][j][k]->children ) + for( int ii=0 ; ii<( (i==2) ? 1 : 2 ) ; ii++ ) + for( int jj=0 ; jj<( (j==2) ? 1 : 2 ) ; jj++ ) + for( int kk=( (k==0) ? 1 : 0 ) ; kk<2 ; kk++ ) + neighbors.neighbors[2*i+ii][2*j+jj][2*k+kk-1] = _neighbors.neighbors[i][j][k]->children + Cube::CornerIndex( ii , jj , kk ); + break; + case 5: + for( int i=0 ; i<3 ; i++ ) for( int j=0 ; j<3 ; j++ ) for( int k=0 ; k<3 ; k++ ) + if( _neighbors.neighbors[i][j][k] && _neighbors.neighbors[i][j][k]->children ) + for( int ii=( (i==0) ? 1 : 0 ) ; ii<2 ; ii++ ) + for( int jj=0 ; jj<( (j==2) ? 1 : 2 ) ; jj++ ) + for( int kk=( (k==0) ? 1 : 0 ) ; kk<2 ; kk++ ) + neighbors.neighbors[2*i+ii-1][2*j+jj][2*k+kk-1] = _neighbors.neighbors[i][j][k]->children + Cube::CornerIndex( ii , jj , kk ); + break; + case 6: + for( int i=0 ; i<3 ; i++ ) for( int j=0 ; j<3 ; j++ ) for( int k=0 ; k<3 ; k++ ) + if( _neighbors.neighbors[i][j][k] && _neighbors.neighbors[i][j][k]->children ) + for( int ii=0 ; ii<( (i==2) ? 1 : 2 ) ; ii++ ) + for( int jj=( (j==0) ? 1 : 0 ) ; jj<2 ; jj++ ) + for( int kk=( (k==0) ? 1 : 0 ) ; kk<2 ; kk++ ) + neighbors.neighbors[2*i+ii][2*j+jj-1][2*k+kk-1] = _neighbors.neighbors[i][j][k]->children + Cube::CornerIndex( ii , jj , kk ); + break; + case 7: + for( int i=0 ; i<3 ; i++ ) for( int j=0 ; j<3 ; j++ ) for( int k=0 ; k<3 ; k++ ) + if( _neighbors.neighbors[i][j][k] && _neighbors.neighbors[i][j][k]->children ) + for( int ii=( (i==0) ? 1 : 0 ) ; ii<2 ; ii++ ) + for( int jj=( (j==0) ? 1 : 0 ) ; jj<2 ; jj++ ) + for( int kk=( (k==0) ? 1 : 0 ) ; kk<2 ; kk++ ) + neighbors.neighbors[2*i+ii-1][2*j+jj-1][2*k+kk-1] = _neighbors.neighbors[i][j][k]->children + Cube::CornerIndex( ii , jj , kk ); + break; + } + } +} + +/////////////////////// +// ConstNeighborKey3 // +/////////////////////// +template< class NodeData > +OctNode< NodeData >::ConstNeighbors3::ConstNeighbors3(void){clear();} +template< class NodeData > +void OctNode< NodeData >::ConstNeighbors3::clear(void){ + for(int i=0;i<3;i++){for(int j=0;j<3;j++){for(int k=0;k<3;k++){neighbors[i][j][k]=NULL;}}} +} +template< class NodeData > +OctNode< NodeData >::ConstNeighborKey3::ConstNeighborKey3( void ){ _depth=-1 , neighbors=NULL; } +template< class NodeData > +OctNode< NodeData >::ConstNeighborKey3::ConstNeighborKey3( const ConstNeighborKey3& key3 ) +{ + _depth = 0 , neighbors = NULL; + set( key3._depth ); + for( int d=0 ; d<=_depth ; d++ ) memcpy( &neighbors[d] , &key3.neighbors[d] , sizeof(ConstNeighbors3) ); +} +template< class NodeData > +OctNode< NodeData >::ConstNeighborKey3::~ConstNeighborKey3(void){ + if( neighbors ) delete[] neighbors; + neighbors=NULL; +} + +template< class NodeData > +void OctNode< NodeData >::ConstNeighborKey3::set( int d ) +{ + if( neighbors ) delete[] neighbors; + neighbors = NULL; + _depth = d; + if( d<0 ) return; + neighbors = new ConstNeighbors3[d+1]; +} +template< class NodeData > +typename OctNode< NodeData >::ConstNeighbors3& OctNode< NodeData >::ConstNeighborKey3::getNeighbors(const OctNode< NodeData >* node) +{ + int d=node->depth(); + if( node!=neighbors[d].neighbors[1][1][1] ) + { + neighbors[d].clear(); + + if(!node->parent) neighbors[d].neighbors[1][1][1]=node; + else + { + int i,j,k,x1,y1,z1,x2,y2,z2; + int idx=int(node-node->parent->children); + Cube::FactorCornerIndex( idx ,x1,y1,z1); + Cube::FactorCornerIndex((~idx)&7,x2,y2,z2); + for( i=0 ; i<2 ; i++ ) for( j=0 ; j<2 ; j++ ) for( k=0 ; k<2 ; k++ ) + neighbors[d].neighbors[x2+i][y2+j][z2+k] = node->parent->children + Cube::CornerIndex(i,j,k); + + ConstNeighbors3& temp=getNeighbors(node->parent); + + // Set the neighbors from across the faces + i=x1<<1; + if( temp.neighbors[i][1][1] && temp.neighbors[i][1][1]->children ) + for( j=0 ; j<2 ; j++ ) for( k=0 ; k<2 ; k++ ) neighbors[d].neighbors[i][y2+j][z2+k] = temp.neighbors[i][1][1]->children + Cube::CornerIndex(x2,j,k); + j=y1<<1; + if( temp.neighbors[1][j][1] && temp.neighbors[1][j][1]->children ) + for( i=0 ; i<2 ; i++ ) for( k=0 ; k<2 ; k++ ) neighbors[d].neighbors[x2+i][j][z2+k] = temp.neighbors[1][j][1]->children + Cube::CornerIndex(i,y2,k); + k=z1<<1; + if( temp.neighbors[1][1][k] && temp.neighbors[1][1][k]->children ) + for( i=0 ; i<2 ; i++ ) for( j=0 ; j<2 ; j++ ) neighbors[d].neighbors[x2+i][y2+j][k] = temp.neighbors[1][1][k]->children + Cube::CornerIndex(i,j,z2); + + // Set the neighbors from across the edges + i=x1<<1; j=y1<<1; + if( temp.neighbors[i][j][1] && temp.neighbors[i][j][1]->children ) + for( k=0 ; k<2 ; k++ ) neighbors[d].neighbors[i][j][z2+k] = temp.neighbors[i][j][1]->children + Cube::CornerIndex(x2,y2,k); + i=x1<<1; k=z1<<1; + if( temp.neighbors[i][1][k] && temp.neighbors[i][1][k]->children ) + for( j=0 ; j<2 ; j++ ) neighbors[d].neighbors[i][y2+j][k] = temp.neighbors[i][1][k]->children + Cube::CornerIndex(x2,j,z2); + j=y1<<1; k=z1<<1; + if( temp.neighbors[1][j][k] && temp.neighbors[1][j][k]->children ) + for( i=0 ; i<2 ; i++ ) neighbors[d].neighbors[x2+i][j][k] = temp.neighbors[1][j][k]->children + Cube::CornerIndex(i,y2,z2); + + // Set the neighbor from across the corner + i=x1<<1; j=y1<<1; k=z1<<1; + if( temp.neighbors[i][j][k] && temp.neighbors[i][j][k]->children ) + neighbors[d].neighbors[i][j][k] = temp.neighbors[i][j][k]->children + Cube::CornerIndex(x2,y2,z2); + } + } + return neighbors[node->depth()]; +} +template< class NodeData > +typename OctNode< NodeData >::ConstNeighbors3& OctNode< NodeData >::ConstNeighborKey3::getNeighbors( const OctNode< NodeData >* node , int minDepth ) +{ + int d=node->depth(); + if( dparent->children); + Cube::FactorCornerIndex( idx ,x1,y1,z1); + Cube::FactorCornerIndex((~idx)&7,x2,y2,z2); + + ConstNeighbors3& temp=getNeighbors( node->parent , minDepth ); + + // Set the syblings + for( i=0 ; i<2 ; i++ ) for( j=0 ; j<2 ; j++ ) for( k=0 ; k<2 ; k++ ) + neighbors[d].neighbors[x2+i][y2+j][z2+k] = node->parent->children + Cube::CornerIndex(i,j,k); + + // Set the neighbors from across the faces + i=x1<<1; + if( temp.neighbors[i][1][1] && temp.neighbors[i][1][1]->children ) + for( j=0 ; j<2 ; j++ ) for( k=0 ; k<2 ; k++ ) neighbors[d].neighbors[i][y2+j][z2+k] = temp.neighbors[i][1][1]->children + Cube::CornerIndex(x2,j,k); + + j=y1<<1; + if( temp.neighbors[1][j][1] && temp.neighbors[1][j][1]->children ) + for( i=0 ; i<2 ; i++ ) for( k=0 ; k<2 ; k++ ) neighbors[d].neighbors[x2+i][j][z2+k] = temp.neighbors[1][j][1]->children + Cube::CornerIndex(i,y2,k); + + k=z1<<1; + if( temp.neighbors[1][1][k] && temp.neighbors[1][1][k]->children ) + for( i=0 ; i<2 ; i++ ) for( j=0 ; j<2 ; j++ ) neighbors[d].neighbors[x2+i][y2+j][k] = temp.neighbors[1][1][k]->children + Cube::CornerIndex(i,j,z2); + + // Set the neighbors from across the edges + i=x1<<1 , j=y1<<1; + if( temp.neighbors[i][j][1] && temp.neighbors[i][j][1]->children ) + for( k=0 ; k<2 ; k++ ) neighbors[d].neighbors[i][j][z2+k] = temp.neighbors[i][j][1]->children + Cube::CornerIndex(x2,y2,k); + + i=x1<<1 , k=z1<<1; + if( temp.neighbors[i][1][k] && temp.neighbors[i][1][k]->children ) + for( j=0 ; j<2 ; j++ ) neighbors[d].neighbors[i][y2+j][k] = temp.neighbors[i][1][k]->children + Cube::CornerIndex(x2,j,z2); + + j=y1<<1 , k=z1<<1; + if( temp.neighbors[1][j][k] && temp.neighbors[1][j][k]->children ) + for( i=0 ; i<2 ; i++ ) neighbors[d].neighbors[x2+i][j][k] = temp.neighbors[1][j][k]->children + Cube::CornerIndex(i,y2,z2); + + // Set the neighbor from across the corner + i=x1<<1 , j=y1<<1 , k=z1<<1; + if( temp.neighbors[i][j][k] && temp.neighbors[i][j][k]->children ) + neighbors[d].neighbors[i][j][k] = temp.neighbors[i][j][k]->children + Cube::CornerIndex(x2,y2,z2); + } + } + return neighbors[node->depth()]; +} + +template< class NodeData > OctNode< NodeData >::Neighbors5::Neighbors5( void ){ clear(); } +template< class NodeData > OctNode< NodeData >::ConstNeighbors5::ConstNeighbors5( void ){ clear(); } +template< class NodeData > +void OctNode< NodeData >::Neighbors5::clear( void ) +{ + for( int i=0 ; i<5 ; i++ ) for( int j=0 ; j<5 ; j++ ) for( int k=0 ; k<5 ; k++ ) neighbors[i][j][k] = NULL; +} +template< class NodeData > +void OctNode< NodeData >::ConstNeighbors5::clear( void ) +{ + for( int i=0 ; i<5 ; i++ ) for( int j=0 ; j<5 ; j++ ) for( int k=0 ; k<5 ; k++ ) neighbors[i][j][k] = NULL; +} +template< class NodeData > +OctNode< NodeData >::NeighborKey5::NeighborKey5( void ) +{ + _depth = -1; + neighbors = NULL; +} +template< class NodeData > +OctNode< NodeData >::ConstNeighborKey5::ConstNeighborKey5( void ) +{ + _depth = -1; + neighbors = NULL; +} +template< class NodeData > +OctNode< NodeData >::NeighborKey5::~NeighborKey5( void ) +{ + if( neighbors ) delete[] neighbors; + neighbors = NULL; +} +template< class NodeData > +OctNode< NodeData >::ConstNeighborKey5::~ConstNeighborKey5( void ) +{ + if( neighbors ) delete[] neighbors; + neighbors = NULL; +} +template< class NodeData > +void OctNode< NodeData >::NeighborKey5::set( int d ) +{ + if( neighbors ) delete[] neighbors; + neighbors = NULL; + if(d<0) return; + _depth = d; + neighbors=new Neighbors5[d+1]; +} +template< class NodeData > +void OctNode< NodeData >::ConstNeighborKey5::set( int d ) +{ + if( neighbors ) delete[] neighbors; + neighbors = NULL; + if(d<0) return; + _depth = d; + neighbors=new ConstNeighbors5[d+1]; +} +template< class NodeData > +typename OctNode< NodeData >::Neighbors5& OctNode< NodeData >::NeighborKey5::getNeighbors( OctNode* node ) +{ + int d=node->depth(); + if( node!=neighbors[d].neighbors[2][2][2] ) + { + neighbors[d].clear(); + + if( !node->parent ) neighbors[d].neighbors[2][2][2]=node; + else + { + getNeighbors( node->parent ); + Neighbors5& temp = neighbors[d-1]; + int x1 , y1 , z1 , x2 , y2 , z2; + int idx = int( node - node->parent->children ); + Cube::FactorCornerIndex( idx , x1 , y1 , z1 ); + + Neighbors5& n = neighbors[d]; + Cube::FactorCornerIndex( (~idx)&7 , x2 , y2 , z2 ); + int i , j , k; + int fx0 = x2+1 , fy0 = y2+1 , fz0 = z2+1; // Indices of the bottom left corner of the parent within the 5x5x5 + int cx1 = x1*2+1 , cy1 = y1*2+1 , cz1 = z1*2+1; + int cx2 = x2*2+1 , cy2 = y2*2+1 , cz2 = z2*2+1; + int fx1 = x1*3 , fy1 = y1*3 , fz1 = z1*3; + int fx2 = x2*4 , fy2 = y2*4 , fz2 = z2*4; + + // Set the syblings + for( i=0 ; i<2 ; i++ ) for( j=0 ; j<2 ; j++ ) for( k=0 ; k<2 ; k++ ) + n.neighbors[fx0+i][fy0+j][fz0+k] = node->parent->children + Cube::CornerIndex( i , j , k ); + + // Set the neighbors from across the faces + if( temp.neighbors[cx1][2][2] && temp.neighbors[cx1][2][2]->children ) + for( i=0 ; i<2 ; i++ ) for( j=0 ; j<2 ; j++ ) for( k=0 ; k<2 ; k++ ) + n.neighbors[fx1+i][fy0+j][fz0+k] = temp.neighbors[cx1][2][2]->children + Cube::CornerIndex( i , j , k ); + if( temp.neighbors[2][cy1][2] && temp.neighbors[2][cy1][2]->children ) + for( i=0 ; i<2 ; i++ ) for( j=0 ; j<2 ; j++ ) for( k=0 ; k<2 ; k++ ) + n.neighbors[fx0+i][fy1+j][fz0+k] = temp.neighbors[2][cy1][2]->children + Cube::CornerIndex( i , j , k ); + if( temp.neighbors[2][2][cz1] && temp.neighbors[2][2][cz1]->children ) + for( i=0 ; i<2 ; i++ ) for( j=0 ; j<2 ; j++ ) for( k=0 ; k<2 ; k++ ) + n.neighbors[fx0+i][fy0+j][fz1+k] = temp.neighbors[2][2][cz1]->children + Cube::CornerIndex( i , j , k ); + if( temp.neighbors[cx2][2][2] && temp.neighbors[cx2][2][2]->children ) + for( j=0 ; j<2 ; j++ ) for( k=0 ; k<2 ; k++ ) + n.neighbors[fx2 ][fy0+j][fz0+k] = temp.neighbors[cx2][2][2]->children + Cube::CornerIndex( x1 , j , k ); + if( temp.neighbors[2][cy2][2] && temp.neighbors[2][cy2][2]->children ) + for( i=0 ; i<2 ; i++ ) for( k=0 ; k<2 ; k++ ) + n.neighbors[fx0+i][fy2 ][fz0+k] = temp.neighbors[2][cy2][2]->children + Cube::CornerIndex( i , y1 , k ); + if( temp.neighbors[2][2][cz2] && temp.neighbors[2][2][cz2]->children ) + for( i=0 ; i<2 ; i++ ) for( j=0 ; j<2 ; j++ ) + n.neighbors[fx0+i][fy0+j][fz2 ] = temp.neighbors[2][2][cz2]->children + Cube::CornerIndex( i , j , z1 ); + + // Set the neighbors from across the edges + if( temp.neighbors[cx1][cy1][2] && temp.neighbors[cx1][cy1][2]->children ) + for( i=0 ; i<2 ; i++ ) for( j=0 ; j<2 ; j++ ) for( k=0 ; k<2 ; k++ ) + n.neighbors[fx1+i][fy1+j][fz0+k] = temp.neighbors[cx1][cy1][2]->children + Cube::CornerIndex( i , j , k ); + if( temp.neighbors[cx1][2][cz1] && temp.neighbors[cx1][2][cz1]->children ) + for( i=0 ; i<2 ; i++ ) for( j=0 ; j<2 ; j++ ) for( k=0 ; k<2 ; k++ ) + n.neighbors[fx1+i][fy0+j][fz1+k] = temp.neighbors[cx1][2][cz1]->children + Cube::CornerIndex( i , j , k ); + if( temp.neighbors[2][cy1][cz1] && temp.neighbors[2][cy1][cz1]->children ) + for( i=0 ; i<2 ; i++ ) for( j=0 ; j<2 ; j++ ) for( k=0 ; k<2 ; k++ ) + n.neighbors[fx0+i][fy1+j][fz1+k] = temp.neighbors[2][cy1][cz1]->children + Cube::CornerIndex( i , j , k ); + if( temp.neighbors[cx1][cy2][2] && temp.neighbors[cx1][cy2][2]->children ) + for( i=0 ; i<2 ; i++ ) for( k=0 ; k<2 ; k++ ) + n.neighbors[fx1+i][fy2 ][fz0+k] = temp.neighbors[cx1][cy2][2]->children + Cube::CornerIndex( i , y1 , k ); + if( temp.neighbors[cx1][2][cz2] && temp.neighbors[cx1][2][cz2]->children ) + for( i=0 ; i<2 ; i++ ) for( j=0 ; j<2 ; j++ ) + n.neighbors[fx1+i][fy0+j][fz2 ] = temp.neighbors[cx1][2][cz2]->children + Cube::CornerIndex( i , j , z1 ); + if( temp.neighbors[cx2][cy1][2] && temp.neighbors[cx2][cy1][2]->children ) + for( j=0 ; j<2 ; j++ ) for( k=0 ; k<2 ; k++ ) + n.neighbors[fx2 ][fy1+j][fz0+k] = temp.neighbors[cx2][cy1][2]->children + Cube::CornerIndex( x1 , j , k ); + if( temp.neighbors[2][cy1][cz2] && temp.neighbors[2][cy1][cz2]->children ) + for( i=0 ; i<2 ; i++ ) for( j=0 ; j<2 ; j++ ) + n.neighbors[fx0+i][fy1+j][fz2 ] = temp.neighbors[2][cy1][cz2]->children + Cube::CornerIndex( i , j , z1 ); + if( temp.neighbors[cx2][2][cz1] && temp.neighbors[cx2][2][cz1]->children ) + for( j=0 ; j<2 ; j++ ) for( k=0 ; k<2 ; k++ ) + n.neighbors[fx2 ][fy0+j][fz1+k] = temp.neighbors[cx2][2][cz1]->children + Cube::CornerIndex( x1 , j , k ); + if( temp.neighbors[2][cy2][cz1] && temp.neighbors[2][cy2][cz1]->children ) + for( i=0 ; i<2 ; i++ ) for( k=0 ; k<2 ; k++ ) + n.neighbors[fx0+i][fy2 ][fz1+k] = temp.neighbors[2][cy2][cz1]->children + Cube::CornerIndex( i , y1 , k ); + if( temp.neighbors[cx2][cy2][2] && temp.neighbors[cx2][cy2][2]->children ) + for( k=0 ; k<2 ; k++ ) + n.neighbors[fx2 ][fy2 ][fz0+k] = temp.neighbors[cx2][cy2][2]->children + Cube::CornerIndex( x1 , y1 , k ); + if( temp.neighbors[cx2][2][cz2] && temp.neighbors[cx2][2][cz2]->children ) + for( j=0 ; j<2 ; j++ ) + n.neighbors[fx2 ][fy0+j][fz2 ] = temp.neighbors[cx2][2][cz2]->children + Cube::CornerIndex( x1 , j , z1 ); + if( temp.neighbors[2][cy2][cz2] && temp.neighbors[2][cy2][cz2]->children ) + for( i=0 ; i<2 ; i++ ) + n.neighbors[fx0+i][fy2 ][fz2 ] = temp.neighbors[2][cy2][cz2]->children + Cube::CornerIndex( i , y1 , z1 ); + + // Set the neighbor from across the corners + if( temp.neighbors[cx1][cy1][cz1] && temp.neighbors[cx1][cy1][cz1]->children ) + for( i=0 ; i<2 ; i++ ) for( j=0 ; j<2 ; j++ ) for( k=0 ; k<2 ; k++ ) + n.neighbors[fx1+i][fy1+j][fz1+k] = temp.neighbors[cx1][cy1][cz1]->children + Cube::CornerIndex( i , j , k ); + if( temp.neighbors[cx1][cy1][cz2] && temp.neighbors[cx1][cy1][cz2]->children ) + for( i=0 ; i<2 ; i++ ) for( j=0 ; j<2 ; j++ ) + n.neighbors[fx1+i][fy1+j][fz2 ] = temp.neighbors[cx1][cy1][cz2]->children + Cube::CornerIndex( i , j , z1 ); + if( temp.neighbors[cx1][cy2][cz1] && temp.neighbors[cx1][cy2][cz1]->children ) + for( i=0 ; i<2 ; i++ ) for( k=0 ; k<2 ; k++ ) + n.neighbors[fx1+i][fy2 ][fz1+k] = temp.neighbors[cx1][cy2][cz1]->children + Cube::CornerIndex( i , y1 , k ); + if( temp.neighbors[cx2][cy1][cz1] && temp.neighbors[cx2][cy1][cz1]->children ) + for( j=0 ; j<2 ; j++ ) for( k=0 ; k<2 ; k++ ) + n.neighbors[fx2 ][fy1+j][fz1+k] = temp.neighbors[cx2][cy1][cz1]->children + Cube::CornerIndex( x1 , j , k ); + if( temp.neighbors[cx1][cy2][cz2] && temp.neighbors[cx1][cy2][cz2]->children ) + for( i=0 ; i<2 ; i++ ) + n.neighbors[fx1+i][fy2 ][fz2 ] = temp.neighbors[cx1][cy2][cz2]->children + Cube::CornerIndex( i , y1 , z1 ); + if( temp.neighbors[cx2][cy1][cz2] && temp.neighbors[cx2][cy1][cz2]->children ) + for( j=0 ; j<2 ; j++ ) + n.neighbors[fx2 ][fy1+j][fz2 ] = temp.neighbors[cx2][cy1][cz2]->children + Cube::CornerIndex( x1 , j , z1 ); + if( temp.neighbors[cx2][cy2][cz1] && temp.neighbors[cx2][cy2][cz1]->children ) + for( k=0 ; k<2 ; k++ ) + n.neighbors[fx2 ][fy2 ][fz1+k] = temp.neighbors[cx2][cy2][cz1]->children + Cube::CornerIndex( x1 , y1 , k ); + if( temp.neighbors[cx2][cy2][cz2] && temp.neighbors[cx2][cy2][cz2]->children ) + n.neighbors[fx2 ][fy2 ][fz2 ] = temp.neighbors[cx2][cy2][cz2]->children + Cube::CornerIndex( x1 , y1 , z1 ); + } + } + return neighbors[d]; +} +template< class NodeData > +typename OctNode< NodeData >::Neighbors5& OctNode< NodeData >::NeighborKey5::setNeighbors( OctNode* node , int xStart , int xEnd , int yStart , int yEnd , int zStart , int zEnd ) +{ + int d=node->depth(); + if( node!=neighbors[d].neighbors[2][2][2] ) + { + neighbors[d].clear(); + + if( !node->parent ) neighbors[d].neighbors[2][2][2] = node; + else + { + setNeighbors( node->parent , xStart , xEnd , yStart , yEnd , zStart , zEnd ); + Neighbors5& temp = neighbors[d-1]; + int x1 , y1 , z1 , x2 , y2 , z2 , ii , jj , kk; + int idx = int( node-node->parent->children ); + Cube::FactorCornerIndex( idx , x1 , y1 , z1 ); + + for( int i=xStart ; i>1); + for( int j=yStart ; j>1); + for( int k=zStart ; k>1); + if(temp.neighbors[x2][y2][z2] ) + { + if( !temp.neighbors[x2][y2][z2]->children ) temp.neighbors[x2][y2][z2]->initChildren(); + neighbors[d].neighbors[i][j][k] = temp.neighbors[x2][y2][z2]->children + Cube::CornerIndex(ii,jj,kk); + } + } + } + } + } + } + return neighbors[d]; +} +template< class NodeData > +typename OctNode< NodeData >::ConstNeighbors5& OctNode< NodeData >::ConstNeighborKey5::getNeighbors( const OctNode* node ) +{ + int d=node->depth(); + if( node!=neighbors[d].neighbors[2][2][2] ) + { + neighbors[d].clear(); + + if(!node->parent) neighbors[d].neighbors[2][2][2]=node; + else + { + getNeighbors( node->parent ); + ConstNeighbors5& temp = neighbors[d-1]; + int x1,y1,z1,x2,y2,z2,ii,jj,kk; + int idx=int(node-node->parent->children); + Cube::FactorCornerIndex(idx,x1,y1,z1); + + for(int i=0;i<5;i++) + { + x2=i+x1; + ii=x2&1; + x2=1+(x2>>1); + for(int j=0;j<5;j++) + { + y2=j+y1; + jj=y2&1; + y2=1+(y2>>1); + for(int k=0;k<5;k++) + { + z2=k+z1; + kk=z2&1; + z2=1+(z2>>1); + if(temp.neighbors[x2][y2][z2] && temp.neighbors[x2][y2][z2]->children) + neighbors[d].neighbors[i][j][k] = temp.neighbors[x2][y2][z2]->children + Cube::CornerIndex(ii,jj,kk); + } + } + } + } + } + return neighbors[d]; +} + + +template< class NodeData > +int OctNode< NodeData >::write(const char* fileName) const{ + FILE* fp=fopen(fileName,"wb"); + if(!fp){return 0;} + int ret=write(fp); + fclose(fp); + return ret; +} +template< class NodeData > +int OctNode< NodeData >::write(FILE* fp) const{ + fwrite(this,sizeof(OctNode< NodeData >),1,fp); + if(children){for(int i=0;i +int OctNode< NodeData >::read(const char* fileName){ + FILE* fp=fopen(fileName,"rb"); + if(!fp){return 0;} + int ret=read(fp); + fclose(fp); + return ret; +} +template< class NodeData > +int OctNode< NodeData >::read(FILE* fp){ + fread(this,sizeof(OctNode< NodeData >),1,fp); + parent=NULL; + if(children){ + children=NULL; + initChildren(); + for(int i=0;i +int OctNode< NodeData >::width(int maxDepth) const { + int d=depth(); + return 1<<(maxDepth-d); +} +template< class NodeData > +void OctNode< NodeData >::centerIndex(int maxDepth,int index[DIMENSION]) const +{ + int d,o[3]; + depthAndOffset(d,o); + for(int i=0;i +#include "Polynomial.h" + +template +class StartingPolynomial{ +public: + Polynomial p; + double start; + + template + StartingPolynomial operator * (const StartingPolynomial& p) const; + StartingPolynomial scale(double s) const; + StartingPolynomial shift(double t) const; + int operator < (const StartingPolynomial& sp) const; + static int Compare(const void* v1,const void* v2); +}; + +template +class PPolynomial +{ +public: + size_t polyCount; + StartingPolynomial* polys; + + PPolynomial(void); + PPolynomial(const PPolynomial& p); + ~PPolynomial(void); + + PPolynomial& operator = (const PPolynomial& p); + + int size(void) const; + + void set( size_t size ); + // Note: this method will sort the elements in sps + void set( StartingPolynomial* sps , int count ); + void reset( size_t newSize ); + + + double operator()( double t ) const; + double integral( double tMin , double tMax ) const; + double Integral( void ) const; + + template + PPolynomial& operator = (const PPolynomial& p); + + PPolynomial operator + (const PPolynomial& p) const; + PPolynomial operator - (const PPolynomial& p) const; + + template + PPolynomial operator * (const Polynomial& p) const; + + template + PPolynomial operator * (const PPolynomial& p) const; + + + PPolynomial& operator += ( double s ); + PPolynomial& operator -= ( double s ); + PPolynomial& operator *= ( double s ); + PPolynomial& operator /= ( double s ); + PPolynomial operator + ( double s ) const; + PPolynomial operator - ( double s ) const; + PPolynomial operator * ( double s ) const; + PPolynomial operator / ( double s ) const; + + PPolynomial& addScaled(const PPolynomial& poly,double scale); + + PPolynomial scale( double s ) const; + PPolynomial shift( double t ) const; + + PPolynomial< Degree-1 > derivative(void) const; + PPolynomial< Degree+1 > integral(void) const; + + void getSolutions(double c,std::vector& roots,double EPS,double min=-DBL_MAX,double max=DBL_MAX) const; + + void printnl( void ) const; + + PPolynomial< Degree+1 > MovingAverage( double radius ) const; + static PPolynomial BSpline( double radius=0.5 ); + + void write( FILE* fp , int samples , double min , double max ) const; +}; +#include "PPolynomial.inl" +#endif // P_POLYNOMIAL_INCLUDED diff --git a/Src/PPolynomial.inl b/Src/PPolynomial.inl new file mode 100644 index 0000000..24b53fc --- /dev/null +++ b/Src/PPolynomial.inl @@ -0,0 +1,431 @@ +/* +Copyright (c) 2006, Michael Kazhdan and Matthew Bolitho +All rights reserved. + +Redistribution and use in source and binary forms, with or without modification, +are permitted provided that the following conditions are met: + +Redistributions of source code must retain the above copyright notice, this list of +conditions and the following disclaimer. Redistributions in binary form must reproduce +the above copyright notice, this list of conditions and the following disclaimer +in the documentation and/or other materials provided with the distribution. + +Neither the name of the Johns Hopkins University nor the names of its contributors +may be used to endorse or promote products derived from this software without specific +prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY +EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO THE IMPLIED WARRANTIES +OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT +SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED +TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR +BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN +ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH +DAMAGE. +*/ + +#include "Factor.h" + +//////////////////////// +// StartingPolynomial // +//////////////////////// +template +template +StartingPolynomial StartingPolynomial::operator * (const StartingPolynomial& p) const{ + StartingPolynomial sp; + if(start>p.start){sp.start=start;} + else{sp.start=p.start;} + sp.p=this->p*p.p; + return sp; +} +template +StartingPolynomial StartingPolynomial::scale(double s) const{ + StartingPolynomial q; + q.start=start*s; + q.p=p.scale(s); + return q; +} +template +StartingPolynomial StartingPolynomial::shift(double s) const{ + StartingPolynomial q; + q.start=start+s; + q.p=p.shift(s); + return q; +} + + +template +int StartingPolynomial::operator < (const StartingPolynomial& sp) const{ + if(start +int StartingPolynomial::Compare(const void* v1,const void* v2){ + double d=((StartingPolynomial*)(v1))->start-((StartingPolynomial*)(v2))->start; + if (d<0) {return -1;} + else if (d>0) {return 1;} + else {return 0;} +} + +///////////////// +// PPolynomial // +///////////////// +template +PPolynomial::PPolynomial(void){ + polyCount=0; + polys=NULL; +} +template +PPolynomial::PPolynomial(const PPolynomial& p){ + polyCount=0; + polys=NULL; + set(p.polyCount); + memcpy(polys,p.polys,sizeof(StartingPolynomial)*p.polyCount); +} + +template +PPolynomial::~PPolynomial(void){ + if(polyCount){free(polys);} + polyCount=0; + polys=NULL; +} +template +void PPolynomial::set(StartingPolynomial* sps,int count){ + int i,c=0; + set(count); + qsort(sps,count,sizeof(StartingPolynomial),StartingPolynomial::Compare); + for( i=0 ; i +int PPolynomial::size(void) const{return int(sizeof(StartingPolynomial)*polyCount);} + +template +void PPolynomial::set( size_t size ) +{ + if(polyCount){free(polys);} + polyCount=0; + polys=NULL; + polyCount=size; + if(size){ + polys=(StartingPolynomial*)malloc(sizeof(StartingPolynomial)*size); + memset(polys,0,sizeof(StartingPolynomial)*size); + } +} +template +void PPolynomial::reset( size_t newSize ) +{ + polyCount=newSize; + polys=(StartingPolynomial*)realloc(polys,sizeof(StartingPolynomial)*newSize); +} + +template +PPolynomial& PPolynomial::operator = (const PPolynomial& p){ + set(p.polyCount); + memcpy(polys,p.polys,sizeof(StartingPolynomial)*p.polyCount); + return *this; +} + +template +template +PPolynomial& PPolynomial::operator = (const PPolynomial& p){ + set(p.polyCount); + for(int i=0;i +double PPolynomial::operator ()( double t ) const +{ + double v=0; + for( int i=0 ; ipolys[i].start ; i++ ) v+=polys[i].p(t); + return v; +} + +template +double PPolynomial::integral( double tMin , double tMax ) const +{ + int m=1; + double start,end,s,v=0; + start=tMin; + end=tMax; + if(tMin>tMax){ + m=-1; + start=tMax; + end=tMin; + } + for(int i=0;i +double PPolynomial::Integral(void) const{return integral(polys[0].start,polys[polyCount-1].start);} +template +PPolynomial PPolynomial::operator + (const PPolynomial& p) const{ + PPolynomial q; + int i,j; + size_t idx=0; + q.set(polyCount+p.polyCount); + i=j=-1; + + while(idx=int(p.polyCount)-1) {q.polys[idx]= polys[++i];} + else if (i>=int( polyCount)-1) {q.polys[idx]=p.polys[++j];} + else if(polys[i+1].start +PPolynomial PPolynomial::operator - (const PPolynomial& p) const{ + PPolynomial q; + int i,j; + size_t idx=0; + q.set(polyCount+p.polyCount); + i=j=-1; + + while(idx=int(p.polyCount)-1) {q.polys[idx]= polys[++i];} + else if (i>=int( polyCount)-1) {q.polys[idx].start=p.polys[++j].start;q.polys[idx].p=p.polys[j].p*(-1.0);} + else if(polys[i+1].start +PPolynomial& PPolynomial::addScaled(const PPolynomial& p,double scale){ + int i,j; + StartingPolynomial* oldPolys=polys; + size_t idx=0,cnt=0,oldPolyCount=polyCount; + polyCount=0; + polys=NULL; + set(oldPolyCount+p.polyCount); + i=j=-1; + while(cnt=int( p.polyCount)-1) {polys[idx]=oldPolys[++i];} + else if (i>=int(oldPolyCount)-1) {polys[idx].start= p.polys[++j].start;polys[idx].p=p.polys[j].p*scale;} + else if (oldPolys[i+1].start +template +PPolynomial PPolynomial::operator * (const PPolynomial& p) const{ + PPolynomial q; + StartingPolynomial *sp; + int i,j,spCount=int(polyCount*p.polyCount); + + sp=(StartingPolynomial*)malloc(sizeof(StartingPolynomial)*spCount); + for(i=0;i +template +PPolynomial PPolynomial::operator * (const Polynomial& p) const{ + PPolynomial q; + q.set(polyCount); + for(int i=0;i +PPolynomial PPolynomial::scale( double s ) const +{ + PPolynomial q; + q.set(polyCount); + for(size_t i=0;i +PPolynomial PPolynomial::shift( double s ) const +{ + PPolynomial q; + q.set(polyCount); + for(size_t i=0;i +PPolynomial PPolynomial::derivative(void) const{ + PPolynomial q; + q.set(polyCount); + for(size_t i=0;i +PPolynomial PPolynomial::integral(void) const{ + int i; + PPolynomial q; + q.set(polyCount); + for(i=0;i +PPolynomial& PPolynomial::operator += ( double s ) {polys[0].p+=s;} +template +PPolynomial& PPolynomial::operator -= ( double s ) {polys[0].p-=s;} +template +PPolynomial& PPolynomial::operator *= ( double s ) +{ + for(int i=0;i +PPolynomial& PPolynomial::operator /= ( double s ) +{ + for(size_t i=0;i +PPolynomial PPolynomial::operator + ( double s ) const +{ + PPolynomial q=*this; + q+=s; + return q; +} +template +PPolynomial PPolynomial::operator - ( double s ) const +{ + PPolynomial q=*this; + q-=s; + return q; +} +template +PPolynomial PPolynomial::operator * ( double s ) const +{ + PPolynomial q=*this; + q*=s; + return q; +} +template +PPolynomial PPolynomial::operator / ( double s ) const +{ + PPolynomial q=*this; + q/=s; + return q; +} + +template +void PPolynomial::printnl(void) const{ + Polynomial p; + + if(!polyCount){ + Polynomial p; + printf("[-Infinity,Infinity]\n"); + } + else{ + for(size_t i=0;i +PPolynomial< 0 > PPolynomial< 0 >::BSpline( double radius ) +{ + PPolynomial q; + q.set(2); + + q.polys[0].start=-radius; + q.polys[1].start= radius; + + q.polys[0].p.coefficients[0]= 1.0; + q.polys[1].p.coefficients[0]=-1.0; + return q; +} +template< int Degree > +PPolynomial< Degree > PPolynomial::BSpline( double radius ) +{ + return PPolynomial< Degree-1 >::BSpline().MovingAverage( radius ); +} +template +PPolynomial PPolynomial::MovingAverage( double radius ) const +{ + PPolynomial A; + Polynomial p; + StartingPolynomial* sps; + + sps=(StartingPolynomial*)malloc(sizeof(StartingPolynomial)*polyCount*2); + + for(int i=0;i +void PPolynomial::getSolutions(double c,std::vector& roots,double EPS,double min,double max) const{ + Polynomial p; + std::vector tempRoots; + + p.setZero(); + for(size_t i=0;imax){break;} + if(ipolys[i].start && (i+1==polyCount || tempRoots[j]<=polys[i+1].start)){ + if(tempRoots[j]>min && tempRoots[j] +void PPolynomial::write(FILE* fp,int samples,double min,double max) const{ + fwrite(&samples,sizeof(int),1,fp); + for(int i=0;i +#include +#include +#include + +#define PLY_ASCII 1 /* ascii PLY file */ +#define PLY_BINARY_BE 2 /* binary PLY file, big endian */ +#define PLY_BINARY_LE 3 /* binary PLY file, little endian */ +#define PLY_BINARY_NATIVE 4 /* binary PLY file, same endianness as current architecture */ + +#define PLY_OKAY 0 /* ply routine worked okay */ +#define PLY_ERROR -1 /* error in ply routine */ + + /* scalar data types supported by PLY format */ + +#define PLY_START_TYPE 0 +#define PLY_CHAR 1 +#define PLY_SHORT 2 +#define PLY_INT 3 +#define PLY_UCHAR 4 +#define PLY_USHORT 5 +#define PLY_UINT 6 +#define PLY_FLOAT 7 +#define PLY_DOUBLE 8 +#define PLY_INT_8 9 +#define PLY_UINT_8 10 +#define PLY_INT_16 11 +#define PLY_UINT_16 12 +#define PLY_INT_32 13 +#define PLY_UINT_32 14 +#define PLY_FLOAT_32 15 +#define PLY_FLOAT_64 16 + +#define PLY_END_TYPE 17 + +#define PLY_SCALAR 0 +#define PLY_LIST 1 + +#define PLY_STRIP_COMMENT_HEADER 0 + +typedef struct PlyProperty { /* description of a property */ + + char *name; /* property name */ + int external_type; /* file's data type */ + int internal_type; /* program's data type */ + int offset; /* offset bytes of prop in a struct */ + + int is_list; /* 1 = list, 0 = scalar */ + int count_external; /* file's count type */ + int count_internal; /* program's count type */ + int count_offset; /* offset byte for list count */ + +} PlyProperty; + +typedef struct PlyElement { /* description of an element */ + char *name; /* element name */ + int num; /* number of elements in this object */ + int size; /* size of element (bytes) or -1 if variable */ + int nprops; /* number of properties for this element */ + PlyProperty **props; /* list of properties in the file */ + char *store_prop; /* flags: property wanted by user? */ + int other_offset; /* offset to un-asked-for props, or -1 if none*/ + int other_size; /* size of other_props structure */ +} PlyElement; + +typedef struct PlyOtherProp { /* describes other properties in an element */ + char *name; /* element name */ + int size; /* size of other_props */ + int nprops; /* number of properties in other_props */ + PlyProperty **props; /* list of properties in other_props */ +} PlyOtherProp; + +typedef struct OtherData { /* for storing other_props for an other element */ + void *other_props; +} OtherData; + +typedef struct OtherElem { /* data for one "other" element */ + char *elem_name; /* names of other elements */ + int elem_count; /* count of instances of each element */ + OtherData **other_data; /* actual property data for the elements */ + PlyOtherProp *other_props; /* description of the property data */ +} OtherElem; + +typedef struct PlyOtherElems { /* "other" elements, not interpreted by user */ + int num_elems; /* number of other elements */ + OtherElem *other_list; /* list of data for other elements */ +} PlyOtherElems; + +typedef struct PlyFile { /* description of PLY file */ + FILE *fp; /* file pointer */ + int file_type; /* ascii or binary */ + float version; /* version number of file */ + int nelems; /* number of elements of object */ + PlyElement **elems; /* list of elements */ + int num_comments; /* number of comments */ + char **comments; /* list of comments */ + int num_obj_info; /* number of items of object information */ + char **obj_info; /* list of object info items */ + PlyElement *which_elem; /* which element we're currently writing */ + PlyOtherElems *other_elems; /* "other" elements from a PLY file */ +} PlyFile; + + /* memory allocation */ +extern char *my_alloc(); +#define myalloc(mem_size) my_alloc((mem_size), __LINE__, __FILE__) + +#ifndef ALLOCN +#define REALLOCN(PTR,TYPE,OLD_N,NEW_N) \ +{ \ + if ((OLD_N) == 0) \ +{ ALLOCN((PTR),TYPE,(NEW_N));} \ + else \ +{ \ + (PTR) = (TYPE *)realloc((PTR),(NEW_N)*sizeof(TYPE)); \ + if (((PTR) == NULL) && ((NEW_N) != 0)) \ +{ \ + fprintf(stderr, "Memory reallocation failed on line %d in %s\n", \ + __LINE__, __FILE__); \ + fprintf(stderr, " tried to reallocate %d->%d\n", \ + (OLD_N), (NEW_N)); \ + exit(-1); \ +} \ + if ((NEW_N)>(OLD_N)) \ + memset((char *)(PTR)+(OLD_N)*sizeof(TYPE), 0, \ + ((NEW_N)-(OLD_N))*sizeof(TYPE)); \ +} \ +} + +#define ALLOCN(PTR,TYPE,N) \ +{ (PTR) = (TYPE *) calloc(((unsigned)(N)),sizeof(TYPE));\ + if ((PTR) == NULL) { \ + fprintf(stderr, "Memory allocation failed on line %d in %s\n", \ + __LINE__, __FILE__); \ + exit(-1); \ + } \ +} + + +#define FREE(PTR) { free((PTR)); (PTR) = NULL; } +#endif + + +/*** delcaration of routines ***/ + +extern PlyFile *ply_write(FILE *, int, const char **, int); +extern PlyFile *ply_open_for_writing(char *, int, const char **, int, float *); +extern void ply_describe_element(PlyFile *, char *, int, int, PlyProperty *); +extern void ply_describe_property(PlyFile *, const char *, PlyProperty *); +extern void ply_element_count(PlyFile *, const char *, int); +extern void ply_header_complete(PlyFile *); +extern void ply_put_element_setup(PlyFile *, const char *); +extern void ply_put_element(PlyFile *, void *); +extern void ply_put_comment(PlyFile *, char *); +extern void ply_put_obj_info(PlyFile *, char *); +extern PlyFile *ply_read(FILE *, int *, char ***); +extern PlyFile *ply_open_for_reading( char *, int *, char ***, int *, float *); +extern PlyProperty **ply_get_element_description(PlyFile *, char *, int*, int*); +extern void ply_get_element_setup( PlyFile *, char *, int, PlyProperty *); +extern int ply_get_property(PlyFile *, char *, PlyProperty *); +extern PlyOtherProp *ply_get_other_properties(PlyFile *, char *, int); +extern void ply_get_element(PlyFile *, void *); +extern char **ply_get_comments(PlyFile *, int *); +extern char **ply_get_obj_info(PlyFile *, int *); +extern void ply_close(PlyFile *); +extern void ply_get_info(PlyFile *, float *, int *); +extern PlyOtherElems *ply_get_other_element (PlyFile *, char *, int); +extern void ply_describe_other_elements ( PlyFile *, PlyOtherElems *); +extern void ply_put_other_elements (PlyFile *); +extern void ply_free_other_elements (PlyOtherElems *); +extern void ply_describe_other_properties(PlyFile *, PlyOtherProp *, int); + +extern int equal_strings(const char *, const char *); + +#ifdef __cplusplus +} +#endif +#include "Geometry.h" +#include + +template< class Real > int PLYType( void ); +template<> inline int PLYType< int >( void ){ return PLY_INT ; } +template<> inline int PLYType< char >( void ){ return PLY_CHAR ; } +template<> inline int PLYType< unsigned char >( void ){ return PLY_UCHAR ; } +template<> inline int PLYType< float >( void ){ return PLY_FLOAT ; } +template<> inline int PLYType< double >( void ){ return PLY_DOUBLE; } +template< class Real > inline int PLYType( void ){ fprintf( stderr , "[ERROR] Unrecognized type\n" ) , exit( 0 ); } + +typedef struct PlyFace +{ + unsigned char nr_vertices; + int *vertices; + int segment; +} PlyFace; +static PlyProperty face_props[] = +{ + { _strdup( "vertex_indices" ) , PLY_INT , PLY_INT , offsetof( PlyFace , vertices ) , 1 , PLY_UCHAR, PLY_UCHAR , offsetof(PlyFace,nr_vertices) }, +}; + +template< class Real > +class PlyVertex +{ +public: + const static int Components=3; + static PlyProperty Properties[]; + + Point3D< Real > point; + + PlyVertex( void ) { ; } + PlyVertex( Point3D< Real > p ) { point=p; } + PlyVertex operator + ( PlyVertex p ) const { return PlyVertex( point+p.point ); } + PlyVertex operator - ( PlyVertex p ) const { return PlyVertex( point-p.point ); } + template< class _Real > PlyVertex operator * ( _Real s ) const { return PlyVertex( point*s ); } + template< class _Real > PlyVertex operator / ( _Real s ) const { return PlyVertex( point/s ); } + PlyVertex& operator += ( PlyVertex p ) { point += p.point ; return *this; } + PlyVertex& operator -= ( PlyVertex p ) { point -= p.point ; return *this; } + template< class _Real > PlyVertex& operator *= ( _Real s ) { point *= s ; return *this; } + template< class _Real > PlyVertex& operator /= ( _Real s ) { point /= s ; return *this; } +}; +template< class Real , class _Real > PlyVertex< Real > operator * ( XForm4x4< _Real > xForm , PlyVertex< Real > v ) { return PlyVertex< Real >( xForm * v.point ); } +template< class Real > PlyProperty PlyVertex< Real >::Properties[]= +{ + { _strdup( "x" ) , PLYType< Real >() , PLYType< Real >() , int( offsetof( PlyVertex , point.coords[0] ) ) , 0 , 0 , 0 , 0 }, + { _strdup( "y" ) , PLYType< Real >() , PLYType< Real >() , int( offsetof( PlyVertex , point.coords[1] ) ) , 0 , 0 , 0 , 0 }, + { _strdup( "z" ) , PLYType< Real >() , PLYType< Real >() , int( offsetof( PlyVertex , point.coords[2] ) ) , 0 , 0 , 0 , 0 } +}; +template< class Real > +class PlyValueVertex +{ +public: + const static int Components=4; + static PlyProperty Properties[]; + + Point3D point; + Real value; + + PlyValueVertex( void ) : value( Real(0) ) { ; } + PlyValueVertex( Point3D< Real > p , Real v ) : point(p) , value(v) { ; } + PlyValueVertex operator + ( PlyValueVertex p ) const { return PlyValueVertex( point+p.point , value+p.value ); } + PlyValueVertex operator - ( PlyValueVertex p ) const { return PlyValueVertex( point-p.value , value-p.value ); } + template< class _Real > PlyValueVertex operator * ( _Real s ) const { return PlyValueVertex( point*s , Real(value*s) ); } + template< class _Real > PlyValueVertex operator / ( _Real s ) const { return PlyValueVertex( point/s , Real(value/s) ); } + PlyValueVertex& operator += ( PlyValueVertex p ) { point += p.point , value += p.value ; return *this; } + PlyValueVertex& operator -= ( PlyValueVertex p ) { point -= p.point , value -= p.value ; return *this; } + template< class _Real > PlyValueVertex& operator *= ( _Real s ) { point *= s , value *= Real(s) ; return *this; } + template< class _Real > PlyValueVertex& operator /= ( _Real s ) { point /= s , value /= Real(s) ; return *this; } +}; +template< class Real , class _Real > PlyValueVertex< Real > operator * ( XForm4x4< _Real > xForm , PlyValueVertex< Real > v ) { return PlyValueVertex< Real >( xForm * v.point , v.value ); } +template< class Real > +PlyProperty PlyValueVertex< Real >::Properties[]= +{ + { _strdup( "x" ) , PLYType< Real >() , PLYType< Real >() , int( offsetof( PlyValueVertex , point.coords[0] ) ) , 0 , 0 , 0 , 0 }, + { _strdup( "y" ) , PLYType< Real >() , PLYType< Real >() , int( offsetof( PlyValueVertex , point.coords[1] ) ) , 0 , 0 , 0 , 0 }, + { _strdup( "z" ) , PLYType< Real >() , PLYType< Real >() , int( offsetof( PlyValueVertex , point.coords[2] ) ) , 0 , 0 , 0 , 0 }, + { _strdup( "value" ) , PLYType< Real >() , PLYType< Real >() , int( offsetof( PlyValueVertex , value ) ) , 0 , 0 , 0 , 0 } +}; +template< class Real > +class PlyOrientedVertex +{ +public: + const static int Components=6; + static PlyProperty Properties[]; + + Point3D point , normal; + + PlyOrientedVertex( void ) { ; } + PlyOrientedVertex( Point3D< Real > p , Point3D< Real > n ) : point(p) , normal(n) { ; } + PlyOrientedVertex operator + ( PlyOrientedVertex p ) const { return PlyOrientedVertex( point+p.point , normal+p.normal ); } + PlyOrientedVertex operator - ( PlyOrientedVertex p ) const { return PlyOrientedVertex( point-p.value , normal-p.normal ); } + template< class _Real > PlyOrientedVertex operator * ( _Real s ) const { return PlyOrientedVertex( point*s , normal*s ); } + template< class _Real > PlyOrientedVertex operator / ( _Real s ) const { return PlyOrientedVertex( point/s , normal/s ); } + PlyOrientedVertex& operator += ( PlyOrientedVertex p ) { point += p.point , normal += p.normal ; return *this; } + PlyOrientedVertex& operator -= ( PlyOrientedVertex p ) { point -= p.point , normal -= p.normal ; return *this; } + template< class _Real > PlyOrientedVertex& operator *= ( _Real s ) { point *= s , normal *= s ; return *this; } + template< class _Real > PlyOrientedVertex& operator /= ( _Real s ) { point /= s , normal /= s ; return *this; } +}; +template< class Real , class _Real > PlyOrientedVertex< Real > operator * ( XForm4x4< _Real > xForm , PlyOrientedVertex< Real > v ) { return PlyOrientedVertex< Real >( xForm * v.point , xForm.inverse().transpose() * v.normal ); } +template< class Real > +PlyProperty PlyOrientedVertex< Real >::Properties[]= +{ + { _strdup( "x" ) , PLYType< Real >() , PLYType< Real >() , int( offsetof( PlyOrientedVertex , point.coords[0] ) ) , 0 , 0 , 0 , 0 }, + { _strdup( "y" ) , PLYType< Real >() , PLYType< Real >() , int( offsetof( PlyOrientedVertex , point.coords[1] ) ) , 0 , 0 , 0 , 0 }, + { _strdup( "z" ) , PLYType< Real >() , PLYType< Real >() , int( offsetof( PlyOrientedVertex , point.coords[2] ) ) , 0 , 0 , 0 , 0 }, + { _strdup( "nx" ) , PLYType< Real >() , PLYType< Real >() , int( offsetof( PlyOrientedVertex , normal.coords[0] ) ) , 0 , 0 , 0 , 0 }, + { _strdup( "ny" ) , PLYType< Real >() , PLYType< Real >() , int( offsetof( PlyOrientedVertex , normal.coords[1] ) ) , 0 , 0 , 0 , 0 }, + { _strdup( "nz" ) , PLYType< Real >() , PLYType< Real >() , int( offsetof( PlyOrientedVertex , normal.coords[2] ) ) , 0 , 0 , 0 , 0 } +}; +template< class Real > +class PlyColorVertex +{ +public: + const static int Components=6; + static PlyProperty Properties[]; + + Point3D point; + unsigned char color[3]; + + operator Point3D& () {return point;} + operator const Point3D& () const {return point;} + PlyColorVertex(void) {point.coords[0]=point.coords[1]=point.coords[2]=0,color[0]=color[1]=color[2]=0;} + PlyColorVertex(const Point3D& p) {point=p;} +}; +template< class Real > +PlyProperty PlyColorVertex< Real >::Properties[]= +{ + { _strdup( "x" ) , PLYType< Real >() , PLYType< Real >(), int( offsetof( PlyColorVertex , point.coords[0] ) ) , 0 , 0 , 0 , 0 }, + { _strdup( "y" ) , PLYType< Real >() , PLYType< Real >(), int( offsetof( PlyColorVertex , point.coords[1] ) ) , 0 , 0 , 0 , 0 }, + { _strdup( "z" ) , PLYType< Real >() , PLYType< Real >(), int( offsetof( PlyColorVertex , point.coords[2] ) ) , 0 , 0 , 0 , 0 }, + { _strdup( "red" ) , PLYType< unsigned char >() , PLYType< unsigned char >(), int( offsetof( PlyColorVertex , color[0] ) ) , 0 , 0 , 0 , 0 }, + { _strdup( "green" ) , PLYType< unsigned char >() , PLYType< unsigned char >(), int( offsetof( PlyColorVertex , color[1] ) ) , 0 , 0 , 0 , 0 }, + { _strdup( "blue" ) , PLYType< unsigned char >() , PLYType< unsigned char >(), int( offsetof( PlyColorVertex , color[2] ) ) , 0 , 0 , 0 , 0 } +}; + +template< class Vertex , class Real > +int PlyWritePolygons( char* fileName , CoredMeshData< Vertex >* mesh , int file_type , const Point3D< float >& translate , float scale , char** comments=NULL , int commentNum=0 , XForm4x4< Real > xForm=XForm4x4< Real >::Identity() ); + +template< class Vertex , class Real > +int PlyWritePolygons( char* fileName , CoredMeshData< Vertex >* mesh , int file_type , char** comments=NULL , int commentNum=0 , XForm4x4< Real > xForm=XForm4x4< Real >::Identity() ); + +template +int PlyReadPolygons(char* fileName, + std::vector& vertices,std::vector >& polygons, + PlyProperty* properties,int propertyNum, + int& file_type, + char*** comments=NULL,int* commentNum=NULL , bool* readFlags=NULL ); + +template +int PlyWritePolygons(char* fileName, + const std::vector& vertices,const std::vector >& polygons, + PlyProperty* properties,int propertyNum, + int file_type, + char** comments=NULL,const int& commentNum=0); + +template +int PlyWritePolygons(char* fileName, + const std::vector& vertices , const std::vector< std::vector< int > >& polygons, + PlyProperty* properties,int propertyNum, + int file_type, + char** comments,const int& commentNum) +{ + int nr_vertices=int(vertices.size()); + int nr_faces=int(polygons.size()); + float version; + const char *elem_names[] = { "vertex" , "face" }; + PlyFile *ply = ply_open_for_writing( fileName , 2 , elem_names , file_type , &version ); + if (!ply){return 0;} + + // + // describe vertex and face properties + // + ply_element_count(ply, "vertex", nr_vertices); + for(int i=0;imaxFaceVerts) + { + delete[] ply_face.vertices; + maxFaceVerts=int(polygons[i].size()); + ply_face.vertices=new int[maxFaceVerts]; + } + ply_face.nr_vertices=int(polygons[i].size()); + for(int j=0;j +int PlyReadPolygons(char* fileName, + std::vector& vertices , std::vector >& polygons , + PlyProperty* properties , int propertyNum , + int& file_type , + char*** comments , int* commentNum , bool* readFlags ) +{ + int nr_elems; + char **elist; + float version; + int i,j,k; + PlyFile* ply; + char* elem_name; + int num_elems; + int nr_props; + PlyProperty** plist; + PlyFace ply_face; + + ply = ply_open_for_reading(fileName, &nr_elems, &elist, &file_type, &version); + if(!ply) return 0; + + if(comments) + { + (*comments)=new char*[*commentNum+ply->num_comments]; + for(int i=0;inum_comments;i++) + (*comments)[i]=_strdup(ply->comments[i]); + *commentNum=ply->num_comments; + } + + for (i=0; i < nr_elems; i++) { + elem_name = elist[i]; + plist = ply_get_element_description(ply, elem_name, &num_elems, &nr_props); + if(!plist) + { + for(i=0;ielems[i]->name); + free(ply->elems[i]->store_prop); + for(j=0;jelems[i]->nprops;j++){ + free(ply->elems[i]->props[j]->name); + free(ply->elems[i]->props[j]); + } + free(ply->elems[i]->props); + } + for(i=0;ielems[i]);} + free(ply->elems); + for(i=0;inum_comments;i++){free(ply->comments[i]);} + free(ply->comments); + for(i=0;inum_obj_info;i++){free(ply->obj_info[i]);} + free(ply->obj_info); + ply_free_other_elements (ply->other_elems); + + for(i=0;iname); + free(plist[j]); + } + free(plist); + } // for each type of element + + for(i=0;ielems[i]->name); + free(ply->elems[i]->store_prop); + for(j=0;jelems[i]->nprops;j++){ + free(ply->elems[i]->props[j]->name); + free(ply->elems[i]->props[j]); + } + if(ply->elems[i]->props && ply->elems[i]->nprops){free(ply->elems[i]->props);} + } + for(i=0;ielems[i]);} + free(ply->elems); + for(i=0;inum_comments;i++){free(ply->comments[i]);} + free(ply->comments); + for(i=0;inum_obj_info;i++){free(ply->obj_info[i]);} + free(ply->obj_info); + ply_free_other_elements (ply->other_elems); + + + for(i=0;i +int PlyWritePolygons( char* fileName , CoredMeshData< Vertex >* mesh , int file_type , const Point3D& translate , float scale , char** comments , int commentNum , XForm4x4< Real > xForm ) +{ + int i; + int nr_vertices=int(mesh->outOfCorePointCount()+mesh->inCorePoints.size()); + int nr_faces=mesh->polygonCount(); + float version; + const char *elem_names[] = { "vertex" , "face" }; + PlyFile *ply = ply_open_for_writing( fileName , 2 , elem_names , file_type , &version ); + if( !ply ) return 0; + + mesh->resetIterator(); + + // + // describe vertex and face properties + // + ply_element_count( ply , "vertex" , nr_vertices ); + for( int i=0 ; iinCorePoints.size() ) ; i++ ) + { + Vertex vertex = xForm * ( mesh->inCorePoints[i] * scale + translate ); + ply_put_element(ply, (void *) &vertex); + } + for( i=0; ioutOfCorePointCount() ; i++ ) + { + Vertex vertex; + mesh->nextOutOfCorePoint( vertex ); + vertex = xForm * ( vertex * scale +translate ); + ply_put_element(ply, (void *) &vertex); + } // for, write vertices + + // write faces + std::vector< CoredVertexIndex > polygon; + ply_put_element_setup( ply , "face" ); + for( i=0 ; inextPolygon( polygon ); + ply_face.nr_vertices = int( polygon.size() ); + ply_face.vertices = new int[ polygon.size() ]; + for( int i=0 ; iinCorePoints.size() ); + ply_put_element( ply, (void *) &ply_face ); + delete[] ply_face.vertices; + } // for, write faces + + ply_close( ply ); + return 1; +} +template< class Vertex , class Real > +int PlyWritePolygons( char* fileName , CoredMeshData< Vertex >* mesh , int file_type , char** comments , int commentNum , XForm4x4< Real > xForm ) +{ + int i; + int nr_vertices=int(mesh->outOfCorePointCount()+mesh->inCorePoints.size()); + int nr_faces=mesh->polygonCount(); + float version; + const char *elem_names[] = { "vertex" , "face" }; + PlyFile *ply = ply_open_for_writing( fileName , 2 , elem_names , file_type , &version ); + if( !ply ) return 0; + + mesh->resetIterator(); + + // + // describe vertex and face properties + // + ply_element_count( ply , "vertex" , nr_vertices ); + for( int i=0 ; iinCorePoints.size() ) ; i++ ) + { + Vertex vertex = xForm * mesh->inCorePoints[i]; + ply_put_element(ply, (void *) &vertex); + } + for( i=0; ioutOfCorePointCount() ; i++ ) + { + Vertex vertex; + mesh->nextOutOfCorePoint( vertex ); + vertex = xForm * ( vertex ); + ply_put_element(ply, (void *) &vertex); + } // for, write vertices + + // write faces + std::vector< CoredVertexIndex > polygon; + ply_put_element_setup( ply , "face" ); + for( i=0 ; inextPolygon( polygon ); + ply_face.nr_vertices = int( polygon.size() ); + ply_face.vertices = new int[ polygon.size() ]; + for( int i=0 ; iinCorePoints.size() ); + ply_put_element( ply, (void *) &ply_face ); + delete[] ply_face.vertices; + } // for, write faces + + ply_close( ply ); + return 1; +} +inline int PlyDefaultFileType(void){return PLY_ASCII;} + +#endif /* !__PLY_H__ */ diff --git a/Src/PlyFile.cpp b/Src/PlyFile.cpp new file mode 100644 index 0000000..3ce7a95 --- /dev/null +++ b/Src/PlyFile.cpp @@ -0,0 +1,2727 @@ +/* + + The interface routines for reading and writing PLY polygon files. + + Greg Turk, February 1994 + + --------------------------------------------------------------- + + A PLY file contains a single polygonal _object_. + + An object is composed of lists of _elements_. Typical elements are + vertices, faces, edges and materials. + + Each type of element for a given object has one or more _properties_ + associated with the element type. For instance, a vertex element may + have as properties the floating-point values x,y,z and the three unsigned + chars representing red, green and blue. + + --------------------------------------------------------------- + + Copyright (c) 1994 The Board of Trustees of The Leland Stanford + Junior University. All rights reserved. + + Permission to use, copy, modify and distribute this software and its + documentation for any purpose is hereby granted without fee, provided + that the above copyright notice and this permission notice appear in + all copies of this software and that you do not sell the software. + + THE SOFTWARE IS PROVIDED "AS IS" AND WITHOUT WARRANTY OF ANY KIND, + EXPRESS, IMPLIED OR OTHERWISE, INCLUDING WITHOUT LIMITATION, ANY + WARRANTY OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. + +*/ + +#include +#include +#include +#include +#include "Ply.h" + +const char *type_names[] = { + "invalid", + "char", + "short", + "int", + "uchar", + "ushort", + "uint", + "float", + "double", + + "int8", // character 1 + "uint8", // unsigned character 1 + "int16", // short integer 2 + "uint16", // unsigned short integer 2 + "int32", // integer 4 + "uint32", // unsigned integer 4 + "float32", // single-precision float 4 + "float64", // double-precision float 8 + +}; + +int ply_type_size[] = { + 0, + 1, + 2, + 4, + 1, + 2, + 4, + 4, + 8, + 1, + 1, + 2, + 2, + 4, + 4, + 8 +}; + +typedef union +{ + int int_value; + char byte_values[sizeof(int)]; +} endian_test_type; + + +static int native_binary_type = -1; +static int types_checked = 0; + +#define NO_OTHER_PROPS -1 + +#define DONT_STORE_PROP 0 +#define STORE_PROP 1 + +#define OTHER_PROP 0 +#define NAMED_PROP 1 + + +/* returns 1 if strings are equal, 0 if not */ +int equal_strings(const char *, const char *); + +/* find an element in a plyfile's list */ +PlyElement *find_element(PlyFile *, const char *); + +/* find a property in an element's list */ +PlyProperty *find_property(PlyElement *, const char *, int *); + +/* write to a file the word describing a PLY file data type */ +void write_scalar_type (FILE *, int); + +/* read a line from a file and break it up into separate words */ +char **get_words(FILE *, int *, char **); +char **old_get_words(FILE *, int *); + +/* write an item to a file */ +void write_binary_item(FILE *, int, int, unsigned int, double, int); +void write_ascii_item(FILE *, int, unsigned int, double, int); +double old_write_ascii_item(FILE *, char *, int); + +/* add information to a PLY file descriptor */ +void add_element(PlyFile *, char **); +void add_property(PlyFile *, char **); +void add_comment(PlyFile *, char *); +void add_obj_info(PlyFile *, char *); + +/* copy a property */ +void copy_property(PlyProperty *, PlyProperty *); + +/* store a value into where a pointer and a type specify */ +void store_item(char *, int, int, unsigned int, double); + +/* return the value of a stored item */ +void get_stored_item( void *, int, int *, unsigned int *, double *); + +/* return the value stored in an item, given ptr to it and its type */ +double get_item_value(char *, int); + +/* get binary or ascii item and store it according to ptr and type */ +void get_ascii_item(char *, int, int *, unsigned int *, double *); +void get_binary_item(FILE *, int, int, int *, unsigned int *, double *); + +/* get a bunch of elements from a file */ +void ascii_get_element(PlyFile *, char *); +void binary_get_element(PlyFile *, char *); + +/* memory allocation */ +char *my_alloc(int, int, const char *); + +/* byte ordering */ +void get_native_binary_type(); +void swap_bytes(char *, int); + +void check_types(); + +/*************/ +/* Writing */ +/*************/ + + +/****************************************************************************** +Given a file pointer, get ready to write PLY data to the file. + + Entry: + fp - the given file pointer + nelems - number of elements in object + elem_names - list of element names + file_type - file type, either ascii or binary + + Exit: + returns a pointer to a PlyFile, used to refer to this file, or NULL if error +******************************************************************************/ + +PlyFile *ply_write( + FILE *fp, + int nelems, + const char **elem_names, + int file_type + ) +{ + int i; + PlyFile *plyfile; + PlyElement *elem; + + /* check for NULL file pointer */ + if (fp == NULL) + return (NULL); + + if (native_binary_type == -1) + get_native_binary_type(); + if (!types_checked) + check_types(); + + /* create a record for this object */ + + plyfile = (PlyFile *) myalloc (sizeof (PlyFile)); + if (file_type == PLY_BINARY_NATIVE) + plyfile->file_type = native_binary_type; + else + plyfile->file_type = file_type; + plyfile->num_comments = 0; + plyfile->num_obj_info = 0; + plyfile->nelems = nelems; + plyfile->version = 1.0; + plyfile->fp = fp; + plyfile->other_elems = NULL; + + /* tuck aside the names of the elements */ + + plyfile->elems = (PlyElement **) myalloc (sizeof (PlyElement *) * nelems); + for (i = 0; i < nelems; i++) { + elem = (PlyElement *) myalloc (sizeof (PlyElement)); + plyfile->elems[i] = elem; + elem->name = _strdup (elem_names[i]); + elem->num = 0; + elem->nprops = 0; + } + + /* return pointer to the file descriptor */ + return (plyfile); +} + + +/****************************************************************************** +Open a polygon file for writing. + + Entry: + filename - name of file to read from + nelems - number of elements in object + elem_names - list of element names + file_type - file type, either ascii or binary + + Exit: + version - version number of PLY file + returns a file identifier, used to refer to this file, or NULL if error +******************************************************************************/ + +PlyFile *ply_open_for_writing( + char *filename, + int nelems, + const char **elem_names, + int file_type, + float *version + ) +{ + PlyFile *plyfile; + char *name; + FILE *fp; + + /* tack on the extension .ply, if necessary */ + + name = (char *) myalloc (int(sizeof (char) * (strlen (filename)) + 5)); + strcpy (name, filename); + if (strlen (name) < 4 || + strcmp (name + strlen (name) - 4, ".ply") != 0) + strcat (name, ".ply"); + + /* open the file for writing */ + + fp = fopen (name, "wb"); + if (fp == NULL) { + return (NULL); + } + + /* create the actual PlyFile structure */ + + plyfile = ply_write (fp, nelems, elem_names, file_type); + if (plyfile == NULL) + return (NULL); + + /* say what PLY file version number we're writing */ + *version = plyfile->version; + + /* return pointer to the file descriptor */ + return (plyfile); +} + + +/****************************************************************************** +Describe an element, including its properties and how many will be written +to the file. + + Entry: + plyfile - file identifier + elem_name - name of element that information is being specified about + nelems - number of elements of this type to be written + nprops - number of properties contained in the element + prop_list - list of properties +******************************************************************************/ + +void ply_describe_element( + PlyFile *plyfile, + char *elem_name, + int nelems, + int nprops, + PlyProperty *prop_list + ) +{ + int i; + PlyElement *elem; + PlyProperty *prop; + + /* look for appropriate element */ + elem = find_element (plyfile, elem_name); + if (elem == NULL) { + fprintf(stderr,"ply_describe_element: can't find element '%s'\n",elem_name); + exit (-1); + } + + elem->num = nelems; + + /* copy the list of properties */ + + elem->nprops = nprops; + elem->props = (PlyProperty **) myalloc (sizeof (PlyProperty *) * nprops); + elem->store_prop = (char *) myalloc (sizeof (char) * nprops); + + for (i = 0; i < nprops; i++) { + prop = (PlyProperty *) myalloc (sizeof (PlyProperty)); + elem->props[i] = prop; + elem->store_prop[i] = NAMED_PROP; + copy_property (prop, &prop_list[i]); + } +} + + +/****************************************************************************** +Describe a property of an element. + + Entry: + plyfile - file identifier + elem_name - name of element that information is being specified about + prop - the new property +******************************************************************************/ + +void ply_describe_property( + PlyFile *plyfile, + const char *elem_name, + PlyProperty *prop + ) +{ + PlyElement *elem; + PlyProperty *elem_prop; + + /* look for appropriate element */ + elem = find_element (plyfile, elem_name); + if (elem == NULL) { + fprintf(stderr, "ply_describe_property: can't find element '%s'\n", + elem_name); + return; + } + + /* create room for new property */ + + if (elem->nprops == 0) { + elem->props = (PlyProperty **) myalloc (sizeof (PlyProperty *)); + elem->store_prop = (char *) myalloc (sizeof (char)); + elem->nprops = 1; + } + else { + elem->nprops++; + elem->props = (PlyProperty **) + realloc (elem->props, sizeof (PlyProperty *) * elem->nprops); + elem->store_prop = (char *) + realloc (elem->store_prop, sizeof (char) * elem->nprops); + } + + /* copy the new property */ + + elem_prop = (PlyProperty *) myalloc (sizeof (PlyProperty)); + elem->props[elem->nprops - 1] = elem_prop; + elem->store_prop[elem->nprops - 1] = NAMED_PROP; + copy_property (elem_prop, prop); +} + + +/****************************************************************************** +Describe what the "other" properties are that are to be stored, and where +they are in an element. +******************************************************************************/ + +void ply_describe_other_properties( + PlyFile *plyfile, + PlyOtherProp *other, + int offset + ) +{ + int i; + PlyElement *elem; + PlyProperty *prop; + + /* look for appropriate element */ + elem = find_element (plyfile, other->name); + if (elem == NULL) { + fprintf(stderr, "ply_describe_other_properties: can't find element '%s'\n", + other->name); + return; + } + + /* create room for other properties */ + + if (elem->nprops == 0) { + elem->props = (PlyProperty **) + myalloc (sizeof (PlyProperty *) * other->nprops); + elem->store_prop = (char *) myalloc (sizeof (char) * other->nprops); + elem->nprops = 0; + } + else { + int newsize; + newsize = elem->nprops + other->nprops; + elem->props = (PlyProperty **) + realloc (elem->props, sizeof (PlyProperty *) * newsize); + elem->store_prop = (char *) + realloc (elem->store_prop, sizeof (char) * newsize); + } + + /* copy the other properties */ + + for (i = 0; i < other->nprops; i++) { + prop = (PlyProperty *) myalloc (sizeof (PlyProperty)); + copy_property (prop, other->props[i]); + elem->props[elem->nprops] = prop; + elem->store_prop[elem->nprops] = OTHER_PROP; + elem->nprops++; + } + + /* save other info about other properties */ + elem->other_size = other->size; + elem->other_offset = offset; +} + + +/****************************************************************************** +State how many of a given element will be written. + + Entry: + plyfile - file identifier + elem_name - name of element that information is being specified about + nelems - number of elements of this type to be written +******************************************************************************/ + +void ply_element_count( + PlyFile *plyfile, + const char *elem_name, + int nelems + ) +{ + PlyElement *elem; + + /* look for appropriate element */ + elem = find_element (plyfile, elem_name); + if (elem == NULL) { + fprintf(stderr,"ply_element_count: can't find element '%s'\n",elem_name); + exit (-1); + } + + elem->num = nelems; +} + + +/****************************************************************************** +Signal that we've described everything a PLY file's header and that the +header should be written to the file. + + Entry: + plyfile - file identifier +******************************************************************************/ + +void ply_header_complete(PlyFile *plyfile) +{ + int i,j; + FILE *fp = plyfile->fp; + PlyElement *elem; + PlyProperty *prop; + + fprintf (fp, "ply\n"); + + switch (plyfile->file_type) { + case PLY_ASCII: + fprintf (fp, "format ascii 1.0\n"); + break; + case PLY_BINARY_BE: + fprintf (fp, "format binary_big_endian 1.0\n"); + break; + case PLY_BINARY_LE: + fprintf (fp, "format binary_little_endian 1.0\n"); + break; + default: + fprintf (stderr, "ply_header_complete: bad file type = %d\n", + plyfile->file_type); + exit (-1); + } + + /* write out the comments */ + + for (i = 0; i < plyfile->num_comments; i++) + fprintf (fp, "comment %s\n", plyfile->comments[i]); + + /* write out object information */ + + for (i = 0; i < plyfile->num_obj_info; i++) + fprintf (fp, "obj_info %s\n", plyfile->obj_info[i]); + + /* write out information about each element */ + + for (i = 0; i < plyfile->nelems; i++) { + + elem = plyfile->elems[i]; + fprintf (fp, "element %s %d\n", elem->name, elem->num); + + /* write out each property */ + for (j = 0; j < elem->nprops; j++) { + prop = elem->props[j]; + if (prop->is_list) { + fprintf (fp, "property list "); + write_scalar_type (fp, prop->count_external); + fprintf (fp, " "); + write_scalar_type (fp, prop->external_type); + fprintf (fp, " %s\n", prop->name); + } + else { + fprintf (fp, "property "); + write_scalar_type (fp, prop->external_type); + fprintf (fp, " %s\n", prop->name); + } + } + } + + fprintf (fp, "end_header\n"); +} + + +/****************************************************************************** +Specify which elements are going to be written. This should be called +before a call to the routine ply_put_element(). + + Entry: + plyfile - file identifier + elem_name - name of element we're talking about +******************************************************************************/ + +void ply_put_element_setup(PlyFile *plyfile, const char *elem_name) +{ + PlyElement *elem; + + elem = find_element (plyfile, elem_name); + if (elem == NULL) { + fprintf(stderr, "ply_elements_setup: can't find element '%s'\n", elem_name); + exit (-1); + } + + plyfile->which_elem = elem; +} + + +/****************************************************************************** +Write an element to the file. This routine assumes that we're +writing the type of element specified in the last call to the routine +ply_put_element_setup(). + + Entry: + plyfile - file identifier + elem_ptr - pointer to the element +******************************************************************************/ + +void ply_put_element(PlyFile *plyfile, void *elem_ptr) +{ + int j,k; + FILE *fp = plyfile->fp; + PlyElement *elem; + PlyProperty *prop; + char *elem_data,*item; + char **item_ptr; + int list_count; + int item_size; + int int_val; + unsigned int uint_val; + double double_val; + char **other_ptr; + + elem = plyfile->which_elem; + elem_data = (char *)elem_ptr; + other_ptr = (char **) (((char *) elem_ptr) + elem->other_offset); + + /* write out either to an ascii or binary file */ + + if (plyfile->file_type == PLY_ASCII) { + + /* write an ascii file */ + + /* write out each property of the element */ + for (j = 0; j < elem->nprops; j++) { + prop = elem->props[j]; + if (elem->store_prop[j] == OTHER_PROP) + elem_data = *other_ptr; + else + elem_data = (char *)elem_ptr; + if (prop->is_list) { + item = elem_data + prop->count_offset; + get_stored_item ((void *) item, prop->count_internal, + &int_val, &uint_val, &double_val); + write_ascii_item (fp, int_val, uint_val, double_val, + prop->count_external); + list_count = uint_val; + item_ptr = (char **) (elem_data + prop->offset); + item = item_ptr[0]; + item_size = ply_type_size[prop->internal_type]; + for (k = 0; k < list_count; k++) { + get_stored_item ((void *) item, prop->internal_type, + &int_val, &uint_val, &double_val); + write_ascii_item (fp, int_val, uint_val, double_val, + prop->external_type); + item += item_size; + } + } + else { + item = elem_data + prop->offset; + get_stored_item ((void *) item, prop->internal_type, + &int_val, &uint_val, &double_val); + write_ascii_item (fp, int_val, uint_val, double_val, + prop->external_type); + } + } + + fprintf (fp, "\n"); + } + else { + + /* write a binary file */ + + /* write out each property of the element */ + for (j = 0; j < elem->nprops; j++) { + prop = elem->props[j]; + if (elem->store_prop[j] == OTHER_PROP) + elem_data = *other_ptr; + else + elem_data = (char *)elem_ptr; + if (prop->is_list) { + item = elem_data + prop->count_offset; + item_size = ply_type_size[prop->count_internal]; + get_stored_item ((void *) item, prop->count_internal, + &int_val, &uint_val, &double_val); + write_binary_item (fp, plyfile->file_type, int_val, uint_val, + double_val, prop->count_external); + list_count = uint_val; + item_ptr = (char **) (elem_data + prop->offset); + item = item_ptr[0]; + item_size = ply_type_size[prop->internal_type]; + for (k = 0; k < list_count; k++) { + get_stored_item ((void *) item, prop->internal_type, + &int_val, &uint_val, &double_val); + write_binary_item (fp, plyfile->file_type, int_val, uint_val, + double_val, prop->external_type); + item += item_size; + } + } + else { + item = elem_data + prop->offset; + item_size = ply_type_size[prop->internal_type]; + get_stored_item ((void *) item, prop->internal_type, + &int_val, &uint_val, &double_val); + write_binary_item (fp, plyfile->file_type, int_val, uint_val, + double_val, prop->external_type); + } + } + + } +} + + +/****************************************************************************** +Specify a comment that will be written in the header. + + Entry: + plyfile - file identifier + comment - the comment to be written + ******************************************************************************/ + + void ply_put_comment(PlyFile *plyfile, char *comment) + { + /* (re)allocate space for new comment */ + if (plyfile->num_comments == 0) + plyfile->comments = (char **) myalloc (sizeof (char *)); + else + plyfile->comments = (char **) realloc (plyfile->comments, + sizeof (char *) * (plyfile->num_comments + 1)); + + /* add comment to list */ + plyfile->comments[plyfile->num_comments] = _strdup (comment); + plyfile->num_comments++; + } + + + /****************************************************************************** + Specify a piece of object information (arbitrary text) that will be written + in the header. + + Entry: + plyfile - file identifier + obj_info - the text information to be written + ******************************************************************************/ + + void ply_put_obj_info(PlyFile *plyfile, char *obj_info) + { + /* (re)allocate space for new info */ + if (plyfile->num_obj_info == 0) + plyfile->obj_info = (char **) myalloc (sizeof (char *)); + else + plyfile->obj_info = (char **) realloc (plyfile->obj_info, + sizeof (char *) * (plyfile->num_obj_info + 1)); + + /* add info to list */ + plyfile->obj_info[plyfile->num_obj_info] = _strdup (obj_info); + plyfile->num_obj_info++; + } + + + + + + + + /*************/ + /* Reading */ + /*************/ + + + + /****************************************************************************** + Given a file pointer, get ready to read PLY data from the file. + + Entry: + fp - the given file pointer + + Exit: + nelems - number of elements in object + elem_names - list of element names + returns a pointer to a PlyFile, used to refer to this file, or NULL if error + ******************************************************************************/ + + PlyFile *ply_read(FILE *fp, int *nelems, char ***elem_names) + { + int i,j; + PlyFile *plyfile; + int nwords; + char **words; + char **elist; + PlyElement *elem; + char *orig_line; + /* check for NULL file pointer */ + if (fp == NULL) + return (NULL); + + if (native_binary_type == -1) + get_native_binary_type(); + if (!types_checked) + check_types(); + /* create record for this object */ + + plyfile = (PlyFile *) myalloc (sizeof (PlyFile)); + plyfile->nelems = 0; + plyfile->comments = NULL; + plyfile->num_comments = 0; + plyfile->obj_info = NULL; + plyfile->num_obj_info = 0; + plyfile->fp = fp; + plyfile->other_elems = NULL; + + /* read and parse the file's header */ + + words = get_words (plyfile->fp, &nwords, &orig_line); + if (!words || !equal_strings (words[0], "ply")) + { + if (words) + free(words); + return (NULL); + } + while (words) { + /* parse words */ + + if (equal_strings (words[0], "format")) { + if (nwords != 3) { + free(words); + return (NULL); + } + if (equal_strings (words[1], "ascii")) + plyfile->file_type = PLY_ASCII; + else if (equal_strings (words[1], "binary_big_endian")) + plyfile->file_type = PLY_BINARY_BE; + else if (equal_strings (words[1], "binary_little_endian")) + plyfile->file_type = PLY_BINARY_LE; + else { + free(words); + return (NULL); + } + plyfile->version = (float)atof (words[2]); + } + else if (equal_strings (words[0], "element")) + add_element (plyfile, words); + else if (equal_strings (words[0], "property")) + add_property (plyfile, words); + else if (equal_strings (words[0], "comment")) + add_comment (plyfile, orig_line); + else if (equal_strings (words[0], "obj_info")) + add_obj_info (plyfile, orig_line); + else if (equal_strings (words[0], "end_header")) { + free(words); + break; + } + + /* free up words space */ + free (words); + + words = get_words (plyfile->fp, &nwords, &orig_line); + } + + /* create tags for each property of each element, to be used */ + /* later to say whether or not to store each property for the user */ + + for (i = 0; i < plyfile->nelems; i++) { + elem = plyfile->elems[i]; + elem->store_prop = (char *) myalloc (sizeof (char) * elem->nprops); + for (j = 0; j < elem->nprops; j++) + elem->store_prop[j] = DONT_STORE_PROP; + elem->other_offset = NO_OTHER_PROPS; /* no "other" props by default */ + } + + /* set return values about the elements */ + + elist = (char **) myalloc (sizeof (char *) * plyfile->nelems); + for (i = 0; i < plyfile->nelems; i++) + elist[i] = _strdup (plyfile->elems[i]->name); + + *elem_names = elist; + *nelems = plyfile->nelems; + + /* return a pointer to the file's information */ + + return (plyfile); +} + + +/****************************************************************************** +Open a polygon file for reading. + + Entry: + filename - name of file to read from + + Exit: + nelems - number of elements in object + elem_names - list of element names + file_type - file type, either ascii or binary + version - version number of PLY file + returns a file identifier, used to refer to this file, or NULL if error + ******************************************************************************/ + + PlyFile *ply_open_for_reading( + char *filename, + int *nelems, + char ***elem_names, + int *file_type, + float *version + ) + { + FILE *fp; + PlyFile *plyfile; + char *name; + + /* tack on the extension .ply, if necessary */ + + name = (char *) myalloc (int(sizeof (char) * (strlen (filename) + 5))); + strcpy (name, filename); + if (strlen (name) < 4 || + strcmp (name + strlen (name) - 4, ".ply") != 0) + strcat (name, ".ply"); + + /* open the file for reading */ + + fp = fopen (name, "rb"); + if (fp == NULL) + return (NULL); + + /* create the PlyFile data structure */ + + plyfile = ply_read (fp, nelems, elem_names); + + /* determine the file type and version */ + + *file_type = plyfile->file_type; + *version = plyfile->version; + + /* return a pointer to the file's information */ + + free(name); + return (plyfile); + } + + + /****************************************************************************** + Get information about a particular element. + + Entry: + plyfile - file identifier + elem_name - name of element to get information about + + Exit: + nelems - number of elements of this type in the file + nprops - number of properties + returns a list of properties, or NULL if the file doesn't contain that elem + ******************************************************************************/ + + PlyProperty **ply_get_element_description( + PlyFile *plyfile, + char *elem_name, + int *nelems, + int *nprops + ) + { + int i; + PlyElement *elem; + PlyProperty *prop; + PlyProperty **prop_list; + + /* find information about the element */ + elem = find_element (plyfile, elem_name); + if (elem == NULL) + return (NULL); + + *nelems = elem->num; + *nprops = elem->nprops; + + /* make a copy of the element's property list */ + prop_list = (PlyProperty **) myalloc (sizeof (PlyProperty *) * elem->nprops); + for (i = 0; i < elem->nprops; i++) { + prop = (PlyProperty *) myalloc (sizeof (PlyProperty)); + copy_property (prop, elem->props[i]); + prop_list[i] = prop; + } + + /* return this duplicate property list */ + return (prop_list); + } + + + /****************************************************************************** + Specify which properties of an element are to be returned. This should be + called before a call to the routine ply_get_element(). + + Entry: + plyfile - file identifier + elem_name - which element we're talking about + nprops - number of properties + prop_list - list of properties + ******************************************************************************/ + + void ply_get_element_setup( + PlyFile *plyfile, + char *elem_name, + int nprops, + PlyProperty *prop_list + ) + { + int i; + PlyElement *elem; + PlyProperty *prop; + int index; + + /* find information about the element */ + elem = find_element (plyfile, elem_name); + plyfile->which_elem = elem; + + /* deposit the property information into the element's description */ + for (i = 0; i < nprops; i++) { + + /* look for actual property */ + prop = find_property (elem, prop_list[i].name, &index); + if (prop == NULL) { + fprintf (stderr, "Warning: Can't find property '%s' in element '%s'\n", + prop_list[i].name, elem_name); + continue; + } + + /* store its description */ + prop->internal_type = prop_list[i].internal_type; + prop->offset = prop_list[i].offset; + prop->count_internal = prop_list[i].count_internal; + prop->count_offset = prop_list[i].count_offset; + + /* specify that the user wants this property */ + elem->store_prop[index] = STORE_PROP; + } + } + + + /****************************************************************************** + Specify a property of an element that is to be returned. This should be + called (usually multiple times) before a call to the routine ply_get_element(). + This routine should be used in preference to the less flexible old routine + called ply_get_element_setup(). + + Entry: + plyfile - file identifier + elem_name - which element we're talking about + prop - property to add to those that will be returned + ******************************************************************************/ + + int ply_get_property( + PlyFile *plyfile, + char *elem_name, + PlyProperty *prop + ) + { + PlyElement *elem; + PlyProperty *prop_ptr; + int index; + + /* find information about the element */ + elem = find_element (plyfile, elem_name); + plyfile->which_elem = elem; + + /* deposit the property information into the element's description */ + + prop_ptr = find_property (elem, prop->name, &index); + if (prop_ptr == NULL) { +// fprintf (stderr, "Warning: Can't find property '%s' in element '%s'\n", +// prop->name, elem_name); +// return; + return 0; + } + prop_ptr->internal_type = prop->internal_type; + prop_ptr->offset = prop->offset; + prop_ptr->count_internal = prop->count_internal; + prop_ptr->count_offset = prop->count_offset; + + /* specify that the user wants this property */ + elem->store_prop[index] = STORE_PROP; + return 1; + } + + + /****************************************************************************** + Read one element from the file. This routine assumes that we're reading + the type of element specified in the last call to the routine + ply_get_element_setup(). + + Entry: + plyfile - file identifier + elem_ptr - pointer to location where the element information should be put + ******************************************************************************/ + + void ply_get_element(PlyFile *plyfile, void *elem_ptr) + { + if (plyfile->file_type == PLY_ASCII) + ascii_get_element (plyfile, (char *) elem_ptr); + else + binary_get_element (plyfile, (char *) elem_ptr); + } + + + /****************************************************************************** + Extract the comments from the header information of a PLY file. + + Entry: + plyfile - file identifier + + Exit: + num_comments - number of comments returned + returns a pointer to a list of comments + ******************************************************************************/ + + char **ply_get_comments(PlyFile *plyfile, int *num_comments) + { + *num_comments = plyfile->num_comments; + return (plyfile->comments); + } + + + /****************************************************************************** + Extract the object information (arbitrary text) from the header information + of a PLY file. + + Entry: + plyfile - file identifier + + Exit: + num_obj_info - number of lines of text information returned + returns a pointer to a list of object info lines + ******************************************************************************/ + + char **ply_get_obj_info(PlyFile *plyfile, int *num_obj_info) + { + *num_obj_info = plyfile->num_obj_info; + return (plyfile->obj_info); + } + + + /****************************************************************************** + Make ready for "other" properties of an element-- those properties that + the user has not explicitly asked for, but that are to be stashed away + in a special structure to be carried along with the element's other + information. + + Entry: + plyfile - file identifier + elem - element for which we want to save away other properties + ******************************************************************************/ + + void setup_other_props(PlyElement *elem) + { + int i; + PlyProperty *prop; + int size = 0; + int type_size; + + /* Examine each property in decreasing order of size. */ + /* We do this so that all data types will be aligned by */ + /* word, half-word, or whatever within the structure. */ + + for (type_size = 8; type_size > 0; type_size /= 2) { + + /* add up the space taken by each property, and save this information */ + /* away in the property descriptor */ + + for (i = 0; i < elem->nprops; i++) { + + /* don't bother with properties we've been asked to store explicitly */ + if (elem->store_prop[i]) + continue; + + prop = elem->props[i]; + + /* internal types will be same as external */ + prop->internal_type = prop->external_type; + prop->count_internal = prop->count_external; + + /* check list case */ + if (prop->is_list) { + + /* pointer to list */ + if (type_size == sizeof (void *)) { + prop->offset = size; + size += sizeof (void *); /* always use size of a pointer here */ + } + + /* count of number of list elements */ + if (type_size == ply_type_size[prop->count_external]) { + prop->count_offset = size; + size += ply_type_size[prop->count_external]; + } + } + /* not list */ + else if (type_size == ply_type_size[prop->external_type]) { + prop->offset = size; + size += ply_type_size[prop->external_type]; + } + } + + } + + /* save the size for the other_props structure */ + elem->other_size = size; + } + + + /****************************************************************************** + Specify that we want the "other" properties of an element to be tucked + away within the user's structure. The user needn't be concerned for how + these properties are stored. + + Entry: + plyfile - file identifier + elem_name - name of element that we want to store other_props in + offset - offset to where other_props will be stored inside user's structure + + Exit: + returns pointer to structure containing description of other_props + ******************************************************************************/ + + PlyOtherProp *ply_get_other_properties( + PlyFile *plyfile, + char *elem_name, + int offset + ) + { + int i; + PlyElement *elem; + PlyOtherProp *other; + PlyProperty *prop; + int nprops; + + /* find information about the element */ + elem = find_element (plyfile, elem_name); + if (elem == NULL) { + fprintf (stderr, "ply_get_other_properties: Can't find element '%s'\n", + elem_name); + return (NULL); + } + + /* remember that this is the "current" element */ + plyfile->which_elem = elem; + + /* save the offset to where to store the other_props */ + elem->other_offset = offset; + + /* place the appropriate pointers, etc. in the element's property list */ + setup_other_props (elem); + + /* create structure for describing other_props */ + other = (PlyOtherProp *) myalloc (sizeof (PlyOtherProp)); + other->name = _strdup (elem_name); + other->size = elem->other_size; + other->props = (PlyProperty **) myalloc (sizeof(PlyProperty) * elem->nprops); + + /* save descriptions of each "other" property */ + nprops = 0; + for (i = 0; i < elem->nprops; i++) { + if (elem->store_prop[i]) + continue; + prop = (PlyProperty *) myalloc (sizeof (PlyProperty)); + copy_property (prop, elem->props[i]); + other->props[nprops] = prop; + nprops++; + } + other->nprops = nprops; + + /* set other_offset pointer appropriately if there are NO other properties */ + if (other->nprops == 0) { + elem->other_offset = NO_OTHER_PROPS; + } + + /* return structure */ + return (other); + } + + + + + /*************************/ + /* Other Element Stuff */ + /*************************/ + + + + + /****************************************************************************** + Grab all the data for an element that a user does not want to explicitly + read in. + + Entry: + plyfile - pointer to file + elem_name - name of element whose data is to be read in + elem_count - number of instances of this element stored in the file + + Exit: + returns pointer to ALL the "other" element data for this PLY file + ******************************************************************************/ + + PlyOtherElems *ply_get_other_element ( + PlyFile *plyfile, + char *elem_name, + int elem_count + ) + { + int i; + PlyElement *elem; + PlyOtherElems *other_elems; + OtherElem *other; + + /* look for appropriate element */ + elem = find_element (plyfile, elem_name); + if (elem == NULL) { + fprintf (stderr, + "ply_get_other_element: can't find element '%s'\n", elem_name); + exit (-1); + } + + /* create room for the new "other" element, initializing the */ + /* other data structure if necessary */ + + if (plyfile->other_elems == NULL) { + plyfile->other_elems = (PlyOtherElems *) myalloc (sizeof (PlyOtherElems)); + other_elems = plyfile->other_elems; + other_elems->other_list = (OtherElem *) myalloc (sizeof (OtherElem)); + other = &(other_elems->other_list[0]); + other_elems->num_elems = 1; + } + else { + other_elems = plyfile->other_elems; + other_elems->other_list = (OtherElem *) realloc (other_elems->other_list, + sizeof (OtherElem) * other_elems->num_elems + 1); + other = &(other_elems->other_list[other_elems->num_elems]); + other_elems->num_elems++; + } + + /* count of element instances in file */ + other->elem_count = elem_count; + + /* save name of element */ + other->elem_name = _strdup (elem_name); + + /* create a list to hold all the current elements */ + other->other_data = (OtherData **) + malloc (sizeof (OtherData *) * other->elem_count); + + /* set up for getting elements */ + other->other_props = ply_get_other_properties (plyfile, elem_name, + offsetof(OtherData,other_props)); + + /* grab all these elements */ + for (i = 0; i < other->elem_count; i++) { + /* grab and element from the file */ + other->other_data[i] = (OtherData *) malloc (sizeof (OtherData)); + ply_get_element (plyfile, (void *) other->other_data[i]); + } + + /* return pointer to the other elements data */ + return (other_elems); + } + + + /****************************************************************************** + Pass along a pointer to "other" elements that we want to save in a given + PLY file. These other elements were presumably read from another PLY file. + + Entry: + plyfile - file pointer in which to store this other element info + other_elems - info about other elements that we want to store + ******************************************************************************/ + + void ply_describe_other_elements ( + PlyFile *plyfile, + PlyOtherElems *other_elems + ) + { + int i; + OtherElem *other; + PlyElement *elem; + + /* ignore this call if there is no other element */ + if (other_elems == NULL) + return; + + /* save pointer to this information */ + plyfile->other_elems = other_elems; + + /* describe the other properties of this element */ + /* store them in the main element list as elements with + only other properties */ + + REALLOCN(plyfile->elems, PlyElement *, + plyfile->nelems, plyfile->nelems + other_elems->num_elems); + for (i = 0; i < other_elems->num_elems; i++) { + other = &(other_elems->other_list[i]); + elem = (PlyElement *) myalloc (sizeof (PlyElement)); + plyfile->elems[plyfile->nelems++] = elem; + elem->name = _strdup (other->elem_name); + elem->num = other->elem_count; + elem->nprops = 0; + ply_describe_other_properties (plyfile, other->other_props, + offsetof(OtherData,other_props)); + } + } + + + /****************************************************************************** + Write out the "other" elements specified for this PLY file. + + Entry: + plyfile - pointer to PLY file to write out other elements for + ******************************************************************************/ + + void ply_put_other_elements (PlyFile *plyfile) + { + int i,j; + OtherElem *other; + + /* make sure we have other elements to write */ + if (plyfile->other_elems == NULL) + return; + + /* write out the data for each "other" element */ + + for (i = 0; i < plyfile->other_elems->num_elems; i++) { + + other = &(plyfile->other_elems->other_list[i]); + ply_put_element_setup (plyfile, other->elem_name); + + /* write out each instance of the current element */ + for (j = 0; j < other->elem_count; j++) + ply_put_element (plyfile, (void *) other->other_data[j]); + } + } + + + /****************************************************************************** + Free up storage used by an "other" elements data structure. + + Entry: + other_elems - data structure to free up + ******************************************************************************/ + + void ply_free_other_elements (PlyOtherElems *other_elems) + { + other_elems = other_elems; + } + + + + /*******************/ + /* Miscellaneous */ + /*******************/ + + + + /****************************************************************************** + Close a PLY file. + + Entry: + plyfile - identifier of file to close + ******************************************************************************/ + + void ply_close(PlyFile *plyfile) + { + fclose (plyfile->fp); + + /* free up memory associated with the PLY file */ + free (plyfile); + } + + + /****************************************************************************** + Get version number and file type of a PlyFile. + + Entry: + ply - pointer to PLY file + + Exit: + version - version of the file + file_type - PLY_ASCII, PLY_BINARY_BE, or PLY_BINARY_LE + ******************************************************************************/ + + void ply_get_info(PlyFile *ply, float *version, int *file_type) + { + if (ply == NULL) + return; + + *version = ply->version; + *file_type = ply->file_type; + } + + + /****************************************************************************** + Compare two strings. Returns 1 if they are the same, 0 if not. + ******************************************************************************/ + + int equal_strings(const char *s1, const char *s2) + { + + while (*s1 && *s2) + if (*s1++ != *s2++) + return (0); + + if (*s1 != *s2) + return (0); + else + return (1); + } + + + /****************************************************************************** + Find an element from the element list of a given PLY object. + + Entry: + plyfile - file id for PLY file + element - name of element we're looking for + + Exit: + returns the element, or NULL if not found + ******************************************************************************/ + + PlyElement *find_element(PlyFile *plyfile, const char *element) + { + int i; + + for (i = 0; i < plyfile->nelems; i++) + if (equal_strings (element, plyfile->elems[i]->name)) + return (plyfile->elems[i]); + + return (NULL); + } + + + /****************************************************************************** + Find a property in the list of properties of a given element. + + Entry: + elem - pointer to element in which we want to find the property + prop_name - name of property to find + + Exit: + index - index to position in list + returns a pointer to the property, or NULL if not found + ******************************************************************************/ + + PlyProperty *find_property(PlyElement *elem, const char *prop_name, int *index) + { + int i; + + for (i = 0; i < elem->nprops; i++) + if (equal_strings (prop_name, elem->props[i]->name)) { + *index = i; + return (elem->props[i]); + } + + *index = -1; + return (NULL); + } + + + /****************************************************************************** + Read an element from an ascii file. + + Entry: + plyfile - file identifier + elem_ptr - pointer to element + ******************************************************************************/ + + void ascii_get_element(PlyFile *plyfile, char *elem_ptr) + { + int j,k; + PlyElement *elem; + PlyProperty *prop; + char **words; + int nwords; + int which_word; + char *elem_data,*item=NULL; + char *item_ptr; + int item_size; + int int_val; + unsigned int uint_val; + double double_val; + int list_count; + int store_it; + char **store_array; + char *orig_line; + char *other_data=NULL; + int other_flag; + + /* the kind of element we're reading currently */ + elem = plyfile->which_elem; + + /* do we need to setup for other_props? */ + + if (elem->other_offset != NO_OTHER_PROPS) { + char **ptr; + other_flag = 1; + /* make room for other_props */ + other_data = (char *) myalloc (elem->other_size); + /* store pointer in user's structure to the other_props */ + ptr = (char **) (elem_ptr + elem->other_offset); + *ptr = other_data; + } + else + other_flag = 0; + + /* read in the element */ + + words = get_words (plyfile->fp, &nwords, &orig_line); + if (words == NULL) { + fprintf (stderr, "ply_get_element: unexpected end of file\n"); + exit (-1); + } + + which_word = 0; + + for (j = 0; j < elem->nprops; j++) { + + prop = elem->props[j]; + store_it = (elem->store_prop[j] | other_flag); + + /* store either in the user's structure or in other_props */ + if (elem->store_prop[j]) + elem_data = elem_ptr; + else + elem_data = other_data; + + if (prop->is_list) { /* a list */ + + /* get and store the number of items in the list */ + get_ascii_item (words[which_word++], prop->count_external, + &int_val, &uint_val, &double_val); + if (store_it) { + item = elem_data + prop->count_offset; + store_item(item, prop->count_internal, int_val, uint_val, double_val); + } + + /* allocate space for an array of items and store a ptr to the array */ + list_count = int_val; + item_size = ply_type_size[prop->internal_type]; + store_array = (char **) (elem_data + prop->offset); + + if (list_count == 0) { + if (store_it) + *store_array = NULL; + } + else { + if (store_it) { + item_ptr = (char *) myalloc (sizeof (char) * item_size * list_count); + item = item_ptr; + *store_array = item_ptr; + } + + /* read items and store them into the array */ + for (k = 0; k < list_count; k++) { + get_ascii_item (words[which_word++], prop->external_type, + &int_val, &uint_val, &double_val); + if (store_it) { + store_item (item, prop->internal_type, + int_val, uint_val, double_val); + item += item_size; + } + } + } + + } + else { /* not a list */ + get_ascii_item (words[which_word++], prop->external_type, + &int_val, &uint_val, &double_val); + if (store_it) { + item = elem_data + prop->offset; + store_item (item, prop->internal_type, int_val, uint_val, double_val); + } + } + + } + + free (words); +} + + +/****************************************************************************** +Read an element from a binary file. + + Entry: + plyfile - file identifier + elem_ptr - pointer to an element + ******************************************************************************/ + + void binary_get_element(PlyFile *plyfile, char *elem_ptr) + { + int j,k; + PlyElement *elem; + PlyProperty *prop; + FILE *fp = plyfile->fp; + char *elem_data,*item=NULL; + char *item_ptr; + int item_size; + int int_val; + unsigned int uint_val; + double double_val; + int list_count; + int store_it; + char **store_array; + char *other_data=NULL; + int other_flag; + + /* the kind of element we're reading currently */ + elem = plyfile->which_elem; + + /* do we need to setup for other_props? */ + + if (elem->other_offset != NO_OTHER_PROPS) { + char **ptr; + other_flag = 1; + /* make room for other_props */ + other_data = (char *) myalloc (elem->other_size); + /* store pointer in user's structure to the other_props */ + ptr = (char **) (elem_ptr + elem->other_offset); + *ptr = other_data; + } + else + other_flag = 0; + + /* read in a number of elements */ + + for (j = 0; j < elem->nprops; j++) { + + prop = elem->props[j]; + store_it = (elem->store_prop[j] | other_flag); + + /* store either in the user's structure or in other_props */ + if (elem->store_prop[j]) + elem_data = elem_ptr; + else + elem_data = other_data; + + if (prop->is_list) { /* a list */ + + /* get and store the number of items in the list */ + get_binary_item (fp, plyfile->file_type, prop->count_external, + &int_val, &uint_val, &double_val); + if (store_it) { + item = elem_data + prop->count_offset; + store_item(item, prop->count_internal, int_val, uint_val, double_val); + } + + /* allocate space for an array of items and store a ptr to the array */ + list_count = int_val; + item_size = ply_type_size[prop->internal_type]; + store_array = (char **) (elem_data + prop->offset); + if (list_count == 0) { + if (store_it) + *store_array = NULL; + } + else { + if (store_it) { + item_ptr = (char *) myalloc (sizeof (char) * item_size * list_count); + item = item_ptr; + *store_array = item_ptr; + } + + /* read items and store them into the array */ + for (k = 0; k < list_count; k++) { + get_binary_item (fp, plyfile->file_type, prop->external_type, + &int_val, &uint_val, &double_val); + if (store_it) { + store_item (item, prop->internal_type, + int_val, uint_val, double_val); + item += item_size; + } + } + } + + } + else { /* not a list */ + get_binary_item (fp, plyfile->file_type, prop->external_type, + &int_val, &uint_val, &double_val); + if (store_it) { + item = elem_data + prop->offset; + store_item (item, prop->internal_type, int_val, uint_val, double_val); + } + } + + } + } + + + /****************************************************************************** + Write to a file the word that represents a PLY data type. + + Entry: + fp - file pointer + code - code for type + ******************************************************************************/ + + void write_scalar_type (FILE *fp, int code) + { + /* make sure this is a valid code */ + + if (code <= PLY_START_TYPE || code >= PLY_END_TYPE) { + fprintf (stderr, "write_scalar_type: bad data code = %d\n", code); + exit (-1); + } + + /* write the code to a file */ + + fprintf (fp, "%s", type_names[code]); + } + + /****************************************************************************** + Reverse the order in an array of bytes. This is the conversion from big + endian to little endian and vice versa + + Entry: + bytes - array of bytes to reverse (in place) + num_bytes - number of bytes in array + ******************************************************************************/ + + void swap_bytes(char *bytes, int num_bytes) + { + int i; + char temp; + + for (i=0; i < num_bytes/2; i++) + { + temp = bytes[i]; + bytes[i] = bytes[(num_bytes-1)-i]; + bytes[(num_bytes-1)-i] = temp; + } + } + + /****************************************************************************** + Find out if this machine is big endian or little endian + + Exit: + set global variable, native_binary_type = + either PLY_BINARY_BE or PLY_BINARY_LE + + ******************************************************************************/ + + void get_native_binary_type() + { + endian_test_type test; + + test.int_value = 0; + test.int_value = 1; + if (test.byte_values[0] == 1) + native_binary_type = PLY_BINARY_LE; + else if (test.byte_values[sizeof(int)-1] == 1) + native_binary_type = PLY_BINARY_BE; + else + { + fprintf(stderr, "ply: Couldn't determine machine endianness.\n"); + fprintf(stderr, "ply: Exiting...\n"); + exit(1); + } + } + + /****************************************************************************** + Verify that all the native types are the sizes we need + + + ******************************************************************************/ + + void check_types() + { + if ((ply_type_size[PLY_CHAR] != sizeof(char)) || + (ply_type_size[PLY_SHORT] != sizeof(short)) || + (ply_type_size[PLY_INT] != sizeof(int)) || + (ply_type_size[PLY_UCHAR] != sizeof(unsigned char)) || + (ply_type_size[PLY_USHORT] != sizeof(unsigned short)) || + (ply_type_size[PLY_UINT] != sizeof(unsigned int)) || + (ply_type_size[PLY_FLOAT] != sizeof(float)) || + (ply_type_size[PLY_DOUBLE] != sizeof(double))) + { + fprintf(stderr, "ply: Type sizes do not match built-in types\n"); + fprintf(stderr, "ply: Exiting...\n"); + exit(1); + } + + types_checked = 1; + } + + /****************************************************************************** + Get a text line from a file and break it up into words. + + IMPORTANT: The calling routine call "free" on the returned pointer once + finished with it. + + Entry: + fp - file to read from + + Exit: + nwords - number of words returned + orig_line - the original line of characters + returns a list of words from the line, or NULL if end-of-file + ******************************************************************************/ + + char **get_words(FILE *fp, int *nwords, char **orig_line) + { +#define BIG_STRING 4096 + static char str[BIG_STRING]; + static char str_copy[BIG_STRING]; + char **words; + int max_words = 10; + int num_words = 0; + char *ptr,*ptr2; + char *result; + + words = (char **) myalloc (sizeof (char *) * max_words); + + /* read in a line */ + result = fgets (str, BIG_STRING, fp); + if (result == NULL) { + *nwords = 0; + *orig_line = NULL; + return (NULL); + } + /* convert line-feed and tabs into spaces */ + /* (this guarentees that there will be a space before the */ + /* null character at the end of the string) */ + + str[BIG_STRING-2] = ' '; + str[BIG_STRING-1] = '\0'; + + for (ptr = str, ptr2 = str_copy; *ptr != '\0'; ptr++, ptr2++) { + *ptr2 = *ptr; + // Added line here to manage carriage returns + if (*ptr == '\t' || *ptr == '\r') { + *ptr = ' '; + *ptr2 = ' '; + } + else if (*ptr == '\n') { + *ptr = ' '; + *ptr2 = '\0'; + break; + } + } + + /* find the words in the line */ + + ptr = str; + while (*ptr != '\0') { + + /* jump over leading spaces */ + while (*ptr == ' ') + ptr++; + + /* break if we reach the end */ + if (*ptr == '\0') + break; + + /* save pointer to beginning of word */ + if (num_words >= max_words) { + max_words += 10; + words = (char **) realloc (words, sizeof (char *) * max_words); + } + words[num_words++] = ptr; + + /* jump over non-spaces */ + while (*ptr != ' ') + ptr++; + + /* place a null character here to mark the end of the word */ + *ptr++ = '\0'; + } + + /* return the list of words */ + *nwords = num_words; + *orig_line = str_copy; + return (words); + } + + + /****************************************************************************** + Return the value of an item, given a pointer to it and its type. + + Entry: + item - pointer to item + type - data type that "item" points to + + Exit: + returns a double-precision float that contains the value of the item + ******************************************************************************/ + + double get_item_value(char *item, int type) + { + unsigned char *puchar; + char *pchar; + short int *pshort; + unsigned short int *pushort; + int *pint; + unsigned int *puint; + float *pfloat; + double *pdouble; + int int_value; + unsigned int uint_value; + double double_value; + + switch (type) { + case PLY_CHAR: + case PLY_INT_8: + pchar = (char *) item; + int_value = *pchar; + return ((double) int_value); + case PLY_UCHAR: + case PLY_UINT_8: + puchar = (unsigned char *) item; + int_value = *puchar; + return ((double) int_value); + case PLY_SHORT: + case PLY_INT_16: + pshort = (short int *) item; + int_value = *pshort; + return ((double) int_value); + case PLY_USHORT: + case PLY_UINT_16: + pushort = (unsigned short int *) item; + int_value = *pushort; + return ((double) int_value); + case PLY_INT: + case PLY_INT_32: + pint = (int *) item; + int_value = *pint; + return ((double) int_value); + case PLY_UINT: + case PLY_UINT_32: + puint = (unsigned int *) item; + uint_value = *puint; + return ((double) uint_value); + case PLY_FLOAT: + case PLY_FLOAT_32: + pfloat = (float *) item; + double_value = *pfloat; + return (double_value); + case PLY_DOUBLE: + case PLY_FLOAT_64: + pdouble = (double *) item; + double_value = *pdouble; + return (double_value); + default: + fprintf (stderr, "get_item_value: bad type = %d\n", type); + exit (-1); + } + } + + + /****************************************************************************** + Write out an item to a file as raw binary bytes. + + Entry: + fp - file to write to + int_val - integer version of item + uint_val - unsigned integer version of item + double_val - double-precision float version of item + type - data type to write out + ******************************************************************************/ + + void write_binary_item( + FILE *fp, + int file_type, + int int_val, + unsigned int uint_val, + double double_val, + int type + ) + { + unsigned char uchar_val; + char char_val; + unsigned short ushort_val; + short short_val; + float float_val; + void *value; + + switch (type) { + case PLY_CHAR: + case PLY_INT_8: + char_val = char(int_val); + value = &char_val; + break; + case PLY_SHORT: + case PLY_INT_16: + short_val = short(int_val); + value = &short_val; + break; + case PLY_INT: + case PLY_INT_32: + value = &int_val; + break; + case PLY_UCHAR: + case PLY_UINT_8: + uchar_val = (unsigned char)(uint_val); + value = &uchar_val; + break; + case PLY_USHORT: + case PLY_UINT_16: + ushort_val = (unsigned short)(uint_val); + value = &ushort_val; + break; + case PLY_UINT: + case PLY_UINT_32: + value = &uint_val; + break; + case PLY_FLOAT: + case PLY_FLOAT_32: + float_val = (float)double_val; + value = &float_val; + break; + case PLY_DOUBLE: + case PLY_FLOAT_64: + value = &double_val; + break; + default: + fprintf (stderr, "write_binary_item: bad type = %d\n", type); + exit (-1); + } + + + if ((file_type != native_binary_type) && (ply_type_size[type] > 1)) + swap_bytes((char *)value, ply_type_size[type]); + + if (fwrite (value, ply_type_size[type], 1, fp) != 1) + { + fprintf(stderr, "PLY ERROR: fwrite() failed -- aborting.\n"); + exit(1); + } + } + + + /****************************************************************************** + Write out an item to a file as ascii characters. + + Entry: + fp - file to write to + int_val - integer version of item + uint_val - unsigned integer version of item + double_val - double-precision float version of item + type - data type to write out + ******************************************************************************/ + + void write_ascii_item( + FILE *fp, + int int_val, + unsigned int uint_val, + double double_val, + int type + ) + { + switch (type) { + case PLY_CHAR: + case PLY_INT_8: + case PLY_SHORT: + case PLY_INT_16: + case PLY_INT: + case PLY_INT_32: + if (fprintf (fp, "%d ", int_val) <= 0) + { + fprintf(stderr, "PLY ERROR: fprintf() failed -- aborting.\n"); + exit(1); + } + break; + case PLY_UCHAR: + case PLY_UINT_8: + case PLY_USHORT: + case PLY_UINT_16: + case PLY_UINT: + case PLY_UINT_32: + + if (fprintf (fp, "%u ", uint_val) <= 0) + { + fprintf(stderr, "PLY ERROR: fprintf() failed -- aborting.\n"); + exit(1); + } + break; + case PLY_FLOAT: + case PLY_FLOAT_32: + case PLY_DOUBLE: + case PLY_FLOAT_64: + if (fprintf (fp, "%g ", double_val) <= 0) + { + fprintf(stderr, "PLY ERROR: fprintf() failed -- aborting.\n"); + exit(1); + } + break; + default: + fprintf (stderr, "write_ascii_item: bad type = %d\n", type); + exit (-1); + } + } + + + /****************************************************************************** + Write out an item to a file as ascii characters. + + Entry: + fp - file to write to + item - pointer to item to write + type - data type that "item" points to + + Exit: + returns a double-precision float that contains the value of the written item + ******************************************************************************/ + + double old_write_ascii_item(FILE *fp, char *item, int type) + { + unsigned char *puchar; + char *pchar; + short int *pshort; + unsigned short int *pushort; + int *pint; + unsigned int *puint; + float *pfloat; + double *pdouble; + int int_value; + unsigned int uint_value; + double double_value; + + switch (type) { + case PLY_CHAR: + case PLY_INT_8: + pchar = (char *) item; + int_value = *pchar; + fprintf (fp, "%d ", int_value); + return ((double) int_value); + case PLY_UCHAR: + case PLY_UINT_8: + puchar = (unsigned char *) item; + int_value = *puchar; + fprintf (fp, "%d ", int_value); + return ((double) int_value); + case PLY_SHORT: + case PLY_INT_16: + pshort = (short int *) item; + int_value = *pshort; + fprintf (fp, "%d ", int_value); + return ((double) int_value); + case PLY_USHORT: + case PLY_UINT_16: + pushort = (unsigned short int *) item; + int_value = *pushort; + fprintf (fp, "%d ", int_value); + return ((double) int_value); + case PLY_INT: + case PLY_INT_32: + pint = (int *) item; + int_value = *pint; + fprintf (fp, "%d ", int_value); + return ((double) int_value); + case PLY_UINT: + case PLY_UINT_32: + puint = (unsigned int *) item; + uint_value = *puint; + fprintf (fp, "%u ", uint_value); + return ((double) uint_value); + case PLY_FLOAT: + case PLY_FLOAT_32: + pfloat = (float *) item; + double_value = *pfloat; + fprintf (fp, "%g ", double_value); + return (double_value); + case PLY_DOUBLE: + case PLY_FLOAT_64: + pdouble = (double *) item; + double_value = *pdouble; + fprintf (fp, "%g ", double_value); + return (double_value); + default: + fprintf (stderr, "old_write_ascii_item: bad type = %d\n", type); + exit (-1); + } + } + + + /****************************************************************************** + Get the value of an item that is in memory, and place the result + into an integer, an unsigned integer and a double. + + Entry: + ptr - pointer to the item + type - data type supposedly in the item + + Exit: + int_val - integer value + uint_val - unsigned integer value + double_val - double-precision floating point value + ******************************************************************************/ + + void get_stored_item( + void *ptr, + int type, + int *int_val, + unsigned int *uint_val, + double *double_val + ) + { + switch (type) { + case PLY_CHAR: + case PLY_INT_8: + *int_val = *((char *) ptr); + *uint_val = *int_val; + *double_val = *int_val; + break; + case PLY_UCHAR: + case PLY_UINT_8: + *uint_val = *((unsigned char *) ptr); + *int_val = *uint_val; + *double_val = *uint_val; + break; + case PLY_SHORT: + case PLY_INT_16: + *int_val = *((short int *) ptr); + *uint_val = *int_val; + *double_val = *int_val; + break; + case PLY_USHORT: + case PLY_UINT_16: + *uint_val = *((unsigned short int *) ptr); + *int_val = *uint_val; + *double_val = *uint_val; + break; + case PLY_INT: + case PLY_INT_32: + *int_val = *((int *) ptr); + *uint_val = *int_val; + *double_val = *int_val; + break; + case PLY_UINT: + case PLY_UINT_32: + *uint_val = *((unsigned int *) ptr); + *int_val = *uint_val; + *double_val = *uint_val; + break; + case PLY_FLOAT: + case PLY_FLOAT_32: + *double_val = *((float *) ptr); + *int_val = (int) *double_val; + *uint_val = (unsigned int) *double_val; + break; + case PLY_DOUBLE: + case PLY_FLOAT_64: + *double_val = *((double *) ptr); + *int_val = (int) *double_val; + *uint_val = (unsigned int) *double_val; + break; + default: + fprintf (stderr, "get_stored_item: bad type = %d\n", type); + exit (-1); + } + } + + + /****************************************************************************** + Get the value of an item from a binary file, and place the result + into an integer, an unsigned integer and a double. + + Entry: + fp - file to get item from + type - data type supposedly in the word + + Exit: + int_val - integer value + uint_val - unsigned integer value + double_val - double-precision floating point value + ******************************************************************************/ + + void get_binary_item( + FILE *fp, + int file_type, + int type, + int *int_val, + unsigned int *uint_val, + double *double_val + ) + { + char c[8]; + void *ptr; + + ptr = (void *) c; + + if (fread (ptr, ply_type_size[type], 1, fp) != 1) + { + fprintf(stderr, "PLY ERROR: fread() failed -- aborting.\n"); + exit(1); + } + + + if ((file_type != native_binary_type) && (ply_type_size[type] > 1)) + swap_bytes((char *)ptr, ply_type_size[type]); + + switch (type) { + case PLY_CHAR: + case PLY_INT_8: + *int_val = *((char *) ptr); + *uint_val = *int_val; + *double_val = *int_val; + break; + case PLY_UCHAR: + case PLY_UINT_8: + *uint_val = *((unsigned char *) ptr); + *int_val = *uint_val; + *double_val = *uint_val; + break; + case PLY_SHORT: + case PLY_INT_16: + *int_val = *((short int *) ptr); + *uint_val = *int_val; + *double_val = *int_val; + break; + case PLY_USHORT: + case PLY_UINT_16: + *uint_val = *((unsigned short int *) ptr); + *int_val = *uint_val; + *double_val = *uint_val; + break; + case PLY_INT: + case PLY_INT_32: + *int_val = *((int *) ptr); + *uint_val = *int_val; + *double_val = *int_val; + break; + case PLY_UINT: + case PLY_UINT_32: + *uint_val = *((unsigned int *) ptr); + *int_val = *uint_val; + *double_val = *uint_val; + break; + case PLY_FLOAT: + case PLY_FLOAT_32: + *double_val = *((float *) ptr); + *int_val = (int) *double_val; + *uint_val = (unsigned int) *double_val; + break; + case PLY_DOUBLE: + case PLY_FLOAT_64: + *double_val = *((double *) ptr); + *int_val = (int) *double_val; + *uint_val = (unsigned int) *double_val; + break; + default: + fprintf (stderr, "get_binary_item: bad type = %d\n", type); + exit (-1); + } + } + + + /****************************************************************************** + Extract the value of an item from an ascii word, and place the result + into an integer, an unsigned integer and a double. + + Entry: + word - word to extract value from + type - data type supposedly in the word + + Exit: + int_val - integer value + uint_val - unsigned integer value + double_val - double-precision floating point value + ******************************************************************************/ + + void get_ascii_item( + char *word, + int type, + int *int_val, + unsigned int *uint_val, + double *double_val + ) + { + switch (type) { + case PLY_CHAR: + case PLY_INT_8: + case PLY_UCHAR: + case PLY_UINT_8: + case PLY_SHORT: + case PLY_INT_16: + case PLY_USHORT: + case PLY_UINT_16: + case PLY_INT: + case PLY_INT_32: + *int_val = atoi (word); + *uint_val = (unsigned int) *int_val; + *double_val = (double) *int_val; + break; + + case PLY_UINT: + case PLY_UINT_32: + *uint_val = strtol (word, (char **) NULL, 10); + *int_val = (int) *uint_val; + *double_val = (double) *uint_val; + break; + + case PLY_FLOAT: + case PLY_FLOAT_32: + case PLY_DOUBLE: + case PLY_FLOAT_64: + *double_val = atof (word); + *int_val = (int) *double_val; + *uint_val = (unsigned int) *double_val; + break; + + default: + fprintf (stderr, "get_ascii_item: bad type = %d\n", type); + exit (-1); + } + } + + + /****************************************************************************** + Store a value into a place being pointed to, guided by a data type. + + Entry: + item - place to store value + type - data type + int_val - integer version of value + uint_val - unsigned integer version of value + double_val - double version of value + + Exit: + item - pointer to stored value + ******************************************************************************/ + + void store_item ( + char *item, + int type, + int int_val, + unsigned int uint_val, + double double_val + ) + { + unsigned char *puchar; + short int *pshort; + unsigned short int *pushort; + int *pint; + unsigned int *puint; + float *pfloat; + double *pdouble; + + + switch (type) { + case PLY_CHAR: + case PLY_INT_8: + *item = char(int_val); + break; + case PLY_UCHAR: + case PLY_UINT_8: + puchar = (unsigned char *) item; + *puchar = (unsigned char)(uint_val); + break; + case PLY_SHORT: + case PLY_INT_16: + pshort = (short *) item; + *pshort = short(int_val); + break; + case PLY_USHORT: + case PLY_UINT_16: + pushort = (unsigned short *) item; + *pushort = (unsigned short)(uint_val); + break; + case PLY_INT: + case PLY_INT_32: + pint = (int *) item; + *pint = int_val; + break; + case PLY_UINT: + case PLY_UINT_32: + puint = (unsigned int *) item; + *puint = uint_val; + break; + case PLY_FLOAT: + case PLY_FLOAT_32: + pfloat = (float *) item; + *pfloat = (float)double_val; + break; + case PLY_DOUBLE: + case PLY_FLOAT_64: + pdouble = (double *) item; + *pdouble = double_val; + break; + default: + fprintf (stderr, "store_item: bad type = %d\n", type); + exit (-1); + } + } + + + /****************************************************************************** + Add an element to a PLY file descriptor. + + Entry: + plyfile - PLY file descriptor + words - list of words describing the element + nwords - number of words in the list + ******************************************************************************/ + + void add_element (PlyFile *plyfile, char **words) + { + PlyElement *elem; + + /* create the new element */ + elem = (PlyElement *) myalloc (sizeof (PlyElement)); + elem->name = _strdup (words[1]); + elem->num = atoi (words[2]); + elem->nprops = 0; + + /* make room for new element in the object's list of elements */ + if (plyfile->nelems == 0) + plyfile->elems = (PlyElement **) myalloc (sizeof (PlyElement *)); + else + plyfile->elems = (PlyElement **) realloc (plyfile->elems, + sizeof (PlyElement *) * (plyfile->nelems + 1)); + + /* add the new element to the object's list */ + plyfile->elems[plyfile->nelems] = elem; + plyfile->nelems++; + } + + + /****************************************************************************** + Return the type of a property, given the name of the property. + + Entry: + name - name of property type + + Exit: + returns integer code for property, or 0 if not found + ******************************************************************************/ + + int get_prop_type(char *type_name) + { + int i; + + for (i = PLY_START_TYPE + 1; i < PLY_END_TYPE; i++) + if (equal_strings (type_name, type_names[i])) + return (i); + + /* if we get here, we didn't find the type */ + return (0); + } + + + /****************************************************************************** + Add a property to a PLY file descriptor. + + Entry: + plyfile - PLY file descriptor + words - list of words describing the property + nwords - number of words in the list + ******************************************************************************/ + + void add_property (PlyFile *plyfile, char **words) + { + PlyProperty *prop; + PlyElement *elem; + + /* create the new property */ + + prop = (PlyProperty *) myalloc (sizeof (PlyProperty)); + + if (equal_strings (words[1], "list")) { /* is a list */ + prop->count_external = get_prop_type (words[2]); + prop->external_type = get_prop_type (words[3]); + prop->name = _strdup (words[4]); + prop->is_list = 1; + } + else { /* not a list */ + prop->external_type = get_prop_type (words[1]); + prop->name = _strdup (words[2]); + prop->is_list = 0; + } + + /* add this property to the list of properties of the current element */ + + elem = plyfile->elems[plyfile->nelems - 1]; + + if (elem->nprops == 0) + elem->props = (PlyProperty **) myalloc (sizeof (PlyProperty *)); + else + elem->props = (PlyProperty **) realloc (elem->props, + sizeof (PlyProperty *) * (elem->nprops + 1)); + + elem->props[elem->nprops] = prop; + elem->nprops++; + } + + + /****************************************************************************** + Add a comment to a PLY file descriptor. + + Entry: + plyfile - PLY file descriptor + line - line containing comment + ******************************************************************************/ + + void add_comment (PlyFile *plyfile, char *line) + { + int i; + + /* skip over "comment" and leading spaces and tabs */ + i = 7; + while (line[i] == ' ' || line[i] == '\t') + i++; + + ply_put_comment (plyfile, &line[i]); + } + + + /****************************************************************************** + Add a some object information to a PLY file descriptor. + + Entry: + plyfile - PLY file descriptor + line - line containing text info + ******************************************************************************/ + + void add_obj_info (PlyFile *plyfile, char *line) + { + int i; + + /* skip over "obj_info" and leading spaces and tabs */ + i = 8; + while (line[i] == ' ' || line[i] == '\t') + i++; + + ply_put_obj_info (plyfile, &line[i]); + } + + + /****************************************************************************** + Copy a property. + ******************************************************************************/ + + void copy_property(PlyProperty *dest, PlyProperty *src) + { + dest->name = _strdup (src->name); + dest->external_type = src->external_type; + dest->internal_type = src->internal_type; + dest->offset = src->offset; + + dest->is_list = src->is_list; + dest->count_external = src->count_external; + dest->count_internal = src->count_internal; + dest->count_offset = src->count_offset; + } + + + /****************************************************************************** + Allocate some memory. + + Entry: + size - amount of memory requested (in bytes) + lnum - line number from which memory was requested + fname - file name from which memory was requested + ******************************************************************************/ + + char *my_alloc(int size, int lnum, const char *fname) + { + char *ptr; + + ptr = (char *) malloc (size); + + if (ptr == 0) { + fprintf(stderr, "Memory allocation bombed on line %d in %s\n", lnum, fname); + } + + return (ptr); + } + diff --git a/Src/PointStream.h b/Src/PointStream.h new file mode 100644 index 0000000..e949623 --- /dev/null +++ b/Src/PointStream.h @@ -0,0 +1,97 @@ +/* +Copyright (c) 2006, Michael Kazhdan and Matthew Bolitho +All rights reserved. + +Redistribution and use in source and binary forms, with or without modification, +are permitted provided that the following conditions are met: + +Redistributions of source code must retain the above copyright notice, this list of +conditions and the following disclaimer. Redistributions in binary form must reproduce +the above copyright notice, this list of conditions and the following disclaimer +in the documentation and/or other materials provided with the distribution. + +Neither the name of the Johns Hopkins University nor the names of its contributors +may be used to endorse or promote products derived from this software without specific +prior writften permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY +EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO THE IMPLIED WARRANTIES +OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT +SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED +TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR +BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN +ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH +DAMAGE. +*/ + +#ifndef POINT_STREAM_INCLUDED +#define POINT_STREAM_INCLUDED +#include "Ply.h" + +template< class Real > +class PointStream +{ +public: + virtual ~PointStream( void ){} + virtual void reset( void ) = 0; + virtual bool nextPoint( Point3D< Real >& p , Point3D< Real >& n ) = 0; +}; + +template< class Real > +class MemoryPointStream : public PointStream< Real > +{ + const std::pair< Point3D< Real > , Point3D< Real > >* _points; + size_t _pointCount; + size_t _current; +public: + MemoryPointStream( size_t pointCount , std::pair< Point3D< Real > , Point3D< Real > >* points ); + ~MemoryPointStream( void ); + void reset( void ); + bool nextPoint( Point3D< Real >& p , Point3D< Real >& n ); +}; + +template< class Real > +class ASCIIPointStream : public PointStream< Real > +{ + FILE* _fp; +public: + ASCIIPointStream( const char* fileName ); + ~ASCIIPointStream( void ); + void reset( void ); + bool nextPoint( Point3D< Real >& p , Point3D< Real >& n ); +}; + +template< class Real > +class BinaryPointStream : public PointStream< Real > +{ + FILE* _fp; + static const int POINT_BUFFER_SIZE=1024; + Real _pointBuffer[ POINT_BUFFER_SIZE * 2 * 3 ]; + int _pointsInBuffer , _currentPointIndex; +public: + BinaryPointStream( const char* filename ); + ~BinaryPointStream( void ); + void reset( void ); + bool nextPoint( Point3D< Real >& p , Point3D< Real >& n ); +}; + +template< class Real > +class PLYPointStream : public PointStream< Real > +{ + char* _fileName; + PlyFile* _ply; + int _nr_elems; + char **_elist; + + int _pCount , _pIdx; + void _free( void ); +public: + PLYPointStream( const char* fileName ); + ~PLYPointStream( void ); + void reset( void ); + bool nextPoint( Point3D< Real >& p , Point3D< Real >& n ); +}; +#include "PointStream.inl" +#endif // POINT_STREAM_INCLUDED diff --git a/Src/PointStream.inl b/Src/PointStream.inl new file mode 100644 index 0000000..0a312d5 --- /dev/null +++ b/Src/PointStream.inl @@ -0,0 +1,197 @@ +/* +Copyright (c) 2006, Michael Kazhdan and Matthew Bolitho +All rights reserved. + +Redistribution and use in source and binary forms, with or without modification, +are permitted provided that the following conditions are met: + +Redistributions of source code must retain the above copyright notice, this list of +conditions and the following disclaimer. Redistributions in binary form must reproduce +the above copyright notice, this list of conditions and the following disclaimer +in the documentation and/or other materials provided with the distribution. + +Neither the name of the Johns Hopkins University nor the names of its contributors +may be used to endorse or promote products derived from this software without specific +prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY +EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO THE IMPLIED WARRANTIES +OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT +SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED +TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR +BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN +ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH +DAMAGE. +*/ + +template< class Real > +MemoryPointStream< Real >::MemoryPointStream( size_t pointCount , std::pair< Point3D< Real > , Point3D< Real > >* points ){ _points = points , _pointCount = pointCount , _current = 0; } +template< class Real > +MemoryPointStream< Real >::~MemoryPointStream( void ){ ; } +template< class Real > +void MemoryPointStream< Real >::reset( void ) { _current=0; } +template< class Real > +bool MemoryPointStream< Real >::nextPoint( Point3D< Real >& p , Point3D< Real >& n ) +{ + if( _current>=_pointCount ) return false; + p = _points[_current].first , n = _points[_current].second; + _current++; + return true; +} + +template< class Real > +ASCIIPointStream< Real >::ASCIIPointStream( const char* fileName ) +{ + _fp = fopen( fileName , "r" ); + if( !_fp ) fprintf( stderr , "Failed to open file for reading: %s\n" , fileName ) , exit( 0 ); +} +template< class Real > +ASCIIPointStream< Real >::~ASCIIPointStream( void ) +{ + fclose( _fp ); + _fp = NULL; +} +template< class Real > +void ASCIIPointStream< Real >::reset( void ) { fseek( _fp , SEEK_SET , 0 ); } +template< class Real > +bool ASCIIPointStream< Real >::nextPoint( Point3D< Real >& p , Point3D< Real >& n ) +{ + float c[2*DIMENSION]; + if( fscanf( _fp , " %f %f %f %f %f %f " , &c[0] , &c[1] , &c[2] , &c[3] , &c[4] , &c[5] )!=2*DIMENSION ) return false; + p[0] = c[0] , p[1] = c[1] , p[2] = c[2]; + n[0] = c[3] , n[1] = c[4] , n[2] = c[5]; + return true; +} +template< class Real > +BinaryPointStream< Real >::BinaryPointStream( const char* fileName ) +{ + _pointsInBuffer = _currentPointIndex = 0; + _fp = fopen( fileName , "rb" ); + if( !_fp ) fprintf( stderr , "Failed to open file for reading: %s\n" , fileName ) , exit( 0 ); +} +template< class Real > +BinaryPointStream< Real >::~BinaryPointStream( void ) +{ + fclose( _fp ); + _fp = NULL; +} +template< class Real > +void BinaryPointStream< Real >::reset( void ) +{ + fseek( _fp , SEEK_SET , 0 ); + _pointsInBuffer = _currentPointIndex = 0; +} +template< class Real > +bool BinaryPointStream< Real >::nextPoint( Point3D< Real >& p , Point3D< Real >& n ) +{ + if( _currentPointIndex<_pointsInBuffer ) + { + p[0] = _pointBuffer[ _currentPointIndex*6+0 ]; + p[1] = _pointBuffer[ _currentPointIndex*6+1 ]; + p[2] = _pointBuffer[ _currentPointIndex*6+2 ]; + n[0] = _pointBuffer[ _currentPointIndex*6+3 ]; + n[1] = _pointBuffer[ _currentPointIndex*6+4 ]; + n[2] = _pointBuffer[ _currentPointIndex*6+5 ]; + _currentPointIndex++; + return true; + } + else + { + _currentPointIndex = 0; + _pointsInBuffer = int( fread( _pointBuffer , sizeof( Real ) * 6 , POINT_BUFFER_SIZE , _fp ) ); + if( !_pointsInBuffer ) return false; + else return nextPoint( p , n ); + } +} + +template< class Real > +PLYPointStream< Real >::PLYPointStream( const char* fileName ) +{ + _fileName = new char[ strlen( fileName )+1 ]; + strcpy( _fileName , fileName ); + _ply = NULL; + reset(); +} +template< class Real > +void PLYPointStream< Real >::reset( void ) +{ + int fileType; + float version; + PlyProperty** plist; + if( _ply ) _free(); + _ply = ply_open_for_reading( _fileName, &_nr_elems, &_elist, &fileType, &version ); + if( !_ply ) + { + fprintf( stderr, "[ERROR] Failed to open ply file for reading: %s\n" , _fileName ); + exit( 0 ); + } + bool foundVertices = false; + for( int i=0 ; i<_nr_elems ; i++ ) + { + int num_elems; + int nr_props; + char* elem_name = _elist[i]; + plist = ply_get_element_description( _ply , elem_name , &num_elems , &nr_props ); + if( !plist ) + { + fprintf( stderr , "[ERROR] Failed to get element description: %s\n" , elem_name ); + exit( 0 ); + } + + if( equal_strings( "vertex" , elem_name ) ) + { + foundVertices = true; + _pCount = num_elems , _pIdx = 0; + for( int i=0 ; i::Components ; i++ ) + if( !ply_get_property( _ply , elem_name , &(PlyOrientedVertex< Real >::Properties[i]) ) ) + { + fprintf( stderr , "[ERROR] Failed to find property in ply file: %s\n" , PlyOrientedVertex< Real >::Properties[i].name ); + exit( 0 ); + } + } + for( int j=0 ; jname ); + free( plist[j] ); + } + free( plist ); + if( foundVertices ) break; + } + if( !foundVertices ) + { + fprintf( stderr , "[ERROR] Could not find vertices in ply file\n" ); + exit( 0 ); + } +} +template< class Real > +void PLYPointStream< Real >::_free( void ) +{ + if( _ply ) ply_close( _ply ) , _ply = NULL; + if( _elist ) + { + for( int i=0 ; i<_nr_elems ; i++ ) free( _elist[i] ); + free( _elist ); + } +} +template< class Real > +PLYPointStream< Real >::~PLYPointStream( void ) +{ + _free(); + if( _fileName ) delete[] _fileName , _fileName = NULL; +} +template< class Real > +bool PLYPointStream< Real >::nextPoint( Point3D< Real >& p , Point3D< Real >& n ) +{ + if( _pIdx<_pCount ) + { + PlyOrientedVertex< Real > op; + ply_get_element( _ply, (void *)&op ); + p = op.point; + n = op.normal; + _pIdx++; + return true; + } + else return false; +} diff --git a/Src/PoissonRecon.cpp b/Src/PoissonRecon.cpp new file mode 100644 index 0000000..30eb284 --- /dev/null +++ b/Src/PoissonRecon.cpp @@ -0,0 +1,478 @@ +/* +Copyright (c) 2006, Michael Kazhdan and Matthew Bolitho +All rights reserved. + +Redistribution and use in source and binary forms, with or without modification, +are permitted provided that the following conditions are met: + +Redistributions of source code must retain the above copyright notice, this list of +conditions and the following disclaimer. Redistributions in binary form must reproduce +the above copyright notice, this list of conditions and the following disclaimer +in the documentation and/or other materials provided with the distribution. + +Neither the name of the Johns Hopkins University nor the names of its contributors +may be used to endorse or promote products derived from this software without specific +prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY +EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO THE IMPLIED WARRANTIES +OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT +SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED +TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR +BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN +ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH +DAMAGE. +*/ + +#include +#include +#include +#include +#ifdef _WIN32 +#include +#include +#endif // _WIN32 +#include "MyTime.h" +#include "MarchingCubes.h" +#include "Octree.h" +#include "SparseMatrix.h" +#include "CmdLineParser.h" +#include "PPolynomial.h" +#include "Ply.h" +#include "MemoryUsage.h" +#ifdef _OPENMP +#include "omp.h" +#endif // _OPENMP +void DumpOutput( const char* format , ... ); +void DumpOutput2( char* str , const char* format , ... ); +#include "MultiGridOctreeData.h" + +#define DEFAULT_FULL_DEPTH 5 + +#define XSTR(x) STR(x) +#define STR(x) #x +#if DEFAULT_FULL_DEPTH +#pragma message ( "[WARNING] Setting default full depth to " XSTR(DEFAULT_FULL_DEPTH) ) +#endif // DEFAULT_FULL_DEPTH + +#include +char* outputFile=NULL; +int echoStdout=0; +void DumpOutput( const char* format , ... ) +{ + if( outputFile ) + { + FILE* fp = fopen( outputFile , "a" ); + va_list args; + va_start( args , format ); + vfprintf( fp , format , args ); + fclose( fp ); + va_end( args ); + } + if( echoStdout ) + { + va_list args; + va_start( args , format ); + vprintf( format , args ); + va_end( args ); + } +} +void DumpOutput2( char* str , const char* format , ... ) +{ + if( outputFile ) + { + FILE* fp = fopen( outputFile , "a" ); + va_list args; + va_start( args , format ); + vfprintf( fp , format , args ); + fclose( fp ); + va_end( args ); + } + if( echoStdout ) + { + va_list args; + va_start( args , format ); + vprintf( format , args ); + va_end( args ); + } + va_list args; + va_start( args , format ); + vsprintf( str , format , args ); + va_end( args ); + if( str[strlen(str)-1]=='\n' ) str[strlen(str)-1] = 0; +} + + +cmdLineString + In( "in" ) , + Out( "out" ) , + VoxelGrid( "voxel" ) , + XForm( "xForm" ); + +cmdLineReadable +#ifdef _WIN32 + Performance( "performance" ) , +#endif // _WIN32 + Complete( "complete" ) , + ShowResidual( "showResidual" ) , + NoComments( "noComments" ) , + PolygonMesh( "polygonMesh" ) , + Confidence( "confidence" ) , + NormalWeights( "nWeights" ) , + NonManifold( "nonManifold" ) , + ASCII( "ascii" ) , + Density( "density" ) , + Verbose( "verbose" ) , + Double( "double" ); + +cmdLineInt + Depth( "depth" , 8 ) , + CGDepth( "cgDepth" , 0 ) , + KernelDepth( "kernelDepth" ) , + AdaptiveExponent( "adaptiveExp" , 1 ) , + Iters( "iters" , 8 ) , + VoxelDepth( "voxelDepth" , -1 ) , + FullDepth( "fullDepth" , DEFAULT_FULL_DEPTH ) , + MinDepth( "minDepth" , 0 ) , + MaxSolveDepth( "maxSolveDepth" ) , + BoundaryType( "boundary" , 1 ) , + Threads( "threads" , omp_get_num_procs() ); + +cmdLineFloat + SamplesPerNode( "samplesPerNode" , 1.f ) , + Scale( "scale" , 1.1f ) , + CSSolverAccuracy( "cgAccuracy" , float(1e-3) ) , + PointWeight( "pointWeight" , 4.f ); + + +cmdLineReadable* params[] = +{ + &In , &Depth , &Out , &XForm , + &Scale , &Verbose , &CSSolverAccuracy , &NoComments , &Double , + &KernelDepth , &SamplesPerNode , &Confidence , &NormalWeights , &NonManifold , &PolygonMesh , &ASCII , &ShowResidual , &VoxelDepth , + &PointWeight , &VoxelGrid , &Threads , &MaxSolveDepth , + &AdaptiveExponent , &BoundaryType , + &Density , + &FullDepth , + &MinDepth , + &CGDepth , &Iters , + &Complete , +#ifdef _WIN32 + &Performance , +#endif // _WIN32 +}; + + +void ShowUsage(char* ex) +{ + printf( "Usage: %s\n" , ex ); + printf( "\t --%s \n" , In.name ); + + printf( "\t[--%s ]\n" , Out.name ); + printf( "\t[--%s ]\n" , VoxelGrid.name ); + + printf( "\t[--%s =%d]\n" , Depth.name , Depth.value ); + printf( "\t\t Running at depth d corresponds to solving on a 2^d x 2^d x 2^d\n" ); + printf( "\t\t voxel grid.\n" ); + + printf( "\t[--%s =%d]\n" , FullDepth.name , FullDepth.value ); + printf( "\t\t This flag specifies the depth up to which the octree should be complete.\n" ); + + printf( "\t[--%s =<%s>]\n" , VoxelDepth.name , Depth.name ); + + printf( "\t[--%s =%d]\n" , CGDepth.name , CGDepth.value ); + printf( "\t\t The depth up to which a conjugate-gradients solver should be used.\n"); + + printf( "\t[--%s =%f]\n" , Scale.name , Scale.value ); + printf( "\t\t Specifies the factor of the bounding cube that the input\n" ); + printf( "\t\t samples should fit into.\n" ); + + printf( "\t[--%s =%f]\n" , SamplesPerNode.name, SamplesPerNode.value ); + printf( "\t\t This parameter specifies the minimum number of points that\n" ); + printf( "\t\t should fall within an octree node.\n" ); + + printf( "\t[--%s =%f]\n" , PointWeight.name , PointWeight.value ); + printf( "\t\t This value specifies the weight that point interpolation constraints are\n" ); + printf( "\t\t given when defining the (screened) Poisson system.\n" ); + + printf( "\t[--%s =%d]\n" , Iters.name , Iters.value ); + printf( "\t\t This flag specifies the (maximum if CG) number of solver iterations.\n" ); + +#ifdef _OPENMP + printf( "\t[--%s =%d]\n" , Threads.name , Threads.value ); + printf( "\t\t This parameter specifies the number of threads across which\n" ); + printf( "\t\t the solver should be parallelized.\n" ); +#endif // _OPENMP + + printf( "\t[--%s]\n" , Confidence.name ); + printf( "\t\t If this flag is enabled, the size of a sample's normals is\n" ); + printf( "\t\t used as a confidence value, affecting the sample's\n" ); + printf( "\t\t constribution to the reconstruction process.\n" ); + + printf( "\t[--%s]\n" , NormalWeights.name ); + printf( "\t\t If this flag is enabled, the size of a sample's normals is\n" ); + printf( "\t\t used as to modulate the interpolation weight.\n" ); + +#if 0 + printf( "\t[--%s]\n" , NonManifold.name ); + printf( "\t\t If this flag is enabled, the isosurface extraction does not add\n" ); + printf( "\t\t a planar polygon's barycenter in order to ensure that the output\n" ); + printf( "\t\t mesh is manifold.\n" ); +#endif + + printf( "\t[--%s]\n" , PolygonMesh.name); + printf( "\t\t If this flag is enabled, the isosurface extraction returns polygons\n" ); + printf( "\t\t rather than triangles.\n" ); + +#if 0 + printf( "\t[--%s =%d]\n" , MinDepth.name , MinDepth.value ); + printf( "\t\t This flag specifies the coarsest depth at which the system is to be solved.\n" ); + + printf( "\t[--%s =%g]\n" , CSSolverAccuracy.name , CSSolverAccuracy.value ); + printf( "\t\t This flag specifies the accuracy cut-off to be used for CG.\n" ); + + printf( "\t[--%s =%d]\n", AdaptiveExponent.name , AdaptiveExponent.value ); + printf( "\t\t This flag specifies the exponent scale for the adaptive weighting.\n" ); + +#ifdef _WIN32 + printf( "\t[--%s]\n" , Performance.name ); + printf( "\t\t If this flag is enabled, the running time and peak memory usage\n" ); + printf( "\t\t is output after the reconstruction.\n" ); +#endif // _WIN32 +#endif + + printf( "\t[--%s]\n" , Density.name ); + printf( "\t\t If this flag is enabled, the sampling density is written out with the vertices.\n" ); + +#if 0 + printf( "\t[--%s]\n" , ASCII.name ); + printf( "\t\t If this flag is enabled, the output file is written out in ASCII format.\n" ); + + printf( "\t[--%s]\n" , NoComments.name ); + printf( "\t\t If this flag is enabled, the output file will not include comments.\n" ); +#endif + + printf( "\t[--%s]\n" , Double.name ); + printf( "\t\t If this flag is enabled, the reconstruction will be performed with double-precision floats.\n" ); + + printf( "\t[--%s]\n" , Verbose.name ); + printf( "\t\t If this flag is enabled, the progress of the reconstructor will be output to STDOUT.\n" ); +} +template< class Real , class Vertex > +int Execute( int argc , char* argv[] ) +{ + Reset< Real >(); + int i; + int paramNum = sizeof(params)/sizeof(cmdLineReadable*); + int commentNum=0; + char **comments; + + comments = new char*[paramNum+7]; + for( i=0 ; i xForm , iXForm; + if( XForm.set ) + { + FILE* fp = fopen( XForm.value , "r" ); + if( !fp ) + { + fprintf( stderr , "[WARNING] Could not read x-form from: %s\n" , XForm.value ); + xForm = XForm4x4< Real >::Identity(); + } + else + { + for( int i=0 ; i<4 ; i++ ) for( int j=0 ; j<4 ; j++ ) + { + float f; + fscanf( fp , " %f " , &f ); + xForm(i,j) = (Real)f; + } + fclose( fp ); + } + } + else xForm = XForm4x4< Real >::Identity(); + iXForm = xForm.inverse(); + + DumpOutput2( comments[commentNum++] , "Running Screened Poisson Reconstruction (Version 6.13a)\n" ); + char str[1024]; + for( int i=0 ; iset ) + { + params[i]->writeValue( str ); + if( strlen( str ) ) DumpOutput2( comments[commentNum++] , "\t--%s %s\n" , params[i]->name , str ); + else DumpOutput2( comments[commentNum++] , "\t--%s\n" , params[i]->name ); + } + + double t; + double tt=Time(); + Real isoValue = 0; + + Octree< Real > tree; + tree.threads = Threads.value; + if( !In.set ) + { + ShowUsage(argv[0]); + return 0; + } + if( !MaxSolveDepth.set ) MaxSolveDepth.value = Depth.value; + + OctNode< TreeNodeData >::SetAllocator( MEMORY_ALLOCATOR_BLOCK_SIZE ); + + t=Time(); + int kernelDepth = KernelDepth.set ? KernelDepth.value : Depth.value-2; + if( kernelDepth>Depth.value ) + { + fprintf( stderr,"[ERROR] %s can't be greater than %s: %d <= %d\n" , KernelDepth.name , Depth.name , KernelDepth.value , Depth.value ); + return EXIT_FAILURE; + } + + double maxMemoryUsage; + t=Time() , tree.maxMemoryUsage=0; + typename Octree< Real >::PointInfo* pointInfo = new typename Octree< Real >::PointInfo(); + typename Octree< Real >::NormalInfo* normalInfo = new typename Octree< Real >::NormalInfo(); + std::vector< Real >* kernelDensityWeights = new std::vector< Real >(); + std::vector< Real >* centerWeights = new std::vector< Real >(); + PointStream< float >* pointStream; + char* ext = GetFileExtension( In.value ); + if ( !strcasecmp( ext , "bnpts" ) ) pointStream = new BinaryPointStream< float >( In.value ); + else if( !strcasecmp( ext , "ply" ) ) pointStream = new PLYPointStream< float >( In.value ); + else pointStream = new ASCIIPointStream< float >( In.value ); + delete[] ext; + int pointCount = tree.template SetTree< float >( pointStream , MinDepth.value , Depth.value , FullDepth.value , kernelDepth , Real(SamplesPerNode.value) , Scale.value , Confidence.set , NormalWeights.set , PointWeight.value , AdaptiveExponent.value , *pointInfo , *normalInfo , *kernelDensityWeights , *centerWeights , BoundaryType.value , xForm , Complete.set ); + if( !Density.set ) delete kernelDensityWeights , kernelDensityWeights = NULL; + + DumpOutput2( comments[commentNum++] , "# Tree set in: %9.1f (s), %9.1f (MB)\n" , Time()-t , tree.maxMemoryUsage ); + DumpOutput( "Input Points: %d\n" , pointCount ); + DumpOutput( "Leaves/Nodes: %d/%d\n" , tree.tree.leaves() , tree.tree.nodes() ); + DumpOutput( "Memory Usage: %.3f MB\n" , float( MemoryInfo::Usage() )/(1<<20) ); + + maxMemoryUsage = tree.maxMemoryUsage; + t=Time() , tree.maxMemoryUsage=0; + Pointer( Real ) constraints = tree.SetLaplacianConstraints( *normalInfo ); + delete normalInfo; + DumpOutput2( comments[commentNum++] , "# Constraints set in: %9.1f (s), %9.1f (MB)\n" , Time()-t , tree.maxMemoryUsage ); + DumpOutput( "Memory Usage: %.3f MB\n" , float( MemoryInfo::Usage())/(1<<20) ); + maxMemoryUsage = std::max< double >( maxMemoryUsage , tree.maxMemoryUsage ); + + t=Time() , tree.maxMemoryUsage=0; + Pointer( Real ) solution = tree.SolveSystem( *pointInfo , constraints , ShowResidual.set , Iters.value , MaxSolveDepth.value , CGDepth.value , CSSolverAccuracy.value ); + delete pointInfo; + FreePointer( constraints ); + DumpOutput2( comments[commentNum++] , "# Linear system solved in: %9.1f (s), %9.1f (MB)\n" , Time()-t , tree.maxMemoryUsage ); + DumpOutput( "Memory Usage: %.3f MB\n" , float( MemoryInfo::Usage() )/(1<<20) ); + maxMemoryUsage = std::max< double >( maxMemoryUsage , tree.maxMemoryUsage ); + + CoredFileMeshData< Vertex > mesh; + + if( Verbose.set ) tree.maxMemoryUsage=0; + t=Time(); + isoValue = tree.GetIsoValue( solution , *centerWeights ); + delete centerWeights; + DumpOutput( "Got average in: %f\n" , Time()-t ); + DumpOutput( "Iso-Value: %e\n" , isoValue ); + + if( VoxelGrid.set ) + { + double t = Time(); + FILE* fp = fopen( VoxelGrid.value , "wb" ); + if( !fp ) fprintf( stderr , "Failed to open voxel file for writing: %s\n" , VoxelGrid.value ); + else + { + int res; + Pointer( Real ) values = tree.Evaluate( solution , res , isoValue , VoxelDepth.value ); + fwrite( &res , sizeof(int) , 1 , fp ); + if( sizeof(Real)==sizeof(float) ) fwrite( values , sizeof(float) , res*res*res , fp ); + else + { + float *fValues = new float[res*res*res]; + for( int i=0 ; i() , solution , isoValue , mesh , true , !NonManifold.set , PolygonMesh.set ); + if( PolygonMesh.set ) DumpOutput2( comments[commentNum++] , "# Got polygons in: %9.1f (s), %9.1f (MB)\n" , Time()-t , tree.maxMemoryUsage ); + else DumpOutput2( comments[commentNum++] , "# Got triangles in: %9.1f (s), %9.1f (MB)\n" , Time()-t , tree.maxMemoryUsage ); + maxMemoryUsage = std::max< double >( maxMemoryUsage , tree.maxMemoryUsage ); + DumpOutput2( comments[commentNum++],"# Total Solve: %9.1f (s), %9.1f (MB)\n" , Time()-tt , maxMemoryUsage ); + + if( NoComments.set ) + { + if( ASCII.set ) PlyWritePolygons( Out.value , &mesh , PLY_ASCII , NULL , 0 , iXForm ); + else PlyWritePolygons( Out.value , &mesh , PLY_BINARY_NATIVE , NULL , 0 , iXForm ); + } + else + { + if( ASCII.set ) PlyWritePolygons( Out.value , &mesh , PLY_ASCII , comments , commentNum , iXForm ); + else PlyWritePolygons( Out.value , &mesh , PLY_BINARY_NATIVE , comments , commentNum , iXForm ); + } + DumpOutput( "Vertices / Polygons: %d / %d\n" , mesh.outOfCorePointCount()+mesh.inCorePoints.size() , mesh.polygonCount() ); + } + FreePointer( solution ); + return 1; +} + +#ifdef _WIN32 +inline double to_seconds( const FILETIME& ft ) +{ + const double low_to_sec=100e-9; // 100 nanoseconds + const double high_to_sec=low_to_sec*4294967296.0; + return ft.dwLowDateTime*low_to_sec+ft.dwHighDateTime*high_to_sec; +} +#endif // _WIN32 + +int main( int argc , char* argv[] ) +{ +#if defined(WIN32) && defined(MAX_MEMORY_GB) + if( MAX_MEMORY_GB>0 ) + { + SIZE_T peakMemory = 1; + peakMemory <<= 30; + peakMemory *= MAX_MEMORY_GB; + printf( "Limiting memory usage to %.2f GB\n" , float( peakMemory>>30 ) ); + HANDLE h = CreateJobObject( NULL , NULL ); + AssignProcessToJobObject( h , GetCurrentProcess() ); + + JOBOBJECT_EXTENDED_LIMIT_INFORMATION jeli = { 0 }; + jeli.BasicLimitInformation.LimitFlags = JOB_OBJECT_LIMIT_JOB_MEMORY; + jeli.JobMemoryLimit = peakMemory; + if( !SetInformationJobObject( h , JobObjectExtendedLimitInformation , &jeli , sizeof( jeli ) ) ) + fprintf( stderr , "Failed to set memory limit\n" ); + } +#endif // defined(WIN32) && defined(MAX_MEMORY_GB) + double t = Time(); + + cmdLineParse( argc-1 , &argv[1] , sizeof(params)/sizeof(cmdLineReadable*) , params , 1 ); + if( Density.set ) + if( Double.set ) Execute< double , PlyValueVertex< float > >( argc , argv ); + else Execute< float , PlyValueVertex< float > >( argc , argv ); + else + if( Double.set ) Execute< double , PlyVertex< float > >( argc , argv ); + else Execute< float , PlyVertex< float > >( argc , argv ); +#ifdef _WIN32 + if( Performance.set ) + { + HANDLE cur_thread=GetCurrentThread(); + FILETIME tcreat, texit, tkernel, tuser; + if( GetThreadTimes( cur_thread , &tcreat , &texit , &tkernel , &tuser ) ) + printf( "Time (Wall/User/Kernel): %.2f / %.2f / %.2f\n" , Time()-t , to_seconds( tuser ) , to_seconds( tkernel ) ); + else printf( "Time: %.2f\n" , Time()-t ); + HANDLE h = GetCurrentProcess(); + PROCESS_MEMORY_COUNTERS pmc; + if( GetProcessMemoryInfo( h , &pmc , sizeof(pmc) ) ) printf( "Peak Memory (MB): %d\n" , pmc.PeakWorkingSetSize>>20 ); + } +#endif // _WIN32 + return EXIT_SUCCESS; +} diff --git a/Src/Polynomial.h b/Src/Polynomial.h new file mode 100644 index 0000000..7154b97 --- /dev/null +++ b/Src/Polynomial.h @@ -0,0 +1,93 @@ +/* +Copyright (c) 2006, Michael Kazhdan and Matthew Bolitho +All rights reserved. + +Redistribution and use in source and binary forms, with or without modification, +are permitted provided that the following conditions are met: + +Redistributions of source code must retain the above copyright notice, this list of +conditions and the following disclaimer. Redistributions in binary form must reproduce +the above copyright notice, this list of conditions and the following disclaimer +in the documentation and/or other materials provided with the distribution. + +Neither the name of the Johns Hopkins University nor the names of its contributors +may be used to endorse or promote products derived from this software without specific +prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY +EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO THE IMPLIED WARRANTIES +OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT +SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED +TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR +BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN +ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH +DAMAGE. +*/ + +#ifndef POLYNOMIAL_INCLUDED +#define POLYNOMIAL_INCLUDED + +#include + +template< int Degree > +class Polynomial{ +public: + double coefficients[Degree+1]; + + Polynomial(void); + template + Polynomial(const Polynomial& P); + double operator()( double t ) const; + double integral( double tMin , double tMax ) const; + + int operator == (const Polynomial& p) const; + int operator != (const Polynomial& p) const; + int isZero(void) const; + void setZero(void); + + template + Polynomial& operator = (const Polynomial &p); + Polynomial& operator += (const Polynomial& p); + Polynomial& operator -= (const Polynomial& p); + Polynomial operator - (void) const; + Polynomial operator + (const Polynomial& p) const; + Polynomial operator - (const Polynomial& p) const; + template + Polynomial operator * (const Polynomial& p) const; + + Polynomial& operator += ( double s ); + Polynomial& operator -= ( double s ); + Polynomial& operator *= ( double s ); + Polynomial& operator /= ( double s ); + Polynomial operator + ( double s ) const; + Polynomial operator - ( double s ) const; + Polynomial operator * ( double s ) const; + Polynomial operator / ( double s ) const; + + Polynomial scale( double s ) const; + Polynomial shift( double t ) const; + + Polynomial derivative(void) const; + Polynomial integral(void) const; + + void printnl(void) const; + + Polynomial& addScaled(const Polynomial& p,double scale); + + static void Negate(const Polynomial& in,Polynomial& out); + static void Subtract(const Polynomial& p1,const Polynomial& p2,Polynomial& q); + static void Scale(const Polynomial& p,double w,Polynomial& q); + static void AddScaled(const Polynomial& p1,double w1,const Polynomial& p2,double w2,Polynomial& q); + static void AddScaled(const Polynomial& p1,const Polynomial& p2,double w2,Polynomial& q); + static void AddScaled(const Polynomial& p1,double w1,const Polynomial& p2,Polynomial& q); + + void getSolutions(double c,std::vector& roots,double EPS) const; + int getSolutions( double c , double* roots , double EPS ) const; + + static Polynomial BSplineComponent( int i ); +}; + +#include "Polynomial.inl" +#endif // POLYNOMIAL_INCLUDED diff --git a/Src/Polynomial.inl b/Src/Polynomial.inl new file mode 100644 index 0000000..6d7ea1d --- /dev/null +++ b/Src/Polynomial.inl @@ -0,0 +1,332 @@ +/* +Copyright (c) 2006, Michael Kazhdan and Matthew Bolitho +All rights reserved. + +Redistribution and use in source and binary forms, with or without modification, +are permitted provided that the following conditions are met: + +Redistributions of source code must retain the above copyright notice, this list of +conditions and the following disclaimer. Redistributions in binary form must reproduce +the above copyright notice, this list of conditions and the following disclaimer +in the documentation and/or other materials provided with the distribution. + +Neither the name of the Johns Hopkins University nor the names of its contributors +may be used to endorse or promote products derived from this software without specific +prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY +EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO THE IMPLIED WARRANTIES +OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT +SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED +TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR +BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN +ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH +DAMAGE. +*/ + +#include +#include +#include +#include "Factor.h" + +//////////////// +// Polynomial // +//////////////// +template +Polynomial::Polynomial(void){memset(coefficients,0,sizeof(double)*(Degree+1));} +template +template +Polynomial::Polynomial(const Polynomial& P){ + memset(coefficients,0,sizeof(double)*(Degree+1)); + for(int i=0;i<=Degree && i<=Degree2;i++){coefficients[i]=P.coefficients[i];} +} + + +template +template +Polynomial& Polynomial::operator = (const Polynomial &p){ + int d=Degree +Polynomial Polynomial::derivative(void) const{ + Polynomial p; + for(int i=0;i +Polynomial Polynomial::integral(void) const{ + Polynomial p; + p.coefficients[0]=0; + for(int i=0;i<=Degree;i++){p.coefficients[i+1]=coefficients[i]/(i+1);} + return p; +} +template<> double Polynomial< 0 >::operator() ( double t ) const { return coefficients[0]; } +template<> double Polynomial< 1 >::operator() ( double t ) const { return coefficients[0]+coefficients[1]*t; } +template<> double Polynomial< 2 >::operator() ( double t ) const { return coefficients[0]+(coefficients[1]+coefficients[2]*t)*t; } +template +double Polynomial::operator() ( double t ) const{ + double v=coefficients[Degree]; + for( int d=Degree-1 ; d>=0 ; d-- ) v = v*t + coefficients[d]; + return v; +} +template +double Polynomial::integral( double tMin , double tMax ) const +{ + double v=0; + double t1,t2; + t1=tMin; + t2=tMax; + for(int i=0;i<=Degree;i++){ + v+=coefficients[i]*(t2-t1)/(i+1); + if(t1!=-DBL_MAX && t1!=DBL_MAX){t1*=tMin;} + if(t2!=-DBL_MAX && t2!=DBL_MAX){t2*=tMax;} + } + return v; +} +template +int Polynomial::operator == (const Polynomial& p) const{ + for(int i=0;i<=Degree;i++){if(coefficients[i]!=p.coefficients[i]){return 0;}} + return 1; +} +template +int Polynomial::operator != (const Polynomial& p) const{ + for(int i=0;i<=Degree;i++){if(coefficients[i]==p.coefficients[i]){return 0;}} + return 1; +} +template +int Polynomial::isZero(void) const{ + for(int i=0;i<=Degree;i++){if(coefficients[i]!=0){return 0;}} + return 1; +} +template +void Polynomial::setZero(void){memset(coefficients,0,sizeof(double)*(Degree+1));} + +template +Polynomial& Polynomial::addScaled(const Polynomial& p,double s){ + for(int i=0;i<=Degree;i++){coefficients[i]+=p.coefficients[i]*s;} + return *this; +} +template +Polynomial& Polynomial::operator += (const Polynomial& p){ + for(int i=0;i<=Degree;i++){coefficients[i]+=p.coefficients[i];} + return *this; +} +template +Polynomial& Polynomial::operator -= (const Polynomial& p){ + for(int i=0;i<=Degree;i++){coefficients[i]-=p.coefficients[i];} + return *this; +} +template +Polynomial Polynomial::operator + (const Polynomial& p) const{ + Polynomial q; + for(int i=0;i<=Degree;i++){q.coefficients[i]=(coefficients[i]+p.coefficients[i]);} + return q; +} +template +Polynomial Polynomial::operator - (const Polynomial& p) const{ + Polynomial q; + for(int i=0;i<=Degree;i++) {q.coefficients[i]=coefficients[i]-p.coefficients[i];} + return q; +} +template +void Polynomial::Scale(const Polynomial& p,double w,Polynomial& q){ + for(int i=0;i<=Degree;i++){q.coefficients[i]=p.coefficients[i]*w;} +} +template +void Polynomial::AddScaled(const Polynomial& p1,double w1,const Polynomial& p2,double w2,Polynomial& q){ + for(int i=0;i<=Degree;i++){q.coefficients[i]=p1.coefficients[i]*w1+p2.coefficients[i]*w2;} +} +template +void Polynomial::AddScaled(const Polynomial& p1,double w1,const Polynomial& p2,Polynomial& q){ + for(int i=0;i<=Degree;i++){q.coefficients[i]=p1.coefficients[i]*w1+p2.coefficients[i];} +} +template +void Polynomial::AddScaled(const Polynomial& p1,const Polynomial& p2,double w2,Polynomial& q){ + for(int i=0;i<=Degree;i++){q.coefficients[i]=p1.coefficients[i]+p2.coefficients[i]*w2;} +} + +template +void Polynomial::Subtract(const Polynomial &p1,const Polynomial& p2,Polynomial& q){ + for(int i=0;i<=Degree;i++){q.coefficients[i]=p1.coefficients[i]-p2.coefficients[i];} +} +template +void Polynomial::Negate(const Polynomial& in,Polynomial& out){ + out=in; + for(int i=0;i<=Degree;i++){out.coefficients[i]=-out.coefficients[i];} +} + +template +Polynomial Polynomial::operator - (void) const{ + Polynomial q=*this; + for(int i=0;i<=Degree;i++){q.coefficients[i]=-q.coefficients[i];} + return q; +} +template +template +Polynomial Polynomial::operator * (const Polynomial& p) const{ + Polynomial q; + for(int i=0;i<=Degree;i++){for(int j=0;j<=Degree2;j++){q.coefficients[i+j]+=coefficients[i]*p.coefficients[j];}} + return q; +} + +template +Polynomial& Polynomial::operator += ( double s ) +{ + coefficients[0]+=s; + return *this; +} +template +Polynomial& Polynomial::operator -= ( double s ) +{ + coefficients[0]-=s; + return *this; +} +template +Polynomial& Polynomial::operator *= ( double s ) +{ + for(int i=0;i<=Degree;i++){coefficients[i]*=s;} + return *this; +} +template +Polynomial& Polynomial::operator /= ( double s ) +{ + for(int i=0;i<=Degree;i++){coefficients[i]/=s;} + return *this; +} +template +Polynomial Polynomial::operator + ( double s ) const +{ + Polynomial q=*this; + q.coefficients[0]+=s; + return q; +} +template +Polynomial Polynomial::operator - ( double s ) const +{ + Polynomial q=*this; + q.coefficients[0]-=s; + return q; +} +template +Polynomial Polynomial::operator * ( double s ) const +{ + Polynomial q; + for(int i=0;i<=Degree;i++){q.coefficients[i]=coefficients[i]*s;} + return q; +} +template +Polynomial Polynomial::operator / ( double s ) const +{ + Polynomial q; + for( int i=0 ; i<=Degree ; i++ ) q.coefficients[i] = coefficients[i]/s; + return q; +} +template +Polynomial Polynomial::scale( double s ) const +{ + Polynomial q=*this; + double s2=1.0; + for(int i=0;i<=Degree;i++){ + q.coefficients[i]*=s2; + s2/=s; + } + return q; +} +template +Polynomial Polynomial::shift( double t ) const +{ + Polynomial q; + for(int i=0;i<=Degree;i++){ + double temp=1; + for(int j=i;j>=0;j--){ + q.coefficients[j]+=coefficients[i]*temp; + temp*=-t*j; + temp/=(i-j+1); + } + } + return q; +} +template +void Polynomial::printnl(void) const{ + for(int j=0;j<=Degree;j++){ + printf("%6.4f x^%d ",coefficients[j],j); + if(j=0){printf("+");} + } + printf("\n"); +} +template +void Polynomial::getSolutions(double c,std::vector& roots,double EPS) const +{ + double r[4][2]; + int rCount=0; + roots.clear(); + switch(Degree){ + case 1: + rCount=Factor(coefficients[1],coefficients[0]-c,r,EPS); + break; + case 2: + rCount=Factor(coefficients[2],coefficients[1],coefficients[0]-c,r,EPS); + break; + case 3: + rCount=Factor(coefficients[3],coefficients[2],coefficients[1],coefficients[0]-c,r,EPS); + break; +// case 4: +// rCount=Factor(coefficients[4],coefficients[3],coefficients[2],coefficients[1],coefficients[0]-c,r,EPS); +// break; + default: + printf("Can't solve polynomial of degree: %d\n",Degree); + } + for(int i=0;i +int Polynomial::getSolutions( double c , double* roots , double EPS ) const +{ + double _roots[4][2]; + int _rCount=0; + switch( Degree ) + { + case 1: _rCount = Factor( coefficients[1] , coefficients[0]-c , _roots , EPS ) ; break; + case 2: _rCount = Factor( coefficients[2] , coefficients[1] , coefficients[0]-c , _roots , EPS ) ; break; + case 3: _rCount = Factor( coefficients[3] , coefficients[2] , coefficients[1] , coefficients[0]-c , _roots , EPS ) ; break; +// case 4: _rCount = Factor( coefficients[4] , coefficients[3] , coefficients[2] , coefficients[1] , coefficients[0]-c , _roots , EPS ) ; break; + default: printf( "Can't solve polynomial of degree: %d\n" , Degree ); + } + int rCount = 0; + for( int i=0 ; i<_rCount ; i++ ) if( fabs(_roots[i][1])<=EPS ) roots[rCount++] = _roots[i][0]; + return rCount; +} +template< > +Polynomial< 0 > Polynomial< 0 >::BSplineComponent( int i ) +{ + Polynomial p; + p.coefficients[0] = 1.; + return p; +} +template< int Degree > +Polynomial< Degree > Polynomial< Degree >::BSplineComponent( int i ) +{ + Polynomial p; + if( i>0 ) + { + Polynomial< Degree > _p = Polynomial< Degree-1 >::BSplineComponent( i-1 ).integral(); + p -= _p; + p.coefficients[0] += _p(1); + } + if( i _p = Polynomial< Degree-1 >::BSplineComponent( i ).integral(); + p += _p; + } + return p; +} \ No newline at end of file diff --git a/Src/SparseMatrix.h b/Src/SparseMatrix.h new file mode 100644 index 0000000..1dffc89 --- /dev/null +++ b/Src/SparseMatrix.h @@ -0,0 +1,210 @@ +/* +Copyright (c) 2006, Michael Kazhdan and Matthew Bolitho +All rights reserved. + +Redistribution and use in source and binary forms, with or without modification, +are permitted provided that the following conditions are met: + +Redistributions of source code must retain the above copyright notice, this list of +conditions and the following disclaimer. Redistributions in binary form must reproduce +the above copyright notice, this list of conditions and the following disclaimer +in the documentation and/or other materials provided with the distribution. + +Neither the name of the Johns Hopkins University nor the names of its contributors +may be used to endorse or promote products derived from this software without specific +prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY +EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO THE IMPLIED WARRANTIES +OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT +SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED +TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR +BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN +ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH +DAMAGE. +*/ + +#ifndef __SPARSEMATRIX_HPP +#define __SPARSEMATRIX_HPP + +#define ZERO_TESTING_JACOBI 1 + + +#include "Vector.h" +#include "Array.h" + +template +struct MatrixEntry +{ + MatrixEntry( void ) { N =-1; Value = 0; } + MatrixEntry( int i ) { N = i; Value = 0; } + MatrixEntry( int i , T v ) { N = i; Value = v; } + int N; + T Value; +}; + +template class SparseMatrix +{ +private: + bool _contiguous; + int _maxEntriesPerRow; + void _init( void ); +public: + int rows; + Pointer( int ) rowSizes; + Pointer( Pointer( MatrixEntry< T > ) ) m_ppElements; + Pointer( MatrixEntry< T > ) operator[] ( int idx ) { return m_ppElements[idx]; } + ConstPointer( MatrixEntry< T > ) operator[] ( int idx ) const { return m_ppElements[idx]; } + + SparseMatrix( void ); + SparseMatrix( int rows ); + SparseMatrix( int rows , int maxEntriesPerRow ); + void Resize( int rows ); + void Resize( int rows , int maxEntriesPerRow ); + void SetRowSize( int row , int count ); + int Entries( void ) const; + + SparseMatrix( const SparseMatrix& M ); + ~SparseMatrix(); + + void SetZero(); + + SparseMatrix& operator = (const SparseMatrix& M); + + SparseMatrix operator * (const T& V) const; + SparseMatrix& operator *= (const T& V); + + + template + Vector operator * (const Vector& V) const; + template + Vector Multiply( const Vector& V ) const; + template + void Multiply( const Vector& In , Vector& Out , int threads=1 ) const; + + static int Solve (const SparseMatrix& M,const Vector& b, int iters,Vector& solution,const T eps=1e-8); + + template + static int SolveSymmetric( const SparseMatrix& M , const Vector& b , int iters , Vector& solution , const T2 eps=1e-8 , int reset=1 , int threads=1 ); + + bool write( FILE* fp ) const; + bool write( const char* fileName ) const; + bool read( FILE* fp ); + bool read( const char* fileName ); + + template< class T2 > + static int SolveJacobi( const SparseMatrix& M , const Vector& diagonal , const Vector& b , Vector& x , Vector& Mx , T2 sor , int threads=1 , int offset=0 ); + template< class T2 > + static int SolveGS( const SparseMatrix& M , const Vector& diagonal , const Vector& b , Vector& x , bool forward , int offset=0 ); + template< class T2 > + static int SolveGS( const std::vector< std::vector< int > >& mcIndices , const SparseMatrix& M , const Vector& diagonal , const Vector& b , Vector& x , bool forward , int threads=1 , int offset=0 ); + + template< class T2 > + static int SolveJacobi( const SparseMatrix& M , const Vector& b , Vector& x , Vector& Mx , T2 sor , int threads=1 , int offset=0 ); + template< class T2 > + static int SolveGS( const SparseMatrix& M , const Vector& b , Vector& x , bool forward , int offset=0 ); + template< class T2 > + static int SolveGS( const std::vector< std::vector< int > >& mcIndices , const SparseMatrix& M , const Vector& b , Vector& x , bool forward , int threads=1 , int offset=0 ); + + template< class T2 > + void getDiagonal( Vector< T2 >& diagonal , int threads=1 , int offset=0 ) const; +}; + + +template< class T2 > +struct MapReduceVector +{ +private: + int _dim; +public: + std::vector< T2* > out; + MapReduceVector( void ) { _dim = 0; } + ~MapReduceVector( void ) + { + if( _dim ) for( int t=0 ; t +class SparseSymmetricMatrix : public SparseMatrix< T > +{ +public: + + template< class T2 > + Vector< T2 > operator * ( const Vector& V ) const; + + template< class T2 > + Vector< T2 > Multiply( const Vector& V ) const; + + template< class T2 > + void Multiply( const Vector& In, Vector& Out , bool addDCTerm=false ) const; + + template< class T2 > + void Multiply( const Vector& In, Vector& Out , MapReduceVector< T2 >& OutScratch , bool addDCTerm=false ) const; + + template< class T2 > + void Multiply( const Vector& In, Vector& Out , std::vector< T2* >& OutScratch , const std::vector< int >& bounds ) const; + + template< class T2 > + static int SolveCG( const SparseSymmetricMatrix& M , const Vector& b , int iters , Vector& x , T2 eps=1e-8 , int reset=1 , int threads=0 , bool addDCTerm=false , bool solveNormal=false ); + + template< class T2 > + static int SolveCG( const SparseSymmetricMatrix& M , const Vector& b , int iters , Vector& x , MapReduceVector& scratch , T2 eps=1e-8 , int reset=1 , bool addDCTerm=false , bool solveNormal=false ); +#ifdef WIN32 + template< class T2 > + static int SolveCGAtomic( const SparseSymmetricMatrix& M , const Vector& b , int iters , Vector& x , T2 eps=1e-8 , int reset=1 , int threads=0 , bool solveNormal=false ); +#endif // WIN32 + template< class T2 > + static int SolveJacobi( const SparseSymmetricMatrix& M , const Vector& diagonal , const Vector& b , Vector& x , MapReduceVector& scratch , Vector& Mx , T2 sor , int reset ); + template< class T2 > + static int SolveJacobi( const SparseSymmetricMatrix& M , const Vector& b , int iters , Vector& x , MapReduceVector& scratch , T2 sor=T2(1.) , int reset=1 ); + template< class T2 > + static int SolveJacobi( const SparseSymmetricMatrix& M , const Vector& diagonal , const Vector& b , Vector& x , Vector& Mx , T2 sor , int reset ); + template< class T2 > + static int SolveJacobi( const SparseSymmetricMatrix& M , const Vector& b , int iters , Vector& x , T2 sor=T2(1.) , int reset=1 ); + + enum + { + ORDERING_UPPER_TRIANGULAR , + ORDERING_LOWER_TRIANGULAR , + ORDERING_NONE + }; + template< class T2 > + static int SolveGS( const std::vector< std::vector< int > >& mcIndices , const SparseSymmetricMatrix& M , const Vector& diagonal , const Vector& b , Vector& x , MapReduceVector& scratch , Vector& Mx , Vector& dx , bool forward , int reset ); + template< class T2 > + static int SolveGS( const std::vector< std::vector< int > >& mcIndices , const SparseSymmetricMatrix& M , const Vector& b , int iters , Vector& x , MapReduceVector& scratch , bool forward , int reset=1 ); + + template< class T2 > + static int SolveGS( const SparseSymmetricMatrix& M , const Vector& diagonal , const Vector& b , Vector& x , MapReduceVector& scratch , Vector& Mx , Vector& dx , bool forward , int reset , int ordering ); + template< class T2 > + static int SolveGS( const SparseSymmetricMatrix& M , const Vector& b , int iters , Vector& x , MapReduceVector& scratch , bool forward , int reset=1 , int ordering=ORDERING_NONE ); + template< class T2 > + static int SolveGS( const SparseSymmetricMatrix& M , const Vector& diagonal , const Vector& b , Vector& x , Vector& Mx , Vector& dx , bool forward , int reset , int ordering ); + template< class T2 > + static int SolveGS( const SparseSymmetricMatrix& M , const Vector& b , int iters , Vector& x , bool forward , int reset=1 , int ordering=ORDERING_NONE ); + + template< class T2 > + void getDiagonal( Vector< T2 >& diagonal , int threads=1 ) const; +}; + +#include "SparseMatrix.inl" + +#endif + diff --git a/Src/SparseMatrix.inl b/Src/SparseMatrix.inl new file mode 100644 index 0000000..e8bf71e --- /dev/null +++ b/Src/SparseMatrix.inl @@ -0,0 +1,1343 @@ +/* +Copyright (c) 2006, Michael Kazhdan and Matthew Bolitho +All rights reserved. + +Redistribution and use in source and binary forms, with or without modification, +are permitted provided that the following conditions are met: + +Redistributions of source code must retain the above copyright notice, this list of +conditions and the following disclaimer. Redistributions in binary form must reproduce +the above copyright notice, this list of conditions and the following disclaimer +in the documentation and/or other materials provided with the distribution. + +Neither the name of the Johns Hopkins University nor the names of its contributors +may be used to endorse or promote products derived from this software without specific +prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY +EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO THE IMPLIED WARRANTIES +OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT +SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED +TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR +BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN +ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH +DAMAGE. +*/ + +#include +#include + + +/////////////////// +// SparseMatrix // +/////////////////// +/////////////////////////////////////// +// SparseMatrix Methods and Memebers // +/////////////////////////////////////// + +template< class T > +void SparseMatrix< T >::_init( void ) +{ + _contiguous = false; + _maxEntriesPerRow = 0; + rows = 0; + rowSizes = NullPointer< int >( ); + m_ppElements = NullPointer< Pointer( MatrixEntry< T > ) >( ); +} + +template< class T > SparseMatrix< T >::SparseMatrix( void ){ _init(); } + +template< class T > SparseMatrix< T >::SparseMatrix( int rows ){ _init() , Resize( rows ); } +template< class T > SparseMatrix< T >::SparseMatrix( int rows , int maxEntriesPerRow ){ _init() , Resize( rows , maxEntriesPerRow ); } + +template< class T > +SparseMatrix< T >::SparseMatrix( const SparseMatrix& M ) +{ + _init(); + if( M._contiguous ) Resize( M.rows , M._maxEntriesPerRow ); + else Resize( M.rows ); + for( int i=0 ; i ) * rowSizes[i] ); + } +} +template +int SparseMatrix::Entries( void ) const +{ + int e = 0; + for( int i=0 ; i +SparseMatrix& SparseMatrix::operator = (const SparseMatrix& M) +{ + if( M._contiguous ) Resize( M.rows , M._maxEntriesPerRow ); + else Resize( M.rows ); + for( int i=0 ; i ) * rowSizes[i] ); + } + return *this; +} + +template +SparseMatrix::~SparseMatrix( void ){ Resize( 0 ); } + +template< class T > +bool SparseMatrix< T >::write( const char* fileName ) const +{ + FILE* fp = fopen( fileName , "wb" ); + if( !fp ) return false; + bool ret = write( fp ); + fclose( fp ); + return ret; +} +template< class T > +bool SparseMatrix< T >::read( const char* fileName ) +{ + FILE* fp = fopen( fileName , "rb" ); + if( !fp ) return false; + bool ret = read( fp ); + fclose( fp ); + return ret; +} +template< class T > +bool SparseMatrix< T >::write( FILE* fp ) const +{ + if( fwrite( &rows , sizeof( int ) , 1 , fp )!=1 ) return false; + if( fwrite( rowSizes , sizeof( int ) , rows , fp )!=rows ) return false; + for( int i=0 ; i ) , rowSizes[i] , fp )!=rowSizes[i] ) return false; + return true; +} +template< class T > +bool SparseMatrix< T >::read( FILE* fp ) +{ + int r; + if( fread( &r , sizeof( int ) , 1 , fp )!=1 ) return false; + Resize( r ); + if( fread( rowSizes , sizeof( int ) , rows , fp )!=rows ) return false; + for( int i=0 ; i ) , rowSizes[i] , fp )!=rowSizes[i] ) return false; + } + return true; +} + + +template< class T > +void SparseMatrix< T >::Resize( int r ) +{ + if( rows>0 ) + { + if( _contiguous ){ if( _maxEntriesPerRow ) FreePointer( m_ppElements[0] ); } + else for( int i=0 ; i( r ); + m_ppElements = AllocPointer< Pointer( MatrixEntry< T > ) >( r ); + memset( rowSizes , 0 , sizeof( int ) * r ); + } + _contiguous = false; + _maxEntriesPerRow = 0; +} +template< class T > +void SparseMatrix< T >::Resize( int r , int e ) +{ + if( rows>0 ) + { + if( _contiguous ){ if( _maxEntriesPerRow ) FreePointer( m_ppElements[0] ); } + else for( int i=0 ; i( r ); + m_ppElements = AllocPointer< Pointer( MatrixEntry< T > ) >( r ); + m_ppElements[0] = AllocPointer< MatrixEntry< T > >( r * e ); + memset( rowSizes , 0 , sizeof( int ) * r ); + for( int i=1 ; i +void SparseMatrix< T >::SetRowSize( int row , int count ) +{ + if( _contiguous ) + { + if( count>_maxEntriesPerRow ) fprintf( stderr , "[ERROR] Cannot set row size on contiguous matrix: %d<=%d\n" , count , _maxEntriesPerRow ) , exit( 0 ); + rowSizes[row] = count; + } + else if( row>=0 && row0 ) m_ppElements[row] = AllocPointer< MatrixEntry< T > >( count ); + // [WARNING] Why wasn't this line here before??? + rowSizes[row] = count; + } +} + + +template +void SparseMatrix::SetZero() +{ + Resize(this->m_N, this->m_M); +} + +template +SparseMatrix SparseMatrix::operator * (const T& V) const +{ + SparseMatrix M(*this); + M *= V; + return M; +} + +template +SparseMatrix& SparseMatrix::operator *= (const T& V) +{ + for( int i=0 ; i +template +Vector SparseMatrix::Multiply( const Vector& V ) const +{ + Vector R( rows ); + Multiply( V , R ); + return R; +} + +template +template +void SparseMatrix::Multiply( const Vector& In , Vector& Out , int threads ) const +{ +#pragma omp parallel for num_threads( threads ) schedule( static ) + for( int i=0 ; i ) start = m_ppElements[i]; + ConstPointer( MatrixEntry< T > ) end = start + rowSizes[i]; + ConstPointer( MatrixEntry< T > ) e; + for( e=start ; e!=end ; e++ ) temp += In[ e->N ] * e->Value; + Out[i] = temp; +#else + for( int j=0 ; j +template +Vector SparseMatrix::operator * (const Vector& V) const +{ + return Multiply(V); +} + +template +template +int SparseMatrix::SolveSymmetric( const SparseMatrix& M , const Vector& b , int iters , Vector& solution , const T2 eps , int reset , int threads ) +{ + if( reset ) + { + solution.Resize( b.Dimensions() ); + solution.SetZero(); + } + Vector< T2 > r; + r.Resize( solution.Dimensions() ); + M.Multiply( solution , r ); + r = b - r; + Vector< T2 > d = r; + double delta_new , delta_0; + for( int i=0 ; i q; + q.Resize( d.Dimensions() ); + for( ii=0; iieps*delta_0 ; ii++ ) + { + M.Multiply( d , q , threads ); + double dDotQ = 0 , alpha = 0; + for( int i=0 ; i +int SparseMatrix::Solve(const SparseMatrix& M,const Vector& b,int iters,Vector& solution,const T eps){ + SparseMatrix mTranspose=M.Transpose(); + Vector bb=mTranspose*b; + Vector d,r,Md; + T alpha,beta,rDotR; + int i; + + solution.Resize(M.Columns()); + solution.SetZero(); + + d=r=bb; + rDotR=r.Dot(r); + for(i=0;ieps;i++){ + T temp; + Md=mTranspose*(M*d); + alpha=rDotR/d.Dot(Md); + solution+=d*alpha; + r-=Md*alpha; + temp=r.Dot(r); + beta=temp/rDotR; + rDotR=temp; + d=r+d*beta; + } + return i; +} + + + + +/////////////////////////// +// SparseSymmetricMatrix // +/////////////////////////// +template +template +Vector SparseSymmetricMatrix::operator * (const Vector& V) const {return Multiply(V);} +template +template +Vector SparseSymmetricMatrix::Multiply( const Vector& V ) const +{ + Vector R( SparseMatrix< T >::rows ); + + for(int i=0; i::rows; i++){ + for(int ii=0;ii::rowSizes[i];ii++) + { + int j=SparseMatrix< T >::m_ppElements[i][ii].N; + R(i) += SparseMatrix< T >::m_ppElements[i][ii].Value * V.m_pV[j]; + R(j) += SparseMatrix< T >::m_ppElements[i][ii].Value * V.m_pV[i]; + } + } + return R; +} + +template +template +void SparseSymmetricMatrix::Multiply( const Vector& In , Vector& Out , bool addDCTerm ) const +{ + Out.SetZero(); + const T2* in = &In[0]; + T2* out = &Out[0]; + T2 dcTerm = T2( 0 ); + if( addDCTerm ) + { + for( int i=0 ; i::rows ; i++ ) dcTerm += in[i]; + dcTerm /= SparseMatrix< T >::rows; + } + for( int i=0 ; i::rows ; i++ ) + { + const MatrixEntry* temp = SparseMatrix< T >::m_ppElements[i]; + const MatrixEntry* end = temp + SparseMatrix< T >::rowSizes[i]; + const T2& in_i_ = in[i]; + T2 out_i = T2(0); + for( ; temp!=end ; temp++ ) + { + int j=temp->N; + T2 v=temp->Value; + out_i += v * in[j]; + out[j] += v * in_i_; + } + out[i] += out_i; + } + if( addDCTerm ) for( int i=0 ; i::rows ; i++ ) out[i] += dcTerm; +} +template +template +void SparseSymmetricMatrix::Multiply( const Vector& In , Vector& Out , MapReduceVector< T2 >& OutScratch , bool addDCTerm ) const +{ + int dim = int( In.Dimensions() ); + const T2* in = &In[0]; + int threads = OutScratch.threads(); + if( addDCTerm ) + { + T2 dcTerm = 0; +#pragma omp parallel for num_threads( threads ) reduction ( + : dcTerm ) + for( int t=0 ; t::rows*t)/threads ; i<(SparseMatrix< T >::rows*(t+1))/threads ; i++ ) + { + const T2& in_i_ = in[i]; + T2& out_i_ = out[i]; + ConstPointer( MatrixEntry< T > ) temp; + ConstPointer( MatrixEntry< T > ) end; + for( temp = SparseMatrix< T >::m_ppElements[i] , end = temp+SparseMatrix< T >::rowSizes[i] ; temp!=end ; temp++ ) + { + int j = temp->N; + T2 v = temp->Value; + out_i_ += v * in[j]; + out[j] += v * in_i_; + } + dcTerm += in_i_; + } + } + dcTerm /= dim; + dim = int( Out.Dimensions() ); + T2* out = &Out[0]; +#pragma omp parallel for num_threads( threads ) schedule( static ) + for( int i=0 ; i::rows*t)/threads ; i<(SparseMatrix< T >::rows*(t+1))/threads ; i++ ) + { + T2 in_i_ = in[i]; + T2 out_i_ = T2(); + ConstPointer( MatrixEntry< T > ) temp; + ConstPointer( MatrixEntry< T > ) end; + for( temp = SparseMatrix< T >::m_ppElements[i] , end = temp+SparseMatrix< T >::rowSizes[i] ; temp!=end ; temp++ ) + { + int j = temp->N; + T2 v = temp->Value; + out_i_ += v * in[j]; + out[j] += v * in_i_; + } + out[i] += out_i_; + } + } + dim = int( Out.Dimensions() ); + T2* out = &Out[0]; +#pragma omp parallel for num_threads( threads ) schedule( static ) + for( int i=0 ; i +template +void SparseSymmetricMatrix::Multiply( const Vector& In , Vector& Out , std::vector< T2* >& OutScratch , const std::vector< int >& bounds ) const +{ + int dim = In.Dimensions(); + const T2* in = &In[0]; + int threads = OutScratch.size(); +#pragma omp parallel for num_threads( threads ) + for( int t=0 ; t* temp = SparseMatrix< T >::m_ppElements[i]; + const MatrixEntry* end = temp + SparseMatrix< T >::rowSizes[i]; + const T2& in_i_ = in[i]; + T2& out_i_ = out[i]; + for( ; temp!=end ; temp++ ) + { + int j = temp->N; + T2 v = temp->Value; + out_i_ += v * in[j]; + out[j] += v * in_i_; + } + } + } + T2* out = &Out[0]; +#pragma omp parallel for num_threads( threads ) schedule( static ) + for( int i=0 ; i +inline void AtomicIncrement( volatile float* ptr , float addend ) +{ + float newValue = *ptr; + LONG& _newValue = *( (LONG*)&newValue ); + LONG _oldValue; + for( ;; ) + { + _oldValue = _newValue; + newValue += addend; + _newValue = InterlockedCompareExchange( (LONG*) ptr , _newValue , _oldValue ); + if( _newValue==_oldValue ) break; + } +} +inline void AtomicIncrement( volatile double* ptr , double addend ) +//inline void AtomicIncrement( double* ptr , double addend ) +{ + double newValue = *ptr; + LONGLONG& _newValue = *( (LONGLONG*)&newValue ); + LONGLONG _oldValue; + do + { + _oldValue = _newValue; + newValue += addend; + _newValue = InterlockedCompareExchange64( (LONGLONG*) ptr , _newValue , _oldValue ); + } + while( _newValue!=_oldValue ); +} +#endif // _AtomicIncrement_ +template< class T > +void MultiplyAtomic( const SparseSymmetricMatrix< T >& A , const Vector< float >& In , Vector< float >& Out , int threads , const int* partition=NULL ) +{ + Out.SetZero(); + const float* in = &In[0]; + float* out = &Out[0]; + if( partition ) +#pragma omp parallel for num_threads( threads ) + for( int t=0 ; t* temp = A[i]; + const MatrixEntry< T >* end = temp + A.rowSizes[i]; + const float& in_i = in[i]; + float out_i = 0.; + for( ; temp!=end ; temp++ ) + { + int j = temp->N; + float v = temp->Value; + out_i += v * in[j]; + AtomicIncrement( out+j , v * in_i ); + } + AtomicIncrement( out+i , out_i ); + } + else +#pragma omp parallel for num_threads( threads ) + for( int i=0 ; i* temp = A[i]; + const MatrixEntry< T >* end = temp + A.rowSizes[i]; + const float& in_i = in[i]; + float out_i = 0.f; + for( ; temp!=end ; temp++ ) + { + int j = temp->N; + float v = temp->Value; + out_i += v * in[j]; + AtomicIncrement( out+j , v * in_i ); + } + AtomicIncrement( out+i , out_i ); + } +} +template< class T > +void MultiplyAtomic( const SparseSymmetricMatrix< T >& A , const Vector< double >& In , Vector< double >& Out , int threads , const int* partition=NULL ) +{ + Out.SetZero(); + const double* in = &In[0]; + double* out = &Out[0]; + + if( partition ) +#pragma omp parallel for num_threads( threads ) + for( int t=0 ; t* temp = A[i]; + const MatrixEntry< T >* end = temp + A.rowSizes[i]; + const double& in_i = in[i]; + double out_i = 0.; + for( ; temp!=end ; temp++ ) + { + int j = temp->N; + T v = temp->Value; + out_i += v * in[j]; + AtomicIncrement( out+j , v * in_i ); + } + AtomicIncrement( out+i , out_i ); + } + else +#pragma omp parallel for num_threads( threads ) + for( int i=0 ; i* temp = A[i]; + const MatrixEntry< T >* end = temp + A.rowSizes[i]; + const double& in_i = in[i]; + double out_i = 0.; + for( ; temp!=end ; temp++ ) + { + int j = temp->N; + T v = temp->Value; + out_i += v * in[j]; + AtomicIncrement( out+j , v * in_i ); + } + AtomicIncrement( out+i , out_i ); + } +} + +template< class T > +template< class T2 > +int SparseSymmetricMatrix< T >::SolveCGAtomic( const SparseSymmetricMatrix< T >& A , const Vector< T2 >& b , int iters , Vector< T2 >& x , T2 eps , int reset , int threads , bool solveNormal ) +{ + eps *= eps; + int dim = b.Dimensions(); + if( reset ) + { + x.Resize( dim ); + x.SetZero(); + } + Vector< T2 > r( dim ) , d( dim ) , q( dim ); + Vector< T2 > temp; + if( solveNormal ) temp.Resize( dim ); + T2 *_x = &x[0] , *_r = &r[0] , *_d = &d[0] , *_q = &q[0]; + const T2* _b = &b[0]; + + std::vector< int > partition( threads+1 ); + { + int eCount = 0; + for( int i=0 ; i=eCount*(t+1) ) + { + partition[t+1] = i; + break; + } + } + } + partition[threads] = A.rows; + } + if( solveNormal ) + { + MultiplyAtomic( A , x , temp , threads , &partition[0] ); + MultiplyAtomic( A , temp , r , threads , &partition[0] ); + MultiplyAtomic( A , b , temp , threads , &partition[0] ); +#pragma omp parallel for num_threads( threads ) schedule( static ) + for( int i=0 ; ieps*delta_0 ; ii++ ) + { + if( solveNormal ) MultiplyAtomic( A , d , temp , threads , &partition[0] ) , MultiplyAtomic( A , temp , q , threads , &partition[0] ); + else MultiplyAtomic( A , d , q , threads , &partition[0] ); + double dDotQ = 0; + for( int i=0 ; i +template< class T2 > +int SparseSymmetricMatrix< T >::SolveCG( const SparseSymmetricMatrix& A , const Vector& b , int iters , Vector& x , MapReduceVector< T2 >& scratch , T2 eps , int reset , bool addDCTerm , bool solveNormal ) +{ + int threads = scratch.threads(); +#if 0 + int dim = b.Dimensions(); +#if 1 + Vector< T2 > r( dim ) , d( dim ) , Ad( dim ) , temp( dim ); + if( reset ) x.Resize( dim ); +#else + Vector< T2 > r ,d , Ad , temp; +#endif + + double delta_new = 0 , delta_0; + + //////////////////////// + // d = r = b - M * x + // \delta_new = ||r||^2 +#if 1 + A.Multiply( x , temp , scratch , addDCTerm ); + d = r = b - temp; +#else + d = r = b - Multiply< AddAverage >( A , x ); +#endif + for( int i=0 ; ieps*delta_0 ; ii++ ) + { + //////////////////////////////////// + // \alpha = ||r||^2 / (d^t * M * d) +#if 1 + A.Multiply( d , Ad , scratch , addDCTerm ); +#else + Ad = Multiply< AddAverage >( A , d ); +#endif + double dDotMd = 0; + for( int i=0 ; i( A , x ); +#endif + for( int i=0 ; i r( dim ) , d( dim ) , q( dim ) , temp; + if( reset ) x.Resize( dim ); + if( solveNormal ) temp.Resize( dim ); + T2 *_x = &x[0] , *_r = &r[0] , *_d = &d[0] , *_q = &q[0]; + const T2* _b = &b[0]; + + double delta_new = 0 , delta_0; + if( solveNormal ) + { + A.Multiply( x , temp , scratch , addDCTerm ) , A.Multiply( temp , r , scratch , addDCTerm ) , A.Multiply( b , temp , scratch , addDCTerm ); +#pragma omp parallel for num_threads( threads ) reduction( + : delta_new ) + for( int i=0 ; ieps*delta_0 ; ii++ ) + { + if( solveNormal ) A.Multiply( d , temp , scratch , addDCTerm ) , A.Multiply( temp , q , scratch , addDCTerm ); + else A.Multiply( d , q , scratch , addDCTerm ); + double dDotQ = 0; +#pragma omp parallel for num_threads( threads ) reduction( + : dDotQ ) + for( int i=0 ; i +template< class T2 > +int SparseSymmetricMatrix::SolveCG( const SparseSymmetricMatrix& A , const Vector& b , int iters , Vector& x , T2 eps , int reset , int threads , bool addDCTerm , bool solveNormal ) +{ + eps *= eps; + int dim = int( b.Dimensions() ); + MapReduceVector< T2 > outScratch; + if( threads<1 ) threads = 1; + if( threads>1 ) outScratch.resize( threads , dim ); + if( reset ) x.Resize( dim ); + Vector< T2 > r( dim ) , d( dim ) , q( dim ); + Vector< T2 > temp; + if( solveNormal ) temp.Resize( dim ); + T2 *_x = &x[0] , *_r = &r[0] , *_d = &d[0] , *_q = &q[0]; + const T2* _b = &b[0]; + + double delta_new = 0 , delta_0; + + if( solveNormal ) + { + if( threads>1 ) A.Multiply( x , temp , outScratch , addDCTerm ) , A.Multiply( temp , r , outScratch , addDCTerm ) , A.Multiply( b , temp , outScratch , addDCTerm ); + else A.Multiply( x , temp , addDCTerm ) , A.Multiply( temp , r , addDCTerm ) , A.Multiply( b , temp , addDCTerm ); +#pragma omp parallel for num_threads( threads ) reduction( + : delta_new ) + for( int i=0 ; i1 ) A.Multiply( x , r , outScratch , addDCTerm ); + else A.Multiply( x , r , addDCTerm ); +#pragma omp parallel for num_threads( threads ) reduction( + : delta_new ) + for( int i=0 ; ieps*delta_0 ; ii++ ) + { + if( solveNormal ) + { + if( threads>1 ) A.Multiply( d , temp , outScratch , addDCTerm ) , A.Multiply( temp , q , outScratch , addDCTerm ); + else A.Multiply( d , temp , addDCTerm ) , A.Multiply( temp , q , addDCTerm ); + } + else + { + if( threads>1 ) A.Multiply( d , q , outScratch , addDCTerm ); + else A.Multiply( d , q , addDCTerm ); + } + double dDotQ = 0; +#pragma omp parallel for num_threads( threads ) reduction( + : dDotQ ) + for( int i=0 ; i1 ) A.Multiply( x , temp , outScratch , addDCTerm ) , A.Multiply( temp , r , outScratch , addDCTerm ); + else A.Multiply( x , temp , addDCTerm ) , A.Multiply( temp , r , addDCTerm ); + } + else + { + if( threads>1 ) A.Multiply( x , r , outScratch , addDCTerm ); + else A.Multiply( x , r , addDCTerm ); + } +#pragma omp parallel for num_threads( threads ) reduction ( + : delta_new ) + for( int i=0 ; i +template< class T2 > +int SparseMatrix::SolveJacobi( const SparseMatrix& M , const Vector& diagonal , const Vector& b , Vector& x , Vector& Mx , T2 sor , int threads , int offset ) +{ + M.Multiply( x , Mx , threads ); +#if ZERO_TESTING_JACOBI + for( int j=0 ; j +template< class T2 > +int SparseMatrix::SolveJacobi( const SparseMatrix& M , const Vector& b , Vector& x , Vector& Mx , T2 sor , int threads , int offset ) +{ + M.Multiply( x , Mx , threads ); +#if ZERO_TESTING_JACOBI + for( int j=0 ; j +template< class T2 > +int SparseSymmetricMatrix::SolveJacobi( const SparseSymmetricMatrix& M , const Vector& diagonal , const Vector& b , Vector& x , Vector& Mx , T2 sor , int reset ) +{ + if( reset ) x.Resize( b.Dimensions() ) , x.SetZero(); + M.Multiply( x , Mx ); + // solution_new[j] * diagonal[j] + ( Md[j] - solution_old[j] * diagonal[j] ) = b[j] + // solution_new[j] = ( b[j] - ( Md[j] - solution_old[j] * diagonal[j] ) ) / diagonal[j] + // solution_new[j] = ( b[j] - Md[j] ) / diagonal[j] + solution_old[j] + // for( int j=0 ; j +template< class T2 > +int SparseSymmetricMatrix::SolveJacobi( const SparseSymmetricMatrix& M , const Vector& diagonal , const Vector& b , Vector& x , MapReduceVector& scratch , Vector& Mx , T2 sor , int reset ) +{ + if( reset ) x.Resize( b.Dimensions() ) , x.SetZero(); + M.Multiply( x , Mx , scratch ); + // solution_new[j] * diagonal[j] + ( Md[j] - solution_old[j] * diagonal[j] ) = b[j] + // solution_new[j] = ( b[j] - ( Md[j] - solution_old[j] * diagonal[j] ) ) / diagonal[j] + // solution_new[j] = ( b[j] - Md[j] ) / diagonal[j] + solution_old[j] + // for( int j=0 ; j +template< class T2 > +int SparseSymmetricMatrix::SolveJacobi( const SparseSymmetricMatrix& M , const Vector& b , int iters , Vector& x , T2 sor , int reset ) +{ + Vector< T2 > diagonal , Mx; + M.getDiagonal( diagonal ); + Mx.Resize( M.rows ); + for( int i=0 ; i +template< class T2 > +int SparseSymmetricMatrix::SolveJacobi( const SparseSymmetricMatrix& M , const Vector& b , int iters , Vector& x , MapReduceVector& scratch , T2 sor , int reset ) +{ + Vector< T2 > diagonal , Mx; + M.getDiagonal( diagonal , scratch.threads() ); + Mx.Resize( M.rows ); + for( int i=0 ; i +template +int SparseMatrix::SolveGS( const SparseMatrix& M , const Vector& diagonal , const Vector& b , Vector& x , bool forward , int offset ) +{ +#define ITERATE \ + { \ + ConstPointer( MatrixEntry< T > ) start = M[j]; \ + ConstPointer( MatrixEntry< T > ) end = start + M.rowSizes[j]; \ + ConstPointer( MatrixEntry< T > ) e; \ + T2 _b = b[j+offset]; \ + for( e=start ; e!=end ; e++ ) _b -= x[ e->N ] * e->Value; \ + x[j+offset] += _b / diagonal[j]; \ + } + +#if ZERO_TESTING_JACOBI + if( forward ) for( int j=0 ; j=0 ; j-- ){ if( diagonal[j] ){ ITERATE; } } +#else // !ZERO_TESTING_JACOBI + if( forward ) for( int j=0 ; j=0 ; j-- ){ ITERATE; } +#endif // ZERO_TESTING_JACOBI +#undef ITERATE + return M.rows; +} +template +template +int SparseMatrix::SolveGS( const std::vector< std::vector< int > >& mcIndices , const SparseMatrix& M , const Vector& diagonal , const Vector& b , Vector& x , bool forward , int threads , int offset ) +{ + int sum=0; +#ifdef _WIN32 +#define SetOMPParallel __pragma( omp parallel for num_threads( threads ) ) +#else // !_WIN32 +#define SetOMPParallel _Pragma( "omp parallel for num_threads( threads )" ) +#endif // _WIN32 +#if ZERO_TESTING_JACOBI +#define ITERATE( indices ) \ + { \ +SetOMPParallel \ + for( int k=0 ; k ) start = M[jj]; \ + ConstPointer( MatrixEntry< T > ) end = start + M.rowSizes[jj]; \ + ConstPointer( MatrixEntry< T > ) e; \ + T2 _b = b[jj+offset]; \ + for( e=start ; e!=end ; e++ ) _b -= x[ e->N ] * e->Value; \ + x[jj+offset] += _b / diagonal[jj]; \ + } \ + } +#else // !ZERO_TESTING_JACOBI +#define ITERATE( indices ) \ + { \ +SetOMPParallel \ + for( int k=0 ; k ) start = M[jj]; \ + ConstPointer( MatrixEntry< T > ) end = start + M.rowSizes[jj]; \ + ConstPointer( MatrixEntry< T > ) e; \ + T2 _b = b[jj+offset]; \ + for( e=start ; e!=end ; e++ ) _b -= x[ e->N ] * e->Value; \ + x[jj+offset] += _b / diagonal[jj]; \ + } \ + } +#endif // ZERO_TESTING_JACOBI + + if( forward ) for( int j=0 ; j=0 ; j-- ){ sum += int( mcIndices[j].size() ) ; ITERATE( mcIndices[j] ); } +#undef ITERATE +#undef SetOMPParallel + return sum; +} +template +template +int SparseMatrix::SolveGS( const SparseMatrix& M , const Vector& b , Vector& x , bool forward , int offset ) +{ + int start = forward ? 0 : M.rows-1 , end = forward ? M.rows : -1 , dir = forward ? 1 : -1; + for( int j=start ; j!=end ; j+=dir ) + { + T diagonal = M[j][0].Value; +#if ZERO_TESTING_JACOBI + if( diagonal ) +#endif // ZERO_TESTING_JACOBI + { + ConstPointer( MatrixEntry< T > ) start = M[j]; + ConstPointer( MatrixEntry< T > ) end = start + M.rowSizes[j]; + ConstPointer( MatrixEntry< T > ) e; + start++; + T2 _b = b[j+offset]; + for( e=start ; e!=end ; e++ ) _b -= x[ e->N ] * e->Value; + x[j+offset] = _b / diagonal; + } + } + return M.rows; +} +template +template +int SparseMatrix::SolveGS( const std::vector< std::vector< int > >& mcIndices , const SparseMatrix& M , const Vector& b , Vector& x , bool forward , int threads , int offset ) +{ + int sum=0 , start = forward ? 0 : int( mcIndices.size() )-1 , end = forward ? int( mcIndices.size() ) : -1 , dir = forward ? 1 : -1; + for( int j=start ; j!=end ; j+=dir ) + { + const std::vector< int >& _mcIndices = mcIndices[j]; + sum += int( _mcIndices.size() ); + { +#pragma omp parallel for num_threads( threads ) + for( int k=0 ; k ) start = M[jj]; + ConstPointer( MatrixEntry< T > ) end = start + M.rowSizes[jj]; + ConstPointer( MatrixEntry< T > ) e; + start++; + T2 _b = b[jj+offset]; + for( e=start ; e!=end ; e++ ) _b -= x[ e->N ] * e->Value; + x[jj+offset] = _b / diagonal; + } + } + } + } + return sum; +} + + +template +template +int SparseSymmetricMatrix::SolveGS( const SparseSymmetricMatrix& M , const Vector& diagonal , const Vector& b , Vector& x , Vector& Mx , Vector& dx , bool forward , int reset , int ordering ) +{ + if( reset ) x.Resize( b.Dimensions() ) , x.SetZero(); + dx.SetZero(); + M.Multiply( x , Mx ); +#define ITERATE \ + { \ + ConstPointer( MatrixEntry< T > ) start = M[j]; \ + ConstPointer( MatrixEntry< T > ) end = start + M.rowSizes[j]; \ + ConstPointer( MatrixEntry< T > ) e; \ + T2 _Mx = Mx[j]; \ + if( ordering!=ORDERING_UPPER_TRIANGULAR ) \ + for( e=start ; e!=end ; e++ ) _Mx += dx[ e->N ] * e->Value; \ + dx[j] = ( b[j]-_Mx ) / diagonal[j]; \ + x[j] += dx[j]; \ + if( ordering!=ORDERING_LOWER_TRIANGULAR ) \ + for( e=start ; e!=end ; e++ ) Mx[ e->N ] += dx[j] * e->Value; \ + } + +#if ZERO_TESTING_JACOBI + if( forward ) for( int j=0 ; j=0 ; j-- ){ if( diagonal[j] ){ ITERATE; } } +#else // !ZERO_TESTING_JACOBI + if( forward ) for( int j=0 ; j=0 ; j-- ){ ITERATE; } +#endif // ZERO_TESTING_JACOBI +#undef ITERATE + return M.rows; +} +template +template +int SparseSymmetricMatrix::SolveGS( const SparseSymmetricMatrix& M , const Vector& diagonal , const Vector& b , Vector& x , MapReduceVector& scratch , Vector& Mx , Vector& dx , bool forward , int reset , int ordering ) +{ + if( reset ) x.Resize( b.Dimensions() ) , x.SetZero(); + dx.SetZero(); + M.Multiply( x , Mx , scratch ); +#define ITERATE \ + { \ + ConstPointer( MatrixEntry< T > ) start = M[j]; \ + ConstPointer( MatrixEntry< T > ) end = start + M.rowSizes[j]; \ + ConstPointer( MatrixEntry< T > ) e; \ + T2 _Mx = Mx[j]; \ + if( ordering!=ORDERING_UPPER_TRIANGULAR ) \ + for( e=start ; e!=end ; e++ ) _Mx += dx[ e->N ] * e->Value; \ + dx[j] = ( b[j]-_Mx ) / diagonal[j]; \ + x[j] += dx[j]; \ + if( ordering!=ORDERING_LOWER_TRIANGULAR ) \ + for( e=start ; e!=end ; e++ ) Mx[ e->N ] += dx[j] * e->Value; \ + } + +#if ZERO_TESTING_JACOBI + if( forward ) for( int j=0 ; j=0 ; j-- ){ if( diagonal[j] ){ ITERATE; } } +#else // !ZERO_TESTING_JACOBI + if( forward ) for( int j=0 ; j=0 ; j-- ){ ITERATE; } +#endif // ZERO_TESTING_JACOBI +#undef ITERATE + return M.rows; +} +template +template +int SparseSymmetricMatrix::SolveGS( const std::vector< std::vector< int > >& mcIndices , const SparseSymmetricMatrix& M , const Vector& diagonal , const Vector& b , Vector& x , MapReduceVector& scratch , Vector& Mx , Vector& dx , bool forward , int reset ) +{ + int sum = 0; + if( reset ) x.Resize( b.Dimensions() ) , x.SetZero(); + M.Multiply( x , Mx , scratch ); + dx.SetZero(); +#ifdef _WIN32 +#define SetOMPParallel __pragma( omp parallel for num_threads( scratch.threads() ) ) +#else // !_WIN32 +#define SetOMPParallel _Pragma( "omp parallel for num_threads( scratch.threads() )" ) +#endif // _WIN32 +#if ZERO_TESTING_JACOBI +#define ITERATE( indices ) \ + { \ +SetOMPParallel \ + for( int k=0 ; k ) start = M[jj]; \ + ConstPointer( MatrixEntry< T > ) end = start + M.rowSizes[jj]; \ + ConstPointer( MatrixEntry< T > ) e; \ + T2 _Mx = Mx[jj]; \ + for( e=start ; e!=end ; e++ ) _Mx += dx[ e->N ] * e->Value; \ + Mx[jj] = _Mx; \ + dx[jj] = ( b[jj]-_Mx ) / diagonal[jj]; \ + x[jj] += dx[jj]; \ + for( e=start ; e!=end ; e++ ) Mx[ e->N ] += dx[jj] * e->Value; \ + } \ + } +#else // !ZERO_TESTING_JACOBI +#define ITERATE( indices ) \ + { \ +SetOMPParallel \ + for( int k=0 ; k ) start = M[jj]; \ + ConstPointer( MatrixEntry< T > ) end = start + M.rowSizes[jj]; \ + ConstPointer( MatrixEntry< T > ) e; \ + T2 _Mx = Mx[jj]; \ + for( e=start ; e!=end ; e++ ) _Mx += dx[ e->N ] * e->Value; \ + Mx[jj] = _Mx; \ + dx[jj] = ( b[jj]-_Mx ) / diagonal[jj]; \ + x[jj] += dx[jj]; \ + for( e=start ; e!=end ; e++ ) Mx[ e->N ] += dx[jj] * e->Value; \ + } \ + } +#endif // ZERO_TESTING_JACOBI + + if( forward ) for( int j=0 ; j=0 ; j-- ){ sum += int(mcIndices[j].size()) ; ITERATE( mcIndices[j] ); } +#undef ITERATE +#undef SetOMPParallel + return sum; +} +template +template +int SparseSymmetricMatrix::SolveGS( const SparseSymmetricMatrix& M , const Vector& b , int iters , Vector& solution , bool forward , int reset , int ordering ) +{ + Vector< T2 > diagonal , Mx , dx; + M.getDiagonal( diagonal ); + Mx.Resize( M.rows ) , dx.Resize( M.rows ); + for( int i=0 ; i +template +int SparseSymmetricMatrix::SolveGS( const SparseSymmetricMatrix& M , const Vector& b , int iters , Vector& solution , MapReduceVector& scratch , bool forward , int reset , int ordering ) +{ + Vector< T2 > diagonal , Mx , dx; + M.getDiagonal( diagonal , scratch.threads() ); + Mx.Resize( M.rows ) , dx.Resize( M.rows ); + for( int i=0 ; i +template +int SparseSymmetricMatrix::SolveGS( const std::vector< std::vector< int > >& mcIndices , const SparseSymmetricMatrix& M , const Vector& b , int iters , Vector& solution , MapReduceVector& scratch , bool forward , int reset ) +{ + Vector< T2 > diagonal , Mx , dx; + M.getDiagonal( diagonal , scratch.threads() ); + Mx.Resize( M.rows ) , dx.Resize( M.rows ); + for( int i=0 ; i +template< class T2 > +void SparseMatrix< T >::getDiagonal( Vector< T2 >& diagonal , int threads , int offset ) const +{ + diagonal.Resize( SparseMatrix< T >::rows ); +#pragma omp parallel for num_threads( threads ) + for( int i=0 ; i ) start = m_ppElements[i]; + ConstPointer( MatrixEntry< T > ) end = start + rowSizes[i]; + ConstPointer( MatrixEntry< T > ) e; + for( e=start ; e!=end ; e++ ) if( e->N==ii ) d += e->Value; + diagonal[i] = d; + } +} +template< class T > +template< class T2 > +void SparseSymmetricMatrix< T >::getDiagonal( Vector< T2 >& diagonal , int threads ) const +{ + diagonal.Resize( SparseMatrix< T >::rows ); +#pragma omp parallel for num_threads( threads ) + for( int i=0 ; i::rows ; i++ ) + { + T2 d = 0.; + ConstPointer( MatrixEntry< T > ) start = SparseMatrix< T >::m_ppElements[i]; + ConstPointer( MatrixEntry< T > ) end = start + SparseMatrix< T >::rowSizes[i]; + ConstPointer( MatrixEntry< T > ) e; + for( e=start ; e!=end ; e++ ) if( e->N==i ) d += e->Value; + diagonal[i] = d * T2(2); + } +} \ No newline at end of file diff --git a/Src/SurfaceTrimmer.cpp b/Src/SurfaceTrimmer.cpp new file mode 100644 index 0000000..9243f35 --- /dev/null +++ b/Src/SurfaceTrimmer.cpp @@ -0,0 +1,426 @@ +/* +Copyright (c) 2013, Michael Kazhdan +All rights reserved. + +Redistribution and use in source and binary forms, with or without modification, +are permitted provided that the following conditions are met: + +Redistributions of source code must retain the above copyright notice, this list of +conditions and the following disclaimer. Redistributions in binary form must reproduce +the above copyright notice, this list of conditions and the following disclaimer +in the documentation and/or other materials provided with the distribution. + +Neither the name of the Johns Hopkins University nor the names of its contributors +may be used to endorse or promote products derived from this software without specific +prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY +EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO THE IMPLIED WARRANTIES +OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT +SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED +TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR +BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN +ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH +DAMAGE. +*/ + +#include +#include +#include +#include +#include +#include "CmdLineParser.h" +#include "Geometry.h" +#include "Ply.h" +#include "MAT.h" +#include "MyTime.h" + +#define FOR_RELEASE 1 + +cmdLineString In( "in" ) , Out( "out" ); +cmdLineInt Smooth( "smooth" , 5 ); +cmdLineFloat Trim( "trim" ) , IslandAreaRatio( "aRatio" , 0.001f ); +cmdLineFloatArray< 2 > ColorRange( "color" ); +cmdLineReadable PolygonMesh( "polygonMesh" ); + +cmdLineReadable* params[] = +{ + &In , &Out , &Trim , &PolygonMesh , &ColorRange , &Smooth , &IslandAreaRatio +}; + +void ShowUsage( char* ex ) +{ + printf( "Usage: %s\n" , ex ); + printf( "\t --%s \n" , In.name ); + printf( "\t[--%s ]\n" , Out.name ); + printf( "\t[--%s =%d]\n" , Smooth.name , Smooth.value ); + printf( "\t[--%s ]\n" , Trim.name ); + printf( "\t[--%s =%f]\n" , IslandAreaRatio.name , IslandAreaRatio.value ); + printf( "\t[--%s]\n" , PolygonMesh.name ); +#if !FOR_RELEASE + printf( "\t[--%s ]\n" , ColorRange.name ); +#endif // !FOR_RELEASE +} + +long long EdgeKey( int key1 , int key2 ) +{ + if( key1 +PlyValueVertex< Real > InterpolateVertices( const PlyValueVertex< Real >& v1 , const PlyValueVertex< Real >& v2 , const float& value ) +{ + if( v1.value==v2.value ) return (v1+v2)/Real(2.); + PlyValueVertex< Real > v; + + Real dx = (v1.value-value)/(v1.value-v2.value); + for( int i=0 ; i<3 ; i++ ) v.point.coords[i]=v1.point.coords[i]*(1.f-dx)+v2.point.coords[i]*dx; + v.value=v1.value*(1.f-dx)+v2.value*dx; + return v; +} + +template< class Real > +void ColorVertices( const std::vector< PlyValueVertex< Real > >& inVertices , std::vector< PlyColorVertex< Real > >& outVertices , float min , float max ) +{ + outVertices.resize( inVertices.size() ); + for( size_t i=0 ; i( 0.f , std::min< float >( 1.f , temp ) ); + temp *= 255; + outVertices[i].color[0] = outVertices[i].color[1] = outVertices[i].color[2] = (int)temp; + } +} +template< class Real > +void SmoothValues( std::vector< PlyValueVertex< Real > >& vertices , const std::vector< std::vector< int > >& polygons ) +{ + std::vector< int > count( vertices.size() ); + std::vector< Real > sums( vertices.size() , 0 ); + for( size_t i=0 ; i +void SmoothValues( std::vector< PlyValueVertex< Real > >& vertices , const std::vector< std::vector< int > >& polygons , Real min , Real max ) +{ + std::vector< int > count( vertices.size() ); + std::vector< Real > sums( vertices.size() , 0 ); + for( int i=0 ; imin && vertices[v1].valuemin && vertices[v2].value +void SplitPolygon + ( + const std::vector< int >& polygon , + std::vector< PlyValueVertex< Real > >& vertices , + std::vector< std::vector< int > >* ltPolygons , std::vector< std::vector< int > >* gtPolygons , + std::vector< bool >* ltFlags , std::vector< bool >* gtFlags , + hash_map< long long , int >& vertexTable , + Real trimValue + ) +{ + int sz = int( polygon.size() ); + std::vector< bool > gt( sz ); + int gtCount = 0; + for( int j=0 ; jtrimValue ); + if( gt[j] ) gtCount++; + } + if ( gtCount==sz ){ if( gtPolygons ) gtPolygons->push_back( polygon ) ; if( gtFlags ) gtFlags->push_back( false ); } + else if( gtCount==0 ){ if( ltPolygons ) ltPolygons->push_back( polygon ) ; if( ltFlags ) ltFlags->push_back( false ); } + else + { + int start; + for( start=0 ; start poly; + + // Add the initial vertex + { + int j1 = (start+int(sz)-1)%sz , j2 = start; + int v1 = polygon[j1] , v2 = polygon[j2]; + int vIdx; + hash_map< long long , int >::iterator iter = vertexTable.find( EdgeKey( v1 , v2 ) ); + if( iter==vertexTable.end() ) + { + vertexTable[ EdgeKey( v1 , v2 ) ] = vIdx = int( vertices.size() ); + vertices.push_back( InterpolateVertices( vertices[v1] , vertices[v2] , trimValue ) ); + } + else vIdx = iter->second; + poly.push_back( vIdx ); + } + + for( int _j=0 ; _j<=sz ; _j++ ) + { + int j1 = (_j+start+sz-1)%sz , j2 = (_j+start)%sz; + int v1 = polygon[j1] , v2 = polygon[j2]; + if( gt[j2]==gtFlag ) poly.push_back( v2 ); + else + { + int vIdx; + hash_map< long long , int >::iterator iter = vertexTable.find( EdgeKey( v1 , v2 ) ); + if( iter==vertexTable.end() ) + { + vertexTable[ EdgeKey( v1 , v2 ) ] = vIdx = int( vertices.size() ); + vertices.push_back( InterpolateVertices( vertices[v1] , vertices[v2] , trimValue ) ); + } + else vIdx = iter->second; + poly.push_back( vIdx ); + if( gtFlag ){ if( gtPolygons ) gtPolygons->push_back( poly ) ; if( ltFlags ) ltFlags->push_back( true ); } + else { if( ltPolygons ) ltPolygons->push_back( poly ) ; if( gtFlags ) gtFlags->push_back( true ); } + poly.clear() , poly.push_back( vIdx ) , poly.push_back( v2 ); + gtFlag = !gtFlag; + } + } + } +} +template< class Real > +void Triangulate( const std::vector< PlyValueVertex< Real > >& vertices , const std::vector< std::vector< int > >& polygons , std::vector< std::vector< int > >& triangles ) +{ + triangles.clear(); + for( size_t i=0 ; i3 ) + { + MinimalAreaTriangulation< Real > mat; + std::vector< Point3D< Real > > _vertices( polygons[i].size() ); + std::vector< TriangleIndex > _triangles; + for( int j=0 ; j +void RemoveHangingVertices( std::vector< Vertex >& vertices , std::vector< std::vector< int > >& polygons ) +{ + hash_map< int , int > vMap; + std::vector< bool > vertexFlags( vertices.size() , false ); + for( size_t i=0 ; i _vertices( vCount ); + for( int i=0 ; i >& polygons , std::vector< std::vector< int > >& components ) +{ + std::vector< int > polygonRoots( polygons.size() ); + for( size_t i=0 ; i edgeTable; + for( size_t i=0 ; i::iterator iter = edgeTable.find( eKey ); + if( iter==edgeTable.end() ) edgeTable[ eKey ] = int(i); + else + { + int p = iter->second; + while( polygonRoots[p]!=p ) + { + int temp = polygonRoots[p]; + polygonRoots[p] = int(i); + p = temp; + } + polygonRoots[p] = int(i); + } + } + } + for( size_t i=0 ; i vMap; + for( int i= 0 ; i +inline Point3D< Real > CrossProduct( Point3D< Real > p1 , Point3D< Real > p2 ){ return Point3D< Real >( p1[1]*p2[2]-p1[2]*p2[1] , p1[2]*p2[0]-p1[0]*p2[2] , p1[0]*p1[1]-p1[1]*p2[0] ); } +template< class Real > +double TriangleArea( Point3D< Real > v1 , Point3D< Real > v2 , Point3D< Real > v3 ) +{ + Point3D< Real > n = CrossProduct( v2-v1 , v3-v1 ); + return sqrt( n[0]*n[0] + n[1]*n[1] + n[2]*n[2] ) / 2.; +} +template< class Real > +double PolygonArea( const std::vector< PlyValueVertex< Real > >& vertices , const std::vector< int >& polygon ) +{ + if( polygon.size()<3 ) return 0.; + else if( polygon.size()==3 ) return TriangleArea( vertices[polygon[0]].point , vertices[polygon[1]].point , vertices[polygon[2]].point ); + else + { + Point3D< Real > center; + for( size_t i=0 ; i > vertices; + std::vector< std::vector< int > > polygons; + + int ft , commentNum = paramNum+2; + char** comments; + bool readFlags[ PlyValueVertex< float >::Components ]; + PlyReadPolygons( In.value , vertices , polygons , PlyValueVertex< float >::Properties , PlyValueVertex< float >::Components , ft , &comments , &commentNum , readFlags ); + if( !readFlags[3] ){ fprintf( stderr , "[ERROR] vertices do not have value flag\n" ) ; return EXIT_FAILURE; } +#if 0 + if( Trim.set ) for( int i=0 ; i( min , vertices[i].value ) , max = std::max< float >( max , vertices[i].value ); + printf( "Value Range: [%f,%f]\n" , min , max ); + + + if( Trim.set ) + { + hash_map< long long , int > vertexTable; + std::vector< std::vector< int > > ltPolygons , gtPolygons; + std::vector< bool > ltFlags , gtFlags; + + for( int i=0 ; i0 ) + { + std::vector< std::vector< int > > _ltPolygons , _gtPolygons; + std::vector< std::vector< int > > ltComponents , gtComponents; + SetConnectedComponents( ltPolygons , ltComponents ); + SetConnectedComponents( gtPolygons , gtComponents ); + std::vector< double > ltAreas( ltComponents.size() , 0. ) , gtAreas( gtComponents.size() , 0. ); + std::vector< bool > ltComponentFlags( ltComponents.size() , false ) , gtComponentFlags( gtComponents.size() , false ); + double area = 0.; + for( size_t i=0 ; i > polys = ltPolygons; + Triangulate( vertices , ltPolygons , polys ) , ltPolygons = polys; + } + { + std::vector< std::vector< int > > polys = gtPolygons; + Triangulate( vertices , gtPolygons , polys ) , gtPolygons = polys; + } + } + + RemoveHangingVertices( vertices , gtPolygons ); + sprintf( comments[commentNum++] , "#Trimmed In: %9.1f (s)" , Time()-t ); + if( Out.set ) PlyWritePolygons( Out.value , vertices , gtPolygons , PlyValueVertex< float >::Properties , PlyValueVertex< float >::Components , ft , comments , commentNum ); + } + else + { + if( ColorRange.set ) min = ColorRange.values[0] , max = ColorRange.values[1]; + std::vector< PlyColorVertex< float > > outVertices; + ColorVertices( vertices , outVertices , min , max ); + if( Out.set ) PlyWritePolygons( Out.value , outVertices , polygons , PlyColorVertex< float >::Properties , PlyColorVertex< float >::Components , ft , comments , commentNum ); + } + + return EXIT_SUCCESS; +} + diff --git a/Src/Vector.h b/Src/Vector.h new file mode 100644 index 0000000..69fda00 --- /dev/null +++ b/Src/Vector.h @@ -0,0 +1,111 @@ +/* +Copyright (c) 2006, Michael Kazhdan and Matthew Bolitho +All rights reserved. + +Redistribution and use in source and binary forms, with or without modification, +are permitted provided that the following conditions are met: + +Redistributions of source code must retain the above copyright notice, this list of +conditions and the following disclaimer. Redistributions in binary form must reproduce +the above copyright notice, this list of conditions and the following disclaimer +in the documentation and/or other materials provided with the distribution. + +Neither the name of the Johns Hopkins University nor the names of its contributors +may be used to endorse or promote products derived from this software without specific +prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY +EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO THE IMPLIED WARRANTIES +OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT +SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED +TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR +BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN +ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH +DAMAGE. +*/ + +#ifndef __VECTOR_HPP +#define __VECTOR_HPP + + +#define Assert assert +#include +#include "Array.h" + +template< class T > +class Vector +{ +public: + Vector( void ); + Vector( const Vector& V ); + Vector( size_t N ); + Vector( size_t N, ConstPointer( T ) pV ); + ~Vector( void ); + + const T& operator () (size_t i) const; + T& operator () (size_t i); + const T& operator [] (size_t i) const; + T& operator [] (size_t i); + + void SetZero(); + + size_t Dimensions() const; + void Resize( size_t N ); + + Vector operator * (const T& A) const; + Vector operator / (const T& A) const; + Vector operator - (const T& A) const; + Vector operator + (const T& A) const; + Vector operator - (const Vector& V) const; + Vector operator + (const Vector& V) const; + + Vector& operator *= ( const T& A ); + Vector& operator /= ( const T& A ); + Vector& operator += ( const T& A ); + Vector& operator -= ( const T& A ); + Vector& operator += ( const Vector& V ); + Vector& operator -= ( const Vector& V ); + + Vector& Add( const Vector* V , int count ); + Vector& AddScaled( const Vector& V , const T& scale ); + Vector& SubtractScaled( const Vector& V , const T& scale ); + static void Add( const Vector& V1 , const T& scale1 , const Vector& V2 , const T& scale2 , Vector& Out ); + static void Add( const Vector& V1 , const T& scale1 , const Vector& V2 , Vector& Out ); + + Vector operator - () const; + + Vector& operator = (const Vector& V); + + T Dot( const Vector& V ) const; + + T Length() const; + + T Average() const; + + T Norm( size_t Ln ) const; + void Normalize(); + + bool write( FILE* fp ) const; + bool write( const char* fileName ) const; + bool read( FILE* fp ); + bool read( const char* fileName ); + + Pointer( T ) m_pV; +protected: + size_t m_N; + +}; + +#if ARRAY_DEBUG +template< class C > Array< C > GetPointer( Vector< C >& v ){ return Array< C >::FromPointer( &v[0] , v.Dimensions() ); } +template< class C > ConstArray< C > GetPointer( const Vector< C >& v ){ return ConstArray< C >::FromPointer( &v[0] , v.Dimensions() ); } +#else // !ARRAY_DEBUG +template< class C > C* GetPointer( Vector< C >& v ){ return &v[0]; } +template< class C > const C* GetPointer( const Vector< C >& v ){ return &v[0]; } +#endif // ARRAY_DEBUG + +#include "Vector.inl" + +#endif diff --git a/Src/Vector.inl b/Src/Vector.inl new file mode 100644 index 0000000..db06a3a --- /dev/null +++ b/Src/Vector.inl @@ -0,0 +1,304 @@ +/* +Copyright (c) 2006, Michael Kazhdan and Matthew Bolitho +All rights reserved. + +Redistribution and use in source and binary forms, with or without modification, +are permitted provided that the following conditions are met: + +Redistributions of source code must retain the above copyright notice, this list of +conditions and the following disclaimer. Redistributions in binary form must reproduce +the above copyright notice, this list of conditions and the following disclaimer +in the documentation and/or other materials provided with the distribution. + +Neither the name of the Johns Hopkins University nor the names of its contributors +may be used to endorse or promote products derived from this software without specific +prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY +EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO THE IMPLIED WARRANTIES +OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT +SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED +TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR +BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN +ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH +DAMAGE. +*/ + +#ifndef __VECTORIMPL_HPP +#define __VECTORIMPL_HPP + +#include + +//////////// +// Vector // +//////////// +template +Vector::Vector( void ) +{ + m_N = 0; + m_pV = NullPointer< T >(); +} +template< class T > +Vector< T >::Vector( const Vector& V ) +{ + m_N = 0; + m_pV = NullPointer< T >(); + Resize( V.m_N ); + memcpy( m_pV , V.m_pV , m_N*sizeof(T) ); +} +template +Vector::Vector( size_t N ) +{ + m_N=0; + m_pV = NullPointer< T >(); + Resize(N); +} +template +void Vector::Resize( size_t N ) +{ + if( m_N!=N ) + { + if( m_N ) DeletePointer( m_pV ); + m_N = N; + m_pV = NewPointer< T >( N ); + } + if( N ) memset( m_pV , 0 , N*sizeof(T) ); +} + +template +Vector::Vector( size_t N, ConstPointer( T ) pV ) +{ + Resize(N); + memcpy( m_pV, pV, N*sizeof(T) ); +} +template +Vector::~Vector(){Resize(0);} +template +Vector& Vector::operator = (const Vector& V) +{ + Resize(V.m_N); + memcpy( m_pV, V.m_pV, m_N*sizeof(T) ); + return *this; +} +template +size_t Vector::Dimensions() const{return m_N;} +template +void Vector::SetZero(void){for (size_t i=0; i +const T& Vector::operator () (size_t i) const +{ + return m_pV[i]; +} +template +T& Vector::operator () (size_t i) +{ + return m_pV[i]; +} +template +const T& Vector::operator [] (size_t i) const +{ + return m_pV[i]; +} +template +T& Vector::operator [] (size_t i) +{ + return m_pV[i]; +} +template +Vector Vector::operator * (const T& A) const +{ + Vector V(*this); + for (size_t i=0; i +Vector& Vector::operator *= (const T& A) +{ + for (size_t i=0; i +Vector Vector::operator / (const T& A) const +{ + Vector V(*this); + for (size_t i=0; i +Vector& Vector::operator /= (const T& A) +{ + for (size_t i=0; i +Vector Vector::operator + (const T& A) const +{ + Vector V(*this); + for (size_t i=0; i +Vector& Vector::operator += (const T& A) +{ + for (size_t i=0; i +Vector Vector::operator - (const T& A) const +{ + Vector V(*this); + for (size_t i=0; i +Vector& Vector::operator -= (const T& A) +{ + for (size_t i=0; i +Vector Vector::operator + (const Vector& V0) const +{ + Vector V(m_N); + for (size_t i=0; i +Vector& Vector::operator += (const Vector& V) +{ + for ( size_t i=0 ; i +Vector Vector::operator - (const Vector& V0) const +{ + Vector V(m_N); + for (size_t i=0; i +Vector& Vector::operator -= (const Vector& V) +{ + for (size_t i=0; i +Vector Vector::operator - (void) const +{ + Vector V(m_N); + for (size_t i=0; i +Vector< T >& Vector< T >::Add( const Vector< T >* V , int count ) +{ + for( int c=0 ; c +Vector< T >& Vector< T >::AddScaled( const Vector& V , const T& scale ) +{ + for (size_t i=0; i +Vector& Vector::SubtractScaled(const Vector& V,const T& scale) +{ + for (size_t i=0; i +void Vector::Add( const Vector& V1 , const T& scale1 , const Vector& V2 , const T& scale2 , Vector& Out ) +{ + for( size_t i=0 ; i +void Vector::Add(const Vector& V1,const T& scale1,const Vector& V2,Vector& Out) +{ + for( size_t i=0 ; i +T Vector::Norm( size_t Ln ) const +{ + T N = T(); + for (size_t i = 0; i +void Vector::Normalize() +{ + T N = 1.0f/Norm(2); + for (size_t i = 0; i +T Vector< T >::Average( void ) const +{ + T N = T(); + for( size_t i=0 ; i +T Vector::Length() const +{ + T N = T(); + for (size_t i = 0; i +T Vector::Dot( const Vector& V ) const +{ + T V0 = T(); + for( size_t i=0 ; i +bool Vector< T >::read( const char* fileName ) +{ + FILE* fp = fopen( fileName , "rb" ); + if( !fp ) return false; + bool ret = read( fp ); + fclose( fp ); + return ret; +} +template< class T > +bool Vector< T >::write( const char* fileName ) const +{ + FILE* fp = fopen( fileName , "wb" ); + if( !fp ) return false; + bool ret = write( fp ); + fclose( fp ); + return ret; +} +template< class T > +bool Vector< T >::read( FILE* fp ) +{ + int d; + if( fread( &d , sizeof(int) , 1 , fp )!=1 ) return false; + Resize( d ); + if( fread( &(*this)[0] , sizeof( T ) , d , fp )!=d ) return false; + return true; +} +template< class T > +bool Vector< T >::write( FILE* fp ) const +{ + if( fwrite( &m_N , sizeof( int ) , 1 , fp )!=1 ) return false; + if( fwrite( &(*this)[0] , sizeof( T ) , m_N , fp )!=m_N ) return false; + return true; +} + + + +#endif diff --git a/SurfaceTrimmer.vcxproj b/SurfaceTrimmer.vcxproj new file mode 100644 index 0000000..249cd94 --- /dev/null +++ b/SurfaceTrimmer.vcxproj @@ -0,0 +1,180 @@ + + + + + Debug + Win32 + + + Debug + x64 + + + Release + Win32 + + + Release + x64 + + + + {99BEAFED-8DB9-4B7D-A0BE-5186158193FE} + MeshClipper + Win32Proj + SurfaceTrimmer + + + + Application + Unicode + true + v120 + + + Application + Unicode + true + v120 + + + Application + Unicode + v120 + + + Application + Unicode + v120 + + + + + + + + + + + + + + + + + + + <_ProjectFileVersion>10.0.40219.1 + $(SolutionDir)$(Configuration)\ + $(SolutionDir)$(Configuration)\ + $(Configuration)\ + $(Configuration)\ + true + true + $(SolutionDir)Bin\$(Platform)\$(Configuration)\ + $(SolutionDir)Bin\$(Platform)\$(Configuration)\ + $(SolutionDir)\Obj\$(TargetName)\$(Platform)\$(Configuration)\ + $(SolutionDir)\Obj\$(TargetName)\$(Platform)\$(Configuration)\ + false + false + AllRules.ruleset + AllRules.ruleset + + + + + AllRules.ruleset + AllRules.ruleset + + + + + + + + Disabled + WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions) + true + EnableFastChecks + MultiThreadedDebugDLL + + + Level3 + EditAndContinue + + + true + Console + MachineX86 + + + + + Disabled + WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions) + EnableFastChecks + MultiThreadedDebugDLL + + + Level3 + ProgramDatabase + + + true + Console + + + + + %(AdditionalIncludeDirectories) + WIN32;NDEBUG;_CONSOLE;_CRT_SECURE_NO_DEPRECATE;%(PreprocessorDefinitions) + MultiThreadedDLL + + + Level3 + ProgramDatabase + + + true + Console + true + true + MachineX86 + + + + + %(AdditionalIncludeDirectories) + _CRT_SECURE_NO_WARNINGS;WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions) + MultiThreadedDLL + + + Level3 + ProgramDatabase + true + + + true + Console + true + true + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/SurfaceTrimmer.vcxproj.filters b/SurfaceTrimmer.vcxproj.filters new file mode 100644 index 0000000..b6ea680 --- /dev/null +++ b/SurfaceTrimmer.vcxproj.filters @@ -0,0 +1,47 @@ + + + + + Source Files + + + Source Files + + + Source Files + + + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + + + {237cb93d-2caa-4f6b-9bb3-5d597e5fd1dd} + cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx + + + {e857a52e-da0b-4eea-9e27-7b9c21e142c9} + h;hpp;hxx;hm;inl;inc;xsd + + + {61c77966-1174-4dae-9079-00d39bbe4aab} + inc;inl + + + + + Include Files + + + \ No newline at end of file