/* Adopted from BM_loop_interp_from_face(), * * Transform matrix is used in cases when target coordinate needs * to be converted to source space (namely when interpolating * boolean result loops from second operand). * * TODO(sergey): Consider making it a generic function in DerivedMesh.c. */ static void DM_loop_interp_from_poly(DerivedMesh *source_dm, MVert *source_mverts, MLoop *source_mloops, MPoly *source_poly, DerivedMesh *target_dm, MVert *target_mverts, MLoop *target_mloop, float transform[4][4], int target_loop_index) { float (*cos_3d)[3] = BLI_array_alloca(cos_3d, source_poly->totloop); int *source_indices = BLI_array_alloca(source_indices, source_poly->totloop); float *weights = BLI_array_alloca(weights, source_poly->totloop); int i; int target_vert_index = target_mloop[target_loop_index].v; float coord[3]; for (i = 0; i < source_poly->totloop; ++i) { MLoop *mloop = &source_mloops[source_poly->loopstart + i]; source_indices[i] = source_poly->loopstart + i; copy_v3_v3(cos_3d[i], source_mverts[mloop->v].co); } if (transform) { mul_v3_m4v3(coord, transform, target_mverts[target_vert_index].co); } else { copy_v3_v3(coord, target_mverts[target_vert_index].co); } interp_weights_poly_v3(weights, cos_3d, source_poly->totloop, coord); DM_interp_loop_data(source_dm, target_dm, source_indices, weights, source_poly->totloop, target_loop_index); }
/* Static function for alloc (duplicate in modifiers_bmesh.c) */ static BMFace *bm_face_create_from_mpoly(MPoly *mp, MLoop *ml, BMesh *bm, BMVert **vtable, BMEdge **etable) { BMVert **verts = BLI_array_alloca(verts, mp->totloop); BMEdge **edges = BLI_array_alloca(edges, mp->totloop); int j; for (j = 0; j < mp->totloop; j++, ml++) { verts[j] = vtable[ml->v]; edges[j] = etable[ml->e]; } return BM_face_create(bm, verts, edges, mp->totloop, BM_CREATE_SKIP_CD); }
/** * \brief Make NGon * * Makes an ngon from an unordered list of edges. * Verts \a v1 and \a v2 define the winding of the new face. * * \a edges are not required to be ordered, simply to to form * a single closed loop as a whole. * * \note While this function will work fine when the edges * are already sorted, if the edges are always going to be sorted, * #BM_face_create should be considered over this function as it * avoids some unnecessary work. */ BMFace *BM_face_create_ngon( BMesh *bm, BMVert *v1, BMVert *v2, BMEdge **edges, const int len, const BMFace *f_example, const eBMCreateFlag create_flag) { BMEdge **edges_sort = BLI_array_alloca(edges_sort, len); BMVert **verts_sort = BLI_array_alloca(verts_sort, len); BLI_assert(len && v1 && v2 && edges && bm); if (bm_edges_sort_winding(v1, v2, edges, len, edges_sort, verts_sort)) { return BM_face_create(bm, verts_sort, edges_sort, len, f_example, create_flag); } return NULL; }
void BLI_polyfill_calc( const float (*coords)[2], const unsigned int coords_tot, unsigned int (*r_tris)[3]) { unsigned int *indices = BLI_array_alloca(indices, coords_tot); eSign *coords_sign = BLI_array_alloca(coords_sign, coords_tot); BLI_polyfill_calc_ex( coords, coords_tot, r_tris, /* cache */ indices, coords_sign); }
/* helper function for 'BM_mesh_copy' */ static BMFace *bm_mesh_copy_new_face( BMesh *bm_new, BMesh *bm_old, BMVert **vtable, BMEdge **etable, BMFace *f) { BMLoop **loops = BLI_array_alloca(loops, f->len); BMVert **verts = BLI_array_alloca(verts, f->len); BMEdge **edges = BLI_array_alloca(edges, f->len); BMFace *f_new; BMLoop *l_iter, *l_first; int j; j = 0; l_iter = l_first = BM_FACE_FIRST_LOOP(f); do { loops[j] = l_iter; verts[j] = vtable[BM_elem_index_get(l_iter->v)]; edges[j] = etable[BM_elem_index_get(l_iter->e)]; j++; } while ((l_iter = l_iter->next) != l_first); f_new = BM_face_create(bm_new, verts, edges, f->len, NULL, BM_CREATE_SKIP_CD); if (UNLIKELY(f_new == NULL)) { return NULL; } /* use totface in case adding some faces fails */ BM_elem_index_set(f_new, (bm_new->totface - 1)); /* set_inline */ BM_elem_attrs_copy_ex(bm_old, bm_new, f, f_new, 0xff, 0x0); f_new->head.hflag = f->head.hflag; /* low level! don't do this for normal api use */ j = 0; l_iter = l_first = BM_FACE_FIRST_LOOP(f_new); do { BM_elem_attrs_copy(bm_old, bm_new, loops[j], l_iter); j++; } while ((l_iter = l_iter->next) != l_first); return f_new; }
/** * Triangulates the given (convex or concave) simple polygon to a list of triangle vertices. * * \param coords: 2D coordinates describing vertices of the polygon, * in either clockwise or counterclockwise order. * \param coords_tot: Total points in the array. * \param coords_sign: Pass this when we know the sign in advance to avoid extra calculations. * * \param r_tris: This array is filled in with triangle indices in clockwise order. * The length of the array must be ``coords_tot - 2``. * Indices are guaranteed to be assigned to unique triangles, with valid indices, * even in the case of degenerate input (self intersecting polygons, zero area ears... etc). */ void BLI_polyfill_calc(const float (*coords)[2], const uint coords_tot, const int coords_sign, uint (*r_tris)[3]) { PolyFill pf; PolyIndex *indices = BLI_array_alloca(indices, coords_tot); #ifdef DEBUG_TIME TIMEIT_START(polyfill2d); #endif polyfill_prepare(&pf, coords, coords_tot, coords_sign, r_tris, /* cache */ indices); #ifdef USE_KDTREE if (pf.coords_tot_concave) { pf.kdtree.nodes = BLI_array_alloca(pf.kdtree.nodes, pf.coords_tot_concave); pf.kdtree.nodes_map = memset(BLI_array_alloca(pf.kdtree.nodes_map, coords_tot), 0xff, sizeof(*pf.kdtree.nodes_map) * coords_tot); } else { pf.kdtree.totnode = 0; } #endif polyfill_calc(&pf); #ifdef DEBUG_TIME TIMEIT_END(polyfill2d); #endif }
void _bli_array_reverse(void *arr_v, unsigned int arr_len, size_t arr_stride) { const unsigned int arr_half_stride = (arr_len / 2) * arr_stride; unsigned int i, i_end; char *arr = arr_v; char *buf = BLI_array_alloca(buf, arr_stride); for (i = 0, i_end = (arr_len - 1) * arr_stride; i < arr_half_stride; i += arr_stride, i_end -= arr_stride) { memcpy(buf, &arr[i], arr_stride); memcpy(&arr[i], &arr[i_end], arr_stride); memcpy(&arr[i_end], buf, arr_stride); } }
void _bli_array_wrap(void *arr_v, unsigned int arr_len, size_t arr_stride, int dir) { char *arr = arr_v; char *buf = BLI_array_alloca(buf, arr_stride); if (dir == -1) { memcpy(buf, arr, arr_stride); memmove(arr, arr + arr_stride, arr_stride * (arr_len - 1)); memcpy(arr + (arr_stride * (arr_len - 1)), buf, arr_stride); } else if (dir == 1) { memcpy(buf, arr + (arr_stride * (arr_len - 1)), arr_stride); memmove(arr + arr_stride, arr, arr_stride * (arr_len - 1)); memcpy(arr, buf, arr_stride); } else { BLI_assert(0); } }
static void face_edges_split( BMesh *bm, BMFace *f, struct LinkBase *e_ls_base, bool use_island_connect, MemArena *mem_arena_edgenet) { unsigned int i; unsigned int edge_arr_len = e_ls_base->list_len; BMEdge **edge_arr = BLI_array_alloca(edge_arr, edge_arr_len); LinkNode *node; BLI_assert(f->head.htype == BM_FACE); for (i = 0, node = e_ls_base->list; i < e_ls_base->list_len; i++, node = node->next) { edge_arr[i] = node->link; } BLI_assert(node == NULL); #ifdef USE_DUMP printf("# %s: %d %u\n", __func__, BM_elem_index_get(f), e_ls_base->list_len); #endif #ifdef USE_NET_ISLAND_CONNECT if (use_island_connect) { unsigned int edge_arr_holes_len; BMEdge **edge_arr_holes; if (BM_face_split_edgenet_connect_islands( bm, f, edge_arr, edge_arr_len, false, mem_arena_edgenet, &edge_arr_holes, &edge_arr_holes_len)) { edge_arr_len = edge_arr_holes_len; edge_arr = edge_arr_holes; /* owned by the arena */ } } #else UNUSED_VARS(use_island_connect, mem_arena_edgenet); #endif BM_face_split_edgenet(bm, f, edge_arr, (int)edge_arr_len, NULL, NULL); }
static void face_edges_split( BMesh *bm, BMFace *f, struct LinkBase *e_ls_base) { unsigned int i; BMEdge **edge_arr = BLI_array_alloca(edge_arr, e_ls_base->list_len); LinkNode *node; BLI_assert(f->head.htype == BM_FACE); for (i = 0, node = e_ls_base->list; i < e_ls_base->list_len; i++, node = node->next) { edge_arr[i] = node->link; } BLI_assert(node == NULL); #ifdef USE_DUMP printf("# %s: %d %u\n", __func__, BM_elem_index_get(f), e_ls_base->list_len); #endif BM_face_split_edgenet(bm, f, edge_arr, (int)e_ls_base->list_len, NULL, NULL); }
static void edge_verts_sort(const float co[3], struct LinkBase *v_ls_base) { /* not optimal but list will be typically < 5 */ unsigned int i; struct vert_sort_t *vert_sort = BLI_array_alloca(vert_sort, v_ls_base->list_len); LinkNode *node; BLI_assert(v_ls_base->list_len > 1); for (i = 0, node = v_ls_base->list; i < v_ls_base->list_len; i++, node = node->next) { BMVert *v = node->link; BLI_assert(v->head.htype == BM_VERT); vert_sort[i].val = len_squared_v3v3(co, v->co); vert_sort[i].v = v; } qsort(vert_sort, v_ls_base->list_len, sizeof(*vert_sort), BLI_sortutil_cmp_float); for (i = 0, node = v_ls_base->list; i < v_ls_base->list_len; i++, node = node->next) { node->link = vert_sort[i].v; } }
/** * \brief Mesh -> BMesh * \param bm: The mesh to write into, while this is typically a newly created BMesh, * merging into existing data is supported. * Note the custom-data layout isn't used. * If more comprehensive merging is needed we should move this into a separate function * since this should be kept fast for edit-mode switching and storing undo steps. * * \warning This function doesn't calculate face normals. */ void BM_mesh_bm_from_me( BMesh *bm, Mesh *me, const struct BMeshFromMeshParams *params) { const bool is_new = !(bm->totvert || (bm->vdata.totlayer || bm->edata.totlayer || bm->pdata.totlayer || bm->ldata.totlayer)); MVert *mvert; MEdge *medge; MLoop *mloop; MPoly *mp; KeyBlock *actkey, *block; BMVert *v, **vtable = NULL; BMEdge *e, **etable = NULL; BMFace *f, **ftable = NULL; float (*keyco)[3] = NULL; int totuv, totloops, i; if (!me || !me->totvert) { if (me && is_new) { /*no verts? still copy customdata layout*/ CustomData_copy(&me->vdata, &bm->vdata, CD_MASK_BMESH, CD_ASSIGN, 0); CustomData_copy(&me->edata, &bm->edata, CD_MASK_BMESH, CD_ASSIGN, 0); CustomData_copy(&me->ldata, &bm->ldata, CD_MASK_BMESH, CD_ASSIGN, 0); CustomData_copy(&me->pdata, &bm->pdata, CD_MASK_BMESH, CD_ASSIGN, 0); CustomData_bmesh_init_pool(&bm->vdata, me->totvert, BM_VERT); CustomData_bmesh_init_pool(&bm->edata, me->totedge, BM_EDGE); CustomData_bmesh_init_pool(&bm->ldata, me->totloop, BM_LOOP); CustomData_bmesh_init_pool(&bm->pdata, me->totpoly, BM_FACE); } return; /* sanity check */ } if (is_new) { CustomData_copy(&me->vdata, &bm->vdata, CD_MASK_BMESH, CD_CALLOC, 0); CustomData_copy(&me->edata, &bm->edata, CD_MASK_BMESH, CD_CALLOC, 0); CustomData_copy(&me->ldata, &bm->ldata, CD_MASK_BMESH, CD_CALLOC, 0); CustomData_copy(&me->pdata, &bm->pdata, CD_MASK_BMESH, CD_CALLOC, 0); /* make sure uv layer names are consisten */ totuv = CustomData_number_of_layers(&bm->pdata, CD_MTEXPOLY); for (i = 0; i < totuv; i++) { int li = CustomData_get_layer_index_n(&bm->pdata, CD_MTEXPOLY, i); CustomData_set_layer_name(&bm->ldata, CD_MLOOPUV, i, bm->pdata.layers[li].name); } } /* -------------------------------------------------------------------- */ /* Shape Key */ int tot_shape_keys = me->key ? BLI_listbase_count(&me->key->block) : 0; if (is_new == false) { tot_shape_keys = min_ii(tot_shape_keys, CustomData_number_of_layers(&bm->vdata, CD_SHAPEKEY)); } const float (**shape_key_table)[3] = tot_shape_keys ? BLI_array_alloca(shape_key_table, tot_shape_keys) : NULL; if ((params->active_shapekey != 0) && (me->key != NULL)) { actkey = BLI_findlink(&me->key->block, params->active_shapekey - 1); } else { actkey = NULL; } if (is_new) { if (tot_shape_keys || params->add_key_index) { CustomData_add_layer(&bm->vdata, CD_SHAPE_KEYINDEX, CD_ASSIGN, NULL, 0); } } if (tot_shape_keys) { if (is_new) { /* check if we need to generate unique ids for the shapekeys. * this also exists in the file reading code, but is here for * a sanity check */ if (!me->key->uidgen) { fprintf(stderr, "%s had to generate shape key uid's in a situation we shouldn't need to! " "(bmesh internal error)\n", __func__); me->key->uidgen = 1; for (block = me->key->block.first; block; block = block->next) { block->uid = me->key->uidgen++; } } } if (actkey && actkey->totelem == me->totvert) { keyco = params->use_shapekey ? actkey->data : NULL; if (is_new) { bm->shapenr = params->active_shapekey; } } for (i = 0, block = me->key->block.first; i < tot_shape_keys; block = block->next, i++) { if (is_new) { CustomData_add_layer_named(&bm->vdata, CD_SHAPEKEY, CD_ASSIGN, NULL, 0, block->name); int j = CustomData_get_layer_index_n(&bm->vdata, CD_SHAPEKEY, i); bm->vdata.layers[j].uid = block->uid; } shape_key_table[i] = (const float (*)[3])block->data; } } if (is_new) { CustomData_bmesh_init_pool(&bm->vdata, me->totvert, BM_VERT); CustomData_bmesh_init_pool(&bm->edata, me->totedge, BM_EDGE); CustomData_bmesh_init_pool(&bm->ldata, me->totloop, BM_LOOP); CustomData_bmesh_init_pool(&bm->pdata, me->totpoly, BM_FACE); BM_mesh_cd_flag_apply(bm, me->cd_flag); } const int cd_vert_bweight_offset = CustomData_get_offset(&bm->vdata, CD_BWEIGHT); const int cd_edge_bweight_offset = CustomData_get_offset(&bm->edata, CD_BWEIGHT); const int cd_edge_crease_offset = CustomData_get_offset(&bm->edata, CD_CREASE); const int cd_shape_key_offset = me->key ? CustomData_get_offset(&bm->vdata, CD_SHAPEKEY) : -1; const int cd_shape_keyindex_offset = is_new && (tot_shape_keys || params->add_key_index) ? CustomData_get_offset(&bm->vdata, CD_SHAPE_KEYINDEX) : -1; vtable = MEM_mallocN(sizeof(BMVert **) * me->totvert, __func__); for (i = 0, mvert = me->mvert; i < me->totvert; i++, mvert++) { v = vtable[i] = BM_vert_create(bm, keyco ? keyco[i] : mvert->co, NULL, BM_CREATE_SKIP_CD); BM_elem_index_set(v, i); /* set_ok */ /* transfer flag */ v->head.hflag = BM_vert_flag_from_mflag(mvert->flag & ~SELECT); /* this is necessary for selection counts to work properly */ if (mvert->flag & SELECT) { BM_vert_select_set(bm, v, true); } normal_short_to_float_v3(v->no, mvert->no); /* Copy Custom Data */ CustomData_to_bmesh_block(&me->vdata, &bm->vdata, i, &v->head.data, true); if (cd_vert_bweight_offset != -1) BM_ELEM_CD_SET_FLOAT(v, cd_vert_bweight_offset, (float)mvert->bweight / 255.0f); /* set shape key original index */ if (cd_shape_keyindex_offset != -1) BM_ELEM_CD_SET_INT(v, cd_shape_keyindex_offset, i); /* set shapekey data */ if (tot_shape_keys) { float (*co_dst)[3] = BM_ELEM_CD_GET_VOID_P(v, cd_shape_key_offset); for (int j = 0; j < tot_shape_keys; j++, co_dst++) { copy_v3_v3(*co_dst, shape_key_table[j][i]); } } } if (is_new) { bm->elem_index_dirty &= ~BM_VERT; /* added in order, clear dirty flag */ } etable = MEM_mallocN(sizeof(BMEdge **) * me->totedge, __func__); medge = me->medge; for (i = 0; i < me->totedge; i++, medge++) { e = etable[i] = BM_edge_create(bm, vtable[medge->v1], vtable[medge->v2], NULL, BM_CREATE_SKIP_CD); BM_elem_index_set(e, i); /* set_ok */ /* transfer flags */ e->head.hflag = BM_edge_flag_from_mflag(medge->flag & ~SELECT); /* this is necessary for selection counts to work properly */ if (medge->flag & SELECT) { BM_edge_select_set(bm, e, true); } /* Copy Custom Data */ CustomData_to_bmesh_block(&me->edata, &bm->edata, i, &e->head.data, true); if (cd_edge_bweight_offset != -1) BM_ELEM_CD_SET_FLOAT(e, cd_edge_bweight_offset, (float)medge->bweight / 255.0f); if (cd_edge_crease_offset != -1) BM_ELEM_CD_SET_FLOAT(e, cd_edge_crease_offset, (float)medge->crease / 255.0f); } if (is_new) { bm->elem_index_dirty &= ~BM_EDGE; /* added in order, clear dirty flag */ } /* only needed for selection. */ if (me->mselect && me->totselect != 0) { ftable = MEM_mallocN(sizeof(BMFace **) * me->totpoly, __func__); } mloop = me->mloop; mp = me->mpoly; for (i = 0, totloops = 0; i < me->totpoly; i++, mp++) { BMLoop *l_iter; BMLoop *l_first; f = bm_face_create_from_mpoly(mp, mloop + mp->loopstart, bm, vtable, etable); if (ftable != NULL) { ftable[i] = f; } if (UNLIKELY(f == NULL)) { printf("%s: Warning! Bad face in mesh" " \"%s\" at index %d!, skipping\n", __func__, me->id.name + 2, i); continue; } /* don't use 'i' since we may have skipped the face */ BM_elem_index_set(f, bm->totface - 1); /* set_ok */ /* transfer flag */ f->head.hflag = BM_face_flag_from_mflag(mp->flag & ~ME_FACE_SEL); /* this is necessary for selection counts to work properly */ if (mp->flag & ME_FACE_SEL) { BM_face_select_set(bm, f, true); } f->mat_nr = mp->mat_nr; if (i == me->act_face) bm->act_face = f; int j = mp->loopstart; l_iter = l_first = BM_FACE_FIRST_LOOP(f); do { /* don't use 'j' since we may have skipped some faces, hence some loops. */ BM_elem_index_set(l_iter, totloops++); /* set_ok */ /* Save index of correspsonding MLoop */ CustomData_to_bmesh_block(&me->ldata, &bm->ldata, j++, &l_iter->head.data, true); } while ((l_iter = l_iter->next) != l_first); /* Copy Custom Data */ CustomData_to_bmesh_block(&me->pdata, &bm->pdata, i, &f->head.data, true); if (params->calc_face_normal) { BM_face_normal_update(f); } } if (is_new) { bm->elem_index_dirty &= ~(BM_FACE | BM_LOOP); /* added in order, clear dirty flag */ } /* -------------------------------------------------------------------- */ /* MSelect clears the array elements (avoid adding multiple times). * * Take care to keep this last and not use (v/e/ftable) after this. */ if (me->mselect && me->totselect != 0) { MSelect *msel; for (i = 0, msel = me->mselect; i < me->totselect; i++, msel++) { BMElem **ele_p; switch (msel->type) { case ME_VSEL: ele_p = (BMElem **)&vtable[msel->index]; break; case ME_ESEL: ele_p = (BMElem **)&etable[msel->index]; break; case ME_FSEL: ele_p = (BMElem **)&ftable[msel->index]; break; default: continue; } if (*ele_p != NULL) { BM_select_history_store_notest(bm, *ele_p); *ele_p = NULL; } } } else { BM_select_history_clear(bm); } MEM_freeN(vtable); MEM_freeN(etable); if (ftable) { MEM_freeN(ftable); } }
static DerivedMesh *applyModifier(ModifierData *md, Object *ob, DerivedMesh *derivedData, ModifierApplyFlag flag) { DerivedMesh *dm = derivedData; DerivedMesh *result; ScrewModifierData *ltmd = (ScrewModifierData *) md; const int useRenderParams = flag & MOD_APPLY_RENDER; int *origindex; int mpoly_index = 0; unsigned int step; unsigned int i, j; unsigned int i1, i2; unsigned int step_tot = useRenderParams ? ltmd->render_steps : ltmd->steps; const bool do_flip = ltmd->flag & MOD_SCREW_NORMAL_FLIP ? 1 : 0; const int quad_ord[4] = { do_flip ? 3 : 0, do_flip ? 2 : 1, do_flip ? 1 : 2, do_flip ? 0 : 3, }; const int quad_ord_ofs[4] = { do_flip ? 2 : 0, do_flip ? 1 : 1, do_flip ? 0 : 2, do_flip ? 3 : 3, }; unsigned int maxVerts = 0, maxEdges = 0, maxPolys = 0; const unsigned int totvert = (unsigned int)dm->getNumVerts(dm); const unsigned int totedge = (unsigned int)dm->getNumEdges(dm); const unsigned int totpoly = (unsigned int)dm->getNumPolys(dm); unsigned int *edge_poly_map = NULL; /* orig edge to orig poly */ unsigned int *vert_loop_map = NULL; /* orig vert to orig loop */ /* UV Coords */ const unsigned int mloopuv_layers_tot = (unsigned int)CustomData_number_of_layers(&dm->loopData, CD_MLOOPUV); MLoopUV **mloopuv_layers = BLI_array_alloca(mloopuv_layers, mloopuv_layers_tot); float uv_u_scale; float uv_v_minmax[2] = {FLT_MAX, -FLT_MAX}; float uv_v_range_inv; float uv_axis_plane[4]; char axis_char = 'X'; bool close; float angle = ltmd->angle; float screw_ofs = ltmd->screw_ofs; float axis_vec[3] = {0.0f, 0.0f, 0.0f}; float tmp_vec1[3], tmp_vec2[3]; float mat3[3][3]; float mtx_tx[4][4]; /* transform the coords by an object relative to this objects transformation */ float mtx_tx_inv[4][4]; /* inverted */ float mtx_tmp_a[4][4]; unsigned int vc_tot_linked = 0; short other_axis_1, other_axis_2; const float *tmpf1, *tmpf2; unsigned int edge_offset; MPoly *mpoly_orig, *mpoly_new, *mp_new; MLoop *mloop_orig, *mloop_new, *ml_new; MEdge *medge_orig, *med_orig, *med_new, *med_new_firstloop, *medge_new; MVert *mvert_new, *mvert_orig, *mv_orig, *mv_new, *mv_new_base; ScrewVertConnect *vc, *vc_tmp, *vert_connect = NULL; const char mpoly_flag = (ltmd->flag & MOD_SCREW_SMOOTH_SHADING) ? ME_SMOOTH : 0; /* don't do anything? */ if (!totvert) return CDDM_from_template(dm, 0, 0, 0, 0, 0); switch (ltmd->axis) { case 0: other_axis_1 = 1; other_axis_2 = 2; break; case 1: other_axis_1 = 0; other_axis_2 = 2; break; default: /* 2, use default to quiet warnings */ other_axis_1 = 0; other_axis_2 = 1; break; } axis_vec[ltmd->axis] = 1.0f; if (ltmd->ob_axis) { /* calc the matrix relative to the axis object */ invert_m4_m4(mtx_tmp_a, ob->obmat); copy_m4_m4(mtx_tx_inv, ltmd->ob_axis->obmat); mul_m4_m4m4(mtx_tx, mtx_tmp_a, mtx_tx_inv); /* calc the axis vec */ mul_mat3_m4_v3(mtx_tx, axis_vec); /* only rotation component */ normalize_v3(axis_vec); /* screw */ if (ltmd->flag & MOD_SCREW_OBJECT_OFFSET) { /* find the offset along this axis relative to this objects matrix */ float totlen = len_v3(mtx_tx[3]); if (totlen != 0.0f) { float zero[3] = {0.0f, 0.0f, 0.0f}; float cp[3]; screw_ofs = closest_to_line_v3(cp, mtx_tx[3], zero, axis_vec); } else { screw_ofs = 0.0f; } } /* angle */ #if 0 /* cant incluide this, not predictable enough, though quite fun. */ if (ltmd->flag & MOD_SCREW_OBJECT_ANGLE) { float mtx3_tx[3][3]; copy_m3_m4(mtx3_tx, mtx_tx); float vec[3] = {0, 1, 0}; float cross1[3]; float cross2[3]; cross_v3_v3v3(cross1, vec, axis_vec); mul_v3_m3v3(cross2, mtx3_tx, cross1); { float c1[3]; float c2[3]; float axis_tmp[3]; cross_v3_v3v3(c1, cross2, axis_vec); cross_v3_v3v3(c2, axis_vec, c1); angle = angle_v3v3(cross1, c2); cross_v3_v3v3(axis_tmp, cross1, c2); normalize_v3(axis_tmp); if (len_v3v3(axis_tmp, axis_vec) > 1.0f) angle = -angle; } } #endif } else { /* exis char is used by i_rotate*/ axis_char = (char)(axis_char + ltmd->axis); /* 'X' + axis */ /* useful to be able to use the axis vec in some cases still */ zero_v3(axis_vec); axis_vec[ltmd->axis] = 1.0f; } /* apply the multiplier */ angle *= (float)ltmd->iter; screw_ofs *= (float)ltmd->iter; uv_u_scale = 1.0f / (float)(step_tot); /* multiplying the steps is a bit tricky, this works best */ step_tot = ((step_tot + 1) * ltmd->iter) - (ltmd->iter - 1); /* will the screw be closed? * Note! smaller then FLT_EPSILON * 100 gives problems with float precision so its never closed. */ if (fabsf(screw_ofs) <= (FLT_EPSILON * 100.0f) && fabsf(fabsf(angle) - ((float)M_PI * 2.0f)) <= (FLT_EPSILON * 100.0f)) { close = 1; step_tot--; if (step_tot < 3) step_tot = 3; maxVerts = totvert * step_tot; /* -1 because we're joining back up */ maxEdges = (totvert * step_tot) + /* these are the edges between new verts */ (totedge * step_tot); /* -1 because vert edges join */ maxPolys = totedge * step_tot; screw_ofs = 0.0f; } else { close = 0; if (step_tot < 3) step_tot = 3; maxVerts = totvert * step_tot; /* -1 because we're joining back up */ maxEdges = (totvert * (step_tot - 1)) + /* these are the edges between new verts */ (totedge * step_tot); /* -1 because vert edges join */ maxPolys = totedge * (step_tot - 1); } if ((ltmd->flag & MOD_SCREW_UV_STRETCH_U) == 0) { uv_u_scale = (uv_u_scale / (float)ltmd->iter) * (angle / ((float)M_PI * 2.0f)); } result = CDDM_from_template(dm, (int)maxVerts, (int)maxEdges, 0, (int)maxPolys * 4, (int)maxPolys); /* copy verts from mesh */ mvert_orig = dm->getVertArray(dm); medge_orig = dm->getEdgeArray(dm); mvert_new = result->getVertArray(result); mpoly_new = result->getPolyArray(result); mloop_new = result->getLoopArray(result); medge_new = result->getEdgeArray(result); if (!CustomData_has_layer(&result->polyData, CD_ORIGINDEX)) { CustomData_add_layer(&result->polyData, CD_ORIGINDEX, CD_CALLOC, NULL, (int)maxPolys); } origindex = CustomData_get_layer(&result->polyData, CD_ORIGINDEX); DM_copy_vert_data(dm, result, 0, 0, (int)totvert); /* copy first otherwise this overwrites our own vertex normals */ if (mloopuv_layers_tot) { float zero_co[3] = {0}; plane_from_point_normal_v3(uv_axis_plane, zero_co, axis_vec); } if (mloopuv_layers_tot) { unsigned int uv_lay; for (uv_lay = 0; uv_lay < mloopuv_layers_tot; uv_lay++) { mloopuv_layers[uv_lay] = CustomData_get_layer_n(&result->loopData, CD_MLOOPUV, (int)uv_lay); } if (ltmd->flag & MOD_SCREW_UV_STRETCH_V) { for (i = 0, mv_orig = mvert_orig; i < totvert; i++, mv_orig++) { const float v = dist_squared_to_plane_v3(mv_orig->co, uv_axis_plane); uv_v_minmax[0] = min_ff(v, uv_v_minmax[0]); uv_v_minmax[1] = max_ff(v, uv_v_minmax[1]); } uv_v_minmax[0] = sqrtf_signed(uv_v_minmax[0]); uv_v_minmax[1] = sqrtf_signed(uv_v_minmax[1]); } uv_v_range_inv = uv_v_minmax[1] - uv_v_minmax[0]; uv_v_range_inv = uv_v_range_inv ? 1.0f / uv_v_range_inv : 0.0f; } /* Set the locations of the first set of verts */ mv_new = mvert_new; mv_orig = mvert_orig; /* Copy the first set of edges */ med_orig = medge_orig; med_new = medge_new; for (i = 0; i < totedge; i++, med_orig++, med_new++) { med_new->v1 = med_orig->v1; med_new->v2 = med_orig->v2; med_new->crease = med_orig->crease; med_new->flag = med_orig->flag & ~ME_LOOSEEDGE; } /* build polygon -> edge map */ if (totpoly) { MPoly *mp_orig; mpoly_orig = dm->getPolyArray(dm); mloop_orig = dm->getLoopArray(dm); edge_poly_map = MEM_mallocN(sizeof(*edge_poly_map) * totedge, __func__); memset(edge_poly_map, 0xff, sizeof(*edge_poly_map) * totedge); vert_loop_map = MEM_mallocN(sizeof(*vert_loop_map) * totvert, __func__); memset(vert_loop_map, 0xff, sizeof(*vert_loop_map) * totvert); for (i = 0, mp_orig = mpoly_orig; i < totpoly; i++, mp_orig++) { unsigned int loopstart = (unsigned int)mp_orig->loopstart; unsigned int loopend = loopstart + (unsigned int)mp_orig->totloop; MLoop *ml_orig = &mloop_orig[loopstart]; unsigned int k; for (k = loopstart; k < loopend; k++, ml_orig++) { edge_poly_map[ml_orig->e] = i; vert_loop_map[ml_orig->v] = k; /* also order edges based on faces */ if (medge_new[ml_orig->e].v1 != ml_orig->v) { SWAP(unsigned int, medge_new[ml_orig->e].v1, medge_new[ml_orig->e].v2); } } } }
/** * Evaluate the expression with the given parameters. * The order and number of parameters must match the names given to parse. */ eExprPyLike_EvalStatus BLI_expr_pylike_eval(ExprPyLike_Parsed *expr, const double *param_values, int param_values_len, double *r_result) { *r_result = 0.0; if (!BLI_expr_pylike_is_valid(expr)) { return EXPR_PYLIKE_INVALID; } #define FAIL_IF(condition) \ if (condition) { \ return EXPR_PYLIKE_FATAL_ERROR; \ } \ ((void)0) /* Check the stack requirement is at least remotely sane and allocate on the actual stack. */ FAIL_IF(expr->max_stack <= 0 || expr->max_stack > 1000); double *stack = BLI_array_alloca(stack, expr->max_stack); /* Evaluate expression. */ ExprOp *ops = expr->ops; int sp = 0, pc; feclearexcept(FE_ALL_EXCEPT); for (pc = 0; pc >= 0 && pc < expr->ops_count; pc++) { switch (ops[pc].opcode) { /* Arithmetic */ case OPCODE_CONST: FAIL_IF(sp >= expr->max_stack); stack[sp++] = ops[pc].arg.dval; break; case OPCODE_PARAMETER: FAIL_IF(sp >= expr->max_stack || ops[pc].arg.ival >= param_values_len); stack[sp++] = param_values[ops[pc].arg.ival]; break; case OPCODE_FUNC1: FAIL_IF(sp < 1); stack[sp - 1] = ops[pc].arg.func1(stack[sp - 1]); break; case OPCODE_FUNC2: FAIL_IF(sp < 2); stack[sp - 2] = ops[pc].arg.func2(stack[sp - 2], stack[sp - 1]); sp--; break; case OPCODE_MIN: FAIL_IF(sp < ops[pc].arg.ival); for (int j = 1; j < ops[pc].arg.ival; j++, sp--) { CLAMP_MAX(stack[sp - 2], stack[sp - 1]); } break; case OPCODE_MAX: FAIL_IF(sp < ops[pc].arg.ival); for (int j = 1; j < ops[pc].arg.ival; j++, sp--) { CLAMP_MIN(stack[sp - 2], stack[sp - 1]); } break; /* Jumps */ case OPCODE_JMP: pc += ops[pc].jmp_offset; break; case OPCODE_JMP_ELSE: FAIL_IF(sp < 1); if (!stack[--sp]) { pc += ops[pc].jmp_offset; } break; case OPCODE_JMP_OR: case OPCODE_JMP_AND: FAIL_IF(sp < 1); if (!stack[sp - 1] == !(ops[pc].opcode == OPCODE_JMP_OR)) { pc += ops[pc].jmp_offset; } else { sp--; } break; /* For chaining comparisons, i.e. "a < b < c" as "a < b and b < c" */ case OPCODE_CMP_CHAIN: FAIL_IF(sp < 2); /* If comparison fails, return 0 and jump to end. */ if (!ops[pc].arg.func2(stack[sp - 2], stack[sp - 1])) { stack[sp - 2] = 0.0; pc += ops[pc].jmp_offset; } /* Otherwise keep b on the stack and proceed. */ else { stack[sp - 2] = stack[sp - 1]; } sp--; break; default: return EXPR_PYLIKE_FATAL_ERROR; } } FAIL_IF(sp != 1 || pc != expr->ops_count); #undef FAIL_IF *r_result = stack[0]; /* Detect floating point evaluation errors. */ int flags = fetestexcept(FE_DIVBYZERO | FE_INVALID); if (flags) { return (flags & FE_INVALID) ? EXPR_PYLIKE_MATH_ERROR : EXPR_PYLIKE_DIV_BY_ZERO; } return EXPR_PYLIKE_SUCCESS; }
/** * Create an ngon from an array of sorted verts * * Special features this has over other functions. * - Optionally calculate winding based on surrounding edges. * - Optionally create edges between vertices. * - Uses verts so no need to find edges (handy when you only have verts) */ BMFace *BM_face_create_ngon_verts( BMesh *bm, BMVert **vert_arr, const int len, const BMFace *f_example, const eBMCreateFlag create_flag, const bool calc_winding, const bool create_edges) { BMEdge **edge_arr = BLI_array_alloca(edge_arr, len); uint winding[2] = {0, 0}; int i, i_prev = len - 1; BMVert *v_winding[2] = {vert_arr[i_prev], vert_arr[0]}; BLI_assert(len > 2); for (i = 0; i < len; i++) { if (create_edges) { edge_arr[i] = BM_edge_create(bm, vert_arr[i_prev], vert_arr[i], NULL, BM_CREATE_NO_DOUBLE); } else { edge_arr[i] = BM_edge_exists(vert_arr[i_prev], vert_arr[i]); if (edge_arr[i] == NULL) { return NULL; } } if (calc_winding) { /* the edge may exist already and be attached to a face * in this case we can find the best winding to use for the new face */ if (edge_arr[i]->l) { BMVert *test_v1, *test_v2; /* we want to use the reverse winding to the existing order */ BM_edge_ordered_verts(edge_arr[i], &test_v2, &test_v1); winding[(vert_arr[i_prev] == test_v2)]++; BLI_assert(vert_arr[i_prev] == test_v2 || vert_arr[i_prev] == test_v1); } } i_prev = i; } /* --- */ if (calc_winding) { if (winding[0] < winding[1]) { winding[0] = 1; winding[1] = 0; } else { winding[0] = 0; winding[1] = 1; } } else { winding[0] = 0; winding[1] = 1; } /* --- */ /* create the face */ return BM_face_create_ngon( bm, v_winding[winding[0]], v_winding[winding[1]], edge_arr, len, f_example, create_flag); }
static MDefBoundIsect *meshdeform_ray_tree_intersect(MeshDeformBind *mdb, const float co1[3], const float co2[3]) { BVHTreeRayHit hit; MeshDeformIsect isect_mdef; struct MeshRayCallbackData data = { mdb, &isect_mdef, }; float end[3], vec_normal[3]; /* happens binding when a cage has no faces */ if (UNLIKELY(mdb->bvhtree == NULL)) return NULL; /* setup isec */ memset(&isect_mdef, 0, sizeof(isect_mdef)); isect_mdef.lambda = 1e10f; copy_v3_v3(isect_mdef.start, co1); copy_v3_v3(end, co2); sub_v3_v3v3(isect_mdef.vec, end, isect_mdef.start); isect_mdef.vec_length = normalize_v3_v3(vec_normal, isect_mdef.vec); hit.index = -1; hit.dist = BVH_RAYCAST_DIST_MAX; if (BLI_bvhtree_ray_cast(mdb->bvhtree, isect_mdef.start, vec_normal, 0.0, &hit, harmonic_ray_callback, &data) != -1) { const MLoop *mloop = mdb->cagedm_cache.mloop; const MLoopTri *lt = &mdb->cagedm_cache.looptri[hit.index]; const MPoly *mp = &mdb->cagedm_cache.mpoly[lt->poly]; const float (*cagecos)[3] = mdb->cagecos; const float len = isect_mdef.lambda; MDefBoundIsect *isect; float (*mp_cagecos)[3] = BLI_array_alloca(mp_cagecos, mp->totloop); int i; /* create MDefBoundIsect, and extra for 'poly_weights[]' */ isect = BLI_memarena_alloc(mdb->memarena, sizeof(*isect) + (sizeof(float) * mp->totloop)); /* compute intersection coordinate */ madd_v3_v3v3fl(isect->co, co1, isect_mdef.vec, len); isect->facing = isect_mdef.isect; isect->poly_index = lt->poly; isect->len = max_ff(len_v3v3(co1, isect->co), MESHDEFORM_LEN_THRESHOLD); /* compute mean value coordinates for interpolation */ for (i = 0; i < mp->totloop; i++) { copy_v3_v3(mp_cagecos[i], cagecos[mloop[mp->loopstart + i].v]); } interp_weights_poly_v3(isect->poly_weights, mp_cagecos, mp->totloop, isect->co); return isect; } return NULL; }
size_t blf_font_width_to_rstrlen(FontBLF *font, const char *str, size_t len, float width, float *r_width) { unsigned int c; GlyphBLF *g, *g_prev = NULL; FT_Vector delta; int pen_x = 0; size_t i = 0, i_prev; GlyphBLF **glyph_ascii_table = font->glyph_cache->glyph_ascii_table; const int width_i = (int)width + 1; int width_new; bool is_malloc; int (*width_accum)[2]; int width_accum_ofs = 0; BLF_KERNING_VARS(font, has_kerning, kern_mode); /* skip allocs in simple cases */ len = BLI_strnlen(str, len); if (width_i <= 1 || len == 0) { if (r_width) { *r_width = 0.0f; } return len; } if (len < 2048) { width_accum = BLI_array_alloca(width_accum, len); is_malloc = false; } else { width_accum = MEM_mallocN(sizeof(*width_accum) * len, __func__); is_malloc = true; } blf_font_ensure_ascii_table(font); while ((i < len) && str[i]) { BLF_UTF8_NEXT_FAST(font, g, str, i, c, glyph_ascii_table); if (UNLIKELY(c == BLI_UTF8_ERR)) break; if (UNLIKELY(g == NULL)) continue; if (has_kerning) BLF_KERNING_STEP(font, kern_mode, g_prev, g, delta, pen_x); pen_x += g->advance_i; width_accum[width_accum_ofs][0] = (int)i; width_accum[width_accum_ofs][1] = pen_x; width_accum_ofs++; g_prev = g; } if (pen_x > width_i && width_accum_ofs != 0) { const int min_x = pen_x - width_i; /* search backwards */ width_new = pen_x; while (width_accum_ofs-- > 0) { if (min_x > width_accum[width_accum_ofs][1]) { break; } } width_accum_ofs++; width_new = pen_x - width_accum[width_accum_ofs][1]; i_prev = (size_t)width_accum[width_accum_ofs][0]; } else { width_new = pen_x; i_prev = 0; } if (is_malloc) { MEM_freeN(width_accum); } if (r_width) { *r_width = (float)width_new; } return i_prev; }
/** * Makes an NGon from an un-ordered set of verts * * assumes... * - that verts are only once in the list. * - that the verts have roughly planer bounds * - that the verts are roughly circular * there can be concave areas but overlapping folds from the center point will fail. * * a brief explanation of the method used * - find the center point * - find the normal of the vcloud * - order the verts around the face based on their angle to the normal vector at the center point. * * \note Since this is a vcloud there is no direction. */ void BM_verts_sort_radial_plane(BMVert **vert_arr, int len) { struct SortIntByFloat *vang = BLI_array_alloca(vang, len); BMVert **vert_arr_map = BLI_array_alloca(vert_arr_map, len); float totv_inv = 1.0f / (float)len; int i = 0; float cent[3], nor[3]; const float *far = NULL, *far_cross = NULL; float far_vec[3]; float far_cross_vec[3]; float sign_vec[3]; /* work out if we are pos/neg angle */ float far_dist_sq, far_dist_max_sq; float far_cross_dist, far_cross_best = 0.0f; /* get the center point and collect vector array since we loop over these a lot */ zero_v3(cent); for (i = 0; i < len; i++) { madd_v3_v3fl(cent, vert_arr[i]->co, totv_inv); } /* find the far point from cent */ far_dist_max_sq = 0.0f; for (i = 0; i < len; i++) { far_dist_sq = len_squared_v3v3(vert_arr[i]->co, cent); if (far_dist_sq > far_dist_max_sq || far == NULL) { far = vert_arr[i]->co; far_dist_max_sq = far_dist_sq; } } sub_v3_v3v3(far_vec, far, cent); // far_dist = len_v3(far_vec); /* real dist */ /* UNUSED */ /* --- */ /* find a point 90deg about to compare with */ far_cross_best = 0.0f; for (i = 0; i < len; i++) { if (far == vert_arr[i]->co) { continue; } sub_v3_v3v3(far_cross_vec, vert_arr[i]->co, cent); far_cross_dist = normalize_v3(far_cross_vec); /* more of a weight then a distance */ far_cross_dist = (/* first we want to have a value close to zero mapped to 1 */ 1.0f - fabsf(dot_v3v3(far_vec, far_cross_vec)) * /* second we multiply by the distance * so points close to the center are not preferred */ far_cross_dist); if (far_cross_dist > far_cross_best || far_cross == NULL) { far_cross = vert_arr[i]->co; far_cross_best = far_cross_dist; } } sub_v3_v3v3(far_cross_vec, far_cross, cent); /* --- */ /* now we have 2 vectors we can have a cross product */ cross_v3_v3v3(nor, far_vec, far_cross_vec); normalize_v3(nor); cross_v3_v3v3(sign_vec, far_vec, nor); /* this vector should match 'far_cross_vec' closely */ /* --- */ /* now calculate every points angle around the normal (signed) */ for (i = 0; i < len; i++) { vang[i].sort_value = angle_signed_on_axis_v3v3v3_v3(far, cent, vert_arr[i]->co, nor); vang[i].data = i; vert_arr_map[i] = vert_arr[i]; } /* sort by angle and magic! - we have our ngon */ qsort(vang, len, sizeof(*vang), BLI_sortutil_cmp_float); /* --- */ for (i = 0; i < len; i++) { vert_arr[i] = vert_arr_map[vang[i].data]; } }