s2d_line_segments.c (16215B)
1 /* Copyright (C) 2016-2021, 2023 |Méso|Star> (contact@meso-star.com) 2 * 3 * This program is free software: you can redistribute it and/or modify 4 * it under the terms of the GNU General Public License as published by 5 * the Free Software Foundation, either version 3 of the License, or 6 * (at your option) any later version. 7 * 8 * This program is distributed in the hope that it will be useful, 9 * but WITHOUT ANY WARRANTY; without even the implied warranty of 10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 11 * GNU General Public License for more details. 12 * 13 * You should have received a copy of the GNU General Public License 14 * along with this program. If not, see <http://www.gnu.org/licenses/>. */ 15 16 #include "s2d_c.h" 17 #include "s2d_device_c.h" 18 #include "s2d_line_segments.h" 19 20 #include <rsys/float2.h> 21 22 /******************************************************************************* 23 * Helper functions 24 ******************************************************************************/ 25 static FINLINE float 26 line_compute_segment_length 27 (struct line_segments* lines, 28 const size_t isegment) 29 { 30 const uint32_t* ids; 31 const float* pos; 32 const float* v0; 33 const float* v1; 34 const size_t id = isegment*2/*#ids per segment*/; 35 float tmp[2]; 36 ASSERT(lines && isegment < line_segments_get_nsegments(lines)); 37 38 ids = line_segments_get_ids(lines); 39 pos = line_segments_get_pos(lines); 40 v0 = pos + ids[id+0]*2/*#coords*/; 41 v1 = pos + ids[id+1]*2/*#coords*/; 42 return f2_len(f2_sub(tmp, v1, v0)); 43 } 44 45 static void 46 line_setup_indices 47 (struct line_segments* lines, 48 const unsigned nsegments, 49 void (*get_indices)(const unsigned isegment, unsigned ids[2], void*), 50 const unsigned nverts, 51 void* data) 52 { 53 uint32_t* indices; 54 unsigned isegment; 55 unsigned nsegments_prev; 56 unsigned nverts_new; 57 res_T res = RES_OK; 58 ASSERT(lines && nsegments && nverts); 59 60 nsegments_prev = (unsigned)line_segments_get_nsegments(lines); 61 ASSERT(get_indices != S2D_KEEP || nsegments == nsegments_prev); 62 63 if(get_indices == S2D_KEEP) 64 return; 65 66 if(nsegments == nsegments_prev) { 67 lines->update_mask |= (INDEX_BUFFER & !lines->resize_mask); 68 } else { 69 lines->resize_mask |= INDEX_BUFFER; 70 lines->update_mask &= !INDEX_BUFFER; 71 } 72 73 if(lines->indices) { /* Release the old index buffer */ 74 index_buffer_ref_put(lines->indices); 75 lines->indices = NULL; 76 } 77 78 /* Allocate the new index buffer */ 79 res = index_buffer_create(lines->dev->allocator, &lines->indices); 80 if(res != RES_OK) FATAL("Insufficient memory\n"); 81 res = darray_u32_resize(&lines->indices->data, nsegments * 2/*# segmet ids*/); 82 if(res != RES_OK) FATAL("Insufficient memory\n"); 83 84 /* Setup the lines indices */ 85 indices = line_segments_get_ids(lines); 86 nverts_new = 0; 87 FOR_EACH(isegment, 0, nsegments) { 88 uint32_t* ids = indices + isegment*2/*#ids per segment*/; 89 STATIC_ASSERT(sizeof(unsigned) == sizeof(uint32_t), Unexpected_Type); 90 get_indices(isegment, ids, data); 91 nverts_new = MMAX(nverts_new, ids[0]); 92 nverts_new = MMAX(nverts_new, ids[1]); 93 } 94 /* Transform nverts from the last vertex id to vertices count */ 95 if(nverts_new > nverts) 96 FATAL("Out of bound indexation\n"); 97 } 98 99 static void 100 line_setup_positions 101 (struct line_segments* lines, 102 const unsigned nverts, 103 struct s2d_vertex_data* attr, 104 void* data) 105 { 106 float* positions; 107 unsigned ivert, nverts_prev; 108 res_T res; 109 ASSERT(lines && nverts && attr && attr->usage == S2D_POSITION); 110 111 if(attr->get == S2D_KEEP) { 112 ASSERT(lines->attribs[S2D_POSITION]); 113 ASSERT(darray_float_size_get(&lines->attribs[S2D_POSITION]->data) == nverts*2); 114 return; 115 } 116 117 nverts_prev = (unsigned)line_segments_get_nverts(lines); 118 if(nverts == nverts_prev) { 119 lines->update_mask |= (VERTEX_BUFFER & ~lines->resize_mask); 120 } else { 121 lines->resize_mask |= VERTEX_BUFFER; 122 lines->update_mask &= ~VERTEX_BUFFER; 123 } 124 125 /* Release the old vertex buffer */ 126 if(lines->attribs[S2D_POSITION]) { 127 vertex_buffer_ref_put(lines->attribs[S2D_POSITION]); 128 lines->attribs[S2D_POSITION] = NULL; 129 } 130 131 /* Allocate the vertex positions */ 132 res = vertex_buffer_create(lines->dev->allocator, &lines->attribs[S2D_POSITION]); 133 if(res != RES_OK) FATAL("Insufficient memory\n"); 134 res = darray_float_resize(&lines->attribs[S2D_POSITION]->data, nverts*2); 135 if(res != RES_OK) FATAL("Insufficient memory\n"); 136 lines->attribs_type[S2D_POSITION] = S2D_FLOAT2; 137 138 /* Setup the vertex positions */ 139 positions = darray_float_data_get(&lines->attribs[S2D_POSITION]->data); 140 if(attr->type == S2D_FLOAT2) { 141 FOR_EACH(ivert, 0, nverts) { 142 attr->get(ivert, positions + ivert*2/*# coords per vertex*/, data); 143 } 144 } else { 145 FOR_EACH(ivert, 0, nverts) { 146 float pos[3]; 147 unsigned ipos = ivert * 2/*# coords per vertex*/; 148 attr->get(ivert, pos, data); 149 switch(attr->type) { 150 case S2D_FLOAT: 151 positions[ipos + 0] = pos[0]; 152 positions[ipos + 1] = 0.f; 153 break; 154 case S2D_FLOAT2: 155 positions[ipos + 0] = pos[0]; 156 positions[ipos + 1] = pos[1]; 157 break; 158 case S2D_FLOAT3: 159 positions[ipos + 0] = pos[0] / pos[2]; 160 positions[ipos + 1] = pos[1] / pos[2]; 161 break; 162 default: FATAL("Unreachable code\n"); break; 163 } 164 } 165 } 166 } 167 168 static void 169 line_setup_attribs 170 (struct line_segments* lines, 171 const unsigned nverts, 172 const struct s2d_vertex_data* attr, 173 void* data) 174 { 175 float* attr_data; 176 unsigned dim; 177 unsigned ivert; 178 res_T res; 179 ASSERT(lines && nverts && attr); 180 ASSERT(attr->usage!=S2D_POSITION && (unsigned)attr->usage<S2D_ATTRIBS_COUNT__); 181 182 dim = s2d_type_get_dimension(attr->type); 183 if(attr->get == S2D_KEEP) { 184 ASSERT(lines->attribs_type[attr->usage] == attr->type); 185 ASSERT(lines->attribs[attr->usage]); 186 ASSERT(darray_float_size_get(&lines->attribs[attr->usage]->data) == nverts*dim); 187 return; 188 } 189 190 if(lines->attribs[attr->usage]) { /* Release the previous vertex buffer */ 191 vertex_buffer_ref_put(lines->attribs[attr->usage]); 192 lines->attribs[attr->usage] = NULL; 193 } 194 195 /* Allocate the new vertex buffer */ 196 res = vertex_buffer_create(lines->dev->allocator, &lines->attribs[attr->usage]); 197 if(res != RES_OK) FATAL("Insufficient memory\n"); 198 res = darray_float_resize(&lines->attribs[attr->usage]->data, nverts * dim); 199 if(res != RES_OK) FATAL("Insufficient memory\n"); 200 201 /* Setup the vertex attrib */ 202 attr_data = darray_float_data_get(&lines->attribs[attr->usage]->data); 203 FOR_EACH(ivert, 0, nverts) { 204 attr->get(ivert, attr_data, data); 205 attr_data += dim; 206 } 207 } 208 209 static void 210 line_release(ref_T* ref) 211 { 212 struct line_segments* lines; 213 struct s2d_device* dev; 214 ASSERT(ref); 215 216 lines = CONTAINER_OF(ref, struct line_segments, ref); 217 line_segments_clear(lines); 218 dev = lines->dev; 219 darray_float_release(&lines->cdf); 220 MEM_RM(dev->allocator, lines); 221 S2D(device_ref_put(dev)); 222 } 223 224 /******************************************************************************* 225 * Local functions 226 ******************************************************************************/ 227 res_T 228 line_segments_create(struct s2d_device* dev, struct line_segments** out_lines) 229 { 230 struct line_segments* lines = NULL; 231 res_T res = RES_OK; 232 ASSERT(dev && out_lines); 233 234 lines = (struct line_segments*)MEM_CALLOC 235 (dev->allocator, 1, sizeof(struct line_segments)); 236 if(!lines) { 237 res = RES_MEM_ERR; 238 goto error; 239 } 240 ref_init(&lines->ref); 241 S2D(device_ref_get(dev)); 242 lines->dev = dev; 243 darray_float_init(dev->allocator, &lines->cdf); 244 245 exit: 246 *out_lines = lines; 247 return res; 248 error: 249 if(lines) { 250 line_segments_ref_put(lines); 251 lines = NULL; 252 } 253 goto exit; 254 } 255 256 void 257 line_segments_ref_get(struct line_segments* lines) 258 { 259 ASSERT(lines); 260 ref_get(&lines->ref); 261 } 262 263 void 264 line_segments_ref_put(struct line_segments* lines) 265 { 266 ASSERT(lines); 267 ref_put(&lines->ref, line_release); 268 } 269 270 void 271 line_segments_clear(struct line_segments* lines) 272 { 273 size_t iattr; 274 ASSERT(lines); 275 if(lines->indices) { 276 index_buffer_ref_put(lines->indices); 277 lines->indices = NULL; 278 } 279 FOR_EACH(iattr, 0, S2D_ATTRIBS_COUNT__) { 280 if(lines->attribs[iattr]) { 281 vertex_buffer_ref_put(lines->attribs[iattr]); 282 lines->attribs[iattr] = NULL; 283 } 284 } 285 lines->resize_mask = 0; 286 lines->update_mask = 0; 287 darray_float_clear(&lines->cdf); 288 } 289 290 size_t 291 line_segments_get_nsegments(const struct line_segments* lines) 292 { 293 size_t nids; 294 ASSERT(lines); 295 if(!lines->indices) 296 return 0; 297 nids = darray_u32_size_get(&lines->indices->data); 298 ASSERT(nids % 2 == 0); /* 2 vertices per segment */ 299 return nids / 2/* #vertices per segement */; 300 } 301 302 size_t 303 line_segments_get_nverts(const struct line_segments* lines) 304 { 305 size_t ncoords; 306 ASSERT(lines); 307 if(!lines->attribs[S2D_POSITION]) 308 return 0; 309 ASSERT(lines->attribs_type[S2D_POSITION] == S2D_FLOAT2); 310 ncoords = darray_float_size_get(&lines->attribs[S2D_POSITION]->data); 311 ASSERT(ncoords % 2 == 0); 312 return ncoords / 2/* #coords per vertices */; 313 } 314 315 uint32_t* 316 line_segments_get_ids(struct line_segments* lines) 317 { 318 ASSERT(lines && lines->indices); 319 return darray_u32_data_get(&lines->indices->data); 320 } 321 322 float* 323 line_segments_get_pos(struct line_segments* lines) 324 { 325 ASSERT(lines && lines->attribs[S2D_POSITION]); 326 ASSERT(lines->attribs_type[S2D_POSITION] == S2D_FLOAT2); 327 return darray_float_data_get(&lines->attribs[S2D_POSITION]->data); 328 } 329 330 float* 331 line_segments_get_attr 332 (struct line_segments* lines, 333 const enum s2d_attrib_usage usage) 334 { 335 ASSERT(lines && usage < S2D_ATTRIBS_COUNT__ && lines->attribs[usage]); 336 return darray_float_data_get(&lines->attribs[usage]->data); 337 } 338 339 res_T 340 line_segments_compute_cdf(struct line_segments* lines) 341 { 342 size_t iseg, nsegs; 343 float length = 0.f; 344 res_T res = RES_OK; 345 ASSERT(lines); 346 347 darray_float_clear(&lines->cdf); 348 349 nsegs = line_segments_get_nsegments(lines); 350 if(!nsegs) goto exit; 351 352 res = darray_float_resize(&lines->cdf, nsegs); 353 if(res != RES_OK) goto error; 354 355 FOR_EACH(iseg, 0, nsegs) { 356 length += line_compute_segment_length(lines, iseg); 357 darray_float_data_get(&lines->cdf)[iseg] = length; 358 } 359 360 exit: 361 return res; 362 error: 363 darray_float_clear(&lines->cdf); 364 goto exit; 365 } 366 367 float 368 line_segments_compute_length(struct line_segments* lines) 369 { 370 size_t iseg, nsegs; 371 float length = 0.f; 372 ASSERT(lines); 373 374 nsegs = line_segments_get_nsegments(lines); 375 if(!nsegs) return 0.f; 376 377 FOR_EACH(iseg, 0, nsegs) 378 length += line_compute_segment_length(lines, iseg); 379 380 return length; 381 } 382 383 float 384 line_segments_compute_area 385 (struct line_segments* lines, 386 const char flip_contour) 387 { 388 const uint32_t* ids; 389 const float* pos; 390 size_t iseg, nsegs; 391 double area2 = 0.f; 392 ASSERT(lines); 393 394 nsegs = line_segments_get_nsegments(lines); 395 if(!nsegs) return 0.f; 396 397 ids = line_segments_get_ids(lines); 398 pos = line_segments_get_pos(lines); 399 400 /* Build a triangle whose base is the contour segment and its appex is the 401 * origin. Then compute the area of the triangle and add or sub it from the 402 * overall area whether the normal point toward or backward the appex */ 403 FOR_EACH(iseg, 0, nsegs) { 404 const size_t id = iseg * 2/*#ids per segment*/; 405 const float* v0 = pos + ids[id+0]*2/*#coords*/; 406 const float* v1 = pos + ids[id+1]*2/*#coords*/; 407 double tmp; 408 float dx, dy, N[2], C; 409 410 dx = v1[0] - v0[0]; 411 dy = v1[1] - v0[1]; 412 413 if(flip_contour) { 414 N[0] = -dy; 415 N[1] = dx; 416 } else { 417 N[0] = dy; 418 N[1] = -dx; 419 } 420 C = -f2_dot(N, v0); /* N.v0 + C = 0 */ 421 422 tmp = v0[0]*v1[1] - v0[1]*v1[0]; /* Cross product */ 423 tmp = fabs(tmp); /* 2 * area of the triangle */ 424 area2 += C > 0 ? tmp : -tmp; 425 } 426 return (float)(area2 * 0.5); 427 } 428 429 res_T 430 line_segments_setup_indexed_vertices 431 (struct line_segments* lines, 432 const unsigned nsegments, 433 void (*get_indices)(const unsigned isegment, unsigned ids[2], void* ctx), 434 const unsigned nverts, 435 struct s2d_vertex_data attribs[], 436 const unsigned nattribs, 437 void* data) 438 { 439 unsigned iattr = 0; 440 int has_position = 0; 441 res_T res = RES_OK; 442 ASSERT(lines); 443 444 if(!nsegments || !nverts || !attribs || !nattribs) { 445 res = RES_BAD_ARG; 446 goto error; 447 } 448 449 /* Check indices description */ 450 if(get_indices == S2D_KEEP) { 451 if(!lines->indices) { /* No indice was previously set */ 452 res = RES_BAD_ARG; 453 goto error; 454 } else { 455 const size_t nsegments_prev = line_segments_get_nsegments(lines); 456 if(nsegments_prev != nsegments) { /* Inconsistant data */ 457 res = RES_BAD_ARG; 458 goto error; 459 } 460 } 461 } 462 463 /* Check the vertex data description */ 464 iattr = 0; 465 has_position = 0; 466 FOR_EACH(iattr, 0, nattribs) { 467 if((unsigned)attribs[iattr].usage >= S2D_ATTRIBS_COUNT__) { 468 res = RES_BAD_ARG; 469 goto error; 470 } 471 if(attribs[iattr].get == S2D_KEEP) { 472 const enum s2d_attrib_usage attr_usage = attribs[iattr].usage; 473 const enum s2d_type type = attribs[iattr].type; 474 if(!lines->attribs[attr_usage]) { /* The vertex attrib was not set */ 475 res = RES_BAD_ARG; 476 goto error; 477 } else { 478 const enum s2d_type type_prev = lines->attribs_type[attr_usage]; 479 const struct darray_float* attr = &lines->attribs[attr_usage]->data; 480 size_t nverts_prev = darray_float_size_get(attr); 481 nverts_prev /= s2d_type_get_dimension(type_prev); 482 if(type_prev != type || nverts_prev != nverts) { /* Inconsistant data */ 483 res = RES_BAD_ARG; 484 goto error; 485 } 486 } 487 } 488 if(attribs[iattr].usage == S2D_POSITION) 489 has_position = 1; 490 } 491 492 if(!has_position) { /* The vertex must have a position */ 493 res = RES_BAD_ARG; 494 goto error; 495 } 496 497 line_setup_indices(lines, nsegments, get_indices, nverts, data); 498 499 /* Setup vertex data */ 500 FOR_EACH(iattr, 0, nattribs) { 501 if(attribs[iattr].usage == S2D_POSITION) { 502 line_setup_positions(lines, nverts, attribs + iattr, data); 503 } else { 504 line_setup_attribs(lines, nverts, attribs + iattr, data); 505 } 506 } 507 508 exit: 509 return res; 510 error: 511 goto exit; 512 } 513 514 void 515 line_segments_compute_aabb 516 (struct line_segments* lines, 517 float lower[2], 518 float upper[2]) 519 { 520 float* pos; 521 size_t ivert, nverts; 522 ASSERT(lines && lower && upper); 523 524 f2_splat(lower, FLT_MAX); 525 f2_splat(upper,-FLT_MAX); 526 527 nverts = line_segments_get_nverts(lines); 528 if(!nverts) return; 529 530 pos = line_segments_get_pos(lines); 531 FOR_EACH(ivert, 0, nverts) { 532 const size_t ipos = ivert * 2/*#coords per vertex*/; 533 f2_min(lower, lower, pos + ipos); 534 f2_max(upper, upper, pos + ipos); 535 } 536 } 537 538 void 539 line_segments_copy_indexed_vertices 540 (const struct line_segments* src, 541 struct line_segments* dst) 542 { 543 size_t nsegments_src; 544 size_t nsegments_dst; 545 size_t nverts_src; 546 size_t nverts_dst; 547 int i; 548 ASSERT(src && dst); 549 550 nsegments_src = line_segments_get_nsegments(src); 551 nsegments_dst = line_segments_get_nsegments(dst); 552 nverts_src = line_segments_get_nsegments(src); 553 nverts_dst = line_segments_get_nsegments(dst); 554 555 /* Setup indexe buffer masks */ 556 if(nsegments_src == nsegments_dst) { 557 dst->update_mask = (INDEX_BUFFER & !dst->resize_mask); 558 } else { 559 dst->resize_mask |= INDEX_BUFFER; 560 dst->update_mask &= !INDEX_BUFFER; 561 } 562 563 /* Release the previous index buffer of dst */ 564 if(dst->indices) { 565 index_buffer_ref_put(dst->indices); 566 dst->indices = NULL; 567 } 568 /* Get a reference onto the index buffer of src */ 569 if(src->indices) { 570 index_buffer_ref_get(src->indices); 571 dst->indices = src->indices; 572 } 573 574 /* Setup the vertex buffer masks */ 575 if(nverts_src == nverts_dst) { 576 dst->update_mask = (VERTEX_BUFFER & ~dst->resize_mask); 577 } else { 578 dst->resize_mask |= VERTEX_BUFFER; 579 dst->update_mask &= ~VERTEX_BUFFER; 580 } 581 582 FOR_EACH(i, 0, S2D_ATTRIBS_COUNT__) { 583 /* Release the previous vertex buffers of dst */ 584 if(dst->attribs[i]) { 585 vertex_buffer_ref_put(dst->attribs[i]); 586 dst->attribs[i] = NULL; 587 } 588 /* Get a reference onto the vertex buffers of src */ 589 if(src->attribs[i]) { 590 vertex_buffer_ref_get(src->attribs[i]); 591 dst->attribs[i] = src->attribs[i]; 592 dst->attribs_type[i] = src->attribs_type[i]; 593 } 594 } 595 } 596