static void svert_from_mvert(SortVertsElem *sv, const MVert *mv, const int i_begin, const int i_end) { int i; for (i = i_begin; i < i_end; i++, sv++, mv++) { sv->vertex_num = i; copy_v3_v3(sv->co, mv->co); sv->sum_co = sum_v3(mv->co); } }
/** * Take as inputs two sets of verts, to be processed for detection of doubles and mapping. * Each set of verts is defined by its start within mverts array and its num_verts; * It builds a mapping for all vertices within source, to vertices within target, or -1 if no double found * The int doubles_map[num_verts_source] array must have been allocated by caller. */ static void dm_mvert_map_doubles( int *doubles_map, const MVert *mverts, const int target_start, const int target_num_verts, const int source_start, const int source_num_verts, const float dist, const bool with_follow) { const float dist3 = ((float)M_SQRT3 + 0.00005f) * dist; /* Just above sqrt(3) */ int i_source, i_target, i_target_low_bound, target_end, source_end; SortVertsElem *sorted_verts_target, *sorted_verts_source; SortVertsElem *sve_source, *sve_target, *sve_target_low_bound; bool target_scan_completed; target_end = target_start + target_num_verts; source_end = source_start + source_num_verts; /* build array of MVerts to be tested for merging */ sorted_verts_target = MEM_mallocN(sizeof(SortVertsElem) * target_num_verts, __func__); sorted_verts_source = MEM_mallocN(sizeof(SortVertsElem) * source_num_verts, __func__); /* Copy target vertices index and cos into SortVertsElem array */ svert_from_mvert(sorted_verts_target, mverts + target_start, target_start, target_end); /* Copy source vertices index and cos into SortVertsElem array */ svert_from_mvert(sorted_verts_source, mverts + source_start, source_start, source_end); /* sort arrays according to sum of vertex coordinates (sumco) */ qsort(sorted_verts_target, target_num_verts, sizeof(SortVertsElem), svert_sum_cmp); qsort(sorted_verts_source, source_num_verts, sizeof(SortVertsElem), svert_sum_cmp); sve_target_low_bound = sorted_verts_target; i_target_low_bound = 0; target_scan_completed = false; /* Scan source vertices, in SortVertsElem sorted array, */ /* all the while maintaining the lower bound of possible doubles in target vertices */ for (i_source = 0, sve_source = sorted_verts_source; i_source < source_num_verts; i_source++, sve_source++) { bool double_found; float sve_source_sumco; /* If source has already been assigned to a target (in an earlier call, with other chunks) */ if (doubles_map[sve_source->vertex_num] != -1) { continue; } /* If target fully scanned already, then all remaining source vertices cannot have a double */ if (target_scan_completed) { doubles_map[sve_source->vertex_num] = -1; continue; } sve_source_sumco = sum_v3(sve_source->co); /* Skip all target vertices that are more than dist3 lower in terms of sumco */ /* and advance the overall lower bound, applicable to all remaining vertices as well. */ while ((i_target_low_bound < target_num_verts) && (sve_target_low_bound->sum_co < sve_source_sumco - dist3)) { i_target_low_bound++; sve_target_low_bound++; } /* If end of target list reached, then no more possible doubles */ if (i_target_low_bound >= target_num_verts) { doubles_map[sve_source->vertex_num] = -1; target_scan_completed = true; continue; } /* Test target candidates starting at the low bound of possible doubles, ordered in terms of sumco */ i_target = i_target_low_bound; sve_target = sve_target_low_bound; /* i_target will scan vertices in the [v_source_sumco - dist3; v_source_sumco + dist3] range */ double_found = false; while ((i_target < target_num_verts) && (sve_target->sum_co <= sve_source_sumco + dist3)) { /* Testing distance for candidate double in target */ /* v_target is within dist3 of v_source in terms of sumco; check real distance */ if (compare_len_v3v3(sve_source->co, sve_target->co, dist)) { /* Double found */ /* If double target is itself already mapped to other vertex, * behavior depends on with_follow option */ int target_vertex = sve_target->vertex_num; if (doubles_map[target_vertex] != -1) { if (with_follow) { /* with_follow option: map to initial target */ target_vertex = doubles_map[target_vertex]; } else { /* not with_follow: if target is mapped, then we do not map source, and stop searching */ break; } } doubles_map[sve_source->vertex_num] = target_vertex; double_found = true; break; } i_target++; sve_target++; } /* End of candidate scan: if none found then no doubles */ if (!double_found) { doubles_map[sve_source->vertex_num] = -1; } } MEM_freeN(sorted_verts_source); MEM_freeN(sorted_verts_target); }
/** * Take as inputs two sets of verts, to be processed for detection of doubles and mapping. * Each set of verts is defined by its start within mverts array and its num_verts; * It builds a mapping for all vertices within source, to vertices within target, or -1 if no double found * The int doubles_map[num_verts_source] array must have been allocated by caller. */ static void dm_mvert_map_doubles( int *doubles_map, const MVert *mverts, const int target_start, const int target_num_verts, const int source_start, const int source_num_verts, const float dist) { const float dist3 = ((float)M_SQRT3 + 0.00005f) * dist; /* Just above sqrt(3) */ int i_source, i_target, i_target_low_bound, target_end, source_end; SortVertsElem *sorted_verts_target, *sorted_verts_source; SortVertsElem *sve_source, *sve_target, *sve_target_low_bound; bool target_scan_completed; target_end = target_start + target_num_verts; source_end = source_start + source_num_verts; /* build array of MVerts to be tested for merging */ sorted_verts_target = MEM_malloc_arrayN(target_num_verts, sizeof(SortVertsElem), __func__); sorted_verts_source = MEM_malloc_arrayN(source_num_verts, sizeof(SortVertsElem), __func__); /* Copy target vertices index and cos into SortVertsElem array */ svert_from_mvert(sorted_verts_target, mverts + target_start, target_start, target_end); /* Copy source vertices index and cos into SortVertsElem array */ svert_from_mvert(sorted_verts_source, mverts + source_start, source_start, source_end); /* sort arrays according to sum of vertex coordinates (sumco) */ qsort(sorted_verts_target, target_num_verts, sizeof(SortVertsElem), svert_sum_cmp); qsort(sorted_verts_source, source_num_verts, sizeof(SortVertsElem), svert_sum_cmp); sve_target_low_bound = sorted_verts_target; i_target_low_bound = 0; target_scan_completed = false; /* Scan source vertices, in SortVertsElem sorted array, */ /* all the while maintaining the lower bound of possible doubles in target vertices */ for (i_source = 0, sve_source = sorted_verts_source; i_source < source_num_verts; i_source++, sve_source++) { int best_target_vertex = -1; float best_dist_sq = dist * dist; float sve_source_sumco; /* If source has already been assigned to a target (in an earlier call, with other chunks) */ if (doubles_map[sve_source->vertex_num] != -1) { continue; } /* If target fully scanned already, then all remaining source vertices cannot have a double */ if (target_scan_completed) { doubles_map[sve_source->vertex_num] = -1; continue; } sve_source_sumco = sum_v3(sve_source->co); /* Skip all target vertices that are more than dist3 lower in terms of sumco */ /* and advance the overall lower bound, applicable to all remaining vertices as well. */ while ((i_target_low_bound < target_num_verts) && (sve_target_low_bound->sum_co < sve_source_sumco - dist3)) { i_target_low_bound++; sve_target_low_bound++; } /* If end of target list reached, then no more possible doubles */ if (i_target_low_bound >= target_num_verts) { doubles_map[sve_source->vertex_num] = -1; target_scan_completed = true; continue; } /* Test target candidates starting at the low bound of possible doubles, ordered in terms of sumco */ i_target = i_target_low_bound; sve_target = sve_target_low_bound; /* i_target will scan vertices in the [v_source_sumco - dist3; v_source_sumco + dist3] range */ while ((i_target < target_num_verts) && (sve_target->sum_co <= sve_source_sumco + dist3)) { /* Testing distance for candidate double in target */ /* v_target is within dist3 of v_source in terms of sumco; check real distance */ float dist_sq; if ((dist_sq = len_squared_v3v3(sve_source->co, sve_target->co)) <= best_dist_sq) { /* Potential double found */ best_dist_sq = dist_sq; best_target_vertex = sve_target->vertex_num; /* If target is already mapped, we only follow that mapping if final target remains * close enough from current vert (otherwise no mapping at all). * Note that if we later find another target closer than this one, then we check it. But if other * potential targets are farther, then there will be no mapping at all for this source. */ while (best_target_vertex != -1 && !ELEM(doubles_map[best_target_vertex], -1, best_target_vertex)) { if (compare_len_v3v3(mverts[sve_source->vertex_num].co, mverts[doubles_map[best_target_vertex]].co, dist)) { best_target_vertex = doubles_map[best_target_vertex]; } else { best_target_vertex = -1; } } } i_target++; sve_target++; } /* End of candidate scan: if none found then no doubles */ doubles_map[sve_source->vertex_num] = best_target_vertex; } MEM_freeN(sorted_verts_source); MEM_freeN(sorted_verts_target); }