├── .clang-format ├── .gitignore ├── README.md ├── deprecated ├── msh_cam.h ├── msh_draw.h └── msh_gfx.h ├── docs └── no_headers.md ├── experimental ├── generic.c ├── generic.h ├── msh_3d_descriptors.h ├── msh_geometry.h ├── msh_hash_grid.h ├── msh_img_proc.h ├── msh_jobs.h ├── msh_mesh.h ├── msh_ml.h ├── msh_nm.c ├── msh_nm.h └── numpy_nm.py ├── msh_argparse.h ├── msh_camera.h ├── msh_containers.h ├── msh_ply.h ├── msh_std.h ├── msh_vec_math.h └── tests ├── msh_argparse_test.inl ├── msh_containers_test.inl ├── msh_generic_vec_math_test.inl ├── msh_hash_grid_test.c ├── msh_jobs_test.c ├── msh_stats_test.inl ├── msh_tests.c ├── msh_vec_math_test.inl └── munit ├── munit.c └── munit.h /.clang-format: -------------------------------------------------------------------------------- 1 | --- 2 | AccessModifierOffset: -2 3 | AlignAfterOpenBracket: Align 4 | AlignConsecutiveMacros: true 5 | AlignConsecutiveAssignments: true 6 | AlignConsecutiveDeclarations: false 7 | AlignEscapedNewlines: Right 8 | AlignOperands: true 9 | AlignTrailingComments: true 10 | AllowAllArgumentsOnNextLine: false 11 | AllowAllConstructorInitializersOnNextLine: false 12 | AllowAllParametersOfDeclarationOnNextLine: false 13 | AllowShortBlocksOnASingleLine: true 14 | AllowShortIfStatementsOnASingleLine: true 15 | AllowShortCaseLabelsOnASingleLine: false 16 | AllowShortFunctionsOnASingleLine: All 17 | AllowShortLambdasOnASingleLine: All 18 | AllowShortLoopsOnASingleLine: true 19 | AlwaysBreakAfterReturnType: AllDefinitions 20 | AlwaysBreakTemplateDeclarations: Yes 21 | BinPackArguments: false 22 | BinPackParameters: false 23 | BreakBeforeBinaryOperators: None 24 | BreakBeforeBraces: Custom 25 | BraceWrapping: 26 | AfterCaseLabel: true 27 | AfterClass: true 28 | AfterControlStatement: true 29 | AfterEnum: true 30 | AfterFunction: true 31 | AfterNamespace: false 32 | AfterObjCDeclaration: true 33 | AfterStruct: true 34 | AfterUnion: true 35 | AfterExternBlock: false 36 | BeforeCatch: true 37 | BeforeElse: true 38 | BeforeLambdaBody: true 39 | BeforeWhile: false 40 | IndentBraces: false 41 | SplitEmptyFunction: false 42 | SplitEmptyRecord: false 43 | SplitEmptyNamespace: false 44 | BreakBeforeTernaryOperators: true 45 | BreakConstructorInitializers: BeforeComma 46 | BreakInheritanceList: BeforeComma 47 | BreakStringLiterals: true 48 | ColumnLimit: 80 49 | CompactNamespaces: false 50 | ConstructorInitializerAllOnOneLineOrOnePerLine: false 51 | ContinuationIndentWidth: 2 52 | Cpp11BracedListStyle: true 53 | DerivePointerAlignment: false 54 | DisableFormat: false 55 | FixNamespaceComments: true 56 | IndentCaseLabels: true 57 | IndentPPDirectives: None 58 | IndentWidth: 2 59 | IndentWrappedFunctionNames: false 60 | KeepEmptyLinesAtTheStartOfBlocks: false 61 | Language: Cpp 62 | MaxEmptyLinesToKeep: 1 63 | NamespaceIndentation: None 64 | PointerAlignment: Left 65 | ReflowComments: false 66 | SortIncludes: false 67 | SpaceAfterCStyleCast: false 68 | SpaceAfterLogicalNot: false 69 | SpaceAfterTemplateKeyword: false 70 | SpaceBeforeAssignmentOperators: true 71 | SpaceBeforeCpp11BracedList: true 72 | SpaceBeforeCtorInitializerColon: false 73 | SpaceBeforeInheritanceColon: false 74 | SpaceBeforeParens: ControlStatements 75 | SpaceBeforeRangeBasedForLoopColon: true 76 | SpaceInEmptyParentheses: false 77 | SpacesBeforeTrailingComments: 3 78 | SpacesInAngles: false 79 | SpacesInCStyleCastParentheses: false 80 | SpacesInContainerLiterals: false 81 | SpacesInParentheses: false 82 | SpacesInSquareBrackets: false 83 | Standard: Auto 84 | TabWidth: 2 85 | UseTab: Never 86 | 87 | ... 88 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | *.dSYM 2 | **/*.dSYM 3 | *.exe 4 | **/*.exe 5 | libs/** 6 | extern/** 7 | example/ 8 | example/** 9 | .vscode/** 10 | bin/** 11 | examples/out/** 12 | **/*.o 13 | tags 14 | **/*.DS_Store 15 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # msh - Maciej's Toolbox 2 | 3 | [Examples repository](https://github.com/mhalber/msh_examples) 4 | 5 | This repository contains a number of libraries and utilities that I use for my daily development, written in C. 6 | 7 | ## Libraries 8 | 9 | Name | version | description 10 | :------------------------|:----------:|:--------------------------------------------- 11 | **msh_std.h** | 0.6 | various helper functions and typedefs 12 | **msh_containers.h** | 0.5 | Dynamic Array, Hash table, Disjoint union set 13 | **msh_argparse.h** | 0.8 | command-line argument parsing 14 | **msh_vec_math.h** | 0.82 | glsl inspired vector math (vectors, matrices and quaternions) 15 | **msh_ply.h** | 1.01 | Fast PLY File I/O [Benchmark](https://github.com/mhalber/ply_io_benchmark) 16 | 17 | ## Utilites 18 | 19 | Name | version | description | dependencies 20 | :------------------------|:----------:|:-----------------------------------:|:------- 21 | **msh_camera.h** | 0.65 | various types of camera controls | msh_vec_math.h 22 | 23 | ## Experimental folder 24 | 25 | Experimental folder contains some code that I am working on during my spare time, and is 26 | generally very unstable, might be completely changed or deleted. Would not encourage anyone to use 27 | these files. 28 | 29 | ## Disclaimer 30 | 31 | These libraries have been inspired by excellent [libraries](https://github.com/nothings/stb) by Sean Barret. 32 | I'd also like to point you to many other single file libraries: 33 | 34 | - [gb](https://github.com/gingerBill/gb) 35 | - [cute_headers](https://github.com/RandyGaul/cute_headers) 36 | - [mattias gustavsson's libs](https://github.com/mattiasgustavsson/libs) 37 | - [rjm](https://github.com/rmitton/rjm) 38 | - [sokol](https://github.com/floooh/sokol) 39 | 40 | They are excellent and you should do yourself a favor and try them out! 41 | -------------------------------------------------------------------------------- /deprecated/msh_cam.h: -------------------------------------------------------------------------------- 1 | /* 2 | ============================================================================== 3 | 4 | MSH_CAM.H - v0.4 5 | 6 | A single header library for first person and arcball camera manipulation 7 | 8 | To use the library you simply add: 9 | 10 | #define MSH_CAM_IMPLEMENTATION 11 | #include "msh_cam.h" 12 | 13 | The define should only include once in your source. If you need to include 14 | library in multiple places, simply use the include: 15 | 16 | #include "msh_cam.h" 17 | 18 | All functions can be made static by definining: 19 | 20 | #ifdef MSH_CAM_STATIC 21 | 22 | before including the "msh_cam.h" 23 | 24 | ============================================================================== 25 | DEPENDENCIES 26 | 27 | This library requires anonymous structs, which is a C11 extension. 28 | Tested compilers: 29 | clang 3.9.1 30 | apple clang (Apple LLVM version 8.0.0 (clang-800.0.42.1)) 31 | gcc 5.3.0 32 | 33 | This library depends on following standard headers: 34 | 35 | TODO: Add dependencies list 36 | 37 | By default this library does not import these headers. Please see 38 | docs/no_headers.md for explanation. Importing heades is enabled by: 39 | 40 | #define MSH_CAM_INCLUDE_HEADERS 41 | 42 | ============================================================================== 43 | AUTHORS 44 | 45 | Maciej Halber (macikuh@gmail.com) 46 | 47 | ============================================================================== 48 | LICENSE 49 | 50 | This software is in the public domain. Where that dedication is not 51 | recognized, you are granted a perpetual, irrevocable license to copy, 52 | distribute, and modify this file as you see fit. 53 | 54 | ============================================================================== 55 | NOTES 56 | 57 | PLAN : Create camera that only uses the position and orientation represented 58 | as a quaternion, and output the view matrix as needed. 59 | 60 | Current implementation relies heavily on the msh_vec_math.h. Once both 61 | msh_cam.h and msh_vec_math.h are thoroughly tested, I'll inline these 62 | functions and remove dependency. 63 | 64 | While writing this file I have been looking at camera implementations by 65 | nlguillemot[1,2] and vurtun[3]. Some tricks that they've linked have 66 | been used, but no explicit code copying was done. 67 | 68 | ============================================================================== 69 | TODOS 70 | [ ] Revise arcball controls. 71 | [ ] Add explicit center of rotation 72 | [ ] Add tweening 73 | [ ] Add quaternions / vectors if no vec math is provided 74 | [ ] Remove dependency on msh_vec_math.h 75 | [ ] Add the capability to use a real camera parameters like focal length 76 | and center of projection. 77 | [ ] What is the interface for generating perspective? 78 | 79 | ============================================================================== 80 | REFERENCES: 81 | [1] nlguillemot/arcball_camera.h https://github.com/nlguillemot/arcball_camera/blob/master/arcball_camera.h 82 | [2] nlguillemot/flythrough_camera.h https://github.com/nlguillemot/flythrough_camera/blob/master/flythrough_camera.h 83 | [3] vurtun/camera.c https://gist.github.com/vurtun/d41914c00b6608da3f6a73373b9533e5 84 | [4] Euler/Quaternion https://gamedev.stackexchange.com/questions/13436/glm-euler-angles-to-quaternion 85 | */ 86 | 87 | /* 88 | * ============================================================================= 89 | * INCLUDES, TYPES AND DEFINES 90 | * ============================================================================= 91 | */ 92 | 93 | 94 | #ifndef MSH_CAM 95 | #define MSH_CAM 96 | 97 | #ifdef __cplusplus 98 | extern "C" { 99 | #endif 100 | 101 | #ifdef MSH_CAM_INCLUDE_HEADERS 102 | #include 103 | #include 104 | #include 105 | #include 106 | #endif 107 | 108 | 109 | #ifdef MSH_CAM_STATIC 110 | #define MSHCAMDEF static 111 | #else 112 | #define MSHCAMDEF extern 113 | #endif 114 | 115 | 116 | typedef struct msh_camera 117 | { 118 | /* State */ 119 | msh_vec3_t position; 120 | msh_quat_t orientation; 121 | 122 | /* Params */ 123 | float fovy; 124 | float aspect_ratio; 125 | float znear; 126 | float zfar; 127 | 128 | /* Generated -- Not sure if should be stored, or computed per request*/ 129 | msh_mat4_t view; 130 | msh_mat4_t proj; 131 | 132 | } msh_camera_t; 133 | 134 | MSHCAMDEF void 135 | msh_camera_init( msh_camera_t * camera, 136 | const msh_vec3_t eye, 137 | const msh_vec3_t center, 138 | const msh_vec3_t up, 139 | const float fovy, 140 | const float aspect_ratio, 141 | const float znear, 142 | const float zfar ); 143 | 144 | MSHCAMDEF void 145 | msh_camera_update_perspective( msh_camera_t *camera, 146 | const float fovy, 147 | const float aspect_ratio, 148 | const float znear, 149 | const float zfar ); 150 | MSHCAMDEF void 151 | msh_camera_update_ortho( msh_camera_t *camera, 152 | const float left, const float right, 153 | const float bottom, const float top, 154 | const float znear, const float zfar ); 155 | 156 | MSHCAMDEF void 157 | msh_arcball_camera_update( msh_camera_t *camera, 158 | const msh_vec2_t scrn_p0, 159 | const msh_vec2_t scrn_p1, 160 | const int lmb_state, 161 | const int rmb_state, 162 | const float scroll_state, 163 | const msh_vec4_t viewport ); 164 | 165 | 166 | 167 | /* TODO: Design better simple api for basic version of the camera 168 | A) Use of msh_camera_t should be optional 169 | B) Implement simple camera for flythrough. 170 | C) Make the camera use lookat point 171 | 172 | MOST BASIC / ADVANCED API ---> Everything else will build from this method. 173 | 174 | void msh_arcball_camera_update( float pos[3], 175 | float look_at[3] 176 | float up[3], 177 | 178 | float fovy, 179 | float aspect_ratio, 180 | float near, float far, 181 | 182 | float p0_x, float p1_y, 183 | float p1_x, float p1_y, 184 | int lmb_press, int rmb_press, 185 | float scroll, 186 | 187 | float w, ms_scalar_t h, 188 | 189 | float * view, 190 | float * quaternion, 191 | float * proj ); 192 | */ 193 | 194 | 195 | /* TODO: Specify control schemes that will mimic certain software */ 196 | /* basic: 197 | - lmb: rotation 198 | - rmb: pan 199 | - scroll: zoom; 200 | blender: 201 | - mmb: rotation 202 | - shift+mmb: pan 203 | - ctrl+mmb: zoom(smooth) 204 | - scroll: zoom 205 | - ctrl+scroll: pan in x 206 | - shift+scroll: pan in y 207 | */ 208 | 209 | #ifdef __cplusplus 210 | } 211 | #endif 212 | 213 | #endif /*MSH_CAM_H*/ 214 | 215 | #ifdef MSH_CAM_IMPLEMENTATION 216 | 217 | static msh_vec3_t 218 | msh__screen_to_sphere( float x, float y, msh_vec4_t viewport ) 219 | { 220 | float w = viewport.data[2] - viewport.data[0]; 221 | float h = viewport.data[3] - viewport.data[1]; 222 | float r = (w > h)? h : w; 223 | msh_vec3_t p = msh_vec3( (x - w * 0.5f) / r, 224 | ((h - y) - h*0.5f) / r, 225 | 0.0f ); 226 | float l_sq = p.x * p.x + p.y * p.y; 227 | float l = (float)sqrt( l_sq ); 228 | p.z = (float)((l_sq > 0.5) ? (0.5 / l) : (sqrt( 1.0 - l_sq))); 229 | p = msh_vec3_normalize(p); 230 | return p; 231 | } 232 | 233 | 234 | 235 | MSHCAMDEF void 236 | msh_camera_init( msh_camera_t * camera, 237 | const msh_vec3_t eye, 238 | const msh_vec3_t center, 239 | const msh_vec3_t up, 240 | const float fovy, 241 | const float aspect_ratio, 242 | const float znear, 243 | const float zfar ) 244 | { 245 | camera->view = msh_look_at( eye, center, up ); 246 | camera->proj = msh_perspective( fovy, aspect_ratio, znear, zfar ); 247 | camera->fovy = fovy; 248 | camera->aspect_ratio = aspect_ratio; 249 | camera->znear = znear; 250 | camera->zfar = zfar; 251 | 252 | msh_mat4_t view_inverse = msh_mat4_inverse( camera->view ); 253 | 254 | camera->orientation = msh_mat4_to_quat( view_inverse ); 255 | camera->position = msh_vec4_to_vec3( view_inverse.col[3] ); 256 | } 257 | 258 | MSHCAMDEF void 259 | msh_camera_update_perspective( msh_camera_t *camera, 260 | const float fovy, 261 | const float aspect_ratio, 262 | const float znear, 263 | const float zfar ) 264 | { 265 | camera->proj = msh_perspective( fovy, aspect_ratio, znear, zfar ); 266 | camera->fovy = fovy; 267 | camera->aspect_ratio = aspect_ratio; 268 | camera->znear = znear; 269 | camera->zfar = zfar; 270 | } 271 | 272 | MSHCAMDEF void 273 | msh_camera_update_ortho( msh_camera_t *camera, 274 | const float left, const float right, 275 | const float bottom, const float top, 276 | const float znear, const float zfar ) 277 | { 278 | camera->proj = msh_ortho( left, right, bottom, top, znear, zfar ); 279 | camera->znear = znear; 280 | camera->zfar = zfar; 281 | } 282 | 283 | MSHCAMDEF void 284 | msh_arcball_camera_update( msh_camera_t *camera, 285 | const msh_vec2_t scrn_p0, 286 | const msh_vec2_t scrn_p1, 287 | const int lmb_state, 288 | const int rmb_state, 289 | const float scroll_state, 290 | const msh_vec4_t viewport ) 291 | { 292 | if( scroll_state ) 293 | { 294 | msh_mat3_t cur_rot = msh_quat_to_mat3( camera->orientation ); 295 | msh_vec3_t forward = msh_vec3_scalar_mul( cur_rot.col[2], -scroll_state ); 296 | camera->position = msh_vec3_add( camera->position, forward ); 297 | } 298 | else if(rmb_state) 299 | { 300 | msh_mat3_t cur_rot = msh_quat_to_mat3( camera->orientation ); 301 | float w = viewport.data[2] - viewport.data[0]; 302 | float h = viewport.data[3] - viewport.data[1]; 303 | msh_vec2_t disp = msh_vec2( (scrn_p1.x - scrn_p0.x) / w, 304 | (scrn_p1.y - scrn_p0.y) / h ); 305 | msh_vec3_t u = msh_vec3_scalar_mul( cur_rot.col[0], 5 * -disp.x ); 306 | msh_vec3_t v = msh_vec3_scalar_mul( cur_rot.col[1], 5 * disp.y ); 307 | camera->position = msh_vec3_add( camera->position, u ); 308 | camera->position = msh_vec3_add( camera->position, v ); 309 | } 310 | else if(lmb_state) 311 | { 312 | /* Current orientation and position */ 313 | msh_quat_t q = camera->orientation; 314 | msh_quat_t p = msh_quat(camera->position.x, 315 | camera->position.y, 316 | camera->position.z, 0.0); 317 | /* Compute the quaternion rotation from inputs */ 318 | msh_mat3_t cur_rot = msh_quat_to_mat3( camera->orientation ); 319 | msh_vec3_t p0 = msh_mat3_vec3_mul( cur_rot, 320 | msh__screen_to_sphere(scrn_p0.x, scrn_p0.y, viewport) ); 321 | msh_vec3_t p1 = msh_mat3_vec3_mul( cur_rot, 322 | msh__screen_to_sphere(scrn_p1.x, scrn_p1.y, viewport) ); 323 | 324 | /* compute rotation */ 325 | msh_quat_t r = msh_quat_from_vectors( p1, p0 ); 326 | r = msh_quat_slerp( msh_quat_identity(), r, 3.5 ); 327 | 328 | /* Modify orientation and position */ 329 | q = msh_quat_normalize( msh_quat_mul( r, q ) ); 330 | p = msh_quat_mul( msh_quat_mul( r, p ), msh_quat_conjugate( r ) ); 331 | 332 | camera->position = msh_vec3( p.x, p.y, p.z ); 333 | camera->orientation = q; 334 | } 335 | 336 | /* Orientation and position are esentailly an inverse of view matrix. 337 | Thus we will convert them to a matrix form and invert to obtain 338 | view matrix. Note that we can use affine inverse formula */ 339 | msh_mat3_t inv_o = msh_mat3_transpose(msh_quat_to_mat3( camera->orientation )); 340 | msh_vec3_t inv_p = msh_mat3_vec3_mul( inv_o, camera->position ); 341 | camera->view = msh_mat3_to_mat4(inv_o); 342 | camera->view.col[3].x = -inv_p.x; 343 | camera->view.col[3].y = -inv_p.y; 344 | camera->view.col[3].z = -inv_p.z; 345 | camera->view.col[3].w = 1.0f; 346 | } 347 | 348 | void 349 | msh_flythrough_camera( msh_camera_t *camera, 350 | msh_vec2_t scrn_p0, msh_vec2_t scrn_p1, 351 | int lmb_state, 352 | int key_w_state, 353 | int key_s_state, 354 | int key_a_state, 355 | int key_d_state ) 356 | { 357 | msh_mat3_t r = msh_quat_to_mat3( camera->orientation ); 358 | float speed = 0.2; 359 | msh_vec3_t n = msh_vec3( -speed * r.col[2].x, -speed * r.col[2].y, -speed * r.col[2].z ); 360 | msh_vec3_t in = msh_vec3( speed * r.col[2].x, speed * r.col[2].y, speed * r.col[2].z ); 361 | msh_vec3_t v = msh_vec3( speed * r.col[0].x, speed * r.col[0].y, speed * r.col[0].z ); 362 | msh_vec3_t iv = msh_vec3( -speed * r.col[0].x, -speed * r.col[0].y, -speed * r.col[0].z ); 363 | 364 | 365 | if( key_w_state ) camera->position = msh_vec3_add( camera->position, n ); 366 | if( key_s_state ) camera->position = msh_vec3_add( camera->position, in ); 367 | if( key_a_state ) camera->position = msh_vec3_add( camera->position, iv ); 368 | if( key_d_state ) camera->position = msh_vec3_add( camera->position, v ); 369 | 370 | if( lmb_state ) 371 | { 372 | /* Current orientation and position */ 373 | msh_quat_t q = camera->orientation; 374 | msh_vec4_t viewport = msh_vec4( 0, 0, 1024, 1024 ); 375 | 376 | /* Compute the quaternion rotation from inputs */ 377 | msh_mat3_t cur_rot = msh_mat3_identity(); 378 | msh_vec3_t p0 = msh_mat3_vec3_mul( cur_rot, 379 | msh__screen_to_sphere(scrn_p0.x, 512.0, viewport) ); 380 | msh_vec3_t p1 = msh_mat3_vec3_mul( cur_rot, 381 | msh__screen_to_sphere(scrn_p1.x, 512.0, viewport) ); 382 | 383 | msh_quat_t r1 = msh_quat_from_vectors( p0, p1 ); 384 | q = msh_quat_normalize( msh_quat_mul( r1, q ) ); 385 | 386 | cur_rot = msh_quat_to_mat3( q ); 387 | msh_vec3_t p2 = msh_mat3_vec3_mul( cur_rot, 388 | msh__screen_to_sphere(512.0, scrn_p0.y, viewport) ); 389 | msh_vec3_t p3 = msh_mat3_vec3_mul( cur_rot, 390 | msh__screen_to_sphere(512.0, scrn_p1.y, viewport) ); 391 | msh_quat_t r2 = msh_quat_from_vectors( p2, p3 ); 392 | q = msh_quat_normalize( msh_quat_mul( r2, q ) ); 393 | 394 | camera->orientation = q; 395 | } 396 | 397 | msh_mat3_t inv_o = msh_mat3_transpose(msh_quat_to_mat3( camera->orientation )); 398 | msh_vec3_t inv_p = msh_mat3_vec3_mul( inv_o, camera->position ); 399 | 400 | camera->view = msh_mat3_to_mat4(inv_o); 401 | camera->view.col[3].x = -inv_p.x; 402 | camera->view.col[3].y = -inv_p.y; 403 | camera->view.col[3].z = -inv_p.z; 404 | camera->view.col[3].w = 1.0f; 405 | } 406 | 407 | 408 | void msh_camera_ray_through_pixel(msh_camera_t *camera, msh_vec4_t viewport, msh_vec2_t p, msh_vec3_t *origin, msh_vec3_t *direction) 409 | { 410 | *origin = camera->position; 411 | msh_mat4_t inv_v = msh_mat4_se3_inverse(camera->view); 412 | msh_mat4_t inv_p = msh_mat4_inverse(camera->proj); 413 | 414 | float clip_x = (2.0f * p.x) / viewport.z - 1.0f; 415 | float clip_y = 1.0f - (2.0f * p.y) / viewport.w; 416 | 417 | msh_vec4_t clip_coords = msh_vec4(clip_x, clip_y, 0.0f, 1.0f); 418 | 419 | msh_vec4_t eye_ray_dir = msh_mat4_vec4_mul(inv_p, clip_coords); 420 | eye_ray_dir.z = -1.0f; 421 | eye_ray_dir.w = 0.0f; 422 | msh_vec3_t world_ray_dir = msh_vec4_to_vec3(msh_mat4_vec4_mul(inv_v, eye_ray_dir)); 423 | *direction = msh_vec3_normalize(world_ray_dir); 424 | } 425 | 426 | #endif -------------------------------------------------------------------------------- /docs/no_headers.md: -------------------------------------------------------------------------------- 1 | Why not #include ? 2 | =================== 3 | 4 | Following the advice from an excellent article by Rob Pike 5 | (https://www.lysator.liu.se/c/pikestyle.html), we do not explicitly import any 6 | headers by default. This should be done once, before the msh libraries are 7 | included. 8 | 9 | If you are unhappy with this behavior, you can add: 10 | ~~~~~~~~~~~~~ 11 | #define MSH__INCLUDE_HEADERS 12 | ~~~~~~~~~~~~~ 13 | 14 | Or simply modify the source to include them without that flag :) -------------------------------------------------------------------------------- /experimental/generic.c: -------------------------------------------------------------------------------- 1 | 2 | #include 3 | #include "generic.h" 4 | 5 | int 6 | main() 7 | { 8 | mh_vec3f_t a = mh_vec3f_init( 1, 2, 3 ); 9 | mh_vec3d_t b = mh_vec3d_init( 1, 2, 3 ); 10 | mh_vec3i_t c = mh_vec3i_ones(); 11 | printf("%f %f %f\n", a.x, a.y, a.z ); 12 | printf("%f %f %f\n", b.x, b.y, b.z ); 13 | printf("%d %d %d\n", c.x, c.y, c.z ); 14 | 15 | mh_vec3i_t v1 = mh_vec3i_init( 2, 2, 2 ); 16 | mh_vec3i_t v2 = mh_vec3i_init( 4, 1, 3 ); 17 | mh_vec3f_t v3 = mh_vec3f_init( 4.0, 1.0, 3.0 ); 18 | mh_vec3f_t v4 = mh_vec3f_init( 2.0, 2.0, 2.0 ); 19 | 20 | int v1dotv2 = mh_vec3i_dot( v1, v2 ); 21 | float v3dotv4 = mh_vec3f_dot( v3, v4 ); 22 | 23 | printf("%d %f\n", v1dotv2, v3dotv4 ); 24 | 25 | return 0; 26 | } -------------------------------------------------------------------------------- /experimental/generic.h: -------------------------------------------------------------------------------- 1 | // This is an experimental file to demonstrate minimal template programming in C. 2 | // With a terrible abuse of preprocessor power we are able to have this file copy itself for each type. 3 | // Everytime it defines a the required types and function for each requested type. 4 | 5 | #ifndef T 6 | 7 | // Type request 8 | # define T int 9 | # define POSTFIX i 10 | # include __FILE__ 11 | # undef POSTFIX 12 | # undef T 13 | # define T float 14 | # define POSTFIX f 15 | # include __FILE__ 16 | # undef POSTFIX 17 | # undef T 18 | # define T double 19 | # define POSTFIX d 20 | # include __FILE__ 21 | 22 | #undef NAMESPACE 23 | 24 | #else 25 | 26 | // We need to do namespaces by hand in C 27 | #define NAMESPACE msh 28 | 29 | // All tremble 'fore the Mighty Preprocessor! 30 | #define _APPEND(a,b) a ## b 31 | #define APPEND(a,b) _APPEND(a,b) 32 | #define _MERGE(a, b) a ## _ ## b 33 | #define MERGE(a, b) _MERGE(a, b) 34 | #define XTYPE_NAME(name, postfix) name##postfix 35 | #define TYPE_NAME(name, postfix) XTYPE_NAME(name, postfix) 36 | #define FUNC(width, op) MERGE( MERGE(NAMESPACE, APPEND(width, POSTFIX)), op) 37 | 38 | #define VEC2 MERGE( TYPE_NAME( MERGE( NAMESPACE, vec2), POSTFIX), t) 39 | #define VEC3 MERGE( TYPE_NAME( MERGE( NAMESPACE, vec3), POSTFIX), t) 40 | #define VEC4 MERGE( TYPE_NAME( MERGE( NAMESPACE, vec4), POSTFIX), t) 41 | 42 | // Write your code (almost) as usual 43 | typedef union VEC3 44 | { 45 | T data[3]; 46 | struct{ T x; T y; T z; }; 47 | } VEC3; 48 | 49 | VEC3 50 | FUNC(vec3,init)( T x, T y, T z ) 51 | { 52 | return (VEC3){ .x = x, .y = y, .z = z }; 53 | } 54 | 55 | VEC3 56 | FUNC(vec3,zeros)() 57 | { 58 | return (VEC3){ .x = 0, .y = 0, .z = 0 }; 59 | } 60 | 61 | VEC3 62 | FUNC(vec3,ones)() 63 | { 64 | return (VEC3){ .x = 1, .y = 1, .z = 1 }; 65 | } 66 | 67 | VEC3 68 | FUNC(vec3,add)( const VEC3 a, const VEC3 b ) 69 | { 70 | return (VEC3){ .x = a.x + b.x, .y = a.y + b.y, .z = a.z + b.z }; 71 | } 72 | 73 | VEC3 74 | FUNC(vec3,sub)( const VEC3 a, const VEC3 b ) 75 | { 76 | return (VEC3){ .x = a.x - b.x, .y = a.y - b.y, .z = a.z - b.z }; 77 | } 78 | 79 | VEC3 80 | FUNC(vec3, mul)( const VEC3 a, const VEC3 b) 81 | { 82 | return (VEC3){ .x = a.x * b.x, .y = a.y * b.y, .z = a.z * b.z }; 83 | } 84 | 85 | VEC3 86 | FUNC(vec3, div)( const VEC3 a, const VEC3 b) 87 | { 88 | return (VEC3){ .x = a.x / b.x, .y = a.y / b.y, .z = a.z / b.z }; 89 | } 90 | 91 | VEC3 92 | FUNC(scalar_vec3,add)( const T b, const VEC3 a ) 93 | { 94 | return (VEC3){ .x = a.x + b, .y = a.y + b, .z = a.z + b }; 95 | } 96 | 97 | VEC3 98 | FUNC(scalar_vec3,sub)( const T b, const VEC3 a ) 99 | { 100 | return (VEC3){ .x = a.x - b, .y = a.y - b, .z = a.z - b }; 101 | } 102 | 103 | VEC3 104 | FUNC(scalar_vec3, mul)( const T b, const VEC3 a ) 105 | { 106 | return (VEC3){ .x = a.x * b, .y = a.y * b, .z = a.z * b }; 107 | } 108 | 109 | VEC3 110 | FUNC(scalar_vec3, div)( const T b, const VEC3 a ) 111 | { 112 | double denom = (1.0 / (double)b); 113 | return (VEC3){ .x = a.x * denom, .y = a.y * denom, .z = a.z * denom }; 114 | } 115 | 116 | T 117 | FUNC(vec3,dot)( const VEC3 a, const VEC3 b ) 118 | { 119 | T out = ( a.x * b.x + a.y * b.y + a.z * b.z ); 120 | return out; 121 | } 122 | 123 | #endif -------------------------------------------------------------------------------- /experimental/msh_3d_descriptors.h: -------------------------------------------------------------------------------- 1 | /* 2 | ============================================================================== 3 | 4 | MSH_3D_DESCRIPTORS.H - WIP! 5 | 6 | A single header library for computing 3d descriptors for pointcloud data. 7 | 8 | To use the library you simply add: 9 | 10 | #define MSH_3D_DESCRIPTORS_IMPLEMENTATION 11 | #include "msh_3d_descriptors.h" 12 | 13 | The define should only include once in your source. If you need to include 14 | library in multiple places, simply use the include: 15 | 16 | #include "msh_3d_descriptors.h" 17 | 18 | All functions can be made static by definining: 19 | 20 | #ifdef MSH_3D_DESCRIPTORS_STATIC 21 | 22 | before including the "msh_3d_descriptors.h" 23 | 24 | ============================================================================== 25 | DEPENDENCIES 26 | 27 | This library requires anonymous structs, which is a C11 extension. 28 | 29 | This library depends on following standard headers: 30 | 31 | TODO: Add dependencies list 32 | 33 | By default this library does not import these headers. Please see 34 | docs/no_headers.md for explanation. Importing heades is enabled by: 35 | 36 | #define MSH_3D_DESCRIPTORS_INCLUDE_HEADERS 37 | 38 | ============================================================================== 39 | AUTHORS 40 | 41 | Maciej Halber (macikuh@gmail.com) 42 | 43 | ============================================================================== 44 | LICENSE 45 | The MIT License (MIT) 46 | 47 | Copyright (c) 2017 Maciej Halber 48 | 49 | Permission is hereby granted, free of charge, to any person obtaining a copy 50 | of this software and associated documentation files (the "Software"), to deal 51 | in the Software without restriction, including without limitation the rights 52 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 53 | copies of the Software, and to permit persons to whom the Software is 54 | furnished to do so, subject to the following conditions: 55 | 56 | The above copyright notice and this permission notice shall be included in all 57 | copies or substantial portions of the Software. 58 | 59 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 60 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 61 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 62 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 63 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 64 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 65 | SOFTWARE. 66 | 67 | 68 | ============================================================================== 69 | NOTES 70 | 71 | ============================================================================== 72 | TODOs 73 | [ ] kdtree functions - first test with off-the-shelf kdtree and then see what 74 | happens with that. 75 | [ ] 76 | 77 | ============================================================================== 78 | REFERENCES: 79 | 80 | */ 81 | 82 | 83 | #ifndef MSH_3D_DESCRIPTORS_H 84 | #define MSH_3D_DESCRIPTORS_H 85 | 86 | #ifdef __cplusplus 87 | extern "C" { 88 | #endif 89 | 90 | #ifdef MSH_3D_DESCRIPTORS_INCLUDE_HEADERS 91 | #include 92 | #include 93 | #include 94 | #include 95 | #endif 96 | 97 | #ifdef MSH_3D_DESCRIPTORS_STATIC 98 | #define MSH3DDDEF static 99 | #else 100 | #define MSH3DDDEF extern 101 | #endif 102 | 103 | typedef struct msh3dd_ctx 104 | { 105 | void* kd_tree; 106 | void* 107 | } msh3dd_ctx_t; 108 | 109 | void msh3dd_init( ); 110 | 111 | void msh3dd_quadric_descriptor( const msh3dd_ctx_t* ctx, 112 | const float* positions, const float* normals, 113 | const size_t n_points, 114 | float* quadric_descriptor ); 115 | 116 | #ifdef __cplusplus 117 | } 118 | #endif 119 | 120 | #endif /*MSH_3D_DESCRIPTORS_H*/ 121 | 122 | #ifdef MSH_3D_DESCRIPTORS_IMPLEMENTATION 123 | 124 | 125 | #endif /*MSH_3D_DESCRIPTORS_IMPLEMENTATION*/ -------------------------------------------------------------------------------- /experimental/msh_img_proc.h: -------------------------------------------------------------------------------- 1 | /* 2 | ============================================================================== 3 | 4 | MSH_IMG_PROC.H - WIP! 5 | 6 | A single header library for simple machine learning algorithms. 7 | 8 | To use the library you simply add: 9 | 10 | #define MSH_IMG_PROC_IMPLEMENTATION 11 | #include "msh_img_proc.h" 12 | 13 | The define should only include once in your source. If you need to include 14 | library in multiple places, simply use the include: 15 | 16 | #include "msh_img_proc.h" 17 | 18 | ============================================================================== 19 | DEPENDENCIES 20 | 21 | This library requires anonymous structs, which is a C11 extension. 22 | 23 | This library depends on following standard headers: 24 | // ADD HEADERS 25 | 26 | The main purpose of this library is image processing, hence we do not provide 27 | loaders. There are implementation of loaders that depend on stb_image.h, 28 | stb_image_write.h and lodepng.h libraries, but these are disabled by 29 | default. To enable them simply define 30 | #define MSH_IMG_PROC_IO 31 | ============================================================================== 32 | AUTHORS 33 | 34 | Maciej Halber (macikuh@gmail.com) 35 | 36 | ============================================================================== 37 | LICENSE 38 | The MIT License (MIT) 39 | 40 | Copyright (c) 2017 Maciej Halber 41 | 42 | Permission is hereby granted, free of charge, to any person obtaining a copy 43 | of this software and associated documentation files (the "Software"), to deal 44 | in the Software without restriction, including without limitation the rights 45 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 46 | copies of the Software, and to permit persons to whom the Software is 47 | furnished to do so, subject to the following conditions: 48 | 49 | The above copyright notice and this permission notice shall be included in all 50 | copies or substantial portions of the Software. 51 | 52 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 53 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 54 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 55 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 56 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 57 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 58 | SOFTWARE. 59 | 60 | 61 | ============================================================================== 62 | NOTES 63 | 64 | ============================================================================== 65 | TODOs 66 | [ ] Make all filter functions generic 67 | ============================================================================== 68 | REFERENCES: 69 | 1. Bilateral filter by mrharicot: https://www.shadertoy.com/view/4dfGDH 70 | */ 71 | 72 | 73 | #ifndef MSH_IMG_PROC_H 74 | #define MSH_IMG_PROC_H 75 | 76 | #ifdef __cplusplus 77 | extern "C" { 78 | #endif 79 | 80 | #ifdef MSH_IMG_PROC_INCLUDE_HEADERS 81 | #include 82 | #include 83 | #include 84 | #include 85 | #include "tt/tiny_time.h" 86 | #endif 87 | 88 | #ifdef MSH_IMG_PROC_STATIC 89 | #define MSHIPDEF static 90 | #else 91 | #define MSHIPDEF extern 92 | #endif 93 | 94 | #if !defined(__cplusplus) 95 | #if defined(_MSC_VER) 96 | #define inline __inline 97 | #else 98 | #define inline 99 | #endif 100 | #endif 101 | 102 | 103 | typedef unsigned char mship_ui8_t; 104 | typedef unsigned short mship_ui16_t; 105 | typedef unsigned int mship_ui32_t; 106 | typedef char mship_i8_t; 107 | typedef short mship_i16_t; 108 | typedef int mship_i32_t; 109 | typedef float mship_f32_t; 110 | typedef double mship_f64_t; 111 | 112 | #define MSH_PIXEL_STRUCT_DEF(id,b,s)\ 113 | typedef struct msh_pixel##s##_##id##b\ 114 | {\ 115 | mship_##id##b##_t data[s];\ 116 | }msh_pixel##s##_##id##b##_t;\ 117 | 118 | #define MSH_IMAGE_STRUCT_DEF(id, b) \ 119 | typedef struct msh_img_##id##b\ 120 | {\ 121 | int width;\ 122 | int height;\ 123 | int n_comp;\ 124 | mship_##id##b##_t *data;\ 125 | }msh_img_##id##b##_t; 126 | 127 | #define MSH_IMAGE_INIT_DECL(id, b)\ 128 | msh_img_##id##b##_t mship_img_##id##b##_init(int w, int h, int n, int init); 129 | 130 | #define MSH_IMAGE_COPY_DECL(id, b)\ 131 | msh_img_##id##b##_t mship_img_##id##b##_copy(const msh_img_##id##b##_t *img); 132 | 133 | #define MSH_IMAGE_FREE_DECL(id, b)\ 134 | msh_img_##id##b##_t mship_img_##id##b##_free(msh_img_##id##b##_t *img); 135 | 136 | #define MSH_IMAGE_SAMPLE_NN_DECL(id, b)\ 137 | inline mship_##id##b##_t \ 138 | mship_sample_nn_##id##b(const msh_img_##id##b##_t *img, float x, float y); 139 | 140 | #define MSH_IMAGE_SAMPLE_NN_PIX_DECL(id, b, s)\ 141 | inline msh_pixel##s##_##id##b##_t \ 142 | mship_sample##s##_nn_##id##b(const msh_img_##id##b##_t *img, float x, float y); 143 | 144 | #define MSH_IMAGE_SAMPLE_BILINEAR_DECL(id, b)\ 145 | inline mship_##id##b##_t mship_sample_bl_##id##b(const msh_img_##id##b##_t *img, float x, float y); 146 | 147 | #define MSH_IMAGE_PIXEL_PTR_DECL(id,b) \ 148 | inline mship_##id##b##_t* mship_pixel_ptr_##id##b(msh_img_##id##b##_t *img, int x, int y); 149 | 150 | #define MSH_IMAGE_PIXEL_CPTR_DECL(id,b) \ 151 | inline const mship_##id##b##_t* mship_pixel_cptr_##id##b(const msh_img_##id##b##_t *img, int x, int y); 152 | 153 | 154 | // Code gen 155 | MSH_PIXEL_STRUCT_DEF(ui, 8, 2) 156 | MSH_PIXEL_STRUCT_DEF(ui, 8, 3) 157 | MSH_PIXEL_STRUCT_DEF(ui, 8, 4) 158 | MSH_PIXEL_STRUCT_DEF(ui, 16, 2) 159 | MSH_PIXEL_STRUCT_DEF(ui, 16, 3) 160 | MSH_PIXEL_STRUCT_DEF(ui, 16, 4) 161 | MSH_PIXEL_STRUCT_DEF(ui, 32, 2) 162 | MSH_PIXEL_STRUCT_DEF(ui, 32, 3) 163 | MSH_PIXEL_STRUCT_DEF(ui, 32, 4) 164 | MSH_PIXEL_STRUCT_DEF(f, 32, 2) 165 | MSH_PIXEL_STRUCT_DEF(f, 32, 3) 166 | MSH_PIXEL_STRUCT_DEF(f, 32, 4) 167 | MSH_PIXEL_STRUCT_DEF(f, 64, 2) 168 | MSH_PIXEL_STRUCT_DEF(f, 64, 3) 169 | MSH_PIXEL_STRUCT_DEF(f, 64, 4) 170 | 171 | MSH_IMAGE_STRUCT_DEF(ui, 8) 172 | MSH_IMAGE_STRUCT_DEF(ui, 16) 173 | MSH_IMAGE_STRUCT_DEF(ui, 32) 174 | MSH_IMAGE_STRUCT_DEF(f, 32) 175 | MSH_IMAGE_STRUCT_DEF(f, 64) 176 | 177 | MSH_IMAGE_INIT_DECL(ui, 8) 178 | MSH_IMAGE_INIT_DECL(ui, 16) 179 | MSH_IMAGE_INIT_DECL(ui, 32) 180 | MSH_IMAGE_INIT_DECL(f, 32) 181 | MSH_IMAGE_INIT_DECL(f, 64) 182 | 183 | MSH_IMAGE_COPY_DECL(ui, 8) 184 | MSH_IMAGE_COPY_DECL(ui, 16) 185 | MSH_IMAGE_COPY_DECL(ui, 32) 186 | MSH_IMAGE_COPY_DECL(f, 32) 187 | MSH_IMAGE_COPY_DECL(f, 64) 188 | 189 | MSH_IMAGE_SAMPLE_NN_DECL(ui, 8) 190 | MSH_IMAGE_SAMPLE_NN_PIX_DECL(ui, 8, 2) 191 | MSH_IMAGE_SAMPLE_NN_PIX_DECL(ui, 8, 3) 192 | MSH_IMAGE_SAMPLE_NN_PIX_DECL(ui, 8, 4) 193 | MSH_IMAGE_SAMPLE_NN_DECL(ui, 16) 194 | MSH_IMAGE_SAMPLE_NN_PIX_DECL(ui, 16, 2) 195 | MSH_IMAGE_SAMPLE_NN_PIX_DECL(ui, 16, 3) 196 | MSH_IMAGE_SAMPLE_NN_PIX_DECL(ui, 16, 4) 197 | MSH_IMAGE_SAMPLE_NN_DECL(ui, 32) 198 | MSH_IMAGE_SAMPLE_NN_PIX_DECL(ui, 32, 2) 199 | MSH_IMAGE_SAMPLE_NN_PIX_DECL(ui, 32, 3) 200 | MSH_IMAGE_SAMPLE_NN_PIX_DECL(ui, 32, 4) 201 | MSH_IMAGE_SAMPLE_NN_DECL(f, 32) 202 | MSH_IMAGE_SAMPLE_NN_PIX_DECL(f, 32, 2) 203 | MSH_IMAGE_SAMPLE_NN_PIX_DECL(f, 32, 3) 204 | MSH_IMAGE_SAMPLE_NN_PIX_DECL(f, 32, 4) 205 | MSH_IMAGE_SAMPLE_NN_DECL(f, 64) 206 | MSH_IMAGE_SAMPLE_NN_PIX_DECL(f, 64, 2) 207 | MSH_IMAGE_SAMPLE_NN_PIX_DECL(f, 64, 3) 208 | MSH_IMAGE_SAMPLE_NN_PIX_DECL(f, 64, 4) 209 | 210 | MSH_IMAGE_SAMPLE_BILINEAR_DECL(ui, 8) 211 | MSH_IMAGE_SAMPLE_BILINEAR_DECL(ui, 16) 212 | MSH_IMAGE_SAMPLE_BILINEAR_DECL(ui, 32) 213 | MSH_IMAGE_SAMPLE_BILINEAR_DECL(f, 32) 214 | MSH_IMAGE_SAMPLE_BILINEAR_DECL(f, 64) 215 | 216 | MSH_IMAGE_PIXEL_PTR_DECL(ui, 8) 217 | MSH_IMAGE_PIXEL_PTR_DECL(ui, 16) 218 | MSH_IMAGE_PIXEL_PTR_DECL(ui, 32) 219 | MSH_IMAGE_PIXEL_PTR_DECL(f, 32) 220 | MSH_IMAGE_PIXEL_PTR_DECL(f, 64) 221 | 222 | MSH_IMAGE_PIXEL_CPTR_DECL(ui, 8) 223 | MSH_IMAGE_PIXEL_CPTR_DECL(ui, 16) 224 | MSH_IMAGE_PIXEL_CPTR_DECL(ui, 32) 225 | MSH_IMAGE_PIXEL_CPTR_DECL(f, 32) 226 | MSH_IMAGE_PIXEL_CPTR_DECL(f, 64) 227 | 228 | #ifdef MSH_IMG_PROC_IO 229 | msh_imgui8 msh_load_png(); 230 | #endif 231 | 232 | 233 | 234 | #ifdef __cplusplus 235 | } 236 | #endif 237 | 238 | #endif /*MSH_IMG_PROC_H*/ 239 | 240 | #ifdef MSH_IMG_PROC_IMPLEMENTATION 241 | 242 | #ifndef mship_max 243 | #define mship_max(a, b) ((a) > (b) ? (a) : (b)) 244 | #endif 245 | 246 | #ifndef mship_min 247 | #define mship_min(a, b) ((a) < (b) ? (a) : (b)) 248 | #endif 249 | 250 | // NOTE(maciej): CPP mode in MSVC is wierd, boy 251 | #if defined(__cplusplus) && defined(_MSC_VER) 252 | #define MSHIP_INIT_CAST(x) x 253 | #else 254 | #define MSHIP_INIT_CAST(x) (x) 255 | #endif 256 | 257 | #define MSH_IMAGE__COMPAR(id, b)\ 258 | static int \ 259 | mship__compar_##id##b(const void* x, const void* y)\ 260 | {\ 261 | return (*(mship_##id##b##_t*)x < *(mship_##id##b##_t*)y) ? 1 : -1;\ 262 | } 263 | 264 | #define MSH_IMAGE__PIXEL2_SCALAR_MUL(id, b)\ 265 | static inline \ 266 | msh_pixel2_##id##b##_t mship__pixel2_##id##b##_mul_scalar(msh_pixel2_##id##b##_t pix, float s)\ 267 | {\ 268 | return MSHIP_INIT_CAST(msh_pixel2_##id##b##_t){{\ 269 | (mship_##id##b##_t)(pix.data[0]*s), \ 270 | (mship_##id##b##_t)(pix.data[1]*s)}};\ 271 | } 272 | 273 | #define MSH_IMAGE__PIXEL3_SCALAR_MUL(id, b)\ 274 | static inline \ 275 | msh_pixel3_##id##b##_t mship__pixel3_##id##b##_mul_scalar(msh_pixel3_##id##b##_t pix, float s)\ 276 | {\ 277 | return MSHIP_INIT_CAST(msh_pixel3_##id##b##_t){{\ 278 | (mship_##id##b##_t)(pix.data[0]*s), \ 279 | (mship_##id##b##_t)(pix.data[1]*s), \ 280 | (mship_##id##b##_t)(pix.data[2]*s)}};\ 281 | } 282 | 283 | #define MSH_IMAGE__PIXEL4_SCALAR_MUL(id, b)\ 284 | static inline \ 285 | msh_pixel4_##id##b##_t mship__pixel4_##id##b##_mul_scalar(msh_pixel4_##id##b##_t pix, float s)\ 286 | {\ 287 | return MSHIP_INIT_CAST(msh_pixel4_##id##b##_t){{\ 288 | (mship_##id##b##_t)(pix.data[0]*s), \ 289 | (mship_##id##b##_t)(pix.data[1]*s), \ 290 | (mship_##id##b##_t)(pix.data[2]*s), \ 291 | (mship_##id##b##_t)(pix.data[3]*s)}};\ 292 | } 293 | 294 | #define MSH_IMAGE__PIXEL2_ADD(id, b)\ 295 | static inline \ 296 | msh_pixel2_##id##b##_t mship__pixel2_##id##b##_add(msh_pixel2_##id##b##_t pix_a, msh_pixel2_##id##b##_t pix_b)\ 297 | {\ 298 | return MSHIP_INIT_CAST(msh_pixel2_##id##b##_t){{\ 299 | (mship_##id##b##_t)(pix_a.data[0]+pix_b.data[0]),\ 300 | (mship_##id##b##_t)(pix_a.data[1]+pix_b.data[1])}};\ 301 | } 302 | 303 | #define MSH_IMAGE__PIXEL3_ADD(id, b)\ 304 | static inline \ 305 | msh_pixel3_##id##b##_t mship__pixel3_##id##b##_add(msh_pixel3_##id##b##_t pix_a, msh_pixel3_##id##b##_t pix_b)\ 306 | {\ 307 | return MSHIP_INIT_CAST(msh_pixel3_##id##b##_t){{\ 308 | (mship_##id##b##_t)(pix_a.data[0]+pix_b.data[0]),\ 309 | (mship_##id##b##_t)(pix_a.data[1]+pix_b.data[1]),\ 310 | (mship_##id##b##_t)(pix_a.data[2]+pix_b.data[2])}};\ 311 | } 312 | 313 | #define MSH_IMAGE__PIXEL4_ADD(id, b)\ 314 | static inline \ 315 | msh_pixel4_##id##b##_t mship__pixel4_##id##b##_add(msh_pixel4_##id##b##_t pix_a, msh_pixel4_##id##b##_t pix_b)\ 316 | {\ 317 | return MSHIP_INIT_CAST(msh_pixel4_##id##b##_t){{\ 318 | (mship_##id##b##_t)(pix_a.data[0]+pix_b.data[0]),\ 319 | (mship_##id##b##_t)(pix_a.data[1]+pix_b.data[1]),\ 320 | (mship_##id##b##_t)(pix_a.data[2]+pix_b.data[2]),\ 321 | (mship_##id##b##_t)(pix_a.data[3]+pix_b.data[3])}};\ 322 | } 323 | 324 | 325 | #define MSH_IMAGE_INIT_DEF(id, b)\ 326 | MSHIPDEF msh_img_##id##b##_t \ 327 | mship_img_##id##b##_init(int width, int height, int n_comp, int initialize)\ 328 | {\ 329 | msh_img_##id##b##_t img;\ 330 | int n_elems = width*height*n_comp;\ 331 | int byte_size = n_elems*sizeof(mship_##id##b##_t);\ 332 | img.width = width;\ 333 | img.height = height;\ 334 | img.n_comp = n_comp;\ 335 | img.data = NULL;\ 336 | if(initialize) {\ 337 | img.data = (mship_##id##b##_t*)malloc(byte_size);\ 338 | memset((void*)img.data, 0, byte_size);\ 339 | }\ 340 | return img;\ 341 | } 342 | 343 | 344 | #define MSH_IMAGE_COPY_DEF(id, b)\ 345 | MSHIPDEF msh_img_##id##b##_t \ 346 | mship_img_##id##b##_copy(const msh_img_##id##b##_t *img)\ 347 | {\ 348 | msh_img_##id##b##_t cpy;\ 349 | int n_elems = img->width*img->height*img->n_comp;\ 350 | int byte_size = n_elems*sizeof(mship_##id##b##_t);\ 351 | cpy.width = img->width;\ 352 | cpy.height = img->height;\ 353 | cpy.n_comp = img->n_comp;\ 354 | cpy.data = (mship_##id##b##_t*)malloc(byte_size);\ 355 | memcpy(cpy.data, img->data, byte_size);\ 356 | return cpy;\ 357 | } 358 | 359 | #define MSH_IMAGE_FREE_DEF(id, b)\ 360 | MSHIPDEF void \ 361 | mship_img_##id##b##_free(msh_img_##id##b##_t* img)\ 362 | {\ 363 | if(img->data) free(img->data);\ 364 | } 365 | 366 | #define MSH_IMAGE_PIXEL_PTR_DEF(id,b) \ 367 | MSHIPDEF inline mship_##id##b##_t* \ 368 | mship_pixel_ptr_##id##b(msh_img_##id##b##_t *img, int x, int y)\ 369 | {\ 370 | return &(img->data[img->n_comp*(img->width*y + x)]);\ 371 | } 372 | 373 | #define MSH_IMAGE_PIXEL_CPTR_DEF(id,b) \ 374 | MSHIPDEF inline const mship_##id##b##_t* \ 375 | mship_pixel_cptr_##id##b( const msh_img_##id##b##_t *img, int x, int y)\ 376 | {\ 377 | return &(img->data[img->n_comp*(img->width*y + x)]);\ 378 | } 379 | 380 | #define MSH_IMAGE_SAMPLE_NN_DEF(id, b)\ 381 | MSHIPDEF inline mship_##id##b##_t \ 382 | mship_sample_nn_##id##b(const msh_img_##id##b##_t *img, float x, float y)\ 383 | {\ 384 | return (img->data[(img->width*(int)(floorf(y)) + (int)(floorf(x)))]); \ 385 | } 386 | 387 | #define MSH_IMAGE_SAMPLE_NN_PIX_DEF(id, b, s)\ 388 | MSHIPDEF inline msh_pixel##s##_##id##b##_t \ 389 | mship_sample##s##_nn_##id##b(const msh_img_##id##b##_t *img, float x, float y)\ 390 | {\ 391 | msh_pixel##s##_##id##b##_t pix;\ 392 | memcpy((&(pix.data[0])),(&(img->data[img->n_comp*(img->width*(int)(floorf(y)) + (int)(floorf(x)))])), (s*b)/8);\ 393 | return pix;\ 394 | } 395 | 396 | // NOTE(maciej): There might be more efficient version of this that writes to a pixel 397 | #define MSH_IMAGE_SAMPLE_BILINEAR_DEF(id, b)\ 398 | MSHIPDEF inline mship_##id##b##_t \ 399 | mship_sample_bl_##id##b(const msh_img_##id##b##_t *img, float x, float y)\ 400 | {\ 401 | float lx = floorf(x); float rx = mship_min(lx+1, img->width-1); \ 402 | float ly = floorf(y); float ry = mship_min(ly+1, img->height-1); \ 403 | double val1 = (double)mship_sample_nn_##id##b(img, lx, ly) * (1.0+lx-x)*(1.0+ly-y);\ 404 | double val2 = (double)mship_sample_nn_##id##b(img, lx, ry) * (1.0+lx-x)*(y-ly);\ 405 | double val3 = (double)mship_sample_nn_##id##b(img, rx, ly) * (x-lx)*(1.0+ly-y);\ 406 | double val4 = (double)mship_sample_nn_##id##b(img, rx, ry) * (x-lx)*(y-ly);\ 407 | return (mship_##id##b##_t)(val1+val2+val3+val4);\ 408 | } 409 | 410 | // NOTE(maciej): Decide whether we want to have pixel with one elem 411 | #define MSH_IMAGE_SAMPLE_BILINEAR_PIX_DEF(id, b, s)\ 412 | MSHIPDEF inline msh_pixel##s##_##id##b##_t \ 413 | mship_sample##s##_bl_##id##b(const msh_img_##id##b##_t *img, float x, float y)\ 414 | {\ 415 | float lx = floorf(x); float rx = mship_min(lx+1, img->width-1); \ 416 | float ly = floorf(y); float ry = mship_min(ly+1, img->height-1); \ 417 | float w1 = (1.0f+lx-x)*(1.0f+ly-y);\ 418 | float w2 = (1.0f+lx-x)*(y-ly);\ 419 | float w3 = (x-lx)*(1.0f+ly-y);\ 420 | float w4 = (x-lx)*(y-ly);\ 421 | msh_pixel##s##_##id##b##_t pix1 = mship_sample##s##_nn_##id##b(img, lx, ly);\ 422 | msh_pixel##s##_##id##b##_t pix2 = mship_sample##s##_nn_##id##b(img, lx, ry);\ 423 | msh_pixel##s##_##id##b##_t pix3 = mship_sample##s##_nn_##id##b(img, rx, ly);\ 424 | msh_pixel##s##_##id##b##_t pix4 = mship_sample##s##_nn_##id##b(img, rx, ry);\ 425 | pix1 = mship__pixel##s##_##id##b##_mul_scalar(pix1, w1);\ 426 | pix2 = mship__pixel##s##_##id##b##_mul_scalar(pix2, w2);\ 427 | pix3 = mship__pixel##s##_##id##b##_mul_scalar(pix3, w3);\ 428 | pix4 = mship__pixel##s##_##id##b##_mul_scalar(pix4, w4);\ 429 | msh_pixel##s##_##id##b##_t pixa = mship__pixel##s##_##id##b##_add(pix1, pix2);\ 430 | msh_pixel##s##_##id##b##_t pixb = mship__pixel##s##_##id##b##_add(pix3, pix4);\ 431 | return (mship__pixel##s##_##id##b##_add(pixa, pixb));\ 432 | } 433 | 434 | MSH_IMAGE__COMPAR(ui, 8) 435 | MSH_IMAGE__COMPAR(ui, 16) 436 | MSH_IMAGE__COMPAR(ui, 32) 437 | MSH_IMAGE__COMPAR(f, 32) 438 | MSH_IMAGE__COMPAR(f, 64) 439 | 440 | MSH_IMAGE__PIXEL2_SCALAR_MUL(ui, 8) 441 | MSH_IMAGE__PIXEL2_SCALAR_MUL(ui, 16) 442 | MSH_IMAGE__PIXEL2_SCALAR_MUL(ui, 32) 443 | MSH_IMAGE__PIXEL2_SCALAR_MUL(f, 32) 444 | MSH_IMAGE__PIXEL2_SCALAR_MUL(f, 64) 445 | 446 | MSH_IMAGE__PIXEL3_SCALAR_MUL(ui, 8) 447 | MSH_IMAGE__PIXEL3_SCALAR_MUL(ui, 16) 448 | MSH_IMAGE__PIXEL3_SCALAR_MUL(ui, 32) 449 | MSH_IMAGE__PIXEL3_SCALAR_MUL(f, 32) 450 | MSH_IMAGE__PIXEL3_SCALAR_MUL(f, 64) 451 | 452 | MSH_IMAGE__PIXEL4_SCALAR_MUL(ui, 8) 453 | MSH_IMAGE__PIXEL4_SCALAR_MUL(ui, 16) 454 | MSH_IMAGE__PIXEL4_SCALAR_MUL(ui, 32) 455 | MSH_IMAGE__PIXEL4_SCALAR_MUL(f, 32) 456 | MSH_IMAGE__PIXEL4_SCALAR_MUL(f, 64) 457 | 458 | MSH_IMAGE__PIXEL2_ADD(ui, 8) 459 | MSH_IMAGE__PIXEL2_ADD(ui, 16) 460 | MSH_IMAGE__PIXEL2_ADD(ui, 32) 461 | MSH_IMAGE__PIXEL2_ADD(f, 32) 462 | MSH_IMAGE__PIXEL2_ADD(f, 64) 463 | 464 | MSH_IMAGE__PIXEL3_ADD(ui, 8) 465 | MSH_IMAGE__PIXEL3_ADD(ui, 16) 466 | MSH_IMAGE__PIXEL3_ADD(ui, 32) 467 | MSH_IMAGE__PIXEL3_ADD(f, 32) 468 | MSH_IMAGE__PIXEL3_ADD(f, 64) 469 | 470 | MSH_IMAGE__PIXEL4_ADD(ui, 8) 471 | MSH_IMAGE__PIXEL4_ADD(ui, 16) 472 | MSH_IMAGE__PIXEL4_ADD(ui, 32) 473 | MSH_IMAGE__PIXEL4_ADD(f, 32) 474 | MSH_IMAGE__PIXEL4_ADD(f, 64) 475 | 476 | MSH_IMAGE_INIT_DEF(ui, 8) 477 | MSH_IMAGE_INIT_DEF(ui, 16) 478 | MSH_IMAGE_INIT_DEF(ui, 32) 479 | MSH_IMAGE_INIT_DEF(f, 32) 480 | MSH_IMAGE_INIT_DEF(f, 64) 481 | 482 | MSH_IMAGE_COPY_DEF(ui, 8) 483 | MSH_IMAGE_COPY_DEF(ui, 16) 484 | MSH_IMAGE_COPY_DEF(ui, 32) 485 | MSH_IMAGE_COPY_DEF(f, 32) 486 | MSH_IMAGE_COPY_DEF(f, 64) 487 | 488 | MSH_IMAGE_FREE_DEF(ui, 8) 489 | MSH_IMAGE_FREE_DEF(ui, 16) 490 | MSH_IMAGE_FREE_DEF(ui, 32) 491 | MSH_IMAGE_FREE_DEF(f, 32) 492 | MSH_IMAGE_FREE_DEF(f, 64) 493 | 494 | MSH_IMAGE_PIXEL_PTR_DEF(ui, 8) 495 | MSH_IMAGE_PIXEL_PTR_DEF(ui, 16) 496 | MSH_IMAGE_PIXEL_PTR_DEF(ui, 32) 497 | MSH_IMAGE_PIXEL_PTR_DEF(f, 32) 498 | MSH_IMAGE_PIXEL_PTR_DEF(f, 64) 499 | 500 | MSH_IMAGE_PIXEL_CPTR_DEF(ui, 8) 501 | MSH_IMAGE_PIXEL_CPTR_DEF(ui, 16) 502 | MSH_IMAGE_PIXEL_CPTR_DEF(ui, 32) 503 | MSH_IMAGE_PIXEL_CPTR_DEF(f, 32) 504 | MSH_IMAGE_PIXEL_CPTR_DEF(f, 64) 505 | 506 | MSH_IMAGE_SAMPLE_NN_DEF(ui, 8) 507 | MSH_IMAGE_SAMPLE_NN_DEF(ui, 16) 508 | MSH_IMAGE_SAMPLE_NN_DEF(ui, 32) 509 | MSH_IMAGE_SAMPLE_NN_DEF(f, 32) 510 | MSH_IMAGE_SAMPLE_NN_DEF(f, 64) 511 | 512 | MSH_IMAGE_SAMPLE_NN_PIX_DEF(ui, 8, 2) 513 | MSH_IMAGE_SAMPLE_NN_PIX_DEF(ui, 16, 2) 514 | MSH_IMAGE_SAMPLE_NN_PIX_DEF(ui, 32, 2) 515 | MSH_IMAGE_SAMPLE_NN_PIX_DEF(f, 32, 2) 516 | MSH_IMAGE_SAMPLE_NN_PIX_DEF(f, 64, 2) 517 | 518 | MSH_IMAGE_SAMPLE_NN_PIX_DEF(ui, 8, 3) 519 | MSH_IMAGE_SAMPLE_NN_PIX_DEF(ui, 16, 3) 520 | MSH_IMAGE_SAMPLE_NN_PIX_DEF(ui, 32, 3) 521 | MSH_IMAGE_SAMPLE_NN_PIX_DEF(f, 32, 3) 522 | MSH_IMAGE_SAMPLE_NN_PIX_DEF(f, 64, 3) 523 | 524 | MSH_IMAGE_SAMPLE_NN_PIX_DEF(ui, 8, 4) 525 | MSH_IMAGE_SAMPLE_NN_PIX_DEF(ui, 16, 4) 526 | MSH_IMAGE_SAMPLE_NN_PIX_DEF(ui, 32, 4) 527 | MSH_IMAGE_SAMPLE_NN_PIX_DEF(f, 32, 4) 528 | MSH_IMAGE_SAMPLE_NN_PIX_DEF(f, 64, 4) 529 | 530 | MSH_IMAGE_SAMPLE_BILINEAR_DEF(ui, 8) 531 | MSH_IMAGE_SAMPLE_BILINEAR_DEF(ui, 16) 532 | MSH_IMAGE_SAMPLE_BILINEAR_DEF(ui, 32) 533 | MSH_IMAGE_SAMPLE_BILINEAR_DEF(f, 32) 534 | MSH_IMAGE_SAMPLE_BILINEAR_DEF(f, 64) 535 | 536 | MSH_IMAGE_SAMPLE_BILINEAR_PIX_DEF(ui, 8, 2) 537 | MSH_IMAGE_SAMPLE_BILINEAR_PIX_DEF(ui, 16, 2) 538 | MSH_IMAGE_SAMPLE_BILINEAR_PIX_DEF(ui, 32, 2) 539 | MSH_IMAGE_SAMPLE_BILINEAR_PIX_DEF(f, 32, 2) 540 | MSH_IMAGE_SAMPLE_BILINEAR_PIX_DEF(f, 64, 2) 541 | 542 | MSH_IMAGE_SAMPLE_BILINEAR_PIX_DEF(ui, 8, 3) 543 | MSH_IMAGE_SAMPLE_BILINEAR_PIX_DEF(ui, 16, 3) 544 | MSH_IMAGE_SAMPLE_BILINEAR_PIX_DEF(ui, 32, 3) 545 | MSH_IMAGE_SAMPLE_BILINEAR_PIX_DEF(f, 32, 3) 546 | MSH_IMAGE_SAMPLE_BILINEAR_PIX_DEF(f, 64, 3) 547 | 548 | MSH_IMAGE_SAMPLE_BILINEAR_PIX_DEF(ui, 8, 4) 549 | MSH_IMAGE_SAMPLE_BILINEAR_PIX_DEF(ui, 16, 4) 550 | MSH_IMAGE_SAMPLE_BILINEAR_PIX_DEF(ui, 32, 4) 551 | MSH_IMAGE_SAMPLE_BILINEAR_PIX_DEF(f, 32, 4) 552 | MSH_IMAGE_SAMPLE_BILINEAR_PIX_DEF(f, 64, 4) 553 | 554 | // TODO(maciej): Test and make generic 555 | msh_img_ui8_t 556 | mship_median_filter(msh_img_ui8_t* img, int filter_size ) 557 | { 558 | int w = img->width; 559 | int h = img->height; 560 | filter_size = (filter_size%2==0) ? filter_size+1 : filter_size; 561 | int r = (filter_size-1)/2; 562 | mship_ui8_t filter_data[1024]; 563 | msh_img_ui8_t filtered= mship_img_ui8_copy(img); 564 | int step = filter_size*filter_size; 565 | for(int y=10; yn_comp; ++c) 578 | { 579 | filter_data[step*c + filter_idx] = pix_ptr[c]; 580 | } 581 | filter_idx++; 582 | } 583 | } 584 | mship_ui8_t* filtered_ptr = mship_pixel_ptr_ui8(&filtered, x, y); 585 | for(int c=0 ; cn_comp; ++c) 586 | { 587 | qsort( &(filter_data[c*step]), step, sizeof(mship_ui8_t), mship__compar_ui8); 588 | filtered_ptr[c] = filter_data[c*step + (step+1)/2]; 589 | } 590 | } 591 | } 592 | return filtered; 593 | } 594 | 595 | msh_img_ui8_t 596 | mship_erode_filter(msh_img_ui8_t* img, int filter_size ) 597 | { 598 | int w = img->width; 599 | int h = img->height; 600 | filter_size = (filter_size%2==0) ? filter_size+1 : filter_size; 601 | int r = (filter_size-1)/2; 602 | msh_img_ui8_t filtered= mship_img_ui8_copy(img); 603 | for(int y=0; yn_comp; ++c) 616 | { 617 | if(pix_ptr[c] < filtered_ptr[c]) filtered_ptr[c] = pix_ptr[c]; 618 | } 619 | } 620 | } 621 | } 622 | } 623 | return filtered; 624 | } 625 | 626 | msh_img_ui8_t 627 | mship_dilate_filter(msh_img_ui8_t* img, int filter_size ) 628 | { 629 | int w = img->width; 630 | int h = img->height; 631 | filter_size = (filter_size%2==0) ? filter_size+1 : filter_size; 632 | int r = (filter_size-1)/2; 633 | msh_img_ui8_t filtered= mship_img_ui8_copy(img); 634 | for(int y=0; yn_comp; ++c) 647 | { 648 | if(pix_ptr[c] > filtered_ptr[c]) filtered_ptr[c] = pix_ptr[c]; 649 | } 650 | } 651 | } 652 | } 653 | } 654 | return filtered; 655 | } 656 | 657 | float 658 | mship__normpdf(float x, float sigma) 659 | { 660 | return 0.39894f*expf((-0.5f*x*x)/(sigma*sigma))/sigma; 661 | } 662 | 663 | void 664 | mship__compute_gaussian_kernel(float* kernel, int radius, float sigma) 665 | { 666 | 667 | for(int x = 0; x <= radius; ++x) 668 | { 669 | kernel[radius+x] = kernel[radius-x] = mship__normpdf((float)x, sigma); 670 | } 671 | } 672 | 673 | // Assume single channel for now. 674 | // This is okay, as long as sigma is not too large. 675 | msh_img_ui16_t 676 | mship_img_ui16_bilateral_filter(const msh_img_ui16_t* img, 677 | float sigma, float bsigma) 678 | { 679 | msh_img_ui16_t filtered = mship_img_ui16_init(img->width, img->height, 680 | img->n_comp, 1); 681 | //calculate radius in pixels given sigma 682 | int r = (int)(3.0 * sigma); 683 | float kernel[1024] = {0}; // Big enough for anything reasonable. 684 | mship__compute_gaussian_kernel( &kernel[0], r, sigma ); 685 | 686 | for(int y = 0; y < img->height; ++y) 687 | { 688 | for(int x = 0; x < img->width; ++x) 689 | { 690 | const mship_ui16_t* i = mship_pixel_cptr_ui16(img, x, y); 691 | const float ival = (*i); 692 | 693 | float diff = 0.0f; 694 | float factor = 0.0f; 695 | float Z = 0.0f; 696 | float bZ = 1.0f / mship__normpdf(0.0f, bsigma); 697 | float final_val = 0.0f; 698 | for(int oy=-r; oy <= r; ++oy) 699 | { 700 | for(int ox=-r; ox <= r; ++ox) 701 | { 702 | int cx = x + ox; int cy = y + oy; 703 | if(cx >= 0 && cx < img->width && cy >= 0 && cy < img->height) 704 | { 705 | const mship_ui16_t* j = mship_pixel_cptr_ui16(img, cx, cy); 706 | diff = (float)(*j) - ival; 707 | factor = mship__normpdf(diff, bsigma) * bZ * kernel[r+ox] * kernel[r+oy]; 708 | Z += factor; 709 | final_val += factor*(float)(*j); 710 | } 711 | } 712 | } 713 | float oval = final_val/Z; 714 | mship_ui16_t* o = mship_pixel_ptr_ui16(&filtered, x, y); 715 | *o = (mship_ui16_t)oval; 716 | } 717 | } 718 | return filtered; 719 | } 720 | 721 | #ifdef MSH_IMG_PROC_IO 722 | #endif 723 | 724 | #endif /*MSH_IMG_PROC_IMPLEMENTATION*/ 725 | -------------------------------------------------------------------------------- /experimental/msh_jobs.h: -------------------------------------------------------------------------------- 1 | // NOTE(maciej): This is very closely modelled after Casey Muratori's work queue, designed 2 | // in handmade hero streams. 3 | // Credits : bgfx for platform detection 4 | 5 | /* TODOs: 6 | [ ] If just one thread is requested, we should just use main thread. 7 | [x] When enqueing wait if the queue is full - Remove spin printf 8 | [ ] Malloc overwrites 9 | [ ] Header / docs 10 | [ ] Error handling - thread detaching? 11 | 12 | [ ] Remove deadlock if a queue size == 1 13 | [ ] Add Multiple priority queuesg 14 | [ ] Improve the test code 15 | [x] Implement functions wrappers fro winapi 16 | [x] Implement function wrappers for posix 17 | [ ] Multiple Producer / Multiple Consumer Queues? 18 | [ ] Async reading - check sokol async 19 | [ ] Avoid recompiling extra code if msh_std is present 20 | [ ] Compare to fibers: https://github.com/JodiTheTigger/sewing 21 | 22 | [ ] Whenever MSVC adds stdatomic.h and threads.h, use these instead (?) 23 | 24 | Some extra meterials: 25 | https://preshing.com/20120612/an-introduction-to-lock-free-programming/ 26 | https://gdcvault.com/play/1022186/Parallelizing-the-Naughty-Dog-Engine 27 | http://www.1024cores.net/home/lock-free-algorithms/queues/bounded-mpmc-queue 28 | */ 29 | 30 | #ifndef MSH_JOBS 31 | #define MSH_JOBS 32 | 33 | #define MSH_JOBS_QUEUE_SIZE 1024 34 | #define MSH_JOBS_DEFAULT_THREAD_COUNT 0 35 | #define MSH_JOBS_JOB_SIGNATURE(name) uint32_t name(int thread_idx, void* params) 36 | typedef uint32_t (*msh_jobs_job_signature_t)( int thread_idx, void* data); 37 | 38 | #define MSH_JOBS_PLATFORM_WINDOWS 0 39 | #define MSH_JOBS_PLATFORM_LINUX 0 40 | #define MSH_JOBS_PLATFORM_MACOS 0 41 | 42 | #if defined(_WIN32) || defined(_WIN64) 43 | #undef MSH_JOBS_PLATFORM_WINDOWS 44 | #define MSH_JOBS_PLATFORM_WINDOWS 1 45 | #elif defined(__linux__) 46 | #undef MSH_JOBS_PLATFORM_LINUX 47 | #define MSH_JOBS_PLATFORM_LINUX 1 48 | #elif defined(__ENVIRONMENT_MAC_OS_X_VERSION_MIN_REQUIRED__) 49 | #undef MSH_JOBS_PLATFORM_MACOS 50 | #define MSH_JOBS_PLATFORM_MACOS 1 51 | #endif 52 | #define MSH_JOBS_PLATFORM_POSIX (0 || MSH_JOBS_PLATFORM_MACOS || MSH_JOBS_PLATFORM_LINUX ) 53 | 54 | #if MSH_JOBS_PLATFORM_WINDOWS 55 | #define MSH_JOBS_PLATFORM_NAME (char*)"windows" 56 | #elif MSH_JOBS_PLATFORM_LINUX 57 | #define MSH_JOBS_PLATFORM_NAME (char*)"linux" 58 | #elif MSH_JOBS_PLATFORM_MACOS 59 | #define MSH_JOBS_PLATFORM_NAME (char*)"macos" 60 | #else 61 | #error "MSH_JOBS: Compiling on unknown platform!" 62 | #endif 63 | 64 | #if MSH_JOBS_PLATFORM_WINDOWS 65 | 66 | #ifdef __cplusplus 67 | #include "intrin.h" 68 | #endif 69 | #ifndef WIN32_LEAN_AND_MEAN 70 | #define WIN32_LEAN_AND_MEAN 71 | #endif 72 | #include "windows.h" 73 | 74 | #elif MSH_JOBS_PLATFORM_LINUX 75 | #include // threads 76 | #include // semaphore 77 | #include // fences 78 | #include // sysconf 79 | 80 | #elif MSH_JOBS_PLATFORM_MACOS 81 | #include // threads 82 | #include // semaphore 83 | #include // fences 84 | #include // sysconf 85 | #include 86 | #else 87 | #error "MSH_JOBS: Platform not supported!" 88 | #endif 89 | 90 | #if MSH_JOBS_PLATFORM_WINDOWS 91 | 92 | typedef HANDLE msh_jobs_semaphore_t; 93 | typedef HANDLE msh_jobs_thread_t; 94 | #define MSH_JOBS_READ_WRITE_BARRIER() _mm_mfence(); _ReadWriteBarrier() 95 | #define MSH_JOBS_READ_BARRIER() _mm_rfence(); _ReadBarrier() 96 | #define MSH_JOBS_WRITE_BARRIER() _mm_sfence(); _WriteBarrier() 97 | typedef DWORD (*msh_jobs_thrd_proc_t)(void *params); 98 | 99 | #elif MSH_JOBS_PLATFORM_POSIX 100 | 101 | typedef sem_t msh_jobs_semaphore_t; 102 | typedef pthread_t msh_jobs_thread_t; 103 | #define MSH_JOBS_READ_WRITE_BARRIER() _mm_fence(); __asm__ volatile("" ::: "memory") 104 | #define MSH_JOBS_READ_BARRIER() _mm_rfence(); __asm__ volatile("" ::: "memory") 105 | #define MSH_JOBS_WRITE_BARRIER() _mm_sfence(); __asm__ volatile("" ::: "memory") 106 | typedef void* (*msh_jobs_thrd_proc_t)(void *params); 107 | 108 | #endif 109 | 110 | 111 | // NOTE(maciej): What other info about the processor we might want to have? 112 | typedef struct msh_jobs_processor_info 113 | { 114 | uint32_t logical_core_count; 115 | } msh_jobs_processor_info_t; 116 | 117 | typedef struct msh_jobs_job_entry 118 | { 119 | uint32_t (*task)(int32_t, void*); 120 | void* data; 121 | } msh_jobs_job_entry_t; 122 | 123 | typedef struct msh_jobs_work_queue 124 | { 125 | uint32_t volatile completion_count; 126 | uint32_t volatile completion_goal; 127 | uint32_t volatile next_entry_to_write; 128 | uint32_t volatile next_entry_to_read; 129 | 130 | uint32_t volatile max_job_count; 131 | msh_jobs_job_entry_t* entries; 132 | msh_jobs_semaphore_t semaphore_handle; 133 | } msh_jobs_work_queue_t; 134 | 135 | struct msh_jobs_thread_into; 136 | 137 | typedef struct msh_jobs_ctx 138 | { 139 | msh_jobs_processor_info_t processor_info; 140 | msh_jobs_work_queue_t queue; 141 | 142 | struct msh_jobs_thread_info* thread_infos; 143 | uint32_t thread_count; 144 | } msh_jobs_ctx_t; 145 | 146 | typedef struct msh_jobs_thread_info 147 | { 148 | msh_jobs_ctx_t *ctx; 149 | uint32_t idx; 150 | msh_jobs_thread_t handle; 151 | } msh_jobs_thread_info_t; 152 | 153 | 154 | char* msh_jobs_get_platform_name(); 155 | int32_t msh_jobs_get_processor_info( msh_jobs_processor_info_t* info ); 156 | int32_t msh_jobs_init_ctx( msh_jobs_ctx_t* ctx, uint32_t n_threads ); 157 | int32_t msh_jobs_push_work( msh_jobs_ctx_t* ctx, msh_jobs_job_signature_t task, void* data ); 158 | void msh_jobs_complete_all_work( msh_jobs_ctx_t* ctx ); 159 | void msh_jobs_term_ctx( msh_jobs_ctx_t* ctx ); 160 | 161 | // sew_stitches_and_wait(sewing, jobs, 10); //-> Nice api, PAss array of jobs and run 162 | // job structure 163 | 164 | #endif /* MSH_JOBS */ 165 | 166 | 167 | 168 | #ifdef MSH_JOBS_IMPLEMENTATION 169 | 170 | typedef enum msh_jobs_error_codes 171 | { 172 | MSH_JOBS_NO_ERR = 0, 173 | MSH_JOBS_FAILED_TO_CREATE_THREAD = 1, 174 | MSH_JOBS_FAILED_TO_CREATE_SEMAPHORE = 2, 175 | MSH_JOBS_OUT_OF_MEMORY = 3, 176 | } msh_jobs_error_codes_t; 177 | 178 | int32_t 179 | msh_jobs_thread_create( msh_jobs_thread_t* thread, msh_jobs_thrd_proc_t func, void* params ) 180 | { 181 | int32_t err = MSH_JOBS_NO_ERR; 182 | 183 | #if MSH_JOBS_PLATFORM_WINDOWS 184 | *thread = CreateThread( NULL, 0, func, params, 0, NULL ); 185 | if (*thread == NULL) { err = MSH_JOBS_FAILED_TO_CREATE_THREAD; } 186 | #else 187 | int32_t pthread_err = pthread_create( thread, NULL, func, params ); 188 | if (pthread_err) { err = MSH_JOBS_FAILED_TO_CREATE_THREAD; } 189 | #endif 190 | 191 | return err; 192 | } 193 | 194 | void 195 | msh_jobs_thread_detach( msh_jobs_thread_t *thread ) 196 | { 197 | #if MSH_JOBS_PLATFORM_WINDOWS 198 | CloseHandle( *thread ); 199 | #else 200 | pthread_detach( *thread ); 201 | #endif 202 | } 203 | 204 | int32_t 205 | msh_jobs_semaphore_create( msh_jobs_semaphore_t* sem, uint32_t initial_count, uint32_t maximum_count ) 206 | { 207 | int32_t err = MSH_JOBS_NO_ERR; 208 | #if MSH_JOBS_PLATFORM_WINDOWS 209 | *sem = CreateSemaphoreA( NULL, initial_count, maximum_count, NULL ); 210 | if (*sem == NULL) { err = MSH_JOBS_FAILED_TO_CREATE_SEMAPHORE; } 211 | #else 212 | (void)initial_count; 213 | (void)maximum_count; 214 | int32_t pthread_err = sem_init( sem, 0, 0 ); 215 | if (pthread_err) { err = MSH_JOBS_FAILED_TO_CREATE_SEMAPHORE; } 216 | #endif 217 | return err; 218 | } 219 | 220 | void 221 | msh_jobs_semaphore_destroy( msh_jobs_semaphore_t* sem ) 222 | { 223 | #if MSH_JOBS_PLATFORM_WINDOWS 224 | CloseHandle( *sem ); 225 | #else 226 | sem_destroy( sem ); 227 | #endif 228 | } 229 | 230 | void 231 | msh_jobs_semaphore_release( msh_jobs_semaphore_t* sem, int32_t release_count ) 232 | { 233 | #if MSH_JOBS_PLATFORM_WINDOWS 234 | ReleaseSemaphore( *sem, release_count, NULL ); 235 | #else 236 | (void)release_count; 237 | sem_post( sem ); 238 | #endif 239 | } 240 | 241 | void 242 | msh_jobs_semaphore_wait( msh_jobs_semaphore_t* sem ) 243 | { 244 | #if MSH_JOBS_PLATFORM_WINDOWS 245 | WaitForSingleObjectEx( *sem, INFINITE, FALSE ); 246 | #else 247 | sem_wait( sem ); 248 | #endif 249 | } 250 | 251 | uint32_t 252 | msh_jobs_atomic_add( uint32_t volatile *value, uint32_t val ) 253 | { 254 | #if MSH_JOBS_PLATFORM_WINDOWS 255 | return (uint32_t)InterlockedAdd( (LONG volatile*)value, val ); 256 | #else 257 | return (uint32_t)__sync_fetch_and_add( value, val ); 258 | #endif 259 | } 260 | 261 | uint32_t 262 | msh_jobs_atomic_increment( uint32_t volatile *value ) 263 | { 264 | #if MSH_JOBS_PLATFORM_WINDOWS 265 | return (uint32_t)InterlockedIncrement( (LONG volatile*)value ); 266 | #else 267 | return (uint32_t)__sync_fetch_and_add( value, 1 ); 268 | #endif 269 | } 270 | 271 | uint32_t 272 | msh_jobs_atomic_compare_exchange( uint32_t volatile *dest, uint32_t new_val, uint32_t old_val ) 273 | { 274 | #if MSH_JOBS_PLATFORM_WINDOWS 275 | return (uint32_t)InterlockedCompareExchange( (LONG volatile *)dest, new_val, old_val ); 276 | #else 277 | return (uint32_t)__sync_val_compare_and_swap( dest, old_val, new_val ); 278 | #endif 279 | } 280 | 281 | void 282 | msh_jobs__sleep(uint64_t ms) { 283 | #if MSH_JOBS_PLATFORM_WINDOWS 284 | Sleep((DWORD)ms); 285 | #elif MSH_JOBS_PLATFORM_POSIX 286 | usleep(1000 * ms); 287 | #endif 288 | } 289 | 290 | int32_t 291 | msh_jobs_push_work( msh_jobs_ctx_t* ctx, msh_jobs_job_signature_t task, void* data ) 292 | { 293 | msh_jobs_work_queue_t* queue = &ctx->queue; 294 | uint32_t next_entry_to_write = queue->next_entry_to_write; 295 | uint32_t new_next_entry_to_write = (next_entry_to_write + 1) % queue->max_job_count; 296 | while( new_next_entry_to_write == queue->next_entry_to_read ) { msh_jobs__sleep(1); };// Spin until we can write again 297 | msh_jobs_job_entry_t *job = queue->entries + next_entry_to_write; 298 | job->task = task; 299 | job->data = data; 300 | ++queue->completion_goal; 301 | MSH_JOBS_WRITE_BARRIER(); 302 | queue->next_entry_to_write = new_next_entry_to_write; 303 | msh_jobs_semaphore_release( &queue->semaphore_handle, 1 ); 304 | return MSH_JOBS_NO_ERR; 305 | } 306 | 307 | int32_t 308 | msh_jobs_execute_next_job_entry( int32_t thread_idx, msh_jobs_work_queue_t* queue ) 309 | { 310 | int32_t should_sleep = false; 311 | if (!queue->entries || 312 | queue->completion_goal == 0 || 313 | queue->max_job_count == 0) { return true; } 314 | 315 | uint32_t original_next_entry_to_read = queue->next_entry_to_read; 316 | uint32_t new_next_entry_to_read = (original_next_entry_to_read + 1) % queue->max_job_count; 317 | if( original_next_entry_to_read != queue->next_entry_to_write ) 318 | { 319 | // 'increment' queue->next_entry_to_read here 320 | uint32_t idx = msh_jobs_atomic_compare_exchange( &queue->next_entry_to_read, 321 | new_next_entry_to_read, 322 | original_next_entry_to_read ); 323 | if (idx == original_next_entry_to_read) 324 | { 325 | msh_jobs_job_entry_t* entry = queue->entries + idx; 326 | entry->task( thread_idx, entry->data ); 327 | msh_jobs_atomic_increment( &queue->completion_count ); 328 | } 329 | } 330 | else 331 | { 332 | should_sleep = true; 333 | } 334 | return should_sleep; 335 | } 336 | 337 | void 338 | msh_jobs_complete_all_work( msh_jobs_ctx_t* ctx ) 339 | { 340 | uint32_t thrd_idx = 0; 341 | if( !ctx->thread_infos || !ctx->queue.entries ) 342 | { 343 | return; 344 | } 345 | 346 | while( ctx->queue.completion_goal != ctx->queue.completion_count ) 347 | { 348 | msh_jobs_execute_next_job_entry( thrd_idx, &ctx->queue ); 349 | } 350 | 351 | ctx->queue.completion_goal = 0; 352 | ctx->queue.completion_count = 0; 353 | } 354 | 355 | #if MSH_JOBS_PLATFORM_WINDOWS 356 | DWORD WINAPI msh_jobs_thread_procedure(void *params) 357 | #else 358 | void* msh_jobs_thread_procedure(void* params ) 359 | #endif 360 | { 361 | msh_jobs_thread_info_t* ti = (msh_jobs_thread_info_t*)params; 362 | msh_jobs_ctx_t* ctx = ti->ctx; 363 | uint32_t thrd_idx = ti->idx; 364 | for(;;) 365 | { 366 | if( msh_jobs_execute_next_job_entry( thrd_idx, &ctx->queue ) ) 367 | { 368 | msh_jobs_semaphore_wait( &ctx->queue.semaphore_handle ); 369 | } 370 | } 371 | return 0; 372 | } 373 | 374 | int32_t 375 | msh_jobs__init_queue( msh_jobs_ctx_t* ctx, uint32_t queue_size ) 376 | { 377 | ctx->queue.max_job_count = queue_size; 378 | ctx->queue.completion_goal = 0; 379 | ctx->queue.completion_count = 0; 380 | ctx->queue.next_entry_to_read = 0; 381 | ctx->queue.next_entry_to_write = 0; 382 | ctx->queue.entries = (msh_jobs_job_entry_t*)malloc( ctx->queue.max_job_count * sizeof(msh_jobs_job_entry_t) ); 383 | if (!ctx->queue.entries) { return MSH_JOBS_OUT_OF_MEMORY; } 384 | 385 | return MSH_JOBS_NO_ERR; 386 | } 387 | 388 | 389 | int32_t 390 | msh_jobs__create_threads( msh_jobs_ctx_t* ctx, uint32_t n_threads ) 391 | { 392 | assert( ctx->processor_info.logical_core_count > 0 ); 393 | int32_t err = MSH_JOBS_NO_ERR; 394 | 395 | // Semaphore 396 | uint32_t initial_count = 0; 397 | ctx->thread_count = n_threads ? n_threads : ctx->processor_info.logical_core_count - 1; 398 | err = msh_jobs_semaphore_create( &ctx->queue.semaphore_handle, initial_count, ctx->thread_count ); 399 | if (err) { return err; } 400 | 401 | // Thread info 402 | ctx->thread_infos = (msh_jobs_thread_info_t*)malloc( ctx->thread_count * sizeof( msh_jobs_thread_info_t ) ); 403 | if (!ctx->thread_infos) { err = MSH_JOBS_OUT_OF_MEMORY; return err; } 404 | 405 | for( uint32_t thrd_idx = 0; thrd_idx < ctx->thread_count; ++thrd_idx ) 406 | { 407 | ctx->thread_infos[thrd_idx].idx = thrd_idx + 1; 408 | ctx->thread_infos[thrd_idx].ctx = ctx; 409 | err = msh_jobs_thread_create( &ctx->thread_infos[thrd_idx].handle, 410 | msh_jobs_thread_procedure, &ctx->thread_infos[thrd_idx] ); 411 | if (err) { return err; } 412 | } 413 | 414 | return err; 415 | } 416 | 417 | int32_t 418 | msh_jobs_init_ctx( msh_jobs_ctx_t* ctx, uint32_t n_threads ) 419 | { 420 | int32_t err = MSH_JOBS_NO_ERR; 421 | err = msh_jobs_get_processor_info( &ctx->processor_info ); 422 | if( err ) { return err; } 423 | 424 | err = msh_jobs__init_queue( ctx, MSH_JOBS_QUEUE_SIZE ); 425 | if( err ) { return err; } 426 | 427 | err = msh_jobs__create_threads( ctx, n_threads ); 428 | if( err ) { return err; } 429 | 430 | return err; 431 | } 432 | 433 | void 434 | msh_jobs_term_ctx( msh_jobs_ctx_t* ctx ) 435 | { 436 | msh_jobs_complete_all_work( ctx ); 437 | for( uint32_t i = 0; i < ctx->thread_count; ++i ) 438 | { 439 | msh_jobs_thread_detach( &ctx->thread_infos[i].handle ); 440 | } 441 | free( ctx->thread_infos ); 442 | ctx->thread_infos = NULL; 443 | ctx->queue.max_job_count = 0; 444 | ctx->queue.completion_goal = 0; 445 | ctx->queue.completion_count = 0; 446 | ctx->queue.next_entry_to_read = 0; 447 | ctx->queue.next_entry_to_write = 0; 448 | free( ctx->queue.entries ); 449 | ctx->queue.entries = NULL; 450 | msh_jobs_semaphore_release( &ctx->queue.semaphore_handle, ctx->thread_count ); 451 | msh_jobs_semaphore_destroy( &ctx->queue.semaphore_handle ); 452 | } 453 | 454 | char* 455 | msh_jobs_get_platform_name() 456 | { 457 | return MSH_JOBS_PLATFORM_NAME; 458 | } 459 | 460 | int32_t 461 | msh_jobs_get_processor_info( msh_jobs_processor_info_t* info ) 462 | { 463 | int32_t error = MSH_JOBS_NO_ERR; 464 | #if MSH_JOBS_PLATFORM_WINDOWS 465 | 466 | SYSTEM_INFO sysinfo; 467 | GetSystemInfo(&sysinfo); 468 | info->logical_core_count = sysinfo.dwNumberOfProcessors; 469 | 470 | #elif MSH_JOBS_PLATFORM_LINUX 471 | 472 | info->logical_core_count = sysconf( _SC_NPROCESSORS_ONLN ); 473 | goto msh_jobs_get_processor_info_exit; 474 | 475 | #elif MSH_JOBS_PLATFORM_MACOS 476 | 477 | #warning "NOT TESTED ON MACOS - ASSUME IT DOES NOT WORK!" 478 | size_t physical_count_len = sizeof(count); 479 | sysctlbyname("hw.logicalcpu", &info->logical_core_count, &logical_count_len, NULL, 0); 480 | 481 | #endif 482 | 483 | return error; 484 | } 485 | 486 | #endif /* MSH_JOBS_IMPLEMENTATION */ 487 | -------------------------------------------------------------------------------- /experimental/msh_mesh.h: -------------------------------------------------------------------------------- 1 | #ifndef MSH_SIMPLE_MESH 2 | #define MSH_SIMPLE_MESH 3 | 4 | #ifndef MSH_VEC_MATH 5 | #error "Please include msh_vec_math.h before including msh_mesh!" 6 | #endif 7 | 8 | typedef union msh_simple_mesh_triange 9 | { 10 | uint32_t ind[3]; 11 | struct { uint32_t i0; uint32_t i1; uint32_t i2; }; 12 | } msh_sm_tri_ind_t; 13 | 14 | typedef union msh_simple_mesh_rgba 15 | { 16 | uint8_t color[4]; 17 | struct { uint8_t r; uint8_t g; uint8_t b; uint8_t a; }; 18 | } msh_sm_rgba_t; 19 | 20 | typedef struct msh_simple_mesh 21 | { 22 | msh_vec3_t* positions; 23 | msh_vec3_t* normals; 24 | msh_sm_rgba_t* colors; 25 | 26 | uint32_t* indices; 27 | 28 | int32_t n_vertices; 29 | int32_t n_faces; 30 | } msh_simple_mesh_t; 31 | 32 | typedef enum msh_indexed_mesh_error_codes 33 | { 34 | MSH_SIMPLE_MESH_NO_ERR, 35 | MSH_SIMPLE_MESH_INIT_FAILURE_ERR, 36 | MSH_SIMPLE_MESH_MISSING_EXTENSION_ERR, 37 | MSH_SIMPLE_MESH_FORMAT_NOT_SUPPORTED_ERR, 38 | MSH_SIMPLE_MESH_PLY_MISSING_VERTEX_POSITION, 39 | MSH_SIMPLE_MESH_FILE_NOT_FOUND_ERR, 40 | MSH_SIMPLE_MESH_PLY_IN_ERR, 41 | MSH_SIMPLE_MESH_PLY_OUT_ERR 42 | } msh_simple_mesh_error_t; 43 | 44 | int32_t 45 | msh_simple_mesh_init( msh_simple_mesh_t* mesh, int32_t n_vertices, int32_t n_faces ) 46 | { 47 | mesh->n_vertices = n_vertices; 48 | mesh->n_faces = n_faces; 49 | mesh->positions = (msh_vec3_t*)malloc( n_vertices * sizeof(msh_vec3_t) ); 50 | mesh->normals = NULL; 51 | mesh->colors = NULL; 52 | mesh->indices = (uint32_t*)malloc( n_faces * 3 * sizeof(uint32_t) ); 53 | if (!mesh->positions || !mesh->indices ) { return MSH_SIMPLE_MESH_INIT_FAILURE_ERR; } 54 | return MSH_SIMPLE_MESH_NO_ERR; 55 | } 56 | 57 | void 58 | msh_simple_mesh_free( msh_simple_mesh_t* mesh ) 59 | { 60 | assert( mesh ); 61 | 62 | mesh->n_vertices = 0; 63 | mesh->n_faces = 0; 64 | free( mesh->positions ); 65 | free( mesh->normals ); 66 | free( mesh->indices ); 67 | } 68 | 69 | void 70 | msh_simple_mesh_compute_vertex_normals( msh_simple_mesh_t* mesh ) 71 | { 72 | assert( mesh ); 73 | assert( mesh->indices ); 74 | assert( mesh->positions ); 75 | free( mesh->normals ); 76 | mesh->normals = (msh_vec3_t*)malloc( mesh->n_vertices * sizeof(msh_vec3_t) ); 77 | double* normals_tmp = (double*)malloc( mesh->n_vertices * 4 * sizeof(double) ); 78 | 79 | memset(normals_tmp, 0, mesh->n_vertices*4*sizeof(double)); 80 | 81 | 82 | for (int32_t i = 0; i < mesh->n_faces; ++i) 83 | { 84 | uint32_t* ind = &mesh->indices[3*i]; 85 | msh_vec3_t p0 = mesh->positions[ ind[0] ]; 86 | msh_vec3_t p1 = mesh->positions[ ind[1] ]; 87 | msh_vec3_t p2 = mesh->positions[ ind[2] ]; 88 | 89 | msh_vec3_t v0 = msh_vec3_sub(p1, p0); 90 | msh_vec3_t v1 = msh_vec3_sub(p2, p0); 91 | 92 | msh_vec3_t n = msh_vec3_cross( v0, v1 ); 93 | 94 | normals_tmp[ 4 * ind[0] + 0 ] = normals_tmp[ 4 * ind[0] + 0 ] + n.x; 95 | normals_tmp[ 4 * ind[0] + 1 ] = normals_tmp[ 4 * ind[0] + 1 ] + n.y; 96 | normals_tmp[ 4 * ind[0] + 2 ] = normals_tmp[ 4 * ind[0] + 2 ] + n.z; 97 | 98 | normals_tmp[ 4 * ind[1] + 0 ] = normals_tmp[ 4 * ind[1] + 0 ] + n.x; 99 | normals_tmp[ 4 * ind[1] + 1 ] = normals_tmp[ 4 * ind[1] + 1 ] + n.y; 100 | normals_tmp[ 4 * ind[1] + 2 ] = normals_tmp[ 4 * ind[1] + 2 ] + n.z; 101 | 102 | normals_tmp[ 4 * ind[2] + 0 ] = normals_tmp[ 4 * ind[2] + 0 ] + n.x; 103 | normals_tmp[ 4 * ind[2] + 1 ] = normals_tmp[ 4 * ind[2] + 1 ] + n.y; 104 | normals_tmp[ 4 * ind[2] + 2 ] = normals_tmp[ 4 * ind[2] + 2 ] + n.z; 105 | 106 | normals_tmp[ 4 * ind[0] + 3] += 1.0; 107 | normals_tmp[ 4 * ind[1] + 3] += 1.0; 108 | normals_tmp[ 4 * ind[2] + 3] += 1.0; 109 | } 110 | 111 | for (size_t i = 0; i < mesh->n_vertices; ++i) 112 | { 113 | double weight = normals_tmp[ 4 * i + 3 ]; 114 | double x = normals_tmp[ 4 * i + 0 ] / weight; 115 | double y = normals_tmp[ 4 * i + 1 ] / weight; 116 | double z = normals_tmp[ 4 * i + 2 ] / weight; 117 | double inv_norm = 1.0 / sqrt(x*x + y*y + z*z); 118 | mesh->normals[i] = msh_vec3( (float)(x * inv_norm), (float)(y * inv_norm), (float)(z * inv_norm) ); 119 | float norm = msh_vec3_norm( mesh->normals[i] ); 120 | if (fabs(norm - 1.0) > 0.000001 ) { printf("WRONG LENGHT!\n"); } 121 | } 122 | 123 | free( normals_tmp ); 124 | } 125 | 126 | msh_vec3_t 127 | msh_simple_mesh_compute_centroid( const msh_simple_mesh_t *mesh ) 128 | { 129 | assert( mesh ); 130 | assert( mesh->positions ); 131 | 132 | double cx = 0.0; 133 | double cy = 0.0; 134 | double cz = 0.0; 135 | for (size_t i = 0; i < mesh->n_vertices; ++i) 136 | { 137 | cx = cx + mesh->positions[i].x; 138 | cy = cy + mesh->positions[i].y; 139 | cz = cz + mesh->positions[i].z; 140 | } 141 | cx /= (double)mesh->n_vertices; 142 | cy /= (double)mesh->n_vertices; 143 | cz /= (double)mesh->n_vertices; 144 | 145 | return msh_vec3((float)cx, (float)cy, (float)cz); 146 | } 147 | 148 | void 149 | msh_simple_mesh_compute_bbox( const msh_simple_mesh_t* mesh, msh_vec3_t* min_pt, msh_vec3_t* max_pt ) 150 | { 151 | assert( mesh ); 152 | assert( mesh->positions ); 153 | 154 | (*min_pt) = msh_vec3( FLT_MAX, FLT_MAX, FLT_MAX ); 155 | (*max_pt) = msh_vec3( -FLT_MAX, -FLT_MAX, -FLT_MAX ); 156 | for (size_t i = 0; i < mesh->n_vertices; ++i) 157 | { 158 | msh_vec3_t* p = &mesh->positions[i]; 159 | if (min_pt->x > p->x) { min_pt->x = p->x; } 160 | if (min_pt->y > p->y) { min_pt->y = p->y; } 161 | if (min_pt->x > p->z) { min_pt->x = p->z; } 162 | 163 | if (max_pt->x < p->x) { max_pt->x = p->x; } 164 | if (max_pt->y < p->y) { max_pt->y = p->y; } 165 | if (max_pt->x < p->z) { max_pt->x = p->z; } 166 | } 167 | } 168 | 169 | msh_mat4_t 170 | msh_simple_mesh_compute_normalizing_transform( const msh_simple_mesh_t* mesh ) 171 | { 172 | assert( mesh ); 173 | assert( mesh->positions ); 174 | 175 | msh_vec3_t centroid = msh_simple_mesh_compute_centroid( mesh ); 176 | msh_vec3_t t = msh_vec3_invert( centroid ); 177 | 178 | msh_vec3_t min_pt, max_pt, s; 179 | msh_simple_mesh_compute_bbox( mesh, &min_pt, &max_pt ); 180 | float sx = 1.0f / (max_pt.x - min_pt.x); 181 | float sy = 1.0f / (max_pt.y - min_pt.y); 182 | float sz = 1.0f / (max_pt.y - min_pt.y); 183 | float ms = fmaxf(fmaxf(sx, sy), sz); 184 | s = msh_vec3( ms, ms, ms ); 185 | 186 | msh_mat4_t xform = msh_mat4_identity(); 187 | xform = msh_post_scale( xform, s ); 188 | xform = msh_post_translate( xform, t ); 189 | 190 | return xform; 191 | } 192 | 193 | void 194 | msh_simple_mesh_translate( msh_simple_mesh_t* mesh, msh_vec3_t v ) 195 | { 196 | assert( mesh ); 197 | assert( mesh->positions ); 198 | 199 | for (size_t i = 0; i < mesh->n_vertices; ++i) 200 | { 201 | mesh->positions[i] = msh_vec3_add( mesh->positions[i], v ); 202 | } 203 | } 204 | 205 | void 206 | msh_simple_mesh_transform( msh_simple_mesh_t* mesh, msh_mat4_t mat ) 207 | { 208 | assert( mesh ); 209 | assert( mesh->positions ); 210 | 211 | msh_mat4_t normal_mat = msh_mat4_transpose( msh_mat4_inverse( mat ) ); 212 | for (size_t i = 0; i < mesh->n_vertices; ++i) 213 | { 214 | mesh->positions[i] = msh_mat4_vec3_mul( mat, mesh->positions[i], 1 ); 215 | if(mesh->normals) mesh->normals[i] = msh_vec3_normalize( msh_mat4_vec3_mul( normal_mat, mesh->normals[i], 0 ) ); 216 | } 217 | } 218 | 219 | void 220 | msh_simple_mesh_split_vertices( msh_simple_mesh_t* mesh ) 221 | { 222 | assert( mesh ); 223 | assert( mesh->positions ); 224 | assert( mesh->indices ); 225 | 226 | int32_t new_n_vertices = 3 * mesh->n_faces; 227 | msh_vec3_t* new_positions = NULL; 228 | msh_vec3_t* new_normals = NULL; 229 | msh_sm_rgba_t* new_colors = NULL; 230 | new_positions = (msh_vec3_t*)malloc( new_n_vertices * sizeof(msh_vec3_t) ); 231 | if (mesh->normals ) new_normals = (msh_vec3_t*)malloc( new_n_vertices * sizeof(msh_vec3_t) ); 232 | if (mesh->colors ) new_colors = (msh_sm_rgba_t*)malloc( new_n_vertices * sizeof(msh_sm_rgba_t) ); 233 | 234 | int32_t j = 0; 235 | for (int32_t i = 0; i < mesh->n_faces; ++i) 236 | { 237 | uint32_t* ind = &mesh->indices[ 3 * i ]; 238 | uint32_t i0 = ind[0]; 239 | uint32_t i1 = ind[1]; 240 | uint32_t i2 = ind[2]; 241 | 242 | new_positions[ j + 0 ] = mesh->positions[ i0 ]; 243 | new_positions[ j + 1 ] = mesh->positions[ i1 ]; 244 | new_positions[ j + 2 ] = mesh->positions[ i2 ]; 245 | 246 | if (mesh->normals ) 247 | { 248 | msh_vec3_t v1 = msh_vec3_sub(new_positions[j+1], new_positions[j+0]); 249 | msh_vec3_t v2 = msh_vec3_sub(new_positions[j+2], new_positions[j+0]); 250 | msh_vec3_t n = msh_vec3_normalize( msh_vec3_cross( v1, v2 ) ); 251 | new_normals[ j + 0 ] = n;//mesh->normals[ i0 ]; 252 | new_normals[ j + 1 ] = n;//mesh->normals[ i0 ]; 253 | new_normals[ j + 2 ] = n;//mesh->normals[ i0 ]; 254 | } 255 | 256 | if (mesh->colors ) 257 | { 258 | new_colors[ j + 0 ] = mesh->colors[ i0 ]; 259 | new_colors[ j + 1 ] = mesh->colors[ i1 ]; 260 | new_colors[ j + 2 ] = mesh->colors[ i2 ]; 261 | } 262 | 263 | ind[0] = j + 0; 264 | ind[1] = j + 1; 265 | ind[2] = j + 2; 266 | 267 | j += 3; 268 | } 269 | 270 | assert( j == new_n_vertices ); 271 | free( mesh->positions ); 272 | mesh->positions = new_positions; 273 | mesh->n_vertices = new_n_vertices; 274 | 275 | if (mesh->normals ) 276 | { 277 | free( mesh->normals ); 278 | mesh->normals = new_normals; 279 | } 280 | if (mesh->colors ) 281 | { 282 | free( mesh->colors ); 283 | mesh->colors = new_colors; 284 | } 285 | } 286 | 287 | 288 | #ifdef MSH_PLY 289 | #define MSH_SIMPLE_MESH__INIT_PLY_DESCRIPTORS() \ 290 | descriptors[0] = (msh_ply_desc_t){ .element_name = "vertex", \ 291 | .property_names = (const char*[]){"x", "y", "z"}, \ 292 | .num_properties = 3, \ 293 | .data = &mesh->positions, \ 294 | .data_count = &mesh->n_vertices, \ 295 | .data_type = MSH_PLY_FLOAT }; \ 296 | descriptors[1] = (msh_ply_desc_t){ .element_name = "vertex", \ 297 | .property_names = (const char*[]){"nx", "ny", "nz"}, \ 298 | .num_properties = 3, \ 299 | .data = &mesh->normals, \ 300 | .data_count = &mesh->n_vertices, \ 301 | .data_type = MSH_PLY_FLOAT }; \ 302 | descriptors[2] = (msh_ply_desc_t){ .element_name = "vertex", \ 303 | .property_names = (const char*[]){"red", "green", "blue", "alpha"}, \ 304 | .num_properties = 4, \ 305 | .data = &mesh->colors, \ 306 | .data_count = &mesh->n_vertices, \ 307 | .data_type = MSH_PLY_UINT8 }; \ 308 | descriptors[3] = (msh_ply_desc_t){ .element_name = "face", \ 309 | .property_names = (const char*[]){"vertex_indices"}, \ 310 | .num_properties = 1, \ 311 | .data_type = MSH_PLY_INT32, \ 312 | .list_type = MSH_PLY_UINT8, \ 313 | .data = &mesh->indices, \ 314 | .data_count = &mesh->n_faces, \ 315 | .list_size_hint = 3 }; 316 | #endif 317 | 318 | int32_t 319 | msh_simple_mesh_load_ply( msh_simple_mesh_t* mesh, const char* filename ) 320 | { 321 | assert( mesh ); 322 | assert( !mesh->indices ); 323 | assert( !mesh->positions ); 324 | assert( !mesh->normals ); 325 | assert( !mesh->colors ); 326 | 327 | #ifdef MSH_PLY 328 | msh_ply_desc_t descriptors[4] = {0}; 329 | MSH_SIMPLE_MESH__INIT_PLY_DESCRIPTORS(); 330 | msh_ply_desc_t* vpos_desc = &descriptors[0]; 331 | msh_ply_desc_t* vnor_desc = &descriptors[1]; 332 | msh_ply_desc_t* vclr_desc = &descriptors[2]; 333 | msh_ply_desc_t* find_desc = &descriptors[3]; 334 | 335 | int32_t msh_mesh_err = MSH_SIMPLE_MESH_NO_ERR; 336 | int32_t msh_ply_err = MSH_PLY_NO_ERR; 337 | bool need_to_calculate_normals = false; 338 | bool need_to_init_color = false; 339 | msh_ply_t* ply_file = msh_ply_open( filename, "rb" ); 340 | if (!ply_file ) 341 | { 342 | msh_mesh_err = MSH_SIMPLE_MESH_FILE_NOT_FOUND_ERR; 343 | return msh_mesh_err; 344 | } 345 | 346 | msh_ply_parse_header( ply_file ); 347 | 348 | if (!msh_ply_has_properties( ply_file, vpos_desc ) ) 349 | { 350 | msh_mesh_err = MSH_SIMPLE_MESH_PLY_MISSING_VERTEX_POSITION; 351 | goto ply_io_failure; 352 | } 353 | else 354 | { 355 | msh_ply_err = msh_ply_add_descriptor( ply_file, vpos_desc ); 356 | if (msh_ply_err) { goto ply_io_failure; } 357 | } 358 | 359 | if (!msh_ply_has_properties( ply_file, vnor_desc ) ) 360 | { 361 | need_to_calculate_normals = true; 362 | } 363 | else 364 | { 365 | msh_ply_err = msh_ply_add_descriptor( ply_file, vnor_desc ); 366 | if (msh_ply_err ) { goto ply_io_failure; } 367 | } 368 | 369 | if (!msh_ply_has_properties( ply_file, vclr_desc ) ) 370 | { 371 | need_to_init_color = true; 372 | } 373 | else 374 | { 375 | msh_ply_err = msh_ply_add_descriptor( ply_file, vclr_desc ); 376 | if (msh_ply_err ) { goto ply_io_failure; } 377 | } 378 | 379 | if (!msh_ply_has_properties( ply_file, find_desc ) ) 380 | { 381 | /* No face info - cancel vertex normal calculation */ 382 | need_to_calculate_normals = false; 383 | } 384 | else 385 | { 386 | msh_ply_err = msh_ply_add_descriptor( ply_file, find_desc ); 387 | if (msh_ply_err ) { goto ply_io_failure; } 388 | } 389 | 390 | msh_ply_err = msh_ply_read( ply_file ); 391 | 392 | if (need_to_calculate_normals ) 393 | { 394 | msh_simple_mesh_compute_vertex_normals( mesh ); 395 | } 396 | 397 | if (need_to_init_color ) 398 | { 399 | mesh->colors = (msh_sm_rgba_t*)malloc( mesh->n_vertices * sizeof(msh_sm_rgba_t) ); 400 | for( size_t i = 0; i < mesh->n_vertices; ++i) 401 | { 402 | mesh->colors[i] = (msh_sm_rgba_t){ .r = 255, .g = 255, .b = 255, .a = 255 }; 403 | } 404 | } 405 | 406 | ply_io_failure: 407 | if (msh_ply_err ) 408 | { 409 | msh_eprintf( "%s\n", msh_ply_error_msg( msh_ply_err ) ); 410 | msh_mesh_err = MSH_SIMPLE_MESH_PLY_IN_ERR; 411 | } 412 | msh_ply_close( ply_file ); 413 | return msh_mesh_err; 414 | #else 415 | return MSH_SIMPLE_MESH_FORMAT_NOT_SUPPORTED_ERR; 416 | #endif 417 | } 418 | 419 | int32_t 420 | msh_simple_mesh_write_ply( msh_simple_mesh_t* mesh, const char* filename ) 421 | { 422 | assert( mesh ); 423 | assert( mesh->positions ); 424 | 425 | 426 | #ifdef MSH_PLY 427 | msh_ply_desc_t descriptors[4] = {0}; 428 | MSH_SIMPLE_MESH__INIT_PLY_DESCRIPTORS(); 429 | msh_ply_desc_t* vpos_desc = &descriptors[0]; 430 | msh_ply_desc_t* vnor_desc = &descriptors[1]; 431 | msh_ply_desc_t* vclr_desc = &descriptors[2]; 432 | msh_ply_desc_t* find_desc = &descriptors[3]; 433 | int32_t msh_mesh_err = MSH_SIMPLE_MESH_NO_ERR; 434 | int32_t msh_ply_err = MSH_PLY_NO_ERR; 435 | 436 | msh_ply_t* ply_file = msh_ply_open( filename, "wb" ); 437 | if (!ply_file ) 438 | { 439 | msh_mesh_err = MSH_SIMPLE_MESH_FILE_NOT_FOUND_ERR; 440 | return msh_mesh_err; 441 | } 442 | 443 | msh_ply_err = msh_ply_add_descriptor( ply_file, vpos_desc ); 444 | if (msh_ply_err ) { goto ply_io_failure; } 445 | 446 | if (mesh->normals ) 447 | { 448 | msh_ply_err = msh_ply_add_descriptor( ply_file, vnor_desc ); 449 | if (msh_ply_err ) { goto ply_io_failure; } 450 | } 451 | 452 | if (mesh->colors ) 453 | { 454 | msh_ply_err = msh_ply_add_descriptor( ply_file, vclr_desc ); 455 | if (msh_ply_err ) { goto ply_io_failure; } 456 | } 457 | 458 | if (mesh->indices ) 459 | { 460 | msh_ply_err = msh_ply_add_descriptor( ply_file, find_desc ); 461 | if (msh_ply_err ) { goto ply_io_failure; } 462 | } 463 | 464 | msh_ply_err = msh_ply_write( ply_file ); 465 | 466 | ply_io_failure: 467 | if (msh_ply_err ) 468 | { 469 | msh_eprintf( "%s\n", msh_ply_error_msg( msh_ply_err ) ); 470 | msh_mesh_err = MSH_SIMPLE_MESH_PLY_OUT_ERR; 471 | } 472 | msh_ply_close( ply_file ); 473 | return msh_mesh_err; 474 | #else 475 | return MSH_SIMPLE_MESH_FORMAT_NOT_SUPPORTED_ERR; 476 | #endif 477 | } 478 | 479 | int32_t 480 | msh_simple_mesh_load_obj( ) 481 | { 482 | #ifdef MSH_OBJ 483 | return MSH_SIMPLE_MESH_NO_ERR; 484 | #else 485 | return MSH_SIMPLE_MESH_FORMAT_NOT_SUPPORTED_ERR; 486 | #endif 487 | } 488 | 489 | int32_t 490 | msh_simple_mesh_write_obj( ) 491 | { 492 | #ifdef MSH_OBJ 493 | return MSH_SIMPLE_MESH_NO_ERR; 494 | #else 495 | return MSH_SIMPLE_MESH_FORMAT_NOT_SUPPORTED_ERR; 496 | #endif 497 | } 498 | 499 | int32_t 500 | msh_simple_mesh_load( msh_simple_mesh_t* mesh, const char* filename ) 501 | { 502 | char* ext = strrchr( filename, '.' ); 503 | if (!ext ) { return MSH_SIMPLE_MESH_MISSING_EXTENSION_ERR; } 504 | 505 | int32_t err = MSH_SIMPLE_MESH_NO_ERR; 506 | if (!strcmp( ext, ".ply" ) ) { err = msh_simple_mesh_load_ply( mesh, filename ); } 507 | else if (!strcmp( ext, ".obj" ) ) { err = msh_simple_mesh_load_obj(); } 508 | else { err = MSH_SIMPLE_MESH_FORMAT_NOT_SUPPORTED_ERR; } 509 | 510 | return err; 511 | } 512 | 513 | int32_t 514 | msh_simple_mesh_write( msh_simple_mesh_t* mesh, const char* filename ) 515 | { 516 | char* ext = strrchr( filename, '.' ); 517 | if (!ext ) { return MSH_SIMPLE_MESH_MISSING_EXTENSION_ERR; } 518 | 519 | int32_t err = MSH_SIMPLE_MESH_NO_ERR; 520 | if (!strcmp( ext, ".ply" ) ) { err = msh_simple_mesh_write_ply( mesh, filename ); } 521 | else if (!strcmp( ext, ".obj" ) ) { err = msh_simple_mesh_write_obj(); } 522 | else { err = MSH_SIMPLE_MESH_FORMAT_NOT_SUPPORTED_ERR; } 523 | 524 | return err; 525 | } 526 | 527 | char* 528 | msh_simple_mesh_error_msg( int32_t error_code ) 529 | { 530 | switch( error_code ) 531 | { 532 | case MSH_SIMPLE_MESH_NO_ERR: 533 | return "MSH_SIMPLE_MESH: No Errors"; 534 | case MSH_SIMPLE_MESH_INIT_FAILURE_ERR: 535 | return "MSH_SIMPLE_MESH: Failed to initialize memory for indexed mesh"; 536 | case MSH_SIMPLE_MESH_MISSING_EXTENSION_ERR: 537 | return "MSH_SIMPLE_MESH: Provided filename is missing an extension"; 538 | case MSH_SIMPLE_MESH_FORMAT_NOT_SUPPORTED_ERR: 539 | return "MSH_SIMPLE_MESH: Provided format is not supported. Supported formats: .ply"; 540 | case MSH_SIMPLE_MESH_PLY_MISSING_VERTEX_POSITION: 541 | return "MSH_SIMPLE_MESH: Ply file is missing vertex data!"; 542 | case MSH_SIMPLE_MESH_FILE_NOT_FOUND_ERR: 543 | return "MSH_SIMPLE_MESH: Could not open mesh file!"; 544 | case MSH_SIMPLE_MESH_PLY_IN_ERR: 545 | return "MSH_SIMPLE_MESH: Issue reading ply file!"; 546 | case MSH_SIMPLE_MESH_PLY_OUT_ERR: 547 | return "MSH_SIMPLE_MESH: Issue writing ply file!"; 548 | default: 549 | return "No Errors"; 550 | } 551 | } 552 | 553 | 554 | #endif /*MSH_MESH*/ 555 | 556 | 557 | #ifdef MSH_SIMPLE_MESH_IMPLEMENTATION 558 | 559 | #endif -------------------------------------------------------------------------------- /experimental/msh_ml.h: -------------------------------------------------------------------------------- 1 | /* 2 | ============================================================================== 3 | 4 | MSH_MACHINE_LEARNING.H - WIP! 5 | 6 | A single header library for simple machine learning algorithms. 7 | 8 | To use the library you simply add: 9 | 10 | #define MSH_MACHINE_LEARNING_IMPLEMENTATION 11 | #include "msh_ml.h" 12 | 13 | The define should only include once in your source. If you need to include 14 | library in multiple places, simply use the include: 15 | 16 | #include "msh_ml.h" 17 | 18 | All functions can be made static by definining: 19 | 20 | #ifdef MSH_MACHINE_LEARNING_STATIC 21 | 22 | before including the "msh_ml.h" 23 | 24 | ============================================================================== 25 | DEPENDENCIES 26 | 27 | This library requires anonymous structs, which is a C11 extension. 28 | 29 | This library depends on following standard headers: 30 | 31 | TODO: Add dependencies list 32 | 33 | By default this library does not import these headers. Please see 34 | docs/no_headers.md for explanation. Importing heades is enabled by: 35 | 36 | #define MSH_MACHINE_LEARNING_INCLUDE_HEADERS 37 | 38 | ============================================================================== 39 | AUTHORS 40 | 41 | Maciej Halber (macikuh@gmail.com) 42 | 43 | ============================================================================== 44 | LICENSE 45 | The MIT License (MIT) 46 | 47 | Copyright (c) 2017 Maciej Halber 48 | 49 | Permission is hereby granted, free of charge, to any person obtaining a copy 50 | of this software and associated documentation files (the "Software"), to deal 51 | in the Software without restriction, including without limitation the rights 52 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 53 | copies of the Software, and to permit persons to whom the Software is 54 | furnished to do so, subject to the following conditions: 55 | 56 | The above copyright notice and this permission notice shall be included in all 57 | copies or substantial portions of the Software. 58 | 59 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 60 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 61 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 62 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 63 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 64 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 65 | SOFTWARE. 66 | 67 | 68 | ============================================================================== 69 | NOTES 70 | 71 | ============================================================================== 72 | TODOs 73 | [ ] clustering: k-means, mean-shift 74 | [ ] classifiers: decision trees, boosting? 75 | 76 | ============================================================================== 77 | REFERENCES: 78 | 79 | */ 80 | 81 | 82 | #ifndef MSH_MACHINE_LEARNING_H 83 | #define MSH_MACHINE_LEARNING_H 84 | 85 | #ifdef __cplusplus 86 | extern "C" { 87 | #endif 88 | 89 | #ifdef MSH_MACHINE_LEARNING_INCLUDE_HEADERS 90 | #include 91 | #include 92 | #include 93 | #include 94 | #endif 95 | 96 | #ifdef MSH_MACHINE_LEARNING_STATIC 97 | #define MSHMLDEF static 98 | #else 99 | #define MSHMLDEF extern 100 | #endif 101 | 102 | 103 | 104 | 105 | #ifdef __cplusplus 106 | } 107 | #endif 108 | 109 | #endif /*MSH_MACHINE_LEARNING_H*/ 110 | 111 | #ifdef MSH_MACHINE_LEARNING_IMPLEMENTATION 112 | 113 | 114 | #endif /*MSH_MACHINE_LEARNING_IMPLEMENTATION*/ -------------------------------------------------------------------------------- /experimental/msh_nm.c: -------------------------------------------------------------------------------- 1 | /* 2 | References: 3 | https://vismor.com/documents/network_analysis/matrix_algorithms/matrix_algorithms.php 4 | https://en.wikipedia.org/wiki/LU_decomposition 5 | Solomon Numerical Algorithms 6 | David Bindel Numerical Algorithms 7 | JAMA 8 | */ 9 | 10 | /* TODO(maciej): 11 | 12 | */ 13 | 14 | 15 | #define MSH_STD_INCLUDE_LIBC_HEADERS 16 | #define MSH_STD_IMPLEMENTATION 17 | #include "msh/msh_std.h" 18 | 19 | 20 | #if defined( MSH_NM_USE_FLOAT ) 21 | typedef float msh_nm_scalar_t; 22 | #else 23 | typedef double msh_nm_scalar_t; 24 | #endif 25 | 26 | typedef struct msh_nm_vector 27 | { 28 | size_t rows; 29 | msh_nm_scalar_t* data; 30 | } msh_nm_vec_t; 31 | 32 | // Implement this with stb trick for accessing data without referring to 'data' 33 | typedef struct msh_nm_matrix 34 | { 35 | size_t cols, rows; 36 | msh_nm_scalar_t* data; 37 | } msh_nm_mat_t; 38 | 39 | void msh_nm_mat_init( msh_nm_mat_t* mat, size_t cols, size_t rows ); 40 | void msh_nm_mat_free( msh_nm_mat_t* mat ); 41 | 42 | void msh_nm_mat_init( msh_nm_mat_t* mat, size_t cols, size_t rows ) 43 | { 44 | mat->cols = cols; 45 | mat->rows = rows; 46 | mat->data = (msh_nm_scalar_t*)malloc( cols*rows*sizeof(msh_nm_scalar_t) ); 47 | } 48 | 49 | void msh_nm_multiply( msh_nm_mat_t* A, msh_nm_mat_t* B, msh_nm_mat_t* C ) 50 | { 51 | } 52 | 53 | void msh_nm_transpose( msh_nm_mat_t *A ) 54 | { 55 | } 56 | 57 | void nm_print_matrixd( double* M, int rows, int cols ); 58 | void nm_print_matrixi( int* M, int rows, int cols ); 59 | // void msh_nm_lu_decompositon( msh_nm_mat_t *A, msh_nm_vec_t *p ) 60 | // { 61 | // 62 | // } 63 | 64 | // msh_nm_vec_t msh_nm_lu_solve(A, p, b) {}; 65 | 66 | // void msh_nm_solve( msh_nm_ctx_t ) <--- I think this is the final api. 67 | 68 | 69 | /*************************************************************************************************** 70 | LU Decomposition Notes: 71 | 72 | LU decomposition decomposes A into a lower triangular matrix L and upper triangular matrix U, such 73 | that: 74 | A = LU 75 | Solving the system Ax=b using LU factorization amounts solving two triangular systems 76 | A) Solve : Ly = b => y = L^(-1)b 77 | B) Solve : Ux = y => x = U^(-1)y 78 | 79 | x = U^(-1)y = U^(-1)L^(-1)b = (LU)^-1b = A^(-1)b 80 | 81 | Solving A) and B) can be done efficiently with forward and backward substitution respectively. 82 | 83 | There are two main algorithms for implementing LU facrotization: 84 | --> Gaussian Elimination 85 | --> Dolittle's/Crout's Algorithm 86 | 87 | Note that there is only a minor difference between Crout and Doolittle's algorithm. See 88 | (https://vismor.com/documents/network_analysis/matrix_algorithms/matrix_algorithms.php) for details. 89 | 90 | All these algorithm require that a_ii element is non-zero, hence the original problem is 91 | transformed to: 92 | PA = LU 93 | where P is a row-permutation matrix that ensures that all a_ii elements are non-zero. This 94 | is called partial pivoting. Partial pivoting requires that A is invertible. Full pivoting 95 | removes this requirement, but at the cost of speed. Full pivoting: 96 | PAQ = LU, 97 | where Q is column-permutation matrix. 98 | 99 | This library implements gaussian elimination, and Crout algorithm with implicit pivoting. 100 | 101 | Main difference between JAMA and NR implementation of Crout algorithm are: 102 | 1) JAMA provides nice implementation of crout algorithm, with nice copy optimization for column access 103 | 2) NR does do partial implicit pivoting... 104 | 105 | This library currently does not provide implementation of full pivoting. 106 | 107 | 108 | TODO(maciej): 109 | 1. Measure performance for all cases 110 | 2. How would one make implicit pivoting optional - seems like asymptotically implicit pivoting is not 111 | changing assymptotic behaviour, it does require another full pass through memory O(N) reads... 112 | 3. Measure best performin algorithm with and without pivoting. 113 | ***************************************************************************************************/ 114 | 115 | // I definietly need to try the aliasing thing that the numerical class is mentioning, especially 116 | // in c and cpp modes 117 | 118 | // In this whole exploration of the topic I think we are going to start with implementing helpers, 119 | // like general matrix multiplication etc. Then we will need to revise the code in my icp to check why 120 | // I even need the svd. Trimesh does not use it at all. It's just LDLT and eigen decompositions. 121 | 122 | 123 | 124 | // After Solomn, Figure 2.3. 125 | // Added implicit pivoting. 126 | void 127 | LU_factor_gauss_elimination( uint32_t m, uint32_t n, double* A, int* piv ) 128 | { 129 | for( int r = 0; r < n; ++r ) 130 | { 131 | piv[r] = r; 132 | } 133 | 134 | double* pivot = malloc(sizeof(double) * m); 135 | 136 | for (int i = 0; i < m; i++) 137 | { 138 | double big = 0; 139 | for (int j = 0; j < n; j++) 140 | { 141 | double tmp = fabs(A[i * n + j]); 142 | if (tmp > big) { big = tmp; } 143 | } 144 | if (big == 0) { return false; } 145 | pivot[i] = 1.0 / big; 146 | } 147 | 148 | for( int p = 0; p < min(m - 1, n); ++p ) 149 | { 150 | // find best row. 151 | int pmax = p; 152 | double max = 0.0; 153 | for( int r = p; r < n; ++r ) 154 | { 155 | double tmp = pivot[r] * fabs(A[r*n+p]); 156 | if( tmp > max ) 157 | { 158 | max = tmp; 159 | pmax = r; 160 | } 161 | } 162 | // Preserve denominator of max element 163 | double denom = 1.0 / A[pmax*n+p]; 164 | 165 | // permute rows 166 | if( pmax != p ) 167 | { 168 | piv[p] = pmax; 169 | pivot[pmax] = pivot[p]; 170 | for( int c = 0; c < m; ++c ) 171 | { 172 | double Atmp = A[p*n+c]; 173 | A[p*n+c] = A[pmax*n+c]; 174 | A[pmax*n+c] = Atmp; 175 | } 176 | } 177 | 178 | // Perform gaussian elimnation 179 | for( int r = p+1; r < m; ++r ) 180 | { 181 | double s = A[r*n+p] * denom; 182 | A[r*n+p] = s; 183 | for( int c = p+1; c < n; ++c ) 184 | { 185 | A[r*n+c] -= s * A[p*n+c]; 186 | } 187 | } 188 | } 189 | free(pivot); 190 | } 191 | 192 | bool 193 | LU_factor_nr( uint32_t m, uint32_t N, double* A, int* ind ) 194 | { 195 | double* pivot = malloc(sizeof(double) * N); 196 | 197 | for (int i = 0; i < N; i++) 198 | { 199 | double big = 0; 200 | for (int j = 0; j < N; j++) 201 | { 202 | double tmp = fabs(A[i * N + j]); 203 | if (tmp > big) { big = tmp; } 204 | } 205 | if (big == 0) { return false; } 206 | pivot[i] = 1.0 / big; 207 | } 208 | printf("%f %f %f\n", pivot[0], pivot[1], pivot[2]); 209 | 210 | for (int j = 0; j < N; j++) 211 | { 212 | for (int i = 0; i < j; i++) 213 | { 214 | double sum = A[i * N + j]; 215 | for (int k = 0; k < i; k++) 216 | sum -= A[i * N + k] * A[k * N + j]; 217 | A[i * N + j] = sum; 218 | } 219 | 220 | double big = 0; 221 | int imax = j; 222 | for (int i = j; i < N; i++) { 223 | double sum = A[i * N + j]; 224 | for (int k = 0; k < j; k++) 225 | sum -= A[i * N + k] * A[k * N + j]; 226 | A[i * N + j] = sum; 227 | double tmp = pivot[i] * fabs(sum); 228 | if (tmp > big) { 229 | big = tmp; 230 | imax = i; 231 | } 232 | } 233 | 234 | if (imax != j) { 235 | for (int k = 0; k < N; k++) 236 | { 237 | double tmp = A[imax * N + k]; 238 | A[imax * N + k] = A[j * N + k]; 239 | A[j * N + k] = tmp; 240 | } 241 | pivot[imax] = pivot[j]; 242 | } 243 | ind[j] = imax; 244 | 245 | if( A[j * N + j] == 0 ) 246 | return false; 247 | 248 | if (j != N - 1) 249 | { 250 | double tmp = 1 / A[j * N + j]; 251 | for (int i = j + 1; i < N; i++) 252 | A[i * N + j] *= tmp; 253 | } 254 | } 255 | 256 | free( pivot ); 257 | return true; 258 | } 259 | 260 | // A clever implementation of Dolittle/Crout from JAMA. It combines the 2 for loops b noticing 261 | // that the only difference there is the division of by the scaling element, which is deffered 262 | // till after dot product is computed. NR is the same, but it cannot split the loops, as it 263 | // needs to do implicit pivoting in the middle. 264 | // JAMA also has nice column copy optimization. 265 | 266 | // Now this also has implicit pivoting. Cool. 267 | void 268 | LU_factor_jama( uint32_t m, uint32_t n, double* A, int* piv ) 269 | { 270 | for (int i = 0; i < m; i++) { 271 | piv[i] = i; 272 | } 273 | 274 | double* LUrowi; 275 | double* LUcolj = malloc( m * sizeof(double) ); 276 | double* pivot = malloc(sizeof(double) * m); 277 | for (int i = 0; i < m; i++) 278 | { 279 | double big = 0; 280 | for (int j = 0; j < n; j++) 281 | { 282 | double tmp = fabs(A[i * n + j]); 283 | if (tmp > big) { big = tmp; } 284 | } 285 | if (big == 0) { return false; } 286 | pivot[i] = 1.0 / big; 287 | } 288 | printf("%f %f %f\n", pivot[0], pivot[1], pivot[2]); 289 | 290 | // Outer loop. 291 | for (int j = 0; j < n; j++) { 292 | 293 | // Make a copy of the j-th column to localize references. 294 | for (int i = 0; i < m; i++) { 295 | LUcolj[i] = A[ i*n +j ]; 296 | } 297 | 298 | // Apply previous transformations. 299 | 300 | for (int i = 0; i < m; i++) { 301 | LUrowi = &A[i*n]; 302 | // printf("TEST: %d %d\n", i, j); 303 | // printf("A : %f %f %f\n", LUcolj[0], LUcolj[1], LUcolj[2]); 304 | // printf("B : %f %f %f\n", LUrowi[0], LUrowi[1], LUrowi[2]); 305 | 306 | // Most of the time is spent in the following dot product. 307 | 308 | int kmax = min(i,j); 309 | double s = 0.0; 310 | for (int k = 0; k < kmax; k++) { 311 | s += LUrowi[k]*LUcolj[k]; 312 | } 313 | 314 | LUrowi[j] -= s; 315 | LUcolj[i] -= s; 316 | // printf("S: %f | kmax: %d\n", s, kmax ); 317 | // printf("A : %f %f %f\n", LUcolj[0], LUcolj[1], LUcolj[2]); 318 | // printf("B : %f %f %f\n", LUrowi[0], LUrowi[1], LUrowi[2]); 319 | // printf("----\n"); 320 | 321 | } 322 | 323 | // Find pivot and exchange if necessary. 324 | printf("===========\n"); 325 | int p = j; 326 | double pmax = 0; 327 | for (int i = j; i < m; i++) { 328 | double tmp = pivot[i] * fabs(LUcolj[i]); 329 | printf(" %f %f %f\n", tmp, pivot[i], fabs(LUcolj[i])); 330 | if( tmp > pmax ) { 331 | p = i; 332 | pmax = tmp; 333 | } 334 | } 335 | 336 | if (p != j) { 337 | for (int k = 0; k < n; k++) { 338 | double t = A[p*n+k]; A[p*n+k] = A[j*n+k]; A[j*n+k] = t; 339 | } 340 | piv[j] = p; 341 | pivot[p] = pivot[j]; 342 | } 343 | 344 | nm_print_matrixd(A, 3, 3); 345 | 346 | // Compute multipliers. 347 | if (j < m && A[j*n+j] != 0.0) { 348 | double denom = 1.0 / A[j*n+j]; 349 | for (int i = j+1; i < m; i++) { 350 | A[i*n+j] *= denom; 351 | } 352 | } 353 | nm_print_matrixd(A, 3, 3); 354 | } 355 | } 356 | 357 | 358 | // TODO(maciej): Pivoting 359 | void 360 | LU_solve( uint32_t m, uint32_t n, double *A, int* piv, double* b ) 361 | { 362 | // forward 363 | for( uint32_t i = 0; i < n; ++i ) 364 | { 365 | int ip = piv[i]; 366 | double alpha = b[ip]; 367 | b[ip] = b[i]; 368 | for( uint32_t j = 0; j < i; ++j ) 369 | { 370 | alpha -= A[i * n + j]*b[j]; 371 | } 372 | b[i] = alpha; 373 | } 374 | 375 | // backward 376 | for( int32_t i = n-1; i >= 0; --i ) 377 | { 378 | double alpha = b[i]; 379 | for( uint32_t j = i+1; j < n; ++j ) 380 | { 381 | alpha -= A[i*n + j]*b[j]; 382 | } 383 | b[i] = alpha / A[i*n + i]; 384 | } 385 | } 386 | 387 | // Check : https://github.com/ldfaiztt/cs267-dgemm/tree/master/src 388 | // Check : https://github.com/ytsutano/dgemm-goto-in-c // not super fast 389 | // Check : https://github.com/cappachu/dgemm // bit faster, but requires sse4 390 | 391 | // Scalar replacement 392 | void mmm1( const int N, const int M, const int P, 393 | const double* restrict A, const double* restrict B, double* restrict C ) 394 | { 395 | int32_t i, j, k; 396 | for( i = 0; i < N; i++) 397 | { 398 | for(j = 0; j < M; j++) 399 | { 400 | double sum = 0; 401 | for(k=0; k < P; k++) 402 | { 403 | sum += A[i*P+k] * B[k*M+j]; 404 | } 405 | C[i*M+j] += sum; 406 | } 407 | } 408 | } 409 | 410 | // Based on: https://www.akkadia.org/drepper/cpumemory.pdf 411 | void mmm3( const int N, const int M, const int P, double* restrict A, double* restrict B, double* restrict C ) 412 | { 413 | uint32_t i, i2, j, j2, k, k2; 414 | uint32_t SM = 64; 415 | double *restrict rres; 416 | double *restrict rmul1; 417 | double *restrict rmul2; 418 | for (i = 0; i < N; i += SM) 419 | { 420 | uint32_t i2lim = min( SM, N-i ); 421 | for (j = 0; j < M; j += SM) 422 | { 423 | uint32_t j2lim = min( SM, M-j ); 424 | for (k = 0; k < P; k += SM) 425 | { 426 | uint32_t k2lim = min( SM, P-k ); 427 | for (i2 = 0, rres = &C[i*M+j], rmul1 = &A[i*P+k]; i2 < i2lim; ++i2, rres += M, rmul1 += P) 428 | { 429 | for (k2 = 0, rmul2 = &B[k*M+j]; k2 < k2lim; ++k2, rmul2 += M) 430 | { 431 | for (j2 = 0; j2 < j2lim; ++j2) 432 | { 433 | rres[j2] += rmul1[k2] * rmul2[j2]; 434 | } 435 | } 436 | } 437 | } 438 | } 439 | } 440 | } 441 | 442 | void measure_mmm1( const int n_iter, const int N, const double* A, const double* B, double* C ) 443 | { 444 | uint64_t t1, t2; 445 | 446 | mmm1( N, N, N, A, B, C ); 447 | for( int i = 0; i < N*N; ++i ) 448 | { 449 | C[i] = 0; 450 | } 451 | t1 = msh_time_now(); 452 | for( int i = 0; i < n_iter; ++i ) mmm1(N, N, N, A, B, C); 453 | t2 = msh_time_now(); 454 | printf("Time taken by %s is %12.7fms\n", __FUNCTION__, msh_time_diff(MSHT_MILLISECONDS, t2, t1)/n_iter ); 455 | } 456 | 457 | void measure_mmm3( const int n_iter, const int N, double* A, double* B, double* C ) 458 | { 459 | uint64_t t1, t2; 460 | 461 | mmm3( N, N, N, A, B, C ); 462 | for( int i = 0; i < N*N; ++i ) 463 | { 464 | C[i] = 0; 465 | } 466 | t1 = msh_time_now(); 467 | for( int i = 0; i < n_iter; ++i ) mmm3(N, N, N, A, B, C); 468 | t2 = msh_time_now(); 469 | printf("Time taken by %s is %12.7fms\n", __FUNCTION__, msh_time_diff(MSHT_MILLISECONDS, t2, t1)/n_iter ); 470 | } 471 | 472 | void nm_print_matrixd( double* M, int rows, int cols ) 473 | { 474 | /* we interpret rows as columns, to get column major ordering in c */ 475 | for( int y = 0; y < rows; ++y ) 476 | { 477 | for( int x = 0; x < cols; ++x ) 478 | { 479 | printf("%8.3f ", M[ y * cols + x ] ); 480 | } 481 | printf("\n"); 482 | } 483 | printf("\n"); 484 | } 485 | 486 | void nm_print_matrixi( int* M, int rows, int cols ) 487 | { 488 | /* we interpret rows as columns, to get column major ordering in c */ 489 | for( int y = 0; y < rows; ++y ) 490 | { 491 | for( int x = 0; x < cols; ++x ) 492 | { 493 | printf("%8d ", M[ y * cols + x ] ); 494 | } 495 | printf("\n"); 496 | } 497 | printf("\n"); 498 | } 499 | 500 | int main( int argc, char** argv ) 501 | { 502 | #if 0 503 | enum{ N_SIZES = 256 }; 504 | int32_t sizes[N_SIZES] = { 0 }; 505 | for( int i = 0; i < 256; ++i ) 506 | { 507 | sizes[i]= i+2; 508 | } 509 | 510 | for( int32_t i = 0; i < N_SIZES; ++i ) 511 | { 512 | int32_t N = sizes[i]; 513 | double* A = malloc( N*N*sizeof(double) ); 514 | double* B = malloc( N*N*sizeof(double) ); 515 | double* C = malloc( N*N*sizeof(double) ); 516 | for( int i = 0; i < N*N; ++i ) 517 | { 518 | A[i] = i; 519 | B[i] = i; 520 | } 521 | 522 | printf("Size: %d\n", N ); 523 | measure_mmm1( 10, N, A, B, C ); 524 | measure_mmm3( 10, N, A, B, C ); 525 | free(A); 526 | free(B); 527 | free(C); 528 | } 529 | #endif 530 | 531 | #if 0 532 | double A[12] = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12 }; 533 | double B[12] = { 1, 0, 0, 0, 0, 0, 1, 0, 0, 1, 0, 0 }; 534 | double C[16] = { 0 }; 535 | mmm3( 4, 4, 3, A, B, C ); 536 | nm_print_matrix( &A[0], 4, 3 ); 537 | nm_print_matrix( &B[0], 3, 4 ); 538 | nm_print_matrix( &C[0], 4, 4 ); 539 | printf("TEST\n"); 540 | #endif 541 | 542 | // double A[9] = { 6, 1, 4, 1, 2, 7, 3, 5, -1 }; 543 | // double A[9] = { 16, 4, 6, 21, 5, 72, 4, 9, 8 }; 544 | double A[9] = { 3, 5, 7, 2, 8, 1, 2, 1, 3 }; 545 | int piv[3] = {0, 1, 2}; 546 | nm_print_matrixd( &A[0], 3, 3 ); 547 | uint64_t t1, t2; 548 | t1 = msh_time_now(); 549 | LU_factor_gauss_elimination( 3, 3, &A[0], &piv[0] ); 550 | t2 = msh_time_now(); 551 | double elapsed = msh_time_diff(MSHT_MILLISECONDS, t2, t1); 552 | printf("Factorization time: %fms\n", elapsed); 553 | nm_print_matrixd( &A[0], 3, 3 ); 554 | nm_print_matrixi( &piv[0], 1, 3 ); 555 | double b[3] = { 52, 34, 21 }; 556 | LU_solve( 3, 3, &A[0], &piv[0], &b[0] ); 557 | 558 | printf("%f %f %f\n", b[0], b[1], b[2]); 559 | 560 | // double B[12] = { 6, 1, 4, 3, 5, -1, 4, 5, 8, 1, 2, 7 }; 561 | // nm_print_matrix( &B[0], 4, 3 ); 562 | // LU_gauss_elimination( 4, 3, &B[0] ); 563 | // nm_print_matrix( &B[0], 4, 3 ); 564 | 565 | } 566 | 567 | -------------------------------------------------------------------------------- /experimental/numpy_nm.py: -------------------------------------------------------------------------------- 1 | import numpy as np 2 | import timeit 3 | 4 | # print("Hello, World! SVD Example!"); 5 | # a = np.array([ [ 8.790, 9.930, 9.830, 5.450, 3.160], 6 | # [ 6.110, 6.910, 5.040,-0.270, 7.980], 7 | # [-9.150,-7.930, 4.860, 4.850, 3.010], 8 | # [ 9.570, 1.640, 8.830, 0.740, 5.800], 9 | # [-3.490, 4.020, 9.800,10.000, 4.270], 10 | # [ 9.840, 0.150,-8.990,-6.020,-5.310] ] ) 11 | 12 | # U, s, Vt = np.linalg.svd( a, full_matrices=True ) 13 | # S = np.zeros((6,5)) 14 | # np.fill_diagonal(S, s) 15 | # rec_a = np.dot( np.dot( U, S ), Vt ) 16 | # print(U) 17 | # print(S) 18 | # print(Vt) 19 | 20 | # A = np.array( [ [ 1, 4, 7, 10], 21 | # [ 2, 5, 8, 11], 22 | # [ 3, 6, 9, 12] ] ) 23 | # B = np.array( [ [1, 5], 24 | # [2, 6], 25 | # [3, 7], 26 | # [4, 8] ] ) 27 | # A = np.array( [ [1, 2, 3, 4], [5, 6, 7, 8], [9, 10, 11, 12], [13, 14, 15, 16] ] ) 28 | # B = np.array( [ [1, 2, 3, 4], [5, 6, 7, 8], [9, 10, 11, 12], [13, 14, 15, 16] ] ) 29 | # A = np.array( [ [1,1,1,1], [1,1,1,1], [1,1,1,1], [1,1,1,1] ] ) 30 | # B = np.array( [ [1,1,1,1], [1,1,1,1], [1,1,1,1], [1,1,1,1] ] ) 31 | 32 | # C = np.dot(A, B) 33 | # print(C) 34 | # print( np.allclose(a, rec_a) ) 35 | # print(a.shape, u.shape, s.shape, vt.shape) 36 | 37 | N = 1024 38 | A = np.arange(N*N, dtype=np.float64).reshape((N, N)) 39 | B = np.arange(N*N, dtype=np.float64).reshape((N, N)) 40 | tic = timeit.default_timer() 41 | C = np.dot(A,B) 42 | toc = timeit.default_timer() 43 | print(A[0][0], A[0, N-1], A[N-1, 0], A[N-1,N-1]) 44 | print(B[0][0], B[0, N-1], B[N-1, 0], B[N-1,N-1]) 45 | print(C[0][0], C[0, N-1], C[N-1, 0], C[N-1,N-1]) 46 | print((toc-tic)*1000) 47 | # print(C) -------------------------------------------------------------------------------- /msh_camera.h: -------------------------------------------------------------------------------- 1 | /* 2 | ============================================================================== 3 | Licensing information can be found at the end of the file. 4 | ============================================================================== 5 | 6 | MSH_CAMERA.H - v0.65 7 | 8 | A single header library for camera manipulation 9 | 10 | To use the library you simply add: 11 | 12 | #define MSH_CAMERA_IMPLEMENTATION 13 | #include "msh_cam.h" 14 | 15 | ============================================================================== 16 | API DOCUMENTATION 17 | 18 | ============================================================================== 19 | TODOs: 20 | [x] Implement camera speeds 21 | [ ] Euler angle alternative? 22 | [ ] API docs 23 | [ ] Custom inverse function for projection and view matrices 24 | [ ] First person camera controls 25 | 26 | ============================================================================== 27 | AUTHORS 28 | Maciej Halber (macikuh@gmail.com) 29 | 30 | ============================================================================== 31 | DEPENDENCES 32 | 33 | This library depends on following standard headers: 34 | 35 | 36 | 37 | 38 | By default this library does not import these headers. Please see 39 | docs/no_headers.md for explanation. Importing heades is enabled by: 40 | 41 | #define MSH_CAMERA_INCLUDE_LIBC_HEADERS 42 | 43 | Additionally, currently it also depends on on msh_vec_math.h for vector math 44 | 45 | ============================================================================== 46 | REFERENCES: 47 | [1] nlguillemot/arcball_camera.h https://github.com/nlguillemot/arcball_camera/blob/master/arcball_camera.h 48 | [2] nlguillemot/flythrough_camera.h https://github.com/nlguillemot/flythrough_camera/blob/master/flythrough_camera.h 49 | [3] vurtun/camera.c https://gist.github.com/vurtun/d41914c00b6608da3f6a73373b9533e5 50 | [4] Euler/Quaternion https://gamedev.stackexchange.com/questions/13436/glm-euler-angles-to-quaternion 51 | */ 52 | 53 | #ifndef MSH_CAMERA 54 | #define MSH_CAMERA 55 | 56 | #ifndef MSH_VEC_MATH 57 | #error "Please include msh_vec_math first!" 58 | #endif 59 | 60 | #ifdef __cplusplus 61 | extern "C" { 62 | #endif 63 | 64 | #ifdef MSH_CAMERA_INCLUDE_LIBC_HEADERS 65 | #include 66 | #include 67 | #include 68 | #endif 69 | 70 | #ifndef MSH_PI 71 | #define MSH_PI \ 72 | 3.14159265358979323846264338327950288419716939937510582097494459231 73 | #endif 74 | 75 | #ifdef MSH_CAMERA_STATIC 76 | #define MSHCAMDEF static 77 | #else 78 | #define MSHCAMDEF extern 79 | #endif 80 | 81 | typedef struct msh_camera_desc 82 | { 83 | msh_vec3_t eye; 84 | msh_vec3_t center; 85 | msh_vec3_t up; 86 | 87 | msh_vec4_t viewport; 88 | float fovy; 89 | float znear; 90 | float zfar; 91 | unsigned char use_ortho; 92 | 93 | float pan_speed; 94 | float zoom_speed; 95 | float rot_speed; 96 | } msh_camera_desc_t; 97 | 98 | typedef struct msh_camera 99 | { 100 | /* View Matrix Params */ 101 | msh_vec3_t origin; 102 | msh_vec3_t offset; 103 | msh_quat_t orientation; 104 | 105 | /* Projection Matrix Params */ 106 | msh_vec4_t viewport; 107 | float znear, zfar; 108 | float fovy; 109 | bool use_ortho; 110 | 111 | /* Options */ 112 | float pan_speed; 113 | float zoom_speed; 114 | float rot_speed; 115 | 116 | /* Generated */ 117 | msh_vec3_t location; 118 | msh_mat4_t view; 119 | msh_mat4_t proj; 120 | } msh_camera_t; 121 | 122 | void msh_camera_init(msh_camera_t* cam, msh_camera_desc_t* desc); 123 | 124 | void 125 | msh_camera_rotate(msh_camera_t* cam, msh_vec2_t prev_pos, msh_vec2_t curr_pos); 126 | void 127 | msh_camera_pan(msh_camera_t* cam, msh_vec2_t prev_pos, msh_vec2_t curr_pos); 128 | 129 | void msh_camera_zoom(msh_camera_t* cam, float zoom_amount); 130 | 131 | void msh_camera_move(msh_camera_t* cam, msh_vec3_t translation); 132 | 133 | void msh_camera_update_view(msh_camera_t* cam); 134 | 135 | void msh_camera_update_proj(msh_camera_t* cam); 136 | 137 | void msh_camera_update(msh_camera_t* cam); 138 | 139 | void msh_camera_ray_through_pixel(msh_camera_t* camera, 140 | msh_vec2_t p, 141 | msh_vec3_t* origin, 142 | msh_vec3_t* direction); 143 | 144 | #ifdef __cplusplus 145 | } 146 | #endif 147 | 148 | #endif /*MSH_CAMERA_H*/ 149 | 150 | #ifdef MSH_CAMERA_IMPLEMENTATION 151 | 152 | void 153 | msh_camera_pan(msh_camera_t* cam, msh_vec2_t prev_pos, msh_vec2_t curr_pos) 154 | { 155 | float h = cam->viewport.w - cam->viewport.y; 156 | float x0 = prev_pos.x; 157 | float y0 = h - prev_pos.y; 158 | float x1 = curr_pos.x; 159 | float y1 = h - curr_pos.y; 160 | 161 | float dx = (x0 - x1) * cam->pan_speed; 162 | float dy = (y0 - y1) * cam->pan_speed; 163 | 164 | if (fabs(dx) < 1 && fabs(dy) < 1) { return; } 165 | 166 | msh_mat3_t orient = msh_quat_to_mat3(cam->orientation); 167 | msh_vec3_t u = msh_vec3_scalar_mul(orient.col[0], dx * 0.005f); 168 | msh_vec3_t v = msh_vec3_scalar_mul(orient.col[1], dy * 0.005f); 169 | 170 | cam->origin = msh_vec3_add(cam->origin, u); 171 | cam->origin = msh_vec3_add(cam->origin, v); 172 | } 173 | 174 | void 175 | msh_camera_zoom(msh_camera_t* cam, float zoom_amount) 176 | { 177 | float norm = msh_vec3_norm(cam->offset); 178 | float zoom_factor = (norm <= 2.0) ? 0.5f * norm : 1.0f; 179 | msh_vec3_t zoom_dir = msh_vec3_scalar_div(cam->offset, norm); 180 | float zoom_mult = cam->zoom_speed * zoom_amount * zoom_factor * zoom_factor; 181 | msh_vec3_t zoom_vec = msh_vec3_scalar_mul(zoom_dir, zoom_mult); 182 | cam->offset = msh_vec3_add(cam->offset, zoom_vec); 183 | } 184 | 185 | void 186 | msh_camera_move(msh_camera_t* cam, msh_vec3_t translation) 187 | { 188 | cam->origin = msh_vec3_add(cam->origin, translation); 189 | } 190 | 191 | void 192 | msh_camera_rotate(msh_camera_t* cam, msh_vec2_t prev_pos, msh_vec2_t curr_pos) 193 | { 194 | float w = cam->viewport.z - cam->viewport.x; 195 | float h = cam->viewport.w - cam->viewport.y; 196 | float r = (w > h) ? h : w; 197 | 198 | float x0 = prev_pos.x; 199 | float y0 = h - prev_pos.y; 200 | float x1 = curr_pos.x; 201 | float y1 = h - curr_pos.y; 202 | 203 | float dx = (x1 - x0); 204 | float dy = (y1 - y0); 205 | 206 | if (fabs(dx) < 1 && fabs(dy) < 1) { return; } 207 | 208 | msh_vec3_t p0 = msh_vec3((x0 - w * 0.5f) / r, (y0 - h * 0.5f) / r, 0.0f); 209 | p0 = msh_vec3_scalar_mul(p0, cam->rot_speed); 210 | float l0_sq = p0.x * p0.x + p0.y * p0.y; 211 | if (l0_sq > (0.5 * cam->rot_speed)) 212 | { 213 | p0.z = (0.5f * cam->rot_speed) / sqrtf(l0_sq); 214 | } 215 | else 216 | { 217 | p0.z = sqrtf((1.0f * cam->rot_speed) - l0_sq); 218 | } 219 | p0 = msh_vec3_normalize(p0); 220 | msh_vec3_t p1 = msh_vec3((x1 - w * 0.5f) / r, (y1 - h * 0.5f) / r, 0.0f); 221 | p1 = msh_vec3_scalar_mul(p1, cam->rot_speed); 222 | float l1_sq = p1.x * p1.x + p1.y * p1.y; 223 | if (l1_sq > (0.5f * cam->rot_speed)) 224 | { 225 | p1.z = (0.5f * cam->rot_speed) / sqrtf(l1_sq); 226 | } 227 | else 228 | { 229 | p1.z = sqrtf((1.0f * cam->rot_speed) - l1_sq); 230 | } 231 | p1 = msh_vec3_normalize(p1); 232 | 233 | // From Shoemake 234 | msh_quat_t rot; 235 | rot.im = msh_vec3_cross(p0, p1); 236 | rot.re = msh_vec3_dot(p0, p1); 237 | 238 | cam->orientation = msh_quat_mul(cam->orientation, msh_quat_conjugate(rot)); 239 | } 240 | 241 | void 242 | msh_camera_update_view(msh_camera_t* cam) 243 | { 244 | msh_mat3_t orientation = msh_quat_to_mat3(cam->orientation); 245 | msh_vec3_t rot_offset = msh_mat3_vec3_mul(orientation, cam->offset); 246 | cam->location = msh_vec3_add(cam->origin, rot_offset); 247 | msh_mat4_t inv_view = msh_mat3_to_mat4(orientation); 248 | inv_view.col[3] = 249 | msh_vec4(cam->location.x, cam->location.y, cam->location.z, 1.0f); 250 | cam->view = msh_mat4_se3_inverse(inv_view); 251 | } 252 | 253 | void 254 | msh_camera_update_proj(msh_camera_t* cam) 255 | { 256 | float w = cam->viewport.z - cam->viewport.x; 257 | float h = cam->viewport.w - cam->viewport.y; 258 | float aspect_ratio = w / h; 259 | if (cam->use_ortho) 260 | { 261 | float top = 0.85f * cam->zoom_speed * msh_vec3_norm(cam->offset); 262 | float left = -aspect_ratio * top; 263 | cam->proj = msh_ortho(left, -left, -top, top, cam->znear, cam->zfar); 264 | } 265 | else 266 | { 267 | cam->proj = msh_perspective(cam->fovy, aspect_ratio, cam->znear, cam->zfar); 268 | } 269 | } 270 | 271 | void 272 | msh_camera_update(msh_camera_t* cam) 273 | { 274 | msh_camera_update_view(cam); 275 | msh_camera_update_proj(cam); 276 | } 277 | 278 | void 279 | msh_camera_init(msh_camera_t* cam, msh_camera_desc_t* desc) 280 | { 281 | cam->fovy = (desc->fovy > 0) ? desc->fovy : (2.0f / 3.0f) * (float)MSH_PI; 282 | cam->viewport.x = desc->viewport.x; 283 | cam->viewport.y = desc->viewport.y; 284 | cam->viewport.z = desc->viewport.z; 285 | cam->viewport.w = desc->viewport.w; 286 | cam->znear = desc->znear; 287 | cam->zfar = desc->zfar; 288 | cam->use_ortho = desc->use_ortho; 289 | 290 | cam->origin = desc->center; 291 | cam->rot_speed = (desc->rot_speed > 0) ? desc->rot_speed : 1.0f; 292 | cam->pan_speed = (desc->pan_speed > 0) ? desc->pan_speed : 1.0f; 293 | cam->zoom_speed = (desc->zoom_speed > 0) ? desc->zoom_speed : 1.0f; 294 | 295 | msh_vec3_t rot_offset = msh_vec3_sub(desc->eye, desc->center); 296 | msh_mat3_t R; 297 | R.col[2] = msh_vec3_normalize(rot_offset); 298 | R.col[0] = msh_vec3_normalize(msh_vec3_cross(desc->up, R.col[2])); 299 | R.col[1] = msh_vec3_normalize(msh_vec3_cross(R.col[2], R.col[0])); 300 | 301 | cam->offset = msh_mat3_vec3_mul(msh_mat3_transpose(R), rot_offset); 302 | cam->orientation = msh_mat3_to_quat(R); 303 | 304 | msh_camera_update(cam); 305 | } 306 | 307 | void 308 | msh_camera_ray_through_pixel(msh_camera_t* camera, 309 | msh_vec2_t p, 310 | msh_vec3_t* origin, 311 | msh_vec3_t* direction) 312 | { 313 | *origin = camera->location; 314 | msh_mat4_t inv_v = msh_mat4_se3_inverse(camera->view); 315 | msh_mat4_t inv_p = msh_mat4_inverse(camera->proj); 316 | 317 | float clip_x = (2.0f * p.x) / camera->viewport.z - 1.0f; 318 | float clip_y = 1.0f - (2.0f * p.y) / camera->viewport.w; 319 | msh_vec4_t clip_coords = msh_vec4(clip_x, clip_y, 0.0f, 1.0f); 320 | 321 | msh_vec4_t eye_ray_dir = msh_mat4_vec4_mul(inv_p, clip_coords); 322 | eye_ray_dir.z = -1.0f; 323 | eye_ray_dir.w = 0.0f; 324 | msh_vec3_t world_ray_dir = 325 | msh_vec4_to_vec3(msh_mat4_vec4_mul(inv_v, eye_ray_dir)); 326 | *direction = msh_vec3_normalize(world_ray_dir); 327 | } 328 | 329 | #endif /* MSH_CAMERA_IMPLEMENTATION*/ 330 | 331 | /* 332 | ------------------------------------------------------------------------------ 333 | 334 | This software is available under 2 licenses - you may choose the one you like. 335 | 336 | ------------------------------------------------------------------------------ 337 | 338 | ALTERNATIVE A - MIT License 339 | 340 | Copyright (c) 2019-2020 Maciej Halber 341 | 342 | Permission is hereby granted, free of charge, to any person obtaining a copy of 343 | this software and associated documentation files (the "Software"), to deal in 344 | the Software without restriction, including without limitation the rights to 345 | use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies 346 | of the Software, and to permit persons to whom the Software is furnished to do 347 | so, subject to the following conditions: 348 | 349 | The above copyright notice and this permission notice shall be included in all 350 | copies or substantial portions of the Software. 351 | 352 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 353 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 354 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 355 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 356 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 357 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 358 | SOFTWARE. 359 | 360 | ------------------------------------------------------------------------------ 361 | 362 | ALTERNATIVE B - Public Domain (www.unlicense.org) 363 | 364 | This is free and unencumbered software released into the public domain. 365 | 366 | Anyone is free to copy, modify, publish, use, compile, sell, or distribute this 367 | software, either in source code form or as a compiled binary, for any purpose, 368 | commercial or non-commercial, and by any means. 369 | 370 | In jurisdictions that recognize copyright laws, the author or authors of this 371 | software dedicate any and all copyright interest in the software to the public 372 | domain. We make this dedication for the benefit of the public at large and to 373 | the detriment of our heirs and successors. We intend this dedication to be an 374 | overt act of relinquishment in perpetuity of all present and future rights to 375 | this software under copyright law. 376 | 377 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 378 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 379 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 380 | AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN 381 | ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION 382 | WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 383 | 384 | ------------------------------------------------------------------------------ 385 | */ 386 | -------------------------------------------------------------------------------- /msh_containers.h: -------------------------------------------------------------------------------- 1 | /* 2 | ================================================================================================== 3 | Licensing information can be found at the end of the file. 4 | ================================================================================================== 5 | 6 | MSH_CONTAINERS.H v0.5 7 | 8 | Minimalistic containers header, contains implementation of a dynamic array 9 | (stretchy buffer) and a simple uint64 to uint64 hashtable. 10 | 11 | To use the library you simply add: 12 | 13 | #define MSH_CONTAINERS_IMPLEMENTATION 14 | #include "msh_containers.h" 15 | 16 | The define should only include once in your source. 17 | 18 | ================================================================================================== 19 | DEPENDENCIES 20 | This file depends on a number c stdlib header (see below). 21 | By default, these are not included by this library. If you'd like these to 22 | be included, define: #define MSH_STD_INCLUDE_LIBC_HEADERS 23 | 24 | ================================================================================================== 25 | AUTHORS: 26 | Maciej Halber 27 | 28 | ADDITIONAL CREDITS: 29 | Sean T. Barrett, Per Vognsen, Mattias Gustavsson, Randy Gaul, Ginger Bill, 30 | Бранимир Караџић 31 | 32 | Please see particular sections for exact source of code / inspiration 33 | 34 | ================================================================================================== 35 | TODOs: 36 | [ ] Sorting and Searching 37 | [ ] Insetion sort 38 | [ ] Radix sort 39 | [ ] QuickSort 40 | [ ] Binary Search 41 | [ ] Quick Select 42 | [ ] Common qsort comparator functions 43 | 44 | ================================================================================================== 45 | REFERENCES: 46 | [1] stb.h https://github.com/nothings/stb/blob/master/stb.h 47 | [2] gb.h https://github.com/gingerBill/gb/blob/master/gb.h 48 | [3] stretchy_buffer 49 | https://github.com/nothings/stb/blob/master/stretchy_buffer.h [4] cute_headers 50 | https://github.com/RandyGaul/cute_headers [5] libs 51 | https://github.com/gingerBill/gb 52 | */ 53 | 54 | #ifndef MSH_CONTAINERS 55 | #define MSH_CONTAINERS 56 | 57 | #ifdef MSH_CONTAINERS_INCLUDE_LIBC_HEADERS 58 | #include 59 | #include 60 | #include 61 | #include 62 | #include 63 | #include 64 | #endif 65 | 66 | #ifdef MSH_CONTAINERS_STATIC 67 | #define MSH_CONT_DEF static 68 | #else 69 | #define MSH_CONT_DEF extern 70 | #endif 71 | 72 | //////////////////////////////////////////////////////////////////////////////// 73 | // Dynamic Size Array 74 | // Credits 75 | // Seat T. Barrett - popularization of stretchy buffers 76 | // Per Vognsen - various changes from his ion implementation 77 | //////////////////////////////////////////////////////////////////////////////// 78 | 79 | typedef struct msh_array_header 80 | { 81 | size_t len; 82 | size_t cap; 83 | } msh_array_hdr_t; 84 | 85 | #define msh_array(T) T* 86 | 87 | MSH_CONT_DEF void* 88 | msh_array__grow_fnct(void* array, size_t new_len, size_t elem_size); 89 | MSH_CONT_DEF char* msh_array__printf(char* buf, const char* fmt, ...); 90 | 91 | #define msh_array__grow_formula(x) ((2 * (x)) + 8) 92 | #define msh_array__hdr(a) \ 93 | ((msh_array_hdr_t*)((char*)(a) - sizeof(msh_array_hdr_t))) 94 | 95 | #define msh_array_len(a) ((a) ? (msh_array__hdr((a))->len) : 0) 96 | #define msh_array_cap(a) ((a) ? (msh_array__hdr((a))->cap) : 0) 97 | #define msh_array_sizeof(a) \ 98 | ((a) ? (msh_array__hdr((a))->len * sizeof(*(a))) : 0) 99 | #define msh_array_isempty(a) ((a) ? (msh_array__hdr((a))->len <= 0) : 1) 100 | 101 | #define msh_array_front(a) ((a) ? (a) : NULL) 102 | #define msh_array_back(a) \ 103 | (msh_array_len((a)) ? ((a) + msh_array_len((a)) - 1) : NULL) 104 | #define msh_array_end(a) ((a) + msh_array_len((a))) // One past the end 105 | #define msh_array_pop(a) ((a) ? (msh_array__hdr((a))->len--) : 0) 106 | 107 | #define msh_array_free(a) ((a) ? (free(msh_array__hdr(a)), (a) = NULL) : 0) 108 | #define msh_array_clear(a) ((a) ? (msh_array__hdr((a))->len = 0) : 0) 109 | #define msh_array_fit(a, n) \ 110 | ((n) <= msh_array_cap(a) ? (0) : msh_array__grow(a, n)) 111 | #define msh_array_push(a, ...) \ 112 | (msh_array_fit((a), 1 + msh_array_len((a))), \ 113 | (a)[msh_array__hdr(a)->len++] = (__VA_ARGS__)) 114 | #define msh_array_copy(d, s, n) \ 115 | (msh_array_fit((d), (n)), \ 116 | msh_array__hdr((d))->len = (n), \ 117 | memcpy((void*)(d), (void*)(s), (n) * sizeof(*(d)))) 118 | #define msh_array_printf(b, ...) ((b) = msh_array__printf((b), __VA_ARGS__)) 119 | #define msh_array__grow(a, n) \ 120 | (*((void**)&(a)) = msh_array__grow_fnct((a), (n), sizeof(*(a)))) 121 | 122 | //////////////////////////////////////////////////////////////////////////////// 123 | // Sorting 124 | // [ ] Make eneric with a code gen? 125 | //////////////////////////////////////////////////////////////////////////////// 126 | 127 | MSH_CONT_DEF void msh_insertion_sort(int32_t* arr, size_t n); 128 | MSH_CONT_DEF void msh_insertion_sortf(float* arr, size_t n); 129 | MSH_CONT_DEF void msh_insertion_sortd(double* arr, size_t n); 130 | 131 | //////////////////////////////////////////////////////////////////////////////// 132 | // Hash Table 133 | // TODO(maciej): 134 | // [ ] Benchmarking? 135 | // 136 | // Credits 137 | // Seat T. Barrett: Judy Array vs. Hash-table text 138 | // Niklas Frykholm: The Machinery Container system blog-series 139 | // Per Vognsen : ios's open-addressing, linear probing hashtable 140 | //////////////////////////////////////////////////////////////////////////////// 141 | 142 | typedef struct msh_map 143 | { 144 | uint64_t* keys; 145 | uint64_t* vals; 146 | size_t _len; 147 | size_t _cap; 148 | } msh_map_t; 149 | 150 | MSH_CONT_DEF uint64_t msh_hash_uint64(uint64_t x); 151 | MSH_CONT_DEF uint64_t msh_hash_ptr(void* ptr); 152 | MSH_CONT_DEF uint64_t msh_hash_str(const char* str); 153 | 154 | MSH_CONT_DEF void msh_map_init(msh_map_t* map, uint32_t cap); 155 | MSH_CONT_DEF void msh_map_clear(msh_map_t* map); 156 | MSH_CONT_DEF void msh_map_free(msh_map_t* map); 157 | 158 | MSH_CONT_DEF size_t msh_map_len(msh_map_t* map); 159 | MSH_CONT_DEF size_t msh_map_cap(msh_map_t* map); 160 | 161 | MSH_CONT_DEF void msh_map_insert(msh_map_t* map, uint64_t key, uint64_t val); 162 | MSH_CONT_DEF void msh_map_remove(msh_map_t* map, uint64_t key); 163 | MSH_CONT_DEF uint64_t* msh_map_get(const msh_map_t* map, uint64_t key); 164 | 165 | MSH_CONT_DEF void msh_map_get_iterable_keys(const msh_map_t* map, 166 | uint64_t** keys); 167 | MSH_CONT_DEF void msh_map_get_iterable_vals(const msh_map_t* map, 168 | uint64_t** vals); 169 | MSH_CONT_DEF void msh_map_get_iterable_keys_and_vals(const msh_map_t* map, 170 | uint64_t** key, 171 | uint64_t** val); 172 | 173 | //////////////////////////////////////////////////////////////////////////////// 174 | // Disjoint Set 175 | //////////////////////////////////////////////////////////////////////////////// 176 | 177 | struct msh_dset; 178 | MSH_CONT_DEF void msh_dset_init(struct msh_dset* dset, size_t n_vals); 179 | MSH_CONT_DEF void msh_dset_term(struct msh_dset* dset); 180 | MSH_CONT_DEF uint64_t msh_dset_find(struct msh_dset* dset, uint64_t idx); 181 | MSH_CONT_DEF void 182 | msh_dset_union(struct msh_dset* dset, uint64_t idx_a, uint64_t idx_b); 183 | 184 | //////////////////////////////////////////////////////////////////////////////// 185 | // Heap for all basic numeric types using C11 Generics 186 | // TODO: check if works with new MSVC 187 | // Credits: 188 | // Joerg Arndt, 'Matters Computational' 189 | //////////////////////////////////////////////////////////////////////////////// 190 | 191 | #define msh_heap_make(x, y) \ 192 | _Generic((x), \ 193 | int8_t * \ 194 | : msh_heap_make_i8, \ 195 | int16_t * \ 196 | : msh_heap_make_i16, \ 197 | int32_t * \ 198 | : msh_heap_make_i32, \ 199 | int64_t * \ 200 | : msh_heap_make_i64, \ 201 | uint8_t * \ 202 | : msh_heap_make_ui8, \ 203 | uint16_t * \ 204 | : msh_heap_make_ui16, \ 205 | uint32_t * \ 206 | : msh_heap_make_ui32, \ 207 | uint64_t * \ 208 | : msh_heap_make_ui64, \ 209 | float * \ 210 | : msh_heap_make_f32, \ 211 | double * \ 212 | : msh_heap_make_f64, \ 213 | default \ 214 | : msh_heap_make_i32)(x, y) 215 | 216 | #define msh_heap_pop(x, y) \ 217 | _Generic((x), \ 218 | int8_t * \ 219 | : msh_heap_pop_i8, \ 220 | int16_t * \ 221 | : msh_heap_pop_i16, \ 222 | int32_t * \ 223 | : msh_heap_pop_i32, \ 224 | int64_t * \ 225 | : msh_heap_pop_i64, \ 226 | uint8_t * \ 227 | : msh_heap_pop_ui8, \ 228 | uint16_t * \ 229 | : msh_heap_pop_ui16, \ 230 | uint32_t * \ 231 | : msh_heap_pop_ui32, \ 232 | uint64_t * \ 233 | : msh_heap_pop_ui64, \ 234 | float * \ 235 | : msh_heap_pop_f32, \ 236 | double * \ 237 | : msh_heap_pop_f64, \ 238 | default \ 239 | : msh_heap_pop_i32)(x, y) 240 | 241 | #define msh_heap_push(x, y) \ 242 | _Generic((x), \ 243 | int8_t * \ 244 | : msh_heap_push_i8, \ 245 | int16_t * \ 246 | : msh_heap_push_i16, \ 247 | int32_t * \ 248 | : msh_heap_push_i32, \ 249 | int64_t * \ 250 | : msh_heap_push_i64, \ 251 | uint8_t * \ 252 | : msh_heap_push_ui8, \ 253 | uint16_t * \ 254 | : msh_heap_push_ui16, \ 255 | uint32_t * \ 256 | : msh_heap_push_ui32, \ 257 | uint64_t * \ 258 | : msh_heap_push_ui64, \ 259 | float * \ 260 | : msh_heap_push_f32, \ 261 | double * \ 262 | : msh_heap_push_f64, \ 263 | default \ 264 | : msh_heap_push_i32)(x, y) 265 | 266 | #define msh_heap_isvalid(x, y) \ 267 | _Generic((x), \ 268 | int8_t * \ 269 | : msh_heap_isvalid_i8, \ 270 | int16_t * \ 271 | : msh_heap_isvalid_i16, \ 272 | int32_t * \ 273 | : msh_heap_isvalid_i32, \ 274 | int64_t * \ 275 | : msh_heap_isvalid_i64, \ 276 | uint8_t * \ 277 | : msh_heap_isvalid_ui8, \ 278 | uint16_t * \ 279 | : msh_heap_isvalid_ui16, \ 280 | uint32_t * \ 281 | : msh_heap_isvalid_ui32, \ 282 | uint64_t * \ 283 | : msh_heap_isvalid_ui64, \ 284 | float * \ 285 | : msh_heap_isvalid_f32, \ 286 | double * \ 287 | : msh_heap_isvalid_f64, \ 288 | default \ 289 | : msh_heap_isvalid_i32)(x, y) 290 | 291 | #endif /* MSH_CONTAINERS */ 292 | 293 | #ifdef MSH_CONTAINERS_IMPLEMENTATION 294 | 295 | //////////////////////////////////////////////////////////////////////////////// 296 | // Dynamic Array 297 | //////////////////////////////////////////////////////////////////////////////// 298 | 299 | MSH_CONT_DEF void* 300 | msh_array__grow_fnct(void* array, size_t new_len, size_t elem_size) 301 | { 302 | if (new_len <= msh_array_len(array)) { return array; } 303 | size_t old_cap = msh_array_cap(array); 304 | size_t new_cap = (size_t)msh_array__grow_formula(old_cap); 305 | new_len = (new_len < 16) ? 16 : new_len; 306 | new_cap = (new_cap < new_len) ? new_len : new_cap; 307 | assert(new_len <= new_cap); 308 | size_t new_size = sizeof(msh_array_hdr_t) + new_cap * elem_size; 309 | msh_array_hdr_t* new_hdr; 310 | if (array) 311 | { 312 | new_hdr = (msh_array_hdr_t*)realloc(msh_array__hdr(array), new_size); 313 | } 314 | else 315 | { 316 | new_hdr = (msh_array_hdr_t*)malloc(new_size); 317 | new_hdr->len = 0; 318 | } 319 | new_hdr->cap = new_cap; 320 | return (void*)((char*)new_hdr + sizeof(msh_array_hdr_t)); 321 | } 322 | 323 | MSH_CONT_DEF char* 324 | msh_array__printf(char* buf, const char* fmt, ...) 325 | { 326 | va_list args; 327 | va_start(args, fmt); 328 | size_t cap = msh_array_cap(buf) - msh_array_len(buf); 329 | int64_t len = vsnprintf(msh_array_end(buf), cap, fmt, args); 330 | if (len < 0) { len = cap; } 331 | size_t n = 1 + len; 332 | va_end(args); 333 | if (n > cap) 334 | { 335 | msh_array_fit(buf, n + msh_array_len(buf)); 336 | va_start(args, fmt); 337 | size_t new_cap = msh_array_cap(buf) - msh_array_len(buf); 338 | len = vsnprintf(msh_array_end(buf), new_cap, fmt, args); 339 | if (len < 0) { len = new_cap; } 340 | n = 1 + len; 341 | assert(n <= new_cap); 342 | va_end(args); 343 | } 344 | msh_array__hdr(buf)->len += (n - 1); 345 | return buf; 346 | } 347 | 348 | //////////////////////////////////////////////////////////////////////////////// 349 | // Sort 350 | //////////////////////////////////////////////////////////////////////////////// 351 | 352 | void 353 | msh_insertion_sort(int32_t* arr, size_t n) 354 | { 355 | assert(arr); 356 | if (n <= 1) { return; } 357 | size_t i, j; 358 | int32_t x; 359 | 360 | i = 1; 361 | while (i < n) 362 | { 363 | x = arr[i]; 364 | j = i - 1; 365 | while (j > 0 && arr[j] > x) 366 | { 367 | arr[j + 1] = arr[j]; 368 | j--; 369 | } 370 | arr[j + 1] = x; 371 | i++; 372 | } 373 | } 374 | 375 | //////////////////////////////////////////////////////////////////////////////// 376 | // Hash Table 377 | //////////////////////////////////////////////////////////////////////////////// 378 | 379 | MSH_CONT_DEF uint64_t 380 | msh_hash_uint64(uint64_t x) 381 | { 382 | x *= 0xff51afd7ed558ccd; 383 | x ^= x >> 32; 384 | return x; 385 | } 386 | 387 | MSH_CONT_DEF uint64_t 388 | msh_hash_ptr(void* ptr) 389 | { 390 | return msh_hash_uint64((uintptr_t)ptr); 391 | } 392 | 393 | MSH_CONT_DEF uint64_t 394 | msh_hash_str(const char* str) 395 | { 396 | uint64_t x = 0xcbf29ce484222325; 397 | char* buf = (char*)str; 398 | while (*buf != 0) 399 | { 400 | x ^= *buf; 401 | x *= 0x100000001b3; 402 | x ^= x >> 32; 403 | buf++; 404 | } 405 | return x; 406 | } 407 | 408 | static uint32_t 409 | msh_map__pow2ceil(uint32_t v) 410 | { 411 | --v; 412 | v |= v >> 1; 413 | v |= v >> 2; 414 | v |= v >> 4; 415 | v |= v >> 8; 416 | v |= v >> 16; 417 | ++v; 418 | v += (v == 0); 419 | return v; 420 | } 421 | 422 | MSH_CONT_DEF void 423 | msh_map_init(msh_map_t* map, uint32_t cap) 424 | { 425 | assert(map); 426 | assert(!map->keys && !map->vals); 427 | cap = msh_map__pow2ceil(cap); 428 | map->keys = (uint64_t*)calloc(cap, sizeof(uint64_t)); 429 | map->vals = (uint64_t*)malloc(cap * sizeof(uint64_t)); 430 | map->_len = 0; 431 | map->_cap = cap; 432 | } 433 | 434 | MSH_CONT_DEF void 435 | msh_map_clear(msh_map_t* map) 436 | { 437 | assert(map); 438 | assert(map->keys && map->vals); 439 | 440 | map->_len = 0; 441 | memset(map->keys, 0, map->_cap * sizeof(map->keys[0])); 442 | } 443 | 444 | MSH_CONT_DEF void 445 | msh_map__grow(msh_map_t* map, size_t new_cap) 446 | { 447 | new_cap = (new_cap < 16) ? 16 : new_cap; 448 | msh_map_t new_map; 449 | new_map.keys = (uint64_t*)calloc(new_cap, sizeof(uint64_t)); 450 | new_map.vals = (uint64_t*)malloc(new_cap * sizeof(uint64_t)); 451 | new_map._len = 0; 452 | new_map._cap = new_cap; 453 | 454 | for (size_t i = 0; i < map->_cap; i++) 455 | { 456 | if (map->keys[i]) 457 | { 458 | msh_map_insert(&new_map, map->keys[i] - 1, map->vals[i]); 459 | } 460 | } 461 | free(map->keys); 462 | free(map->vals); 463 | *map = new_map; 464 | } 465 | 466 | MSH_CONT_DEF size_t 467 | msh_map_len(msh_map_t* map) 468 | { 469 | return map->_len; 470 | } 471 | 472 | MSH_CONT_DEF size_t 473 | msh_map_cap(msh_map_t* map) 474 | { 475 | return map->_cap; 476 | } 477 | 478 | MSH_CONT_DEF void 479 | msh_map_insert(msh_map_t* map, uint64_t key, uint64_t val) 480 | { 481 | // Increment the key, so that key == 0 is valid, even though 0 is marking 482 | // empty slot. 483 | assert(key < (0xffffffffffffffffULL - 1)); 484 | key += 1; 485 | if (2 * map->_len >= map->_cap) { msh_map__grow(map, 2 * map->_cap); } 486 | assert(2 * map->_len < map->_cap); 487 | size_t i = (size_t)msh_hash_uint64(key); 488 | for (;;) 489 | { 490 | i &= map->_cap - 1; 491 | if (!map->keys[i]) 492 | { 493 | map->_len++; 494 | map->keys[i] = key; 495 | map->vals[i] = val; 496 | return; 497 | } 498 | else if (map->keys[i] == key) 499 | { 500 | map->vals[i] = val; 501 | return; 502 | } 503 | i++; 504 | } 505 | } 506 | 507 | MSH_CONT_DEF void 508 | msh_map_remove(msh_map_t* map, uint64_t key) 509 | { 510 | if (map->_len == 0) { return; } 511 | key += 1; 512 | size_t i = (size_t)msh_hash_uint64(key); 513 | assert(map->_len < map->_cap); 514 | for (;;) 515 | { 516 | i &= map->_cap - 1; 517 | if (map->keys[i] == key) 518 | { 519 | map->keys[i] = 0; 520 | map->_len--; 521 | return; 522 | } 523 | else if (!map->keys[i]) 524 | { 525 | return; 526 | } 527 | i++; 528 | } 529 | } 530 | 531 | MSH_CONT_DEF uint64_t* 532 | msh_map_get(const msh_map_t* map, uint64_t key) 533 | { 534 | if (map->_len == 0) { return NULL; } 535 | key += 1; 536 | size_t i = (size_t)msh_hash_uint64(key); 537 | assert(map->_len < map->_cap); 538 | for (;;) 539 | { 540 | i &= map->_cap - 1; 541 | if (map->keys[i] == key) { return &map->vals[i]; } 542 | else if (!map->keys[i]) 543 | { 544 | return NULL; 545 | } 546 | i++; 547 | } 548 | } 549 | 550 | MSH_CONT_DEF void 551 | msh_map_free(msh_map_t* map) 552 | { 553 | free(map->keys); 554 | free(map->vals); 555 | map->_cap = 0; 556 | map->_len = 0; 557 | } 558 | 559 | MSH_CONT_DEF void 560 | msh_map_get_iterable_keys(const msh_map_t* map, uint64_t** keys) 561 | { 562 | assert((*keys) == NULL); 563 | (*keys) = (uint64_t*)malloc(map->_len * sizeof(uint64_t)); 564 | size_t j = 0; 565 | for (size_t i = 0; i < map->_cap; ++i) 566 | { 567 | if (map->keys[i]) { (*keys)[j++] = map->keys[i] - 1; } 568 | } 569 | } 570 | 571 | MSH_CONT_DEF void 572 | msh_map_get_iterable_vals(const msh_map_t* map, uint64_t** vals) 573 | { 574 | assert((*vals) == NULL); 575 | (*vals) = (uint64_t*)malloc(map->_len * sizeof(uint64_t)); 576 | size_t j = 0; 577 | for (size_t i = 0; i < map->_cap; ++i) 578 | { 579 | if (map->keys[i]) { (*vals)[j++] = map->vals[i]; } 580 | } 581 | } 582 | 583 | MSH_CONT_DEF void 584 | msh_map_get_iterable_keys_and_vals(const msh_map_t* map, 585 | uint64_t** keys, 586 | uint64_t** vals) 587 | { 588 | assert((*keys) == NULL); 589 | assert((*vals) == NULL); 590 | (*keys) = (uint64_t*)malloc(map->_len * sizeof(uint64_t)); 591 | (*vals) = (uint64_t*)malloc(map->_len * sizeof(uint64_t)); 592 | size_t j = 0; 593 | for (size_t i = 0; i < map->_cap; ++i) 594 | { 595 | if (map->keys[i]) 596 | { 597 | (*keys)[j] = map->keys[i] - 1; 598 | (*vals)[j] = map->vals[i]; 599 | j++; 600 | } 601 | } 602 | } 603 | 604 | //////////////////////////////////////////////////////////////////////////////// 605 | // Generic Heap 606 | //////////////////////////////////////////////////////////////////////////////// 607 | 608 | #define MSH__HEAPIFY_DEF(T, postfix) \ 609 | MSH_CONT_DEF void msh__heapify_##postfix(T* vals, \ 610 | size_t vals_len, \ 611 | size_t cur) \ 612 | { \ 613 | size_t max = cur; \ 614 | const size_t left = (cur << 1) + 1; /* multiply by two */ \ 615 | const size_t right = (cur << 1) + 2; /* multiply by two */ \ 616 | if ((left < vals_len) && (vals[left] > vals[cur])) { max = left; } \ 617 | if ((right < vals_len) && (vals[right] > vals[max])) { max = right; } \ 618 | if (max != cur) /* need to swap */ \ 619 | { \ 620 | T tmp = vals[cur]; \ 621 | vals[cur] = vals[max]; \ 622 | vals[max] = tmp; \ 623 | msh__heapify_##postfix(vals, vals_len, max); \ 624 | } \ 625 | } 626 | 627 | #define MSH_HEAP_MAKE_DEF(T, postfix) \ 628 | MSH_CONT_DEF void msh_heap_make_##postfix(T* vals, size_t vals_len) \ 629 | { \ 630 | int64_t i = vals_len >> 1; \ 631 | while (i >= 0) { msh__heapify_##postfix(vals, vals_len, i--); } \ 632 | } 633 | 634 | #define MSH_HEAP_POP_DEF(T, postfix) \ 635 | MSH_CONT_DEF void msh_heap_pop_##postfix(T* vals, size_t vals_len) \ 636 | { \ 637 | T max = vals[0]; \ 638 | vals[0] = vals[vals_len - 1]; \ 639 | vals[vals_len - 1] = max; \ 640 | vals_len--; \ 641 | if (vals_len > 0) { msh__heapify_##postfix(vals, vals_len, 0); } \ 642 | } 643 | 644 | #define MSH_HEAP_PUSH_DEF(T, postfix) \ 645 | MSH_CONT_DEF void msh_heap_push_##postfix(T* vals, size_t vals_len) \ 646 | { \ 647 | int64_t i = vals_len - 1; \ 648 | T v = vals[i]; \ 649 | while (i > 0) \ 650 | { \ 651 | int64_t j = (i - 1) >> 1; \ 652 | if (vals[j] >= v) break; \ 653 | vals[i] = vals[j]; \ 654 | i = j; \ 655 | } \ 656 | vals[i] = v; \ 657 | } 658 | 659 | #define MSH_HEAP_ISVALID_DEF(T, postfix) \ 660 | MSH_CONT_DEF bool msh_heap_isvalid_##postfix(T* vals, int32_t vals_len) \ 661 | { \ 662 | for (int32_t i = vals_len - 1; i > 0; --i) \ 663 | { \ 664 | int32_t parent = (i - 1) >> 1; \ 665 | if (vals[i] > vals[parent]) { return false; } \ 666 | } \ 667 | return true; \ 668 | } 669 | 670 | MSH__HEAPIFY_DEF(int8_t, i8); 671 | MSH_HEAP_MAKE_DEF(int8_t, i8); 672 | MSH_HEAP_POP_DEF(int8_t, i8) 673 | MSH_HEAP_PUSH_DEF(int8_t, i8) 674 | MSH_HEAP_ISVALID_DEF(int8_t, i8) 675 | 676 | MSH__HEAPIFY_DEF(int16_t, i16) 677 | MSH_HEAP_MAKE_DEF(int16_t, i16) 678 | MSH_HEAP_POP_DEF(int16_t, i16) 679 | MSH_HEAP_PUSH_DEF(int16_t, i16) 680 | MSH_HEAP_ISVALID_DEF(int16_t, i16) 681 | 682 | MSH__HEAPIFY_DEF(int32_t, i32) 683 | MSH_HEAP_MAKE_DEF(int32_t, i32) 684 | MSH_HEAP_POP_DEF(int32_t, i32) 685 | MSH_HEAP_PUSH_DEF(int32_t, i32) 686 | MSH_HEAP_ISVALID_DEF(int32_t, i32) 687 | 688 | MSH__HEAPIFY_DEF(int64_t, i64) 689 | MSH_HEAP_MAKE_DEF(int64_t, i64) 690 | MSH_HEAP_POP_DEF(int64_t, i64) 691 | MSH_HEAP_PUSH_DEF(int64_t, i64) 692 | MSH_HEAP_ISVALID_DEF(int64_t, i64) 693 | 694 | MSH__HEAPIFY_DEF(uint8_t, ui8) 695 | MSH_HEAP_MAKE_DEF(uint8_t, ui8) 696 | MSH_HEAP_POP_DEF(uint8_t, ui8) 697 | MSH_HEAP_PUSH_DEF(uint8_t, ui8) 698 | MSH_HEAP_ISVALID_DEF(uint8_t, ui8) 699 | 700 | MSH__HEAPIFY_DEF(uint16_t, ui16) 701 | MSH_HEAP_MAKE_DEF(uint16_t, ui16) 702 | MSH_HEAP_POP_DEF(uint16_t, ui16) 703 | MSH_HEAP_PUSH_DEF(uint16_t, ui16) 704 | MSH_HEAP_ISVALID_DEF(uint16_t, ui16) 705 | 706 | MSH__HEAPIFY_DEF(uint32_t, ui32) 707 | MSH_HEAP_MAKE_DEF(uint32_t, ui32) 708 | MSH_HEAP_POP_DEF(uint32_t, ui32) 709 | MSH_HEAP_PUSH_DEF(uint32_t, ui32) 710 | MSH_HEAP_ISVALID_DEF(uint32_t, ui32) 711 | 712 | MSH__HEAPIFY_DEF(uint64_t, ui64) 713 | MSH_HEAP_MAKE_DEF(uint64_t, ui64) 714 | MSH_HEAP_POP_DEF(uint64_t, ui64) 715 | MSH_HEAP_PUSH_DEF(uint64_t, ui64) 716 | MSH_HEAP_ISVALID_DEF(uint64_t, ui64) 717 | 718 | MSH__HEAPIFY_DEF(float, f32) 719 | MSH_HEAP_MAKE_DEF(float, f32) 720 | MSH_HEAP_POP_DEF(float, f32) 721 | MSH_HEAP_PUSH_DEF(float, f32) 722 | MSH_HEAP_ISVALID_DEF(float, f32) 723 | 724 | MSH__HEAPIFY_DEF(double, f64) 725 | MSH_HEAP_MAKE_DEF(double, f64) 726 | MSH_HEAP_POP_DEF(double, f64) 727 | MSH_HEAP_PUSH_DEF(double, f64) 728 | MSH_HEAP_ISVALID_DEF(double, f64) 729 | 730 | //////////////////////////////////////////////////////////////////////////////// 731 | // Disjoint Set 732 | //////////////////////////////////////////////////////////////////////////////// 733 | 734 | typedef struct msh_dset_el 735 | { 736 | uint64_t parent; 737 | uint32_t rank; 738 | uint32_t size; 739 | } msh_dset_el_t; 740 | 741 | typedef struct msh_dset 742 | { 743 | msh_dset_el_t* elems; 744 | uint64_t num_sets; 745 | } msh_dset_t; 746 | 747 | MSH_CONT_DEF void 748 | msh_dset_init(msh_dset_t* dset, uint64_t n_vals) 749 | { 750 | assert(dset); 751 | assert(dset->elems == NULL); 752 | dset->elems = (msh_dset_el_t*)malloc(sizeof(msh_dset_el_t) * n_vals); 753 | for (size_t i = 0; i < n_vals; ++i) 754 | { 755 | dset->elems[i].parent = i; 756 | dset->elems[i].rank = 0; 757 | dset->elems[i].size = 1; 758 | } 759 | dset->num_sets = n_vals; 760 | } 761 | 762 | MSH_CONT_DEF void 763 | msh_dset_term(msh_dset_t* dset) 764 | { 765 | assert(dset); 766 | assert(dset->elems); 767 | free(dset->elems); 768 | dset->elems = NULL; 769 | dset->num_sets = 0; 770 | } 771 | 772 | MSH_CONT_DEF uint64_t 773 | msh_dset_find(msh_dset_t* dset, uint64_t idx) 774 | { 775 | // Path compression 776 | uint64_t parent = dset->elems[idx].parent; 777 | if (parent == idx) { return parent; } 778 | parent = msh_dset_find(dset, parent); 779 | return parent; 780 | } 781 | 782 | MSH_CONT_DEF void 783 | msh_dset_union(msh_dset_t* dset, uint64_t idx_a, uint64_t idx_b) 784 | { 785 | uint64_t root_a = msh_dset_find(dset, idx_a); 786 | uint64_t root_b = msh_dset_find(dset, idx_b); 787 | 788 | // check if in the same set 789 | if (root_a == root_b) { return; } 790 | 791 | // if not need to do union(by rank) 792 | if (dset->elems[root_a].rank < dset->elems[root_b].rank) 793 | { 794 | uint64_t tmp = root_a; 795 | root_a = root_b; 796 | root_b = tmp; 797 | } 798 | dset->elems[root_b].parent = root_a; 799 | dset->elems[root_a].size += dset->elems[root_b].size; 800 | if (dset->elems[root_a].rank == dset->elems[root_b].rank) 801 | { 802 | dset->elems[root_a].rank++; 803 | } 804 | dset->num_sets--; 805 | } 806 | 807 | #endif /* MSH_CONTAINERS_IMPLEMENTATION*/ 808 | 809 | /* 810 | ------------------------------------------------------------------------------ 811 | 812 | This software is available under 2 licenses - you may choose the one you like. 813 | 814 | ------------------------------------------------------------------------------ 815 | 816 | ALTERNATIVE A - MIT License 817 | 818 | Copyright (c) 2016-2020 Maciej Halber 819 | 820 | Permission is hereby granted, free of charge, to any person obtaining a copy of 821 | this software and associated documentation files (the "Software"), to deal in 822 | the Software without restriction, including without limitation the rights to 823 | use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies 824 | of the Software, and to permit persons to whom the Software is furnished to do 825 | so, subject to the following conditions: 826 | 827 | The above copyright notice and this permission notice shall be included in all 828 | copies or substantial portions of the Software. 829 | 830 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 831 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 832 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 833 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 834 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 835 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 836 | SOFTWARE. 837 | 838 | ------------------------------------------------------------------------------ 839 | 840 | ALTERNATIVE B - Public Domain (www.unlicense.org) 841 | 842 | This is free and unencumbered software released into the public domain. 843 | 844 | Anyone is free to copy, modify, publish, use, compile, sell, or distribute this 845 | software, either in source code form or as a compiled binary, for any purpose, 846 | commercial or non-commercial, and by any means. 847 | 848 | In jurisdictions that recognize copyright laws, the author or authors of this 849 | software dedicate any and all copyright interest in the software to the public 850 | domain. We make this dedication for the benefit of the public at large and to 851 | the detriment of our heirs and successors. We intend this dedication to be an 852 | overt act of relinquishment in perpetuity of all present and future rights to 853 | this software under copyright law. 854 | 855 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 856 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 857 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 858 | AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN 859 | ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION 860 | WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 861 | 862 | ------------------------------------------------------------------------------ 863 | */ -------------------------------------------------------------------------------- /tests/msh_argparse_test.inl: -------------------------------------------------------------------------------- 1 | 2 | MunitResult 3 | test_msh_argparse(const MunitParameter params[], void* fixture) 4 | { 5 | (void) params; 6 | (void) fixture; 7 | 8 | typedef struct local_struct 9 | { 10 | char* filename; 11 | int an_integer; 12 | double a_real_number; 13 | uint8_t a_bunch_of_numbers[3]; 14 | bool an_option; 15 | float dummy; 16 | } local_struct; 17 | 18 | local_struct ls = {0}; 19 | msh_argparse_t parser = {0}; 20 | msh_ap_init( &parser, "Test", "This is a test of argparse" ); 21 | msh_ap_add_string_argument( &parser, "filename", NULL, "Name to a file", &ls.filename, 1 ); 22 | msh_ap_add_int_argument( &parser, "--integer", NULL, "An integer", &ls.an_integer, 1 ); 23 | msh_ap_add_double_argument( &parser, "--real", NULL, "A real number", &ls.a_real_number, 1 ); 24 | msh_ap_add_unsigned_char_argument( &parser, "--some_numbers", "-s", "Bunch of numbers", &ls.a_bunch_of_numbers[0], 3 ); 25 | msh_ap_add_float_argument( &parser, "--dummy", "-d", "Dummy", &ls.dummy, 1 ); 26 | msh_ap_add_bool_argument( &parser, "--option", "-o", "Option", &ls.an_option, 0 ); 27 | 28 | munit_assert_ptr_equal(ls.filename, NULL); 29 | munit_assert_int(ls.an_integer, ==, 0); 30 | munit_assert_double(ls.a_real_number, ==, 0.0 ); 31 | 32 | char* argv1[] = {"program_name", "The_File.txt"}; 33 | int argc1 = msh_count_of(argv1); 34 | 35 | msh_ap_parse(&parser, argc1, argv1); 36 | 37 | munit_assert_string_equal(ls.filename, "The_File.txt"); 38 | munit_assert_float(ls.dummy,==,0.0f); 39 | 40 | char* argv2[] = { "program_name", "The_Other_File.txt", 41 | "--integer", "10", 42 | "--real", "2.0", 43 | "--some_numbers", "1", "2", "3", 44 | "--option" }; 45 | int argc2 = msh_count_of(argv2); 46 | msh_ap_parse(&parser, argc2, argv2); 47 | munit_assert_string_equal(ls.filename, "The_Other_File.txt"); 48 | munit_assert_int(ls.an_integer,==,10 ); 49 | munit_assert_double( ls.a_real_number,==,2.0); 50 | munit_assert_uint8(ls.a_bunch_of_numbers[0],==,1); 51 | munit_assert_uint8(ls.a_bunch_of_numbers[1],==,2); 52 | munit_assert_uint8(ls.a_bunch_of_numbers[2],==,3); 53 | munit_assert_true(ls.an_option); 54 | munit_assert_float(ls.dummy,==,0.0f); 55 | 56 | return MUNIT_OK; 57 | } -------------------------------------------------------------------------------- /tests/msh_containers_test.inl: -------------------------------------------------------------------------------- 1 | 2 | MunitResult 3 | test_msh_array_api(const MunitParameter params[], void* fixture) 4 | { 5 | (void) params; 6 | (void) fixture; 7 | 8 | msh_array( int32_t ) arr = {0}; 9 | munit_assert_size( msh_array_len(arr), ==, 0 ); 10 | munit_assert_size( msh_array_cap(arr), ==, 0 ); 11 | msh_array_fit( arr, 32 ); 12 | munit_assert_size( msh_array_len(arr), ==, 0 ); 13 | munit_assert_size( msh_array_cap(arr), ==, 32 ); 14 | 15 | munit_assert_size( msh_array_len(arr), ==, 0 ); 16 | munit_assert_size( msh_array_cap(arr), ==, 32 ); 17 | munit_assert_size( msh_array_sizeof(arr), ==, 0 ); 18 | munit_assert_int8( msh_array_isempty(arr), ==, true ); 19 | 20 | msh_array_push( arr, 10 ); 21 | msh_array_push( arr, 20 ); 22 | msh_array_push( arr, 30 ); 23 | munit_assert_size( msh_array_len(arr), ==, 3 ); 24 | munit_assert_size( msh_array_cap(arr), ==, 32 ); 25 | 26 | msh_array_pop( arr ); 27 | munit_assert_size( msh_array_len(arr), ==, 2 ); 28 | munit_assert_size( msh_array_cap(arr), ==, 32 ); 29 | 30 | int32_t* front = msh_array_front( arr ); 31 | int32_t* back = msh_array_back( arr ); 32 | munit_assert_int32( *front, ==, 10 ); 33 | munit_assert_int32( *back, ==, 20 ); 34 | 35 | int32_t* end = msh_array_end(arr); 36 | munit_assert_int32( *end, ==, 30 ); 37 | 38 | msh_array_clear( arr ); 39 | munit_assert_size( msh_array_len(arr), ==, 0 ); 40 | munit_assert_size( msh_array_cap(arr), ==, 32 ); 41 | 42 | for( int32_t i = 0; i < 33; ++i ) 43 | { 44 | msh_array_push( arr, i*10 ); 45 | } 46 | munit_assert_size( msh_array_sizeof(arr), ==, 33 * sizeof(int32_t) ); 47 | munit_assert_size( msh_array_len(arr), ==, 33 ); 48 | munit_assert_size( msh_array_cap(arr), ==, msh_array__grow_formula(32) ); 49 | 50 | for( int32_t i = 0; i < 33; ++i ) 51 | { 52 | munit_assert_int32( arr[i], ==, i*10 ); 53 | } 54 | 55 | msh_array_free( arr ); 56 | munit_assert_size( msh_array_len(arr), ==, 0 ); 57 | munit_assert_size( msh_array_cap(arr), ==, 0 ); 58 | 59 | return MUNIT_OK; 60 | } 61 | 62 | MunitResult 63 | test_msh_array_printf(const MunitParameter params[], void* fixture) 64 | { 65 | (void) params; 66 | (void) fixture; 67 | 68 | msh_array( char ) str = {0}; 69 | msh_array_printf( str, "This" ); 70 | munit_assert_string_equal( str, "This" ); 71 | msh_array_printf( str, " is a "); 72 | munit_assert_string_equal( str, "This is a " ); 73 | msh_array_printf( str, "string-builder. "); 74 | munit_assert_string_equal( str, "This is a string-builder. " ); 75 | msh_array_printf( str, "It supports formatting: %03d %2.1f", 12, 0.5 ); 76 | munit_assert_string_equal( str, "This is a string-builder. It supports formatting: 012 0.5" ); 77 | 78 | msh_array_free(str); 79 | munit_assert_size( msh_array_len(str), ==, 0 ); 80 | munit_assert_size( msh_array_cap(str), ==, 0 ); 81 | 82 | return MUNIT_OK; 83 | } 84 | 85 | MunitResult 86 | test_msh_array_copy(const MunitParameter params[], void* fixture) 87 | { 88 | (void) params; 89 | (void) fixture; 90 | 91 | msh_array( int16_t ) src = {0}; 92 | msh_array( int16_t ) dst = {0}; 93 | for( int16_t i = 0; i < 100; ++i ) 94 | { 95 | msh_array_push( src, i ); 96 | } 97 | msh_array_copy( dst, src, 20 ); 98 | munit_assert_size( msh_array_len(src), ==, 100 ); 99 | munit_assert_size( msh_array_len(dst), ==, 20 ); 100 | 101 | for( size_t i = 0; i < msh_array_len(dst); ++i ) 102 | { 103 | munit_assert_int32( src[i], ==, dst[i] ); 104 | } 105 | 106 | msh_array_free( src ); 107 | msh_array_free( dst ); 108 | munit_assert_size( msh_array_len(src), ==, 0 ); 109 | munit_assert_size( msh_array_len(dst), ==, 0 ); 110 | 111 | return MUNIT_OK; 112 | } 113 | 114 | MunitResult 115 | test_msh_map_api(const MunitParameter params[], void* fixture) 116 | { 117 | (void) params; 118 | (void) fixture; 119 | 120 | msh_map_t map = {0}; 121 | 122 | msh_map_init( &map, 255 ); 123 | munit_assert_size( msh_map_len(&map), ==, 0 ); 124 | munit_assert_size( msh_map_cap(&map), ==, 256 ); 125 | 126 | msh_map_insert( &map, 0, 9 ); 127 | msh_map_insert( &map, 0, 10 ); 128 | msh_map_insert( &map, 1, 11 ); 129 | munit_assert_size( msh_map_len( &map ), ==, 2 ); 130 | msh_map_insert( &map, 2, 12 ); 131 | msh_map_insert( &map, 3, 13 ); 132 | msh_map_insert( &map, 3, 14 ); 133 | munit_assert_size( msh_map_len(&map), ==, 4 ); 134 | 135 | uint64_t* val = NULL; 136 | val = msh_map_get( &map, 0 ); 137 | munit_assert_ptr_not_equal( val, NULL ); 138 | munit_assert_uint64( *val, ==, 10 ); 139 | 140 | val = msh_map_get( &map, 1 ); 141 | munit_assert_ptr_not_equal( val, NULL ); 142 | munit_assert_uint64( *val, ==, 11 ); 143 | 144 | val = msh_map_get( &map, 2 ); 145 | munit_assert_ptr_not_equal( val, NULL ); 146 | munit_assert_uint64( *val, ==, 12 ); 147 | 148 | val = msh_map_get( &map, 3 ); 149 | munit_assert_ptr_not_equal( val, NULL ); 150 | munit_assert_uint64( *val, ==, 14 ); 151 | 152 | val = msh_map_get( &map, 5 ); 153 | munit_assert_ptr_equal( val, NULL ); 154 | 155 | msh_map_remove( &map, 0 ); 156 | munit_assert_size( msh_map_len( &map ), ==, 3 ); 157 | 158 | val = msh_map_get( &map, 0 ); 159 | munit_assert_ptr_equal( val, NULL ); 160 | 161 | msh_map_remove( &map, 7 ); 162 | munit_assert_size( msh_map_len( &map ), ==, 3 ); 163 | 164 | msh_map_insert( &map, 0, 9 ); 165 | val = msh_map_get( &map, 0 ); 166 | munit_assert_ptr_not_equal( val, NULL ); 167 | munit_assert_uint64( *val, ==, 9 ); 168 | munit_assert_size( msh_map_len(&map), ==, 4 ); 169 | 170 | msh_map_free( &map ); 171 | munit_assert_size( msh_map_len( &map ), ==, 0 ); 172 | munit_assert_size( msh_map_cap( &map ), ==, 0 ); 173 | 174 | return MUNIT_OK; 175 | } 176 | 177 | MunitResult 178 | test_msh_map_iteration(const MunitParameter params[], void* fixture) 179 | { 180 | (void) params; 181 | (void) fixture; 182 | 183 | msh_map_t map = {0}; 184 | 185 | msh_map_init( &map, 255 ); 186 | munit_assert_size( msh_map_len(&map), ==, 0 ); 187 | munit_assert_size( msh_map_cap(&map), ==, 256 ); 188 | 189 | for( int32_t i = 0; i < 10; ++i ) 190 | { 191 | msh_map_insert( &map, i, i*10 + i); 192 | } 193 | 194 | uint64_t* keys = NULL; 195 | uint64_t* vals = NULL; 196 | msh_map_get_iterable_keys_and_vals( &map, &keys, &vals ); 197 | munit_assert_ptr_not_equal( keys, NULL ); 198 | munit_assert_ptr_not_equal( vals, NULL ); 199 | for( size_t i = 0; i < msh_map_len( &map ); ++i ) 200 | { 201 | if( keys[i] == i ) 202 | { 203 | munit_assert_uint64( vals[i], ==, keys[i]*10+keys[i] ); 204 | } 205 | } 206 | free( keys ); 207 | free( vals ); 208 | keys = NULL; vals = NULL; 209 | 210 | msh_map_get_iterable_keys( &map, &keys ); 211 | munit_assert_ptr_not_equal( keys, NULL ); 212 | for( size_t i = 0; i < msh_map_len( &map ); ++i ) 213 | { 214 | uint64_t* val = msh_map_get( &map, keys[i] ); 215 | munit_assert_ptr_not_equal( val, NULL); 216 | munit_assert_uint64( *val, ==, keys[i]*10 + keys[i] ); 217 | } 218 | free(keys); 219 | keys = NULL; 220 | 221 | msh_map_get_iterable_keys( &map, &vals ); 222 | munit_assert_ptr_not_equal( vals, NULL ); 223 | for( size_t i = 0; i < msh_map_len( &map ); ++i ) 224 | { 225 | uint64_t* val = msh_map_get( &map, i ); 226 | munit_assert_ptr_not_equal( val, NULL); 227 | munit_assert_uint64( *val, ==, i*10 + i ); 228 | } 229 | free( vals ); 230 | return MUNIT_OK; 231 | } 232 | 233 | 234 | MunitResult 235 | test_msh_map_str_hash(const MunitParameter params[], void* fixture) 236 | { 237 | (void) params; 238 | (void) fixture; 239 | 240 | uint64_t h1 = msh_hash_str( "Dog" ); 241 | uint64_t h2 = msh_hash_str( "Dog" ); 242 | uint64_t h3 = msh_hash_str( "dog" ); 243 | 244 | munit_assert_uint64( h1, ==, h2 ); 245 | munit_assert_uint64( h2, !=, h3 ); 246 | munit_assert_uint64( h1, !=, h3 ); 247 | 248 | return MUNIT_OK; 249 | } 250 | 251 | MunitResult 252 | test_msh_heap_api(const MunitParameter params[], void* fixture) 253 | { 254 | (void) params; 255 | (void) fixture; 256 | 257 | msh_array( int64_t ) heap = {0}; 258 | 259 | msh_array_push( heap, 1 ); 260 | msh_array_push( heap, 8 ); 261 | msh_array_push( heap, 10 ); 262 | msh_array_push( heap, 5 ); 263 | msh_array_push( heap, 3 ); 264 | 265 | munit_assert_false( msh_heap_isvalid_i64(heap, (int32_t)msh_array_len(heap)) ); 266 | msh_heap_make_i64( heap, msh_array_len(heap)); 267 | munit_assert_true( msh_heap_isvalid_i64(heap, (int32_t)msh_array_len(heap)) ); 268 | 269 | msh_array_push( heap, 18 ); 270 | msh_heap_push_i64( heap, msh_array_len(heap) ); 271 | munit_assert_int64( heap[0], ==, 18 ); 272 | munit_assert_true( msh_heap_isvalid_i64(heap, (int32_t)msh_array_len(heap)) ); 273 | 274 | msh_array_push( heap, 17 ); 275 | msh_heap_push_i64( heap, msh_array_len(heap) ); 276 | 277 | munit_assert_int64( heap[0], ==, 18 ); 278 | munit_assert_true( msh_heap_isvalid_i64(heap, (int32_t)msh_array_len(heap)) ); 279 | 280 | msh_heap_pop_i64( heap, msh_array_len(heap) ); 281 | int64_t max = *msh_array_back(heap); 282 | msh_array_pop(heap); 283 | 284 | munit_assert_int64( heap[0], ==, 17 ); 285 | munit_assert_int64( max, ==, 18 ); 286 | munit_assert_true( msh_heap_isvalid_i64(heap, (int32_t)msh_array_len(heap)) ); 287 | 288 | return MUNIT_OK; 289 | } 290 | 291 | MunitResult 292 | test_msh_disjoint_set_api(const MunitParameter params[], void* fixture) 293 | { 294 | (void) params; 295 | (void) fixture; 296 | 297 | static const int32_t map_size = 6; 298 | char map[] = "111000" 299 | "111000" 300 | "100010" 301 | "011000" 302 | "011001" 303 | "011010"; 304 | static const uint64_t num_sets = 6; 305 | int32_t n_vals = map_size*map_size; 306 | msh_dset_t dset = {0}; 307 | msh_dset_init( &dset, n_vals ); 308 | 309 | int32_t first_zero_idx = 0; 310 | for( int32_t i = 0; i < map_size * map_size; ++i ) 311 | { 312 | if( map[i] == '0' ) 313 | { 314 | first_zero_idx = i; 315 | break; 316 | } 317 | } 318 | 319 | for( int32_t y = 0; y < map_size; ++y ) 320 | { 321 | for( int32_t x = 0; x < map_size; ++x ) 322 | { 323 | int32_t idx = y * map_size + x; 324 | char c = map[idx]; 325 | if( c == '0' ) // background 326 | { 327 | msh_dset_union( &dset, first_zero_idx, idx ); 328 | } 329 | else // islands 330 | { 331 | int32_t idx_u = msh_max( y - 1, 0 ) * map_size + x; 332 | int32_t idx_d = msh_min( y + 1, map_size - 1 ) * map_size + x; 333 | int32_t idx_l = y * map_size + msh_max( x - 1, 0 ); 334 | int32_t idx_r = y * map_size + msh_min( x + 1, map_size - 1 ); 335 | if( c == map[idx_u] ) { msh_dset_union( &dset, idx_u, idx ); } 336 | if( c == map[idx_d] ) { msh_dset_union( &dset, idx_d, idx ); } 337 | if( c == map[idx_l] ) { msh_dset_union( &dset, idx_l, idx ); } 338 | if( c == map[idx_r] ) { msh_dset_union( &dset, idx_r, idx ); } 339 | } 340 | } 341 | } 342 | 343 | munit_assert_uint64( dset.num_sets, ==, num_sets ); 344 | munit_assert_uint64( msh_dset_find( &dset, 1 ), ==, msh_dset_find( &dset, 6 ) ); 345 | munit_assert_uint64( msh_dset_find( &dset, 5 ), ==, msh_dset_find( &dset, 11 ) ); 346 | 347 | msh_dset_term(&dset); 348 | munit_assert_uint64( dset.num_sets, ==, 0 ); 349 | munit_assert_ptr_null( dset.elems ); 350 | 351 | return MUNIT_OK; 352 | } -------------------------------------------------------------------------------- /tests/msh_generic_vec_math_test.inl: -------------------------------------------------------------------------------- 1 | 2 | void 3 | assert_equal_vec3i( msh_vec3i_t v, int a, int b, int c ) 4 | { 5 | munit_assert_int( v.x, ==, a ); 6 | munit_assert_int( v.y, ==, b ); 7 | munit_assert_int( v.z, ==, c ); 8 | } 9 | 10 | void 11 | assert_equal_vec3f( msh_vec3f_t v, float a, float b, float c ) 12 | { 13 | munit_assert_float( v.x, ==, a ); 14 | munit_assert_float( v.y, ==, b ); 15 | munit_assert_float( v.z, ==, c ); 16 | } 17 | 18 | void 19 | assert_equal_vec3d( msh_vec3d_t v, double a, double b, double c ) 20 | { 21 | munit_assert_double( v.x, ==, a ); 22 | munit_assert_double( v.y, ==, b ); 23 | munit_assert_double( v.z, ==, c ); 24 | } 25 | 26 | MunitResult 27 | test_msh_vector( const MunitParameter params[], void* fixture ) 28 | { 29 | (void) params; 30 | (void) fixture; 31 | 32 | msh_vec3i_t v01 = msh_vec3i_init( 1, 2, 3 ); 33 | msh_vec3f_t v02 = msh_vec3f_init( 4.0, 5.0, 6.0 ); 34 | msh_vec3d_t v03 = msh_vec3d_init( 7.0, 8.0, 9.0 ); 35 | assert_equal_vec3i( v01, 1, 2, 3 ); 36 | assert_equal_vec3f( v02, 4.0, 5.0, 6.0 ); 37 | assert_equal_vec3d( v03, 7.0, 8.0, 9.0 ); 38 | 39 | msh_vec3i_t v04 = msh_vec3i_init( 1, 2, 3 ); 40 | msh_vec3i_t v05 = msh_vec3i_add( v01, v04 ); 41 | assert_equal_vec3i( v05, 2, 4, 6 ); 42 | 43 | msh_vec3i_t v06 = msh_vec3i_sub( v05, msh_vec3i_ones() ); 44 | assert_equal_vec3i( v06, 1, 3, 5 ); 45 | 46 | msh_vec3i_t v07 = msh_vec3i_mul( v06, msh_vec3i_init( 2, 4, 6 ) ); 47 | assert_equal_vec3i( v07, 2, 12, 30 ); 48 | 49 | msh_vec3i_t v08 = msh_vec3i_div( v07, msh_vec3i_init(2, 4, 6)); 50 | assert_equal_vec3i( v08, 1, 3, 5 ); 51 | 52 | int dot = msh_vec3i_dot( v08, v08 ); 53 | munit_assert_int( dot, ==, 35 ); 54 | 55 | msh_vec3i_t v09 = msh_scalar_vec3i_mul( 2, v06 ); 56 | assert_equal_vec3i( v09, 2, 6, 10 ); 57 | 58 | return MUNIT_OK; 59 | } 60 | -------------------------------------------------------------------------------- /tests/msh_hash_grid_test.c: -------------------------------------------------------------------------------- 1 | /* Poor man's tests for various parts of msh_std.h */ 2 | #define MSH_STD_INCLUDE_LIBC_HEADERS 3 | #define MSH_STD_IMPLEMENTATION 4 | #define MSH_VEC_MATH_IMPLEMENTATION 5 | #define MSH_HASH_GRID_IMPLEMENTATION 6 | #include "msh/msh_std.h" 7 | #include "msh/msh_vec_math.h" 8 | #include "msh/msh_hash_grid.h" 9 | 10 | msh_vec3_t 11 | generate_random_point_within_sphere_shell( msh_rand_ctx_t* rand_gen, msh_vec3_t center, 12 | real32_t radius_a, real32_t radius_b ) 13 | { 14 | assert( radius_a > radius_b ); 15 | assert( radius_a >= 0.0f ); 16 | assert( radius_b >= 0.0f ); 17 | 18 | real32_t x = 2.0f * msh_rand_nextf( rand_gen ) - 1.0f; 19 | real32_t y = 2.0f * msh_rand_nextf( rand_gen ) - 1.0f; 20 | real32_t z = 2.0f * msh_rand_nextf( rand_gen ) - 1.0f; 21 | real32_t s = msh_rand_nextf( rand_gen ) * (radius_a - radius_b) + radius_b ; 22 | msh_vec3_t pt = msh_vec3( x, y, z ); 23 | pt = msh_vec3_scalar_mul( msh_vec3_normalize( pt ), s ); 24 | pt = msh_vec3_add( pt, center ); 25 | return pt; 26 | } 27 | 28 | msh_vec3_t 29 | generate_random_point_within_sphere( msh_rand_ctx_t* rand_gen, msh_vec3_t center, real32_t radius ) 30 | { 31 | return generate_random_point_within_sphere_shell( rand_gen, center, radius, 0.0f ); 32 | } 33 | 34 | void 35 | knn_search_test() 36 | { 37 | msh_rand_ctx_t rand_gen = {0}; 38 | msh_rand_init( &rand_gen, 12346ULL ); 39 | msh_array( msh_vec3_t ) pts = {0}; 40 | 41 | // generate knn pts around origin 42 | size_t knn = 10; 43 | real32_t radius_a = 0.3; 44 | for( size_t i = 0; i < knn; ++i ) 45 | { 46 | msh_vec3_t pt = generate_random_point_within_sphere( &rand_gen, msh_vec3_zeros(), radius_a ); 47 | msh_array_push( pts, pt ); 48 | } 49 | 50 | // generate 1000 points around in a shell 51 | real32_t radius_b = 0.6; 52 | real32_t radius_c = 0.4; 53 | for( size_t i = 0; i < 1000; ++i ) 54 | { 55 | msh_vec3_t pt = generate_random_point_within_sphere_shell( &rand_gen, msh_vec3_zeros(), 56 | radius_b, radius_c ); 57 | msh_array_push( pts, pt ); 58 | } 59 | 60 | // setup the hash grid 61 | msh_hash_grid_t hg = {0}; 62 | msh_hash_grid_init_3d( &hg, (real32_t*)&pts[0], msh_array_len(pts), 0.1 ); 63 | 64 | msh_hash_grid_search_desc_t search_opts = 65 | { 66 | .n_query_pts = 1, 67 | .k = knn, 68 | .distances_sq = malloc( sizeof(real32_t) * knn ), 69 | .indices = malloc( sizeof(int32_t) * knn ), 70 | }; 71 | 72 | // check for points produced around query 73 | msh_vec3_t query = msh_vec3_zeros(); 74 | search_opts.query_pts = (float*)&query; 75 | size_t n_neigh = msh_hash_grid_knn_search( &hg, &search_opts ); 76 | assert( n_neigh == knn ); 77 | for( size_t i = 0; i < n_neigh; ++i ) 78 | { 79 | assert( search_opts.indices[i] < (int32_t)knn ); 80 | } 81 | } 82 | 83 | void 84 | radius_search_test() 85 | { 86 | msh_rand_ctx_t rand_gen = {0}; 87 | msh_rand_init( &rand_gen, 12346ULL ); 88 | msh_array( msh_vec3_t ) pts = {0}; 89 | 90 | // randomly generate 10 points in a volume of a sphere with 0.1 radius around origin 91 | size_t n_pts_a = 10; 92 | real32_t radius_a = 0.1; 93 | for( size_t i = 0; i < n_pts_a; ++i ) 94 | { 95 | msh_vec3_t pt = generate_random_point_within_sphere( &rand_gen, msh_vec3_zeros(), radius_a ); 96 | msh_array_push( pts, pt ); 97 | } 98 | 99 | // randomly generate 100 points in a volume of a sphere with 0.3 radius around pt. 3.0, 3.0, 3.0 100 | size_t n_pts_b = 100; 101 | real32_t radius_b = 0.3; 102 | for( size_t i = 0; i < n_pts_b; ++i ) 103 | { 104 | msh_vec3_t pt = generate_random_point_within_sphere( &rand_gen, msh_vec3(3.0f, 3.0f, 3.0f), 105 | radius_b ); 106 | msh_array_push( pts, pt ); 107 | } 108 | 109 | 110 | // now randomly generate 1000 points in a volume that is a difference of two spheres 111 | real32_t radius_c = 0.5; 112 | real32_t radius_d = 0.6; 113 | for( size_t i = 0; i < 1000; ++i ) 114 | { 115 | msh_vec3_t pt = generate_random_point_within_sphere_shell( &rand_gen, msh_vec3_zeros(), 116 | radius_d, radius_c ); 117 | msh_array_push( pts, pt ); 118 | } 119 | 120 | 121 | 122 | // Move all points away from origin by the vector given by a query_a pt 123 | msh_vec3_t query_a = msh_vec3( msh_rand_nextf(&rand_gen), 124 | msh_rand_nextf(&rand_gen), 125 | msh_rand_nextf(&rand_gen) ); 126 | msh_vec3_t query_b = msh_vec3_add( query_a, msh_vec3( 3.0f, 3.0f, 3.0f ) ); 127 | for( size_t i = 0; i < msh_array_len(pts); ++i ) 128 | { 129 | pts[i] = msh_vec3_add( query_a, pts[i] ); 130 | } 131 | 132 | 133 | // setup the hash grid 134 | msh_hash_grid_t hg = {0}; 135 | msh_hash_grid_init_3d( &hg, (real32_t*)&pts[0], msh_array_len(pts), radius_a ); 136 | 137 | size_t max_n_neigh = msh_max( n_pts_a, n_pts_b); 138 | msh_hash_grid_search_desc_t search_opts = 139 | { 140 | .n_query_pts = 1, 141 | .max_n_neigh = max_n_neigh, 142 | .distances_sq = malloc( sizeof(real32_t) * max_n_neigh ), 143 | .indices = malloc( sizeof(int32_t) * max_n_neigh ), 144 | .sort = 1 145 | }; 146 | 147 | // check for points produced around query_a 148 | search_opts.query_pts = (float*)&query_a; 149 | search_opts.radius = radius_a; 150 | size_t n_neigh = msh_hash_grid_radius_search( &hg, &search_opts ); 151 | assert( n_neigh == n_pts_a ); 152 | for( size_t i = 0; i < n_pts_a; ++i ) 153 | { 154 | assert( search_opts.indices[i] < (int32_t)n_pts_a ); 155 | } 156 | 157 | 158 | // check for points produced around query_b 159 | search_opts.query_pts = (float*)&query_b; 160 | search_opts.radius = radius_b; 161 | n_neigh = msh_hash_grid_radius_search( &hg, &search_opts ); 162 | assert( n_neigh == n_pts_b ); 163 | for( size_t i = 0; i < n_pts_b; ++i ) 164 | { 165 | assert( (search_opts.indices[i] >= (int32_t)n_pts_a) && 166 | (search_opts.indices[i] < (int32_t)(n_pts_a + n_pts_b)) ); 167 | } 168 | } 169 | 170 | int 171 | main() 172 | { 173 | printf( "Running msh_hash_grid.h tests!\n" ); 174 | 175 | printf( "| Testing msh_hash_grid_radius_search\n" ); 176 | radius_search_test(); 177 | printf( "| -> Passed!\n" ); 178 | 179 | printf( "| Testing msh_hash_grid_knn_search\n" ); 180 | knn_search_test(); 181 | printf( "| -> Passed!\n" ); 182 | 183 | return 1; 184 | } -------------------------------------------------------------------------------- /tests/msh_jobs_test.c: -------------------------------------------------------------------------------- 1 | // #define MSH_STD_INCLUDE_LIBC_HEADERS 2 | #define MSH_JOBS_IMPLEMENTATION 3 | // #define MSH_STD_IMPLEMENTATION 4 | // #include "msh_std.h" 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include "experimental/msh_jobs.h" 11 | 12 | 13 | MSH_JOBS_JOB_SIGNATURE(task_a) 14 | { 15 | // msh_sleep(1000); 16 | Sleep(1000); 17 | printf("%6d | TASK_A %s\n", thread_idx, (char*)params ); 18 | return 0; 19 | } 20 | 21 | int32_t 22 | main() 23 | { 24 | msh_jobs_ctx_t work_ctx = {0}; 25 | msh_jobs_init_ctx( &work_ctx, 1 ); 26 | printf("Running on %s\n", msh_jobs_get_platform_name() ); 27 | printf("Spawned Thread Count: %d | Logical Core Count: %d\n", 28 | work_ctx.thread_count, 29 | work_ctx.processor_info.logical_core_count ); 30 | 31 | // uint64_t t1, t2; 32 | // t1 = msh_time_now(); 33 | 34 | msh_jobs_push_work( &work_ctx, task_a, "00" ); 35 | msh_jobs_push_work( &work_ctx, task_a, "01" ); 36 | msh_jobs_push_work( &work_ctx, task_a, "02" ); 37 | msh_jobs_push_work( &work_ctx, task_a, "03" ); 38 | msh_jobs_push_work( &work_ctx, task_a, "04" ); 39 | msh_jobs_push_work( &work_ctx, task_a, "05" ); 40 | msh_jobs_push_work( &work_ctx, task_a, "06" ); 41 | msh_jobs_push_work( &work_ctx, task_a, "07" ); 42 | msh_jobs_push_work( &work_ctx, task_a, "08" ); 43 | msh_jobs_push_work( &work_ctx, task_a, "09" ); 44 | msh_jobs_push_work( &work_ctx, task_a, "10" ); 45 | msh_jobs_push_work( &work_ctx, task_a, "11" ); 46 | msh_jobs_push_work( &work_ctx, task_a, "12" ); 47 | msh_jobs_push_work( &work_ctx, task_a, "13" ); 48 | msh_jobs_push_work( &work_ctx, task_a, "14" ); 49 | msh_jobs_push_work( &work_ctx, task_a, "15" ); 50 | msh_jobs_push_work( &work_ctx, task_a, "16" ); 51 | msh_jobs_push_work( &work_ctx, task_a, "17" ); 52 | msh_jobs_push_work( &work_ctx, task_a, "18" ); 53 | msh_jobs_push_work( &work_ctx, task_a, "19" ); 54 | msh_jobs_push_work( &work_ctx, task_a, "20" ); 55 | msh_jobs_push_work( &work_ctx, task_a, "21" ); 56 | msh_jobs_push_work( &work_ctx, task_a, "22" ); 57 | msh_jobs_push_work( &work_ctx, task_a, "23" ); 58 | 59 | printf("TEST\n"); 60 | 61 | msh_jobs_complete_all_work( &work_ctx ); 62 | // t2 = msh_time_now(); 63 | // printf("Time: %f\n", msh_time_diff_ms(t2, t1)); 64 | 65 | msh_jobs_term_ctx( &work_ctx ); 66 | 67 | 68 | 69 | return 0; 70 | } 71 | -------------------------------------------------------------------------------- /tests/msh_stats_test.inl: -------------------------------------------------------------------------------- 1 | MunitResult 2 | test_msh_discrete_distrib_sampling( const MunitParameter params[], void* fixture ) 3 | { 4 | (void) params; 5 | (void) fixture; 6 | 7 | double weights[] = { 1, 2, 3, 4, 5, 4, 3, 2, 1 }; 8 | msh_discrete_distrib_t sampler = {0}; 9 | msh_discrete_distribution_init( &sampler, weights, msh_count_of(weights), munit_rand_uint32() ); 10 | munit_assert_size( msh_count_of(weights), ==, sampler.n_weights ); 11 | 12 | int32_t n_samples = 1000000; 13 | int32_t counts[msh_count_of(weights)] = {0}; 14 | for( int32_t i = 0; i < n_samples; ++i ) 15 | { 16 | int32_t sample = msh_discrete_distribution_sample( &sampler ); 17 | counts[sample]++; 18 | } 19 | 20 | munit_assert_double_equal( ((float)counts[4] / counts[0]), (double)weights[4], 1 ); 21 | munit_assert_double_equal( ((float)counts[4] / counts[8]), (double)weights[4], 1 ); 22 | munit_assert_double_equal( ((float)counts[1] / counts[7]), 1.0, 1 ); 23 | munit_assert_double_equal( ((float)counts[2] / counts[6]), 1.0, 1 ); 24 | munit_assert_double_equal( ((float)counts[3] / counts[5]), 1.0, 1 ); 25 | 26 | for( size_t i = 0; i < msh_count_of(weights); ++i ) 27 | { 28 | weights[i] = 0; 29 | counts[i] = 0; 30 | } 31 | weights[8] = 1; 32 | 33 | msh_discrete_distribution_update( &sampler, weights, msh_count_of(weights) ); 34 | for( int32_t i = 0; i < n_samples; ++i ) 35 | { 36 | int32_t sample = msh_discrete_distribution_sample( &sampler ); 37 | counts[sample]++; 38 | } 39 | munit_assert_int32( counts[8], ==, n_samples ); 40 | 41 | msh_discrete_distribution_free( &sampler ); 42 | munit_assert_size( 0, ==, sampler.n_weights ); 43 | return MUNIT_OK; 44 | } 45 | -------------------------------------------------------------------------------- /tests/msh_tests.c: -------------------------------------------------------------------------------- 1 | // Compile with MSVC: 2 | // cl -Fobin\ -Febin\msh_tests.exe -I . -I tests\munit\ -I tests\ tests\munit\munit.c tests\msh_tests.c 3 | 4 | #define MSH_STD_INCLUDE_LIBC_HEADERS 5 | #define MSH_STD_IMPLEMENTATION 6 | #define MSH_VEC_MATH_IMPLEMENTATION 7 | #define MSH_VEC_MATH_DOUBLE_PRECISION 8 | #define MSH_CONTAINERS_IMPLEMENTATION 9 | #define MSH_ARGPARSE_IMPLEMENTATION 10 | #define _CRT_SECURE_NO_WARNINGS 11 | #include "msh_std.h" 12 | #include "msh_containers.h" 13 | #include "msh_vec_math.h" 14 | #include "msh_argparse.h" 15 | 16 | #include "tests/munit/munit.h" 17 | 18 | #include "tests/msh_containers_test.inl" 19 | #include "tests/msh_stats_test.inl" 20 | #include "tests/msh_vec_math_test.inl" 21 | #include "tests/msh_argparse_test.inl" 22 | 23 | 24 | int32_t 25 | main(int argc, char* const argv[MUNIT_ARRAY_PARAM(argc + 1)]) 26 | { 27 | MunitTest argparse_tests[] = 28 | { 29 | { .name = (char*)"/argparse", .test = test_msh_argparse }, 30 | { 0 } 31 | }; 32 | 33 | MunitSuite argparse_test_suite = 34 | { 35 | .prefix = (char*) "/argparse", 36 | .tests = argparse_tests, 37 | .suites = NULL, 38 | .iterations = 1, 39 | .options = MUNIT_SUITE_OPTION_NONE 40 | }; 41 | 42 | MunitTest vec_math_tests[] = 43 | { 44 | { .name = (char*) "/vector_init", .test = test_msh_vec_math_vector_init }, 45 | { .name = (char*) "/vector_arithmetic", .test = test_msh_vec_math_vector_arithmetic }, 46 | { .name = (char*) "/vector_convert", .test= test_msh_vec_math_vector_convert }, 47 | { 0 } 48 | }; 49 | 50 | MunitSuite vec_math_test_suite = 51 | { 52 | .prefix = (char*) "/vec_math", 53 | .tests = vec_math_tests, 54 | .suites = NULL, 55 | .iterations = 1, 56 | .options = MUNIT_SUITE_OPTION_NONE 57 | }; 58 | 59 | MunitTest array_tests[] = 60 | { 61 | { .name = (char*) "/api", .test = test_msh_array_api, }, 62 | { .name = (char*) "/copy", .test = test_msh_array_copy, }, 63 | { .name = (char*) "/printf", .test = test_msh_array_printf, }, 64 | { 0 } 65 | }; 66 | 67 | MunitSuite array_test_suite = 68 | { 69 | .prefix = (char*) "/array", 70 | .tests = array_tests, 71 | .suites = NULL, 72 | .iterations = 1, 73 | .options = MUNIT_SUITE_OPTION_NONE 74 | }; 75 | 76 | MunitTest misc_test[] = 77 | { 78 | { .name = (char*) "/disjoint_set_api", .test = test_msh_disjoint_set_api, }, 79 | { .name = (char*) "/heap_api", .test = test_msh_heap_api, }, 80 | { 0 } 81 | }; 82 | 83 | MunitSuite misc_test_suite = 84 | { 85 | .prefix = (char*) "/misc", 86 | .tests = misc_test, 87 | .suites = NULL, 88 | .iterations = 1, 89 | .options = MUNIT_SUITE_OPTION_NONE 90 | }; 91 | 92 | MunitTest map_tests[] = 93 | { 94 | { .name = (char*) "/api", .test = test_msh_map_api, }, 95 | { .name = (char*) "/iteration", .test = test_msh_map_iteration, }, 96 | { .name = (char*) "/str_hash", .test = test_msh_map_str_hash, }, 97 | { 0 } 98 | }; 99 | 100 | MunitSuite map_test_suite = 101 | { 102 | .prefix = (char*) "/map", 103 | .tests = map_tests, 104 | .suites = NULL, 105 | .iterations = 1, 106 | .options = MUNIT_SUITE_OPTION_NONE 107 | }; 108 | 109 | MunitTest stats_tests[] = 110 | { 111 | { .name = (char*) "/pdf_sampling", .test = test_msh_discrete_distrib_sampling, }, 112 | { 0 } 113 | }; 114 | 115 | MunitSuite stats_test_suite = 116 | { 117 | .prefix = (char*) "/stats", 118 | .tests = stats_tests, 119 | .suites = NULL, 120 | .iterations = 1, 121 | .options = MUNIT_SUITE_OPTION_NONE 122 | }; 123 | 124 | MunitSuite test_suites_array[] = 125 | { 126 | argparse_test_suite, 127 | vec_math_test_suite, 128 | array_test_suite, 129 | misc_test_suite, 130 | map_test_suite, 131 | stats_test_suite, 132 | {0} 133 | }; 134 | 135 | const MunitSuite test_suites = 136 | { 137 | .prefix = (char*) "msh", 138 | .tests = NULL, 139 | .suites = test_suites_array, 140 | .iterations = 1, 141 | .options = MUNIT_SUITE_OPTION_NONE 142 | }; 143 | 144 | return munit_suite_main( &test_suites, NULL, argc, argv ); 145 | } -------------------------------------------------------------------------------- /tests/msh_vec_math_test.inl: -------------------------------------------------------------------------------- 1 | static inline void 2 | assert_equal_vec2(msh_vec2_t v, msh_scalar_t a, msh_scalar_t b) 3 | { 4 | #ifndef MSH_VEC_MATH_DOUBLE_PRECISION 5 | munit_assert_float(v.x, ==, a); 6 | munit_assert_float(v.y, ==, b); 7 | #else 8 | munit_assert_double(v.x, ==, a); 9 | munit_assert_double(v.y, ==, b); 10 | #endif 11 | } 12 | 13 | static inline void 14 | assert_equal_vec3(msh_vec3_t v, msh_scalar_t a, msh_scalar_t b, msh_scalar_t c) 15 | { 16 | #ifndef MSH_VEC_MATH_DOUBLE_PRECISION 17 | munit_assert_float(v.x, ==, a); 18 | munit_assert_float(v.y, ==, b); 19 | munit_assert_float(v.z, ==, c); 20 | #else 21 | munit_assert_double(v.x, ==, a); 22 | munit_assert_double(v.y, ==, b); 23 | munit_assert_double(v.z, ==, c); 24 | #endif 25 | } 26 | 27 | static inline void 28 | assert_equal_vec4(msh_vec4_t v, 29 | msh_scalar_t a, 30 | msh_scalar_t b, 31 | msh_scalar_t c, 32 | msh_scalar_t d) 33 | { 34 | #ifndef MSH_VEC_MATH_DOUBLE_PRECISION 35 | munit_assert_float(v.x, ==, a); 36 | munit_assert_float(v.y, ==, b); 37 | munit_assert_float(v.z, ==, c); 38 | munit_assert_float(v.w, ==, d); 39 | #else 40 | munit_assert_double(v.x, ==, a); 41 | munit_assert_double(v.y, ==, b); 42 | munit_assert_double(v.z, ==, c); 43 | munit_assert_double(v.w, ==, d); 44 | #endif 45 | } 46 | 47 | MunitResult 48 | test_msh_vec_math_vector_init(const MunitParameter params[], void* fixture) 49 | { 50 | (void)fixture; 51 | (void)params; 52 | msh_vec2_t v2; 53 | msh_vec3_t v3; 54 | msh_vec4_t v4; 55 | v2 = (msh_vec2_t) {.x = 0.0, .y = 0.0}; 56 | v3 = msh_vec3_zeros(); 57 | v4 = msh_vec4_zeros(); 58 | assert_equal_vec2(v2, 0.0, 0.0); 59 | assert_equal_vec3(v3, 0.0, 0.0, 0.0); 60 | assert_equal_vec4(v4, 0.0, 0.0, 0.0, 0.0); 61 | 62 | v2 = msh_vec2_ones(); 63 | v3 = msh_vec3_ones(); 64 | v4 = msh_vec4_ones(); 65 | assert_equal_vec2(v2, 1.0, 1.0); 66 | assert_equal_vec3(v3, 1.0, 1.0, 1.0); 67 | assert_equal_vec4(v4, 1.0, 1.0, 1.0, 1.0); 68 | 69 | v2 = msh_vec2_posx(); 70 | v3 = msh_vec3_posx(); 71 | v4 = msh_vec4_posx(); 72 | assert_equal_vec2(v2, 1.0, 0.0); 73 | assert_equal_vec3(v3, 1.0, 0.0, 0.0); 74 | assert_equal_vec4(v4, 1.0, 0.0, 0.0, 0.0); 75 | 76 | v2 = msh_vec2_posy(); 77 | v3 = msh_vec3_posy(); 78 | v4 = msh_vec4_posy(); 79 | assert_equal_vec2(v2, 0.0, 1.0); 80 | assert_equal_vec3(v3, 0.0, 1.0, 0.0); 81 | assert_equal_vec4(v4, 0.0, 1.0, 0.0, 0.0); 82 | 83 | v3 = msh_vec3_posz(); 84 | v4 = msh_vec4_posz(); 85 | assert_equal_vec3(v3, 0.0, 0.0, 1.0); 86 | assert_equal_vec4(v4, 0.0, 0.0, 1.0, 0.0); 87 | 88 | v4 = msh_vec4_posw(); 89 | assert_equal_vec4(v4, 0.0, 0.0, 0.0, 1.0); 90 | 91 | v2 = msh_vec2_negx(); 92 | v3 = msh_vec3_negx(); 93 | v4 = msh_vec4_negx(); 94 | assert_equal_vec2(v2, -1.0, 0.0); 95 | assert_equal_vec3(v3, -1.0, 0.0, 0.0); 96 | assert_equal_vec4(v4, -1.0, 0.0, 0.0, 0.0); 97 | 98 | v2 = msh_vec2_negy(); 99 | v3 = msh_vec3_negy(); 100 | v4 = msh_vec4_negy(); 101 | assert_equal_vec2(v2, 0.0, -1.0); 102 | assert_equal_vec3(v3, 0.0, -1.0, 0.0); 103 | assert_equal_vec4(v4, 0.0, -1.0, 0.0, 0.0); 104 | 105 | v3 = msh_vec3_negz(); 106 | v4 = msh_vec4_negz(); 107 | assert_equal_vec3(v3, 0.0, 0.0, -1.0); 108 | assert_equal_vec4(v4, 0.0, 0.0, -1.0, 0.0); 109 | 110 | v4 = msh_vec4_negw(); 111 | assert_equal_vec4(v4, 0.0, 0.0, 0.0, -1.0); 112 | 113 | v2 = msh_vec2(1.0, 2.0); 114 | v3 = msh_vec3(1.0, 2.0, 3.0); 115 | v4 = msh_vec4(1.0, 2.0, 3.0, 4.0); 116 | assert_equal_vec2(v2, 1.0, 2.0); 117 | assert_equal_vec3(v3, 1.0, 2.0, 3.0); 118 | assert_equal_vec4(v4, 1.0, 2.0, 3.0, 4.0); 119 | 120 | v2 = msh_vec2_value(2.0); 121 | v3 = msh_vec3_value(2.0); 122 | v4 = msh_vec4_value(2.0); 123 | assert_equal_vec2(v2, 2.0, 2.0); 124 | assert_equal_vec3(v3, 2.0, 2.0, 2.0); 125 | assert_equal_vec4(v4, 2.0, 2.0, 2.0, 2.0); 126 | 127 | return MUNIT_OK; 128 | } 129 | 130 | MunitResult 131 | test_msh_vec_math_vector_convert(const MunitParameter params[], void* fixture) 132 | { 133 | (void)fixture; 134 | (void)params; 135 | msh_vec2_t v2; 136 | msh_vec3_t v3; 137 | msh_vec4_t v4; 138 | v4 = msh_vec4(1.0, 2.0, 3.0, 4.0); 139 | v3 = msh_vec4_to_vec3(v4); 140 | 141 | assert_equal_vec3(v3, 1.0, 2.0, 3.0); 142 | 143 | v2 = msh_vec3_to_vec2(v3); 144 | assert_equal_vec2(v2, 1.0, 2.0); 145 | 146 | v4 = msh_vec2_to_vec4(v2, 5.0f, 6.0f); 147 | assert_equal_vec4(v4, 1.0, 2.0, 5.0, 6.0); 148 | 149 | v4 = msh_vec3_to_vec4(v3, 8.0f); 150 | assert_equal_vec4(v4, 1.0, 2.0, 3.0, 8.0); 151 | 152 | return MUNIT_OK; 153 | } 154 | 155 | MunitResult 156 | test_msh_vec_math_vector_arithmetic(const MunitParameter params[], 157 | void* fixture) 158 | { 159 | (void)fixture; 160 | (void)params; 161 | msh_scalar_t d = 2.0; 162 | msh_vec2_t a2, b2, c2; 163 | a2 = msh_vec2(1.0, 2.0); 164 | b2 = msh_vec2(3.0, 4.0); 165 | 166 | c2 = msh_vec2_add(a2, b2); 167 | assert_equal_vec2(c2, 4.0, 6.0); 168 | c2 = msh_vec2_sub(b2, a2); 169 | assert_equal_vec2(c2, 2.0, 2.0); 170 | c2 = msh_vec2_mul(a2, b2); 171 | assert_equal_vec2(c2, 3.0, 8.0); 172 | c2 = msh_vec2_div(b2, a2); 173 | assert_equal_vec2(c2, 3.0, 2.0); 174 | 175 | c2 = msh_vec2_scalar_add(a2, d); 176 | assert_equal_vec2(c2, 3.0, 4.0); 177 | c2 = msh_vec2_scalar_sub(a2, d); 178 | assert_equal_vec2(c2, -1.0, 0.0); 179 | c2 = msh_vec2_scalar_mul(a2, d); 180 | assert_equal_vec2(c2, 2.0, 4.0); 181 | c2 = msh_vec2_scalar_div(a2, d); 182 | assert_equal_vec2(c2, 0.5, 1.0); 183 | 184 | msh_vec3_t a3, b3, c3; 185 | a3 = msh_vec3(1.0, 2.0, 3.0); 186 | b3 = msh_vec3(3.0, 4.0, 6.0); 187 | 188 | c3 = msh_vec3_add(a3, b3); 189 | assert_equal_vec3(c3, 4.0, 6.0, 9.0); 190 | c3 = msh_vec3_sub(b3, a3); 191 | assert_equal_vec3(c3, 2.0, 2.0, 3.0); 192 | c3 = msh_vec3_mul(a3, b3); 193 | assert_equal_vec3(c3, 3.0, 8.0, 18.0); 194 | c3 = msh_vec3_div(b3, a3); 195 | assert_equal_vec3(c3, 3.0, 2.0, 2.0); 196 | 197 | c3 = msh_vec3_scalar_add(a3, d); 198 | assert_equal_vec3(c3, 3.0, 4.0, 5.0); 199 | c3 = msh_vec3_scalar_sub(a3, d); 200 | assert_equal_vec3(c3, -1.0, 0.0, 1.0); 201 | c3 = msh_vec3_scalar_mul(a3, d); 202 | assert_equal_vec3(c3, 2.0, 4.0, 6.0); 203 | c3 = msh_vec3_scalar_div(a3, d); 204 | assert_equal_vec3(c3, 0.5, 1.0, 1.5); 205 | 206 | msh_vec4_t a4, b4, c4; 207 | a4 = msh_vec4(1.0, 2.0, 3.0, 4.0); 208 | b4 = msh_vec4(3.0, 4.0, 6.0, 8.0); 209 | 210 | c4 = msh_vec4_add(a4, b4); 211 | assert_equal_vec4(c4, 4.0, 6.0, 9.0, 12.0); 212 | c4 = msh_vec4_sub(b4, a4); 213 | assert_equal_vec4(c4, 2.0, 2.0, 3.0, 4.0); 214 | c4 = msh_vec4_mul(a4, b4); 215 | assert_equal_vec4(c4, 3.0, 8.0, 18.0, 32.0); 216 | c4 = msh_vec4_div(b4, a4); 217 | assert_equal_vec4(c4, 3.0, 2.0, 2.0, 2.0); 218 | 219 | c4 = msh_vec4_scalar_add(a4, d); 220 | assert_equal_vec4(c4, 3.0, 4.0, 5.0, 6.0); 221 | c4 = msh_vec4_scalar_sub(a4, d); 222 | assert_equal_vec4(c4, -1.0, 0.0, 1.0, 2.0); 223 | c4 = msh_vec4_scalar_mul(a4, d); 224 | assert_equal_vec4(c4, 2.0, 4.0, 6.0, 8.0); 225 | c4 = msh_vec4_scalar_div(a4, d); 226 | assert_equal_vec4(c4, 0.5, 1.0, 1.5, 2.0); 227 | 228 | return MUNIT_OK; 229 | } -------------------------------------------------------------------------------- /tests/munit/munit.h: -------------------------------------------------------------------------------- 1 | /* µnit Testing Framework 2 | * Copyright (c) 2013-2017 Evan Nemerson 3 | * 4 | * Permission is hereby granted, free of charge, to any person 5 | * obtaining a copy of this software and associated documentation 6 | * files (the "Software"), to deal in the Software without 7 | * restriction, including without limitation the rights to use, copy, 8 | * modify, merge, publish, distribute, sublicense, and/or sell copies 9 | * of the Software, and to permit persons to whom the Software is 10 | * furnished to do so, subject to the following conditions: 11 | * 12 | * The above copyright notice and this permission notice shall be 13 | * included in all copies or substantial portions of the Software. 14 | * 15 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 16 | * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 17 | * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 18 | * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS 19 | * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN 20 | * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 21 | * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | * SOFTWARE. 23 | */ 24 | 25 | #if !defined(MUNIT_H) 26 | #define MUNIT_H 27 | 28 | #include 29 | #include 30 | 31 | #define MUNIT_VERSION(major, minor, revision) \ 32 | (((major) << 16) | ((minor) << 8) | (revision)) 33 | 34 | #define MUNIT_CURRENT_VERSION MUNIT_VERSION(0, 4, 1) 35 | 36 | #if defined(_MSC_VER) && (_MSC_VER < 1600) 37 | # define munit_int8_t __int8 38 | # define munit_uint8_t unsigned __int8 39 | # define munit_int16_t __int16 40 | # define munit_uint16_t unsigned __int16 41 | # define munit_int32_t __int32 42 | # define munit_uint32_t unsigned __int32 43 | # define munit_int64_t __int64 44 | # define munit_uint64_t unsigned __int64 45 | #else 46 | # include 47 | # define munit_int8_t int8_t 48 | # define munit_uint8_t uint8_t 49 | # define munit_int16_t int16_t 50 | # define munit_uint16_t uint16_t 51 | # define munit_int32_t int32_t 52 | # define munit_uint32_t uint32_t 53 | # define munit_int64_t int64_t 54 | # define munit_uint64_t uint64_t 55 | #endif 56 | 57 | #if defined(_MSC_VER) && (_MSC_VER < 1800) 58 | # if !defined(PRIi8) 59 | # define PRIi8 "i" 60 | # endif 61 | # if !defined(PRIi16) 62 | # define PRIi16 "i" 63 | # endif 64 | # if !defined(PRIi32) 65 | # define PRIi32 "i" 66 | # endif 67 | # if !defined(PRIi64) 68 | # define PRIi64 "I64i" 69 | # endif 70 | # if !defined(PRId8) 71 | # define PRId8 "d" 72 | # endif 73 | # if !defined(PRId16) 74 | # define PRId16 "d" 75 | # endif 76 | # if !defined(PRId32) 77 | # define PRId32 "d" 78 | # endif 79 | # if !defined(PRId64) 80 | # define PRId64 "I64d" 81 | # endif 82 | # if !defined(PRIx8) 83 | # define PRIx8 "x" 84 | # endif 85 | # if !defined(PRIx16) 86 | # define PRIx16 "x" 87 | # endif 88 | # if !defined(PRIx32) 89 | # define PRIx32 "x" 90 | # endif 91 | # if !defined(PRIx64) 92 | # define PRIx64 "I64x" 93 | # endif 94 | # if !defined(PRIu8) 95 | # define PRIu8 "u" 96 | # endif 97 | # if !defined(PRIu16) 98 | # define PRIu16 "u" 99 | # endif 100 | # if !defined(PRIu32) 101 | # define PRIu32 "u" 102 | # endif 103 | # if !defined(PRIu64) 104 | # define PRIu64 "I64u" 105 | # endif 106 | #else 107 | # include 108 | #endif 109 | 110 | #if !defined(munit_bool) 111 | # if defined(bool) 112 | # define munit_bool bool 113 | # elif defined(__STDC_VERSION__) && (__STDC_VERSION__ >= 199901L) 114 | # define munit_bool _Bool 115 | # else 116 | # define munit_bool int 117 | # endif 118 | #endif 119 | 120 | #if defined(__cplusplus) 121 | extern "C" { 122 | #endif 123 | 124 | #if defined(__GNUC__) 125 | # define MUNIT_LIKELY(expr) (__builtin_expect ((expr), 1)) 126 | # define MUNIT_UNLIKELY(expr) (__builtin_expect ((expr), 0)) 127 | # define MUNIT_UNUSED __attribute__((__unused__)) 128 | #else 129 | # define MUNIT_LIKELY(expr) (expr) 130 | # define MUNIT_UNLIKELY(expr) (expr) 131 | # define MUNIT_UNUSED 132 | #endif 133 | 134 | #if defined(__STDC_VERSION__) && (__STDC_VERSION__ >= 199901L) && !defined(__PGI) 135 | # define MUNIT_ARRAY_PARAM(name) name 136 | #else 137 | # define MUNIT_ARRAY_PARAM(name) 138 | #endif 139 | 140 | #if !defined(_WIN32) 141 | # define MUNIT_SIZE_MODIFIER "z" 142 | # define MUNIT_CHAR_MODIFIER "hh" 143 | # define MUNIT_SHORT_MODIFIER "h" 144 | #else 145 | # if defined(_M_X64) || defined(__amd64__) 146 | # define MUNIT_SIZE_MODIFIER "I64" 147 | # else 148 | # define MUNIT_SIZE_MODIFIER "" 149 | # endif 150 | # define MUNIT_CHAR_MODIFIER "" 151 | # define MUNIT_SHORT_MODIFIER "" 152 | #endif 153 | 154 | #if defined(__STDC_VERSION__) && __STDC_VERSION__ >= 201112L 155 | # define MUNIT_NO_RETURN _Noreturn 156 | #elif defined(__GNUC__) 157 | # define MUNIT_NO_RETURN __attribute__((__noreturn__)) 158 | #elif defined(_MSC_VER) 159 | # define MUNIT_NO_RETURN __declspec(noreturn) 160 | #else 161 | # define MUNIT_NO_RETURN 162 | #endif 163 | 164 | #if defined(_MSC_VER) && (_MSC_VER >= 1500) 165 | # define MUNIT_PUSH_DISABLE_MSVC_C4127_ __pragma(warning(push)) __pragma(warning(disable:4127)) 166 | # define MUNIT_POP_DISABLE_MSVC_C4127_ __pragma(warning(pop)) 167 | #else 168 | # define MUNIT_PUSH_DISABLE_MSVC_C4127_ 169 | # define MUNIT_POP_DISABLE_MSVC_C4127_ 170 | #endif 171 | 172 | typedef enum { 173 | MUNIT_LOG_DEBUG, 174 | MUNIT_LOG_INFO, 175 | MUNIT_LOG_WARNING, 176 | MUNIT_LOG_ERROR 177 | } MunitLogLevel; 178 | 179 | #if defined(__GNUC__) && !defined(__MINGW32__) 180 | # define MUNIT_PRINTF(string_index, first_to_check) __attribute__((format (printf, string_index, first_to_check))) 181 | #else 182 | # define MUNIT_PRINTF(string_index, first_to_check) 183 | #endif 184 | 185 | MUNIT_PRINTF(4, 5) 186 | void munit_logf_ex(MunitLogLevel level, const char* filename, int line, const char* format, ...); 187 | 188 | #define munit_logf(level, format, ...) \ 189 | munit_logf_ex(level, __FILE__, __LINE__, format, __VA_ARGS__) 190 | 191 | #define munit_log(level, msg) \ 192 | munit_logf(level, "%s", msg) 193 | 194 | MUNIT_NO_RETURN 195 | MUNIT_PRINTF(3, 4) 196 | void munit_errorf_ex(const char* filename, int line, const char* format, ...); 197 | 198 | #define munit_errorf(format, ...) \ 199 | munit_errorf_ex(__FILE__, __LINE__, format, __VA_ARGS__) 200 | 201 | #define munit_error(msg) \ 202 | munit_errorf("%s", msg) 203 | 204 | #define munit_assert(expr) \ 205 | do { \ 206 | if (!MUNIT_LIKELY(expr)) { \ 207 | munit_error("assertion failed: " #expr); \ 208 | } \ 209 | MUNIT_PUSH_DISABLE_MSVC_C4127_ \ 210 | } while (0) \ 211 | MUNIT_POP_DISABLE_MSVC_C4127_ 212 | 213 | #define munit_assert_true(expr) \ 214 | do { \ 215 | if (!MUNIT_LIKELY(expr)) { \ 216 | munit_error("assertion failed: " #expr " is not true"); \ 217 | } \ 218 | MUNIT_PUSH_DISABLE_MSVC_C4127_ \ 219 | } while (0) \ 220 | MUNIT_POP_DISABLE_MSVC_C4127_ 221 | 222 | #define munit_assert_false(expr) \ 223 | do { \ 224 | if (!MUNIT_LIKELY(!(expr))) { \ 225 | munit_error("assertion failed: " #expr " is not false"); \ 226 | } \ 227 | MUNIT_PUSH_DISABLE_MSVC_C4127_ \ 228 | } while (0) \ 229 | MUNIT_POP_DISABLE_MSVC_C4127_ 230 | 231 | #define munit_assert_type_full(prefix, suffix, T, fmt, a, op, b) \ 232 | do { \ 233 | T munit_tmp_a_ = (a); \ 234 | T munit_tmp_b_ = (b); \ 235 | if (!(munit_tmp_a_ op munit_tmp_b_)) { \ 236 | munit_errorf("assertion failed: %s %s %s (" prefix "%" fmt suffix " %s " prefix "%" fmt suffix ")", \ 237 | #a, #op, #b, munit_tmp_a_, #op, munit_tmp_b_); \ 238 | } \ 239 | MUNIT_PUSH_DISABLE_MSVC_C4127_ \ 240 | } while (0) \ 241 | MUNIT_POP_DISABLE_MSVC_C4127_ 242 | 243 | #define munit_assert_type(T, fmt, a, op, b) \ 244 | munit_assert_type_full("", "", T, fmt, a, op, b) 245 | 246 | #define munit_assert_char(a, op, b) \ 247 | munit_assert_type_full("'\\x", "'", char, "02" MUNIT_CHAR_MODIFIER "x", a, op, b) 248 | #define munit_assert_uchar(a, op, b) \ 249 | munit_assert_type_full("'\\x", "'", unsigned char, "02" MUNIT_CHAR_MODIFIER "x", a, op, b) 250 | #define munit_assert_short(a, op, b) \ 251 | munit_assert_type(short, MUNIT_SHORT_MODIFIER "d", a, op, b) 252 | #define munit_assert_ushort(a, op, b) \ 253 | munit_assert_type(unsigned short, MUNIT_SHORT_MODIFIER "u", a, op, b) 254 | #define munit_assert_int(a, op, b) \ 255 | munit_assert_type(int, "d", a, op, b) 256 | #define munit_assert_uint(a, op, b) \ 257 | munit_assert_type(unsigned int, "u", a, op, b) 258 | #define munit_assert_long(a, op, b) \ 259 | munit_assert_type(long int, "ld", a, op, b) 260 | #define munit_assert_ulong(a, op, b) \ 261 | munit_assert_type(unsigned long int, "lu", a, op, b) 262 | #define munit_assert_llong(a, op, b) \ 263 | munit_assert_type(long long int, "lld", a, op, b) 264 | #define munit_assert_ullong(a, op, b) \ 265 | munit_assert_type(unsigned long long int, "llu", a, op, b) 266 | 267 | #define munit_assert_size(a, op, b) \ 268 | munit_assert_type(size_t, MUNIT_SIZE_MODIFIER "u", a, op, b) 269 | 270 | #define munit_assert_float(a, op, b) \ 271 | munit_assert_type(float, "f", a, op, b) 272 | #define munit_assert_double(a, op, b) \ 273 | munit_assert_type(double, "g", a, op, b) 274 | #define munit_assert_ptr(a, op, b) \ 275 | munit_assert_type(const void*, "p", a, op, b) 276 | 277 | #define munit_assert_int8(a, op, b) \ 278 | munit_assert_type(munit_int8_t, PRIi8, a, op, b) 279 | #define munit_assert_uint8(a, op, b) \ 280 | munit_assert_type(munit_uint8_t, PRIu8, a, op, b) 281 | #define munit_assert_int16(a, op, b) \ 282 | munit_assert_type(munit_int16_t, PRIi16, a, op, b) 283 | #define munit_assert_uint16(a, op, b) \ 284 | munit_assert_type(munit_uint16_t, PRIu16, a, op, b) 285 | #define munit_assert_int32(a, op, b) \ 286 | munit_assert_type(munit_int32_t, PRIi32, a, op, b) 287 | #define munit_assert_uint32(a, op, b) \ 288 | munit_assert_type(munit_uint32_t, PRIu32, a, op, b) 289 | #define munit_assert_int64(a, op, b) \ 290 | munit_assert_type(munit_int64_t, PRIi64, a, op, b) 291 | #define munit_assert_uint64(a, op, b) \ 292 | munit_assert_type(munit_uint64_t, PRIu64, a, op, b) 293 | 294 | #define munit_assert_double_equal(a, b, precision) \ 295 | do { \ 296 | const double munit_tmp_a_ = (a); \ 297 | const double munit_tmp_b_ = (b); \ 298 | const double munit_tmp_diff_ = ((munit_tmp_a_ - munit_tmp_b_) < 0) ? \ 299 | -(munit_tmp_a_ - munit_tmp_b_) : \ 300 | (munit_tmp_a_ - munit_tmp_b_); \ 301 | if (MUNIT_UNLIKELY(munit_tmp_diff_ > 1e-##precision)) { \ 302 | munit_errorf("assertion failed: %s == %s (%0." #precision "g == %0." #precision "g)", \ 303 | #a, #b, munit_tmp_a_, munit_tmp_b_); \ 304 | } \ 305 | MUNIT_PUSH_DISABLE_MSVC_C4127_ \ 306 | } while (0) \ 307 | MUNIT_POP_DISABLE_MSVC_C4127_ 308 | 309 | #include 310 | #define munit_assert_string_equal(a, b) \ 311 | do { \ 312 | const char* munit_tmp_a_ = a; \ 313 | const char* munit_tmp_b_ = b; \ 314 | if (MUNIT_UNLIKELY(strcmp(munit_tmp_a_, munit_tmp_b_) != 0)) { \ 315 | munit_errorf("assertion failed: string %s == %s (\"%s\" == \"%s\")", \ 316 | #a, #b, munit_tmp_a_, munit_tmp_b_); \ 317 | } \ 318 | MUNIT_PUSH_DISABLE_MSVC_C4127_ \ 319 | } while (0) \ 320 | MUNIT_POP_DISABLE_MSVC_C4127_ 321 | 322 | #define munit_assert_string_not_equal(a, b) \ 323 | do { \ 324 | const char* munit_tmp_a_ = a; \ 325 | const char* munit_tmp_b_ = b; \ 326 | if (MUNIT_UNLIKELY(strcmp(munit_tmp_a_, munit_tmp_b_) == 0)) { \ 327 | munit_errorf("assertion failed: string %s != %s (\"%s\" == \"%s\")", \ 328 | #a, #b, munit_tmp_a_, munit_tmp_b_); \ 329 | } \ 330 | MUNIT_PUSH_DISABLE_MSVC_C4127_ \ 331 | } while (0) \ 332 | MUNIT_POP_DISABLE_MSVC_C4127_ 333 | 334 | #define munit_assert_memory_equal(size, a, b) \ 335 | do { \ 336 | const unsigned char* munit_tmp_a_ = (const unsigned char*) (a); \ 337 | const unsigned char* munit_tmp_b_ = (const unsigned char*) (b); \ 338 | const size_t munit_tmp_size_ = (size); \ 339 | if (MUNIT_UNLIKELY(memcmp(munit_tmp_a_, munit_tmp_b_, munit_tmp_size_)) != 0) { \ 340 | size_t munit_tmp_pos_; \ 341 | for (munit_tmp_pos_ = 0 ; munit_tmp_pos_ < munit_tmp_size_ ; munit_tmp_pos_++) { \ 342 | if (munit_tmp_a_[munit_tmp_pos_] != munit_tmp_b_[munit_tmp_pos_]) { \ 343 | munit_errorf("assertion failed: memory %s == %s, at offset %" MUNIT_SIZE_MODIFIER "u", \ 344 | #a, #b, munit_tmp_pos_); \ 345 | break; \ 346 | } \ 347 | } \ 348 | } \ 349 | MUNIT_PUSH_DISABLE_MSVC_C4127_ \ 350 | } while (0) \ 351 | MUNIT_POP_DISABLE_MSVC_C4127_ 352 | 353 | #define munit_assert_memory_not_equal(size, a, b) \ 354 | do { \ 355 | const unsigned char* munit_tmp_a_ = (const unsigned char*) (a); \ 356 | const unsigned char* munit_tmp_b_ = (const unsigned char*) (b); \ 357 | const size_t munit_tmp_size_ = (size); \ 358 | if (MUNIT_UNLIKELY(memcmp(munit_tmp_a_, munit_tmp_b_, munit_tmp_size_)) == 0) { \ 359 | munit_errorf("assertion failed: memory %s != %s (%zu bytes)", \ 360 | #a, #b, munit_tmp_size_); \ 361 | } \ 362 | MUNIT_PUSH_DISABLE_MSVC_C4127_ \ 363 | } while (0) \ 364 | MUNIT_POP_DISABLE_MSVC_C4127_ 365 | 366 | #define munit_assert_ptr_equal(a, b) \ 367 | munit_assert_ptr(a, ==, b) 368 | #define munit_assert_ptr_not_equal(a, b) \ 369 | munit_assert_ptr(a, !=, b) 370 | #define munit_assert_null(ptr) \ 371 | munit_assert_ptr(ptr, ==, NULL) 372 | #define munit_assert_not_null(ptr) \ 373 | munit_assert_ptr(ptr, !=, NULL) 374 | #define munit_assert_ptr_null(ptr) \ 375 | munit_assert_ptr(ptr, ==, NULL) 376 | #define munit_assert_ptr_not_null(ptr) \ 377 | munit_assert_ptr(ptr, !=, NULL) 378 | 379 | /*** Memory allocation ***/ 380 | 381 | void* munit_malloc_ex(const char* filename, int line, size_t size); 382 | 383 | #define munit_malloc(size) \ 384 | munit_malloc_ex(__FILE__, __LINE__, (size)) 385 | 386 | #define munit_new(type) \ 387 | ((type*) munit_malloc(sizeof(type))) 388 | 389 | #define munit_calloc(nmemb, size) \ 390 | munit_malloc((nmemb) * (size)) 391 | 392 | #define munit_newa(type, nmemb) \ 393 | ((type*) munit_calloc((nmemb), sizeof(type))) 394 | 395 | /*** Random number generation ***/ 396 | 397 | void munit_rand_seed(munit_uint32_t seed); 398 | munit_uint32_t munit_rand_uint32(void); 399 | int munit_rand_int_range(int min, int max); 400 | double munit_rand_double(void); 401 | void munit_rand_memory(size_t size, munit_uint8_t buffer[MUNIT_ARRAY_PARAM(size)]); 402 | 403 | /*** Tests and Suites ***/ 404 | 405 | typedef enum { 406 | /* Test successful */ 407 | MUNIT_OK, 408 | /* Test failed */ 409 | MUNIT_FAIL, 410 | /* Test was skipped */ 411 | MUNIT_SKIP, 412 | /* Test failed due to circumstances not intended to be tested 413 | * (things like network errors, invalid parameter value, failure to 414 | * allocate memory in the test harness, etc.). */ 415 | MUNIT_ERROR 416 | } MunitResult; 417 | 418 | typedef struct { 419 | char* name; 420 | char** values; 421 | } MunitParameterEnum; 422 | 423 | typedef struct { 424 | char* name; 425 | char* value; 426 | } MunitParameter; 427 | 428 | const char* munit_parameters_get(const MunitParameter params[], const char* key); 429 | 430 | typedef enum { 431 | MUNIT_TEST_OPTION_NONE = 0, 432 | MUNIT_TEST_OPTION_SINGLE_ITERATION = 1 << 0, 433 | MUNIT_TEST_OPTION_TODO = 1 << 1 434 | } MunitTestOptions; 435 | 436 | typedef MunitResult (* MunitTestFunc)(const MunitParameter params[], void* user_data_or_fixture); 437 | typedef void* (* MunitTestSetup)(const MunitParameter params[], void* user_data); 438 | typedef void (* MunitTestTearDown)(void* fixture); 439 | 440 | typedef struct { 441 | char* name; 442 | MunitTestFunc test; 443 | MunitTestSetup setup; 444 | MunitTestTearDown tear_down; 445 | MunitTestOptions options; 446 | MunitParameterEnum* parameters; 447 | } MunitTest; 448 | 449 | typedef enum { 450 | MUNIT_SUITE_OPTION_NONE = 0 451 | } MunitSuiteOptions; 452 | 453 | typedef struct MunitSuite_ MunitSuite; 454 | 455 | struct MunitSuite_ { 456 | char* prefix; 457 | MunitTest* tests; 458 | MunitSuite* suites; 459 | unsigned int iterations; 460 | MunitSuiteOptions options; 461 | }; 462 | 463 | int munit_suite_main(const MunitSuite* suite, void* user_data, int argc, char* const argv[MUNIT_ARRAY_PARAM(argc + 1)]); 464 | 465 | /* Note: I'm not very happy with this API; it's likely to change if I 466 | * figure out something better. Suggestions welcome. */ 467 | 468 | typedef struct MunitArgument_ MunitArgument; 469 | 470 | struct MunitArgument_ { 471 | char* name; 472 | munit_bool (* parse_argument)(const MunitSuite* suite, void* user_data, int* arg, int argc, char* const argv[MUNIT_ARRAY_PARAM(argc + 1)]); 473 | void (* write_help)(const MunitArgument* argument, void* user_data); 474 | }; 475 | 476 | int munit_suite_main_custom(const MunitSuite* suite, 477 | void* user_data, 478 | int argc, char* const argv[MUNIT_ARRAY_PARAM(argc + 1)], 479 | const MunitArgument arguments[]); 480 | 481 | #if defined(MUNIT_ENABLE_ASSERT_ALIASES) 482 | 483 | #define assert_true(expr) munit_assert_true(expr) 484 | #define assert_false(expr) munit_assert_false(expr) 485 | #define assert_char(a, op, b) munit_assert_char(a, op, b) 486 | #define assert_uchar(a, op, b) munit_assert_uchar(a, op, b) 487 | #define assert_short(a, op, b) munit_assert_short(a, op, b) 488 | #define assert_ushort(a, op, b) munit_assert_ushort(a, op, b) 489 | #define assert_int(a, op, b) munit_assert_int(a, op, b) 490 | #define assert_uint(a, op, b) munit_assert_uint(a, op, b) 491 | #define assert_long(a, op, b) munit_assert_long(a, op, b) 492 | #define assert_ulong(a, op, b) munit_assert_ulong(a, op, b) 493 | #define assert_llong(a, op, b) munit_assert_llong(a, op, b) 494 | #define assert_ullong(a, op, b) munit_assert_ullong(a, op, b) 495 | #define assert_size(a, op, b) munit_assert_size(a, op, b) 496 | #define assert_float(a, op, b) munit_assert_float(a, op, b) 497 | #define assert_double(a, op, b) munit_assert_double(a, op, b) 498 | #define assert_ptr(a, op, b) munit_assert_ptr(a, op, b) 499 | 500 | #define assert_int8(a, op, b) munit_assert_int8(a, op, b) 501 | #define assert_uint8(a, op, b) munit_assert_uint8(a, op, b) 502 | #define assert_int16(a, op, b) munit_assert_int16(a, op, b) 503 | #define assert_uint16(a, op, b) munit_assert_uint16(a, op, b) 504 | #define assert_int32(a, op, b) munit_assert_int32(a, op, b) 505 | #define assert_uint32(a, op, b) munit_assert_uint32(a, op, b) 506 | #define assert_int64(a, op, b) munit_assert_int64(a, op, b) 507 | #define assert_uint64(a, op, b) munit_assert_uint64(a, op, b) 508 | 509 | #define assert_double_equal(a, b, precision) munit_assert_double_equal(a, b, precision) 510 | #define assert_string_equal(a, b) munit_assert_string_equal(a, b) 511 | #define assert_string_not_equal(a, b) munit_assert_string_not_equal(a, b) 512 | #define assert_memory_equal(size, a, b) munit_assert_memory_equal(size, a, b) 513 | #define assert_memory_not_equal(size, a, b) munit_assert_memory_not_equal(size, a, b) 514 | #define assert_ptr_equal(a, b) munit_assert_ptr_equal(a, b) 515 | #define assert_ptr_not_equal(a, b) munit_assert_ptr_not_equal(a, b) 516 | #define assert_ptr_null(ptr) munit_assert_null_equal(ptr) 517 | #define assert_ptr_not_null(ptr) munit_assert_not_null(ptr) 518 | 519 | #define assert_null(ptr) munit_assert_null(ptr) 520 | #define assert_not_null(ptr) munit_assert_not_null(ptr) 521 | 522 | #endif /* defined(MUNIT_ENABLE_ASSERT_ALIASES) */ 523 | 524 | #if defined(__cplusplus) 525 | } 526 | #endif 527 | 528 | #endif /* !defined(MUNIT_H) */ 529 | 530 | #if defined(MUNIT_ENABLE_ASSERT_ALIASES) 531 | # if defined(assert) 532 | # undef assert 533 | # endif 534 | # define assert(expr) munit_assert(expr) 535 | #endif --------------------------------------------------------------------------------