static void splitlist(ScanFillContext *sf_ctx, ListBase *tempve, ListBase *temped, unsigned short nr) { /* everything is in templist, write only poly nr to fillist */ ScanFillVert *eve, *eve_next; ScanFillEdge *eed, *eed_next; BLI_movelisttolist(tempve, &sf_ctx->fillvertbase); BLI_movelisttolist(temped, &sf_ctx->filledgebase); for (eve = tempve->first; eve; eve = eve_next) { eve_next = eve->next; if (eve->poly_nr == nr) { BLI_remlink(tempve, eve); BLI_addtail(&sf_ctx->fillvertbase, eve); } } for (eed = temped->first; eed; eed = eed_next) { eed_next = eed->next; if (eed->poly_nr == nr) { BLI_remlink(temped, eed); BLI_addtail(&sf_ctx->filledgebase, eed); } } }
static void splitlist(ListBase *tempve, ListBase *temped, short nr) { /* everything is in templist, write only poly nr to fillist */ EditVert *eve,*nextve; EditEdge *eed,*nexted; BLI_movelisttolist(tempve,&fillvertbase); BLI_movelisttolist(temped,&filledgebase); eve= tempve->first; while(eve) { nextve= eve->next; if(eve->xs==nr) { BLI_remlink(tempve,eve); BLI_addtail(&fillvertbase,eve); } eve= nextve; } eed= temped->first; while(eed) { nexted= eed->next; if(eed->f1==nr) { BLI_remlink(temped,eed); BLI_addtail(&filledgebase,eed); } eed= nexted; } }
static void splitlist(ScanFillContext *sf_ctx, ListBase *tempve, ListBase *temped, short nr) { /* everything is in templist, write only poly nr to fillist */ ScanFillVert *eve, *nextve; ScanFillEdge *eed, *nexted; BLI_movelisttolist(tempve, &sf_ctx->fillvertbase); BLI_movelisttolist(temped, &sf_ctx->filledgebase); eve = tempve->first; while (eve) { nextve = eve->next; if (eve->poly_nr == nr) { BLI_remlink(tempve, eve); BLI_addtail(&sf_ctx->fillvertbase, eve); } eve = nextve; } eed = temped->first; while (eed) { nexted = eed->next; if (eed->poly_nr == nr) { BLI_remlink(temped, eed); BLI_addtail(&sf_ctx->filledgebase, eed); } eed = nexted; } }
void ED_preview_icon_job(const bContext *C, void *owner, ID *id, unsigned int *rect, int sizex, int sizey) { wmJob *wm_job; IconPreview *ip, *old_ip; /* suspended start means it starts after 1 timer step, see WM_jobs_timer below */ wm_job = WM_jobs_get(CTX_wm_manager(C), CTX_wm_window(C), owner, "Icon Preview", WM_JOB_EXCL_RENDER | WM_JOB_SUSPEND, WM_JOB_TYPE_RENDER_PREVIEW); ip = MEM_callocN(sizeof(IconPreview), "icon preview"); /* render all resolutions from suspended job too */ old_ip = WM_jobs_customdata_get(wm_job); if (old_ip) BLI_movelisttolist(&ip->sizes, &old_ip->sizes); /* customdata for preview thread */ ip->scene = CTX_data_scene(C); ip->owner = id; ip->id = id; icon_preview_add_size(ip, rect, sizex, sizey); /* setup job */ WM_jobs_customdata_set(wm_job, ip, icon_preview_free); WM_jobs_timer(wm_job, 0.1, NC_MATERIAL, NC_MATERIAL); WM_jobs_callbacks(wm_job, icon_preview_startjob_all_sizes, NULL, NULL, icon_preview_endjob); WM_jobs_start(CTX_wm_manager(C), wm_job); }
void ED_preview_icon_job( const bContext *C, void *owner, ID *id, unsigned int *rect, int sizex, int sizey) { wmJob *wm_job; IconPreview *ip, *old_ip; ED_preview_ensure_dbase(); /* suspended start means it starts after 1 timer step, see WM_jobs_timer below */ wm_job = WM_jobs_get(CTX_wm_manager(C), CTX_wm_window(C), owner, "Icon Preview", WM_JOB_EXCL_RENDER | WM_JOB_SUSPEND, WM_JOB_TYPE_RENDER_PREVIEW); ip = MEM_callocN(sizeof(IconPreview), "icon preview"); /* render all resolutions from suspended job too */ old_ip = WM_jobs_customdata_get(wm_job); if (old_ip) { BLI_movelisttolist(&ip->sizes, &old_ip->sizes); } /* customdata for preview thread */ ip->bmain = CTX_data_main(C); ip->scene = CTX_data_scene(C); ip->depsgraph = CTX_data_depsgraph(C); ip->owner = owner; ip->id = id; ip->id_copy = duplicate_ids(id); icon_preview_add_size(ip, rect, sizex, sizey); /* Special threading hack: * warn main code that this preview is being rendered and cannot be freed... */ { PreviewImage *prv_img = owner; if (prv_img->tag & PRV_TAG_DEFFERED) { prv_img->tag |= PRV_TAG_DEFFERED_RENDERING; } } /* setup job */ WM_jobs_customdata_set(wm_job, ip, icon_preview_free); WM_jobs_timer(wm_job, 0.1, NC_WINDOW, NC_WINDOW); WM_jobs_callbacks(wm_job, icon_preview_startjob_all_sizes, NULL, NULL, icon_preview_endjob); WM_jobs_start(CTX_wm_manager(C), wm_job); }
void BLI_voronoi_compute( const VoronoiSite *sites, int sites_total, int width, int height, ListBase *edges) { VoronoiProcess process; VoronoiEdge *edge; int i; memset(&process, 0, sizeof(VoronoiProcess)); process.width = width; process.height = height; for (i = 0; i < sites_total; i++) { VoronoiEvent *event = MEM_callocN(sizeof(VoronoiEvent), "voronoi site event"); event->type = voronoiEventType_Site; copy_v2_v2(event->site, sites[i].co); voronoi_insertEvent(&process, event); } while (process.queue.first) { VoronoiEvent *event = process.queue.first; process.current_y = event->site[1]; if (event->type == voronoiEventType_Site) { voronoi_addParabola(&process, event->site); } else { voronoi_removeParabola(&process, event); } BLI_freelinkN(&process.queue, event); } voronoi_finishEdge(&process, process.root); edge = process.edges.first; while (edge) { if (edge->neighbor) { copy_v2_v2(edge->start, edge->neighbor->end); MEM_freeN(edge->neighbor); } edge = edge->next; } BLI_movelisttolist(edges, &process.edges); }
/* This function adds data to the copy/paste buffer, freeing existing data first * Only the selected GP-layers get their selected keyframes copied. * * Returns whether the copy operation was successful or not */ bool ED_gpencil_anim_copybuf_copy(bAnimContext *ac) { ListBase anim_data = {NULL, NULL}; bAnimListElem *ale; int filter; Scene *scene = ac->scene; /* clear buffer first */ ED_gpencil_anim_copybuf_free(); /* filter data */ filter = (ANIMFILTER_DATA_VISIBLE | ANIMFILTER_LIST_VISIBLE | ANIMFILTER_NODUPLIS); ANIM_animdata_filter(ac, &anim_data, filter, ac->data, ac->datatype); /* assume that each of these is a GP layer */ for (ale = anim_data.first; ale; ale = ale->next) { ListBase copied_frames = {NULL, NULL}; bGPDlayer *gpl = (bGPDlayer *)ale->data; bGPDframe *gpf; /* loop over frames, and copy only selected frames */ for (gpf = gpl->frames.first; gpf; gpf = gpf->next) { /* if frame is selected, make duplicate it and its strokes */ if (gpf->flag & GP_FRAME_SELECT) { /* make a copy of this frame */ bGPDframe *new_frame = gpencil_frame_duplicate(gpf); BLI_addtail(&copied_frames, new_frame); /* extend extents for keyframes encountered */ if (gpf->framenum < gp_anim_copy_firstframe) gp_anim_copy_firstframe = gpf->framenum; if (gpf->framenum > gp_anim_copy_lastframe) gp_anim_copy_lastframe = gpf->framenum; } } /* create a new layer in buffer if there were keyframes here */ if (BLI_listbase_is_empty(&copied_frames) == false) { bGPDlayer *new_layer = MEM_callocN(sizeof(bGPDlayer), "GPCopyPasteLayer"); BLI_addtail(&gp_anim_copybuf, new_layer); /* move over copied frames */ BLI_movelisttolist(&new_layer->frames, &copied_frames); BLI_assert(copied_frames.first == NULL); /* make a copy of the layer's name - for name-based matching later... */ BLI_strncpy(new_layer->info, gpl->info, sizeof(new_layer->info)); } } /* in case 'relative' paste method is used */ gp_anim_copy_cfra = CFRA; /* clean up */ ANIM_animdata_freelist(&anim_data); /* check if anything ended up in the buffer */ if (ELEM(NULL, gp_anim_copybuf.first, gp_anim_copybuf.last)) { BKE_report(ac->reports, RPT_ERROR, "No keyframes copied to keyframes copy/paste buffer"); return false; } /* report success */ return true; }
unsigned int BLI_scanfill_calc_ex(ScanFillContext *sf_ctx, const int flag, const float nor_proj[3]) { /* * - fill works with its own lists, so create that first (no faces!) * - for vertices, put in ->tmp.v the old pointer * - struct elements xs en ys are not used here: don't hide stuff in it * - edge flag ->f becomes 2 when it's a new edge * - mode: & 1 is check for crossings, then create edges (TO DO ) * - returns number of triangle faces added. */ ListBase tempve, temped; ScanFillVert *eve; ScanFillEdge *eed, *eed_next; PolyFill *pflist, *pf; float *min_xy_p, *max_xy_p; unsigned int totfaces = 0; /* total faces added */ unsigned short a, c, poly = 0; bool ok; float mat_2d[3][3]; BLI_assert(!nor_proj || len_squared_v3(nor_proj) > FLT_EPSILON); #ifdef DEBUG for (eve = sf_ctx->fillvertbase.first; eve; eve = eve->next) { /* these values used to be set, * however they should always be zero'd so check instead */ BLI_assert(eve->f == 0); BLI_assert(sf_ctx->poly_nr || eve->poly_nr == 0); BLI_assert(eve->edge_tot == 0); } #endif #if 0 if (flag & BLI_SCANFILL_CALC_QUADTRI_FASTPATH) { const int totverts = BLI_countlist(&sf_ctx->fillvertbase); if (totverts == 3) { eve = sf_ctx->fillvertbase.first; addfillface(sf_ctx, eve, eve->next, eve->next->next); return 1; } else if (totverts == 4) { float vec1[3], vec2[3]; eve = sf_ctx->fillvertbase.first; /* no need to check 'eve->next->next->next' is valid, already counted */ /* use shortest diagonal for quad */ sub_v3_v3v3(vec1, eve->co, eve->next->next->co); sub_v3_v3v3(vec2, eve->next->co, eve->next->next->next->co); if (dot_v3v3(vec1, vec1) < dot_v3v3(vec2, vec2)) { addfillface(sf_ctx, eve, eve->next, eve->next->next); addfillface(sf_ctx, eve->next->next, eve->next->next->next, eve); } else { addfillface(sf_ctx, eve->next, eve->next->next, eve->next->next->next); addfillface(sf_ctx, eve->next->next->next, eve, eve->next); } return 2; } } #endif /* first test vertices if they are in edges */ /* including resetting of flags */ for (eed = sf_ctx->filledgebase.first; eed; eed = eed->next) { BLI_assert(sf_ctx->poly_nr != SF_POLY_UNSET || eed->poly_nr == SF_POLY_UNSET); eed->v1->f = SF_VERT_AVAILABLE; eed->v2->f = SF_VERT_AVAILABLE; } for (eve = sf_ctx->fillvertbase.first; eve; eve = eve->next) { if (eve->f == SF_VERT_AVAILABLE) { break; } } if (UNLIKELY(eve == NULL)) { return 0; } else { float n[3]; if (nor_proj) { copy_v3_v3(n, nor_proj); } else { /* define projection: with 'best' normal */ /* Newell's Method */ /* Similar code used elsewhere, but this checks for double ups * which historically this function supports so better not change */ /* warning: this only gives stable direction with single polygons, * ideally we'd calcualte connectivity and calculate each polys normal, see T41047 */ const float *v_prev; zero_v3(n); eve = sf_ctx->fillvertbase.last; v_prev = eve->co; for (eve = sf_ctx->fillvertbase.first; eve; eve = eve->next) { if (LIKELY(!compare_v3v3(v_prev, eve->co, SF_EPSILON))) { add_newell_cross_v3_v3v3(n, v_prev, eve->co); v_prev = eve->co; } } } if (UNLIKELY(normalize_v3(n) == 0.0f)) { return 0; } axis_dominant_v3_to_m3(mat_2d, n); } /* STEP 1: COUNT POLYS */ if (sf_ctx->poly_nr != SF_POLY_UNSET) { poly = (unsigned short)(sf_ctx->poly_nr + 1); sf_ctx->poly_nr = SF_POLY_UNSET; } if (flag & BLI_SCANFILL_CALC_POLYS && (poly == 0)) { for (eve = sf_ctx->fillvertbase.first; eve; eve = eve->next) { mul_v2_m3v3(eve->xy, mat_2d, eve->co); /* get first vertex with no poly number */ if (eve->poly_nr == SF_POLY_UNSET) { unsigned int toggle = 0; /* now a sort of select connected */ ok = true; eve->poly_nr = poly; while (ok) { ok = false; toggle++; for (eed = (toggle & 1) ? sf_ctx->filledgebase.first : sf_ctx->filledgebase.last; eed; eed = (toggle & 1) ? eed->next : eed->prev) { if (eed->v1->poly_nr == SF_POLY_UNSET && eed->v2->poly_nr == poly) { eed->v1->poly_nr = poly; eed->poly_nr = poly; ok = true; } else if (eed->v2->poly_nr == SF_POLY_UNSET && eed->v1->poly_nr == poly) { eed->v2->poly_nr = poly; eed->poly_nr = poly; ok = true; } else if (eed->poly_nr == SF_POLY_UNSET) { if (eed->v1->poly_nr == poly && eed->v2->poly_nr == poly) { eed->poly_nr = poly; ok = true; } } } } poly++; } } /* printf("amount of poly's: %d\n", poly); */ } else if (poly) { /* we pre-calculated poly_nr */ for (eve = sf_ctx->fillvertbase.first; eve; eve = eve->next) { mul_v2_m3v3(eve->xy, mat_2d, eve->co); } } else { poly = 1; for (eve = sf_ctx->fillvertbase.first; eve; eve = eve->next) { mul_v2_m3v3(eve->xy, mat_2d, eve->co); eve->poly_nr = 0; } for (eed = sf_ctx->filledgebase.first; eed; eed = eed->next) { eed->poly_nr = 0; } } /* STEP 2: remove loose edges and strings of edges */ if (flag & BLI_SCANFILL_CALC_LOOSE) { unsigned int toggle = 0; for (eed = sf_ctx->filledgebase.first; eed; eed = eed->next) { if (eed->v1->edge_tot++ > 250) break; if (eed->v2->edge_tot++ > 250) break; } if (eed) { /* otherwise it's impossible to be sure you can clear vertices */ #ifdef DEBUG printf("No vertices with 250 edges allowed!\n"); #endif return 0; } /* does it only for vertices with (->edge_tot == 1) */ testvertexnearedge(sf_ctx); ok = true; while (ok) { ok = false; toggle++; for (eed = (toggle & 1) ? sf_ctx->filledgebase.first : sf_ctx->filledgebase.last; eed; eed = eed_next) { eed_next = (toggle & 1) ? eed->next : eed->prev; if (eed->v1->edge_tot == 1) { eed->v2->edge_tot--; BLI_remlink(&sf_ctx->fillvertbase, eed->v1); BLI_remlink(&sf_ctx->filledgebase, eed); ok = true; } else if (eed->v2->edge_tot == 1) { eed->v1->edge_tot--; BLI_remlink(&sf_ctx->fillvertbase, eed->v2); BLI_remlink(&sf_ctx->filledgebase, eed); ok = true; } } } if (BLI_listbase_is_empty(&sf_ctx->filledgebase)) { /* printf("All edges removed\n"); */ return 0; } } else { /* skip checks for loose edges */ for (eed = sf_ctx->filledgebase.first; eed; eed = eed->next) { eed->v1->edge_tot++; eed->v2->edge_tot++; } #ifdef DEBUG /* ensure we're right! */ for (eed = sf_ctx->filledgebase.first; eed; eed = eed->next) { BLI_assert(eed->v1->edge_tot != 1); BLI_assert(eed->v2->edge_tot != 1); } #endif } /* CURRENT STATUS: * - eve->f :1 = available in edges * - eve->poly_nr :polynumber * - eve->edge_tot :amount of edges connected to vertex * - eve->tmp.v :store! original vertex number * * - eed->f :1 = boundary edge (optionally set by caller) * - eed->poly_nr :poly number */ /* STEP 3: MAKE POLYFILL STRUCT */ pflist = MEM_mallocN(sizeof(*pflist) * (size_t)poly, "edgefill"); pf = pflist; for (a = 0; a < poly; a++) { pf->edges = pf->verts = 0; pf->min_xy[0] = pf->min_xy[1] = 1.0e20f; pf->max_xy[0] = pf->max_xy[1] = -1.0e20f; pf->f = SF_POLY_NEW; pf->nr = a; pf++; } for (eed = sf_ctx->filledgebase.first; eed; eed = eed->next) { pflist[eed->poly_nr].edges++; } for (eve = sf_ctx->fillvertbase.first; eve; eve = eve->next) { pflist[eve->poly_nr].verts++; min_xy_p = pflist[eve->poly_nr].min_xy; max_xy_p = pflist[eve->poly_nr].max_xy; min_xy_p[0] = (min_xy_p[0]) < (eve->xy[0]) ? (min_xy_p[0]) : (eve->xy[0]); min_xy_p[1] = (min_xy_p[1]) < (eve->xy[1]) ? (min_xy_p[1]) : (eve->xy[1]); max_xy_p[0] = (max_xy_p[0]) > (eve->xy[0]) ? (max_xy_p[0]) : (eve->xy[0]); max_xy_p[1] = (max_xy_p[1]) > (eve->xy[1]) ? (max_xy_p[1]) : (eve->xy[1]); if (eve->edge_tot > 2) { pflist[eve->poly_nr].f = SF_POLY_VALID; } } /* STEP 4: FIND HOLES OR BOUNDS, JOIN THEM * ( bounds just to divide it in pieces for optimization, * the edgefill itself has good auto-hole detection) * WATCH IT: ONLY WORKS WITH SORTED POLYS!!! */ if ((flag & BLI_SCANFILL_CALC_HOLES) && (poly > 1)) { unsigned short *polycache, *pc; /* so, sort first */ qsort(pflist, (size_t)poly, sizeof(PolyFill), vergpoly); #if 0 pf = pflist; for (a = 0; a < poly; a++) { printf("poly:%d edges:%d verts:%d flag: %d\n", a, pf->edges, pf->verts, pf->f); PRINT2(f, f, pf->min[0], pf->min[1]); pf++; } #endif polycache = pc = MEM_callocN(sizeof(*polycache) * (size_t)poly, "polycache"); pf = pflist; for (a = 0; a < poly; a++, pf++) { for (c = (unsigned short)(a + 1); c < poly; c++) { /* if 'a' inside 'c': join (bbox too) * Careful: 'a' can also be inside another poly. */ if (boundisect(pf, pflist + c)) { *pc = c; pc++; } /* only for optimize! */ /* else if (pf->max_xy[0] < (pflist+c)->min[cox]) break; */ } while (pc != polycache) { pc--; mergepolysSimp(sf_ctx, pf, pflist + *pc); } } MEM_freeN(polycache); } #if 0 printf("after merge\n"); pf = pflist; for (a = 0; a < poly; a++) { printf("poly:%d edges:%d verts:%d flag: %d\n", a, pf->edges, pf->verts, pf->f); pf++; } #endif /* STEP 5: MAKE TRIANGLES */ tempve.first = sf_ctx->fillvertbase.first; tempve.last = sf_ctx->fillvertbase.last; temped.first = sf_ctx->filledgebase.first; temped.last = sf_ctx->filledgebase.last; BLI_listbase_clear(&sf_ctx->fillvertbase); BLI_listbase_clear(&sf_ctx->filledgebase); pf = pflist; for (a = 0; a < poly; a++) { if (pf->edges > 1) { splitlist(sf_ctx, &tempve, &temped, pf->nr); totfaces += scanfill(sf_ctx, pf, flag); } pf++; } BLI_movelisttolist(&sf_ctx->fillvertbase, &tempve); BLI_movelisttolist(&sf_ctx->filledgebase, &temped); /* FREE */ MEM_freeN(pflist); return totfaces; }
int BLI_edgefill(int mat_nr) { /* - fill works with its own lists, so create that first (no faces!) - for vertices, put in ->tmp.v the old pointer - struct elements xs en ys are not used here: don't hide stuff in it - edge flag ->f becomes 2 when it's a new edge - mode: & 1 is check for crossings, then create edges (TO DO ) */ ListBase tempve, temped; EditVert *eve; EditEdge *eed,*nexted; PolyFill *pflist,*pf; float *minp, *maxp, *v1, *v2, norm[3], len; short a,c,poly=0,ok=0,toggle=0; /* reset variables */ eve= fillvertbase.first; while(eve) { eve->f= 0; eve->xs= 0; eve->h= 0; eve= eve->next; } /* first test vertices if they are in edges */ /* including resetting of flags */ eed= filledgebase.first; while(eed) { eed->f= eed->f1= eed->h= 0; eed->v1->f= 1; eed->v2->f= 1; eed= eed->next; } eve= fillvertbase.first; while(eve) { if(eve->f & 1) { ok=1; break; } eve= eve->next; } if(ok==0) return 0; /* NEW NEW! define projection: with 'best' normal */ /* just use the first three different vertices */ /* THIS PART STILL IS PRETTY WEAK! (ton) */ eve= fillvertbase.last; len= 0.0; v1= eve->co; v2= 0; eve= fillvertbase.first; while(eve) { if(v2) { if( compare_v3v3(v2, eve->co, COMPLIMIT)==0) { len= normal_tri_v3( norm,v1, v2, eve->co); if(len != 0.0) break; } } else if(compare_v3v3(v1, eve->co, COMPLIMIT)==0) { v2= eve->co; } eve= eve->next; } if(len==0.0) return 0; /* no fill possible */ norm[0]= fabs(norm[0]); norm[1]= fabs(norm[1]); norm[2]= fabs(norm[2]); if(norm[2]>=norm[0] && norm[2]>=norm[1]) { cox= 0; coy= 1; } else if(norm[1]>=norm[0] && norm[1]>=norm[2]) { cox= 0; coy= 2; } else { cox= 1; coy= 2; } /* STEP 1: COUNT POLYS */ eve= fillvertbase.first; while(eve) { /* get first vertex with no poly number */ if(eve->xs==0) { poly++; /* now a sortof select connected */ ok= 1; eve->xs= poly; while(ok) { ok= 0; toggle++; if(toggle & 1) eed= filledgebase.first; else eed= filledgebase.last; while(eed) { if(eed->v1->xs==0 && eed->v2->xs==poly) { eed->v1->xs= poly; eed->f1= poly; ok= 1; } else if(eed->v2->xs==0 && eed->v1->xs==poly) { eed->v2->xs= poly; eed->f1= poly; ok= 1; } else if(eed->f1==0) { if(eed->v1->xs==poly && eed->v2->xs==poly) { eed->f1= poly; ok= 1; } } if(toggle & 1) eed= eed->next; else eed= eed->prev; } } } eve= eve->next; } /* printf("amount of poly's: %d\n",poly); */ /* STEP 2: remove loose edges and strings of edges */ eed= filledgebase.first; while(eed) { if(eed->v1->h++ >250) break; if(eed->v2->h++ >250) break; eed= eed->next; } if(eed) { /* otherwise it's impossible to be sure you can clear vertices */ callLocalErrorCallBack("No vertices with 250 edges allowed!"); return 0; } /* does it only for vertices with ->h==1 */ testvertexnearedge(); ok= 1; while(ok) { ok= 0; toggle++; if(toggle & 1) eed= filledgebase.first; else eed= filledgebase.last; while(eed) { if(toggle & 1) nexted= eed->next; else nexted= eed->prev; if(eed->v1->h==1) { eed->v2->h--; BLI_remlink(&fillvertbase,eed->v1); BLI_remlink(&filledgebase,eed); ok= 1; } else if(eed->v2->h==1) { eed->v1->h--; BLI_remlink(&fillvertbase,eed->v2); BLI_remlink(&filledgebase,eed); ok= 1; } eed= nexted; } } if(filledgebase.first==0) { /* printf("All edges removed\n"); */ return 0; } /* CURRENT STATUS: - eve->f :1= availalble in edges - eve->xs :polynumber - eve->h :amount of edges connected to vertex - eve->tmp.v :store! original vertex number - eed->f : - eed->f1 :poly number */ /* STEP 3: MAKE POLYFILL STRUCT */ pflist= (PolyFill *)MEM_callocN(poly*sizeof(PolyFill),"edgefill"); pf= pflist; for(a=1;a<=poly;a++) { pf->nr= a; pf->min[0]=pf->min[1]=pf->min[2]= 1.0e20; pf->max[0]=pf->max[1]=pf->max[2]= -1.0e20; pf++; } eed= filledgebase.first; while(eed) { pflist[eed->f1-1].edges++; eed= eed->next; } eve= fillvertbase.first; while(eve) { pflist[eve->xs-1].verts++; minp= pflist[eve->xs-1].min; maxp= pflist[eve->xs-1].max; minp[cox]= (minp[cox])<(eve->co[cox]) ? (minp[cox]) : (eve->co[cox]); minp[coy]= (minp[coy])<(eve->co[coy]) ? (minp[coy]) : (eve->co[coy]); maxp[cox]= (maxp[cox])>(eve->co[cox]) ? (maxp[cox]) : (eve->co[cox]); maxp[coy]= (maxp[coy])>(eve->co[coy]) ? (maxp[coy]) : (eve->co[coy]); if(eve->h>2) pflist[eve->xs-1].f= 1; eve= eve->next; } /* STEP 4: FIND HOLES OR BOUNDS, JOIN THEM * ( bounds just to divide it in pieces for optimization, * the edgefill itself has good auto-hole detection) * WATCH IT: ONLY WORKS WITH SORTED POLYS!!! */ if(poly>1) { short *polycache, *pc; /* so, sort first */ qsort(pflist, poly, sizeof(PolyFill), vergpoly); /*pf= pflist; for(a=1;a<=poly;a++) { printf("poly:%d edges:%d verts:%d flag: %d\n",a,pf->edges,pf->verts,pf->f); PRINT2(f, f, pf->min[0], pf->min[1]); pf++; }*/ polycache= pc= MEM_callocN(sizeof(short)*poly, "polycache"); pf= pflist; for(a=0; a<poly; a++, pf++) { for(c=a+1;c<poly;c++) { /* if 'a' inside 'c': join (bbox too) * Careful: 'a' can also be inside another poly. */ if(boundisect(pf, pflist+c)) { *pc= c; pc++; } /* only for optimize! */ /* else if(pf->max[cox] < (pflist+c)->min[cox]) break; */ } while(pc!=polycache) { pc--; mergepolysSimp(pf, pflist+ *pc); } } MEM_freeN(polycache); } /* printf("after merge\n"); pf= pflist; for(a=1;a<=poly;a++) { printf("poly:%d edges:%d verts:%d flag: %d\n",a,pf->edges,pf->verts,pf->f); pf++; } */ /* STEP 5: MAKE TRIANGLES */ tempve.first= fillvertbase.first; tempve.last= fillvertbase.last; temped.first= filledgebase.first; temped.last= filledgebase.last; fillvertbase.first=fillvertbase.last= 0; filledgebase.first=filledgebase.last= 0; pf= pflist; for(a=0;a<poly;a++) { if(pf->edges>1) { splitlist(&tempve,&temped,pf->nr); scanfill(pf, mat_nr); } pf++; } BLI_movelisttolist(&fillvertbase,&tempve); BLI_movelisttolist(&filledgebase,&temped); /* FREE */ MEM_freeN(pflist); return 1; }
void BKE_maskrasterize_handle_init(MaskRasterHandle *mr_handle, struct Mask *mask, const int width, const int height, const bool do_aspect_correct, const bool do_mask_aa, const bool do_feather) { const rctf default_bounds = {0.0f, 1.0f, 0.0f, 1.0f}; const float pixel_size = 1.0f / (float)min_ii(width, height); const float asp_xy[2] = {(do_aspect_correct && width > height) ? (float)height / (float)width : 1.0f, (do_aspect_correct && width < height) ? (float)width / (float)height : 1.0f}; const float zvec[3] = {0.0f, 0.0f, 1.0f}; MaskLayer *masklay; unsigned int masklay_index; MemArena *sf_arena; mr_handle->layers_tot = (unsigned int)BLI_countlist(&mask->masklayers); mr_handle->layers = MEM_mallocN(sizeof(MaskRasterLayer) * mr_handle->layers_tot, "MaskRasterLayer"); BLI_rctf_init_minmax(&mr_handle->bounds); sf_arena = BLI_memarena_new(BLI_SCANFILL_ARENA_SIZE, __func__); for (masklay = mask->masklayers.first, masklay_index = 0; masklay; masklay = masklay->next, masklay_index++) { /* we need to store vertex ranges for open splines for filling */ unsigned int tot_splines; MaskRasterSplineInfo *open_spline_ranges; unsigned int open_spline_index = 0; MaskSpline *spline; /* scanfill */ ScanFillContext sf_ctx; ScanFillVert *sf_vert = NULL; ScanFillVert *sf_vert_next = NULL; ScanFillFace *sf_tri; unsigned int sf_vert_tot = 0; unsigned int tot_feather_quads = 0; #ifdef USE_SCANFILL_EDGE_WORKAROUND unsigned int tot_boundary_used = 0; unsigned int tot_boundary_found = 0; #endif if (masklay->restrictflag & MASK_RESTRICT_RENDER) { /* skip the layer */ mr_handle->layers_tot--; masklay_index--; continue; } tot_splines = (unsigned int)BLI_countlist(&masklay->splines); open_spline_ranges = MEM_callocN(sizeof(*open_spline_ranges) * tot_splines, __func__); BLI_scanfill_begin_arena(&sf_ctx, sf_arena); for (spline = masklay->splines.first; spline; spline = spline->next) { const bool is_cyclic = (spline->flag & MASK_SPLINE_CYCLIC) != 0; const bool is_fill = (spline->flag & MASK_SPLINE_NOFILL) == 0; float (*diff_points)[2]; unsigned int tot_diff_point; float (*diff_feather_points)[2]; float (*diff_feather_points_flip)[2]; unsigned int tot_diff_feather_points; const unsigned int resol_a = BKE_mask_spline_resolution(spline, width, height) / 4; const unsigned int resol_b = BKE_mask_spline_feather_resolution(spline, width, height) / 4; const unsigned int resol = CLAMPIS(MAX2(resol_a, resol_b), 4, 512); diff_points = BKE_mask_spline_differentiate_with_resolution( spline, &tot_diff_point, resol); if (do_feather) { diff_feather_points = BKE_mask_spline_feather_differentiated_points_with_resolution( spline, &tot_diff_feather_points, resol, FALSE); BLI_assert(diff_feather_points); } else { tot_diff_feather_points = 0; diff_feather_points = NULL; } if (tot_diff_point > 3) { ScanFillVert *sf_vert_prev; unsigned int j; float co[3]; co[2] = 0.0f; sf_ctx.poly_nr++; if (do_aspect_correct) { if (width != height) { float *fp; float *ffp; unsigned int i; float asp; if (width < height) { fp = &diff_points[0][0]; ffp = tot_diff_feather_points ? &diff_feather_points[0][0] : NULL; asp = (float)width / (float)height; } else { fp = &diff_points[0][1]; ffp = tot_diff_feather_points ? &diff_feather_points[0][1] : NULL; asp = (float)height / (float)width; } for (i = 0; i < tot_diff_point; i++, fp += 2) { (*fp) = (((*fp) - 0.5f) / asp) + 0.5f; } if (tot_diff_feather_points) { for (i = 0; i < tot_diff_feather_points; i++, ffp += 2) { (*ffp) = (((*ffp) - 0.5f) / asp) + 0.5f; } } } } /* fake aa, using small feather */ if (do_mask_aa == TRUE) { if (do_feather == FALSE) { tot_diff_feather_points = tot_diff_point; diff_feather_points = MEM_mallocN(sizeof(*diff_feather_points) * (size_t)tot_diff_feather_points, __func__); /* add single pixel feather */ maskrasterize_spline_differentiate_point_outset(diff_feather_points, diff_points, tot_diff_point, pixel_size, FALSE); } else { /* ensure single pixel feather, on any zero feather areas */ maskrasterize_spline_differentiate_point_outset(diff_feather_points, diff_points, tot_diff_point, pixel_size, TRUE); } } if (is_fill) { /* applt intersections depending on fill settings */ if (spline->flag & MASK_SPLINE_NOINTERSECT) { BKE_mask_spline_feather_collapse_inner_loops(spline, diff_feather_points, tot_diff_feather_points); } copy_v2_v2(co, diff_points[0]); sf_vert_prev = BLI_scanfill_vert_add(&sf_ctx, co); sf_vert_prev->tmp.u = sf_vert_tot; sf_vert_prev->keyindex = sf_vert_tot + tot_diff_point; /* absolute index of feather vert */ sf_vert_tot++; /* TODO, an alternate functions so we can avoid double vector copy! */ for (j = 1; j < tot_diff_point; j++) { copy_v2_v2(co, diff_points[j]); sf_vert = BLI_scanfill_vert_add(&sf_ctx, co); sf_vert->tmp.u = sf_vert_tot; sf_vert->keyindex = sf_vert_tot + tot_diff_point; /* absolute index of feather vert */ sf_vert_tot++; } sf_vert = sf_vert_prev; sf_vert_prev = sf_ctx.fillvertbase.last; for (j = 0; j < tot_diff_point; j++) { ScanFillEdge *sf_edge = BLI_scanfill_edge_add(&sf_ctx, sf_vert_prev, sf_vert); #ifdef USE_SCANFILL_EDGE_WORKAROUND if (diff_feather_points) { sf_edge->tmp.c = SF_EDGE_IS_BOUNDARY; tot_boundary_used++; } #else (void)sf_edge; #endif sf_vert_prev = sf_vert; sf_vert = sf_vert->next; } if (diff_feather_points) { float co_feather[3]; co_feather[2] = 1.0f; BLI_assert(tot_diff_feather_points == tot_diff_point); /* note: only added for convenience, we don't infact use these to scanfill, * only to create feather faces after scanfill */ for (j = 0; j < tot_diff_feather_points; j++) { copy_v2_v2(co_feather, diff_feather_points[j]); sf_vert = BLI_scanfill_vert_add(&sf_ctx, co_feather); /* no need for these attrs */ #if 0 sf_vert->tmp.u = sf_vert_tot; sf_vert->keyindex = sf_vert_tot + tot_diff_point; /* absolute index of feather vert */ #endif sf_vert->keyindex = SF_KEYINDEX_TEMP_ID; sf_vert_tot++; } tot_feather_quads += tot_diff_point; } } else { /* unfilled spline */ if (diff_feather_points) { float co_diff[2]; float co_feather[3]; co_feather[2] = 1.0f; if (spline->flag & MASK_SPLINE_NOINTERSECT) { diff_feather_points_flip = MEM_mallocN(sizeof(float) * 2 * tot_diff_feather_points, "diff_feather_points_flip"); for (j = 0; j < tot_diff_point; j++) { sub_v2_v2v2(co_diff, diff_points[j], diff_feather_points[j]); add_v2_v2v2(diff_feather_points_flip[j], diff_points[j], co_diff); } BKE_mask_spline_feather_collapse_inner_loops(spline, diff_feather_points, tot_diff_feather_points); BKE_mask_spline_feather_collapse_inner_loops(spline, diff_feather_points_flip, tot_diff_feather_points); } else { diff_feather_points_flip = NULL; } open_spline_ranges[open_spline_index].vertex_offset = sf_vert_tot; open_spline_ranges[open_spline_index].vertex_total = tot_diff_point; /* TODO, an alternate functions so we can avoid double vector copy! */ for (j = 0; j < tot_diff_point; j++) { /* center vert */ copy_v2_v2(co, diff_points[j]); sf_vert = BLI_scanfill_vert_add(&sf_ctx, co); sf_vert->tmp.u = sf_vert_tot; sf_vert->keyindex = SF_KEYINDEX_TEMP_ID; sf_vert_tot++; /* feather vert A */ copy_v2_v2(co_feather, diff_feather_points[j]); sf_vert = BLI_scanfill_vert_add(&sf_ctx, co_feather); sf_vert->tmp.u = sf_vert_tot; sf_vert->keyindex = SF_KEYINDEX_TEMP_ID; sf_vert_tot++; /* feather vert B */ if (diff_feather_points_flip) { copy_v2_v2(co_feather, diff_feather_points_flip[j]); } else { sub_v2_v2v2(co_diff, co, co_feather); add_v2_v2v2(co_feather, co, co_diff); } sf_vert = BLI_scanfill_vert_add(&sf_ctx, co_feather); sf_vert->tmp.u = sf_vert_tot; sf_vert->keyindex = SF_KEYINDEX_TEMP_ID; sf_vert_tot++; tot_feather_quads += 2; } if (!is_cyclic) { tot_feather_quads -= 2; } if (diff_feather_points_flip) { MEM_freeN(diff_feather_points_flip); diff_feather_points_flip = NULL; } /* cap ends */ /* dummy init value */ open_spline_ranges[open_spline_index].vertex_total_cap_head = 0; open_spline_ranges[open_spline_index].vertex_total_cap_tail = 0; if (!is_cyclic) { float *fp_cent; float *fp_turn; unsigned int k; fp_cent = diff_points[0]; fp_turn = diff_feather_points[0]; #define CALC_CAP_RESOL \ clampis_uint((unsigned int )(len_v2v2(fp_cent, fp_turn) / \ (pixel_size * SPLINE_RESOL_CAP_PER_PIXEL)), \ SPLINE_RESOL_CAP_MIN, SPLINE_RESOL_CAP_MAX) { const unsigned int vertex_total_cap = CALC_CAP_RESOL; for (k = 1; k < vertex_total_cap; k++) { const float angle = (float)k * (1.0f / (float)vertex_total_cap) * (float)M_PI; rotate_point_v2(co_feather, fp_turn, fp_cent, angle, asp_xy); sf_vert = BLI_scanfill_vert_add(&sf_ctx, co_feather); sf_vert->tmp.u = sf_vert_tot; sf_vert->keyindex = SF_KEYINDEX_TEMP_ID; sf_vert_tot++; } tot_feather_quads += vertex_total_cap; open_spline_ranges[open_spline_index].vertex_total_cap_head = vertex_total_cap; } fp_cent = diff_points[tot_diff_point - 1]; fp_turn = diff_feather_points[tot_diff_point - 1]; { const unsigned int vertex_total_cap = CALC_CAP_RESOL; for (k = 1; k < vertex_total_cap; k++) { const float angle = (float)k * (1.0f / (float)vertex_total_cap) * (float)M_PI; rotate_point_v2(co_feather, fp_turn, fp_cent, -angle, asp_xy); sf_vert = BLI_scanfill_vert_add(&sf_ctx, co_feather); sf_vert->tmp.u = sf_vert_tot; sf_vert->keyindex = SF_KEYINDEX_TEMP_ID; sf_vert_tot++; } tot_feather_quads += vertex_total_cap; open_spline_ranges[open_spline_index].vertex_total_cap_tail = vertex_total_cap; } } open_spline_ranges[open_spline_index].is_cyclic = is_cyclic; open_spline_index++; #undef CALC_CAP_RESOL /* end capping */ } } } if (diff_points) { MEM_freeN(diff_points); } if (diff_feather_points) { MEM_freeN(diff_feather_points); } } { unsigned int (*face_array)[4], *face; /* access coords */ float (*face_coords)[3], *cos; /* xy, z 0-1 (1.0 == filled) */ unsigned int sf_tri_tot; rctf bounds; unsigned int face_index; int scanfill_flag = 0; bool is_isect = false; ListBase isect_remvertbase = {NULL, NULL}; ListBase isect_remedgebase = {NULL, NULL}; /* now we have all the splines */ face_coords = MEM_mallocN((sizeof(float) * 3) * sf_vert_tot, "maskrast_face_coords"); /* init bounds */ BLI_rctf_init_minmax(&bounds); /* coords */ cos = (float *)face_coords; for (sf_vert = sf_ctx.fillvertbase.first; sf_vert; sf_vert = sf_vert_next) { sf_vert_next = sf_vert->next; copy_v3_v3(cos, sf_vert->co); /* remove so as not to interfere with fill (called after) */ if (sf_vert->keyindex == SF_KEYINDEX_TEMP_ID) { BLI_remlink(&sf_ctx.fillvertbase, sf_vert); } /* bounds */ BLI_rctf_do_minmax_v(&bounds, cos); cos += 3; } /* --- inefficient self-intersect case --- */ /* if self intersections are found, its too trickty to attempt to map vertices * so just realloc and add entirely new vertices - the result of the self-intersect check */ if ((masklay->flag & MASK_LAYERFLAG_FILL_OVERLAP) && (is_isect = BLI_scanfill_calc_self_isect(&sf_ctx, &isect_remvertbase, &isect_remedgebase))) { unsigned int sf_vert_tot_isect = (unsigned int)BLI_countlist(&sf_ctx.fillvertbase); unsigned int i = sf_vert_tot; face_coords = MEM_reallocN(face_coords, sizeof(float[3]) * (sf_vert_tot + sf_vert_tot_isect)); cos = (float *)&face_coords[sf_vert_tot][0]; for (sf_vert = sf_ctx.fillvertbase.first; sf_vert; sf_vert = sf_vert->next) { copy_v3_v3(cos, sf_vert->co); sf_vert->tmp.u = i++; cos += 3; } sf_vert_tot += sf_vert_tot_isect; /* we need to calc polys after self intersect */ scanfill_flag |= BLI_SCANFILL_CALC_POLYS; } /* --- end inefficient code --- */ /* main scan-fill */ if ((masklay->flag & MASK_LAYERFLAG_FILL_DISCRETE) == 0) scanfill_flag |= BLI_SCANFILL_CALC_HOLES; sf_tri_tot = (unsigned int)BLI_scanfill_calc_ex(&sf_ctx, scanfill_flag, zvec); if (is_isect) { /* add removed data back, we only need edges for feather, * but add verts back so they get freed along with others */ BLI_movelisttolist(&sf_ctx.fillvertbase, &isect_remvertbase); BLI_movelisttolist(&sf_ctx.filledgebase, &isect_remedgebase); } face_array = MEM_mallocN(sizeof(*face_array) * ((size_t)sf_tri_tot + (size_t)tot_feather_quads), "maskrast_face_index"); face_index = 0; /* faces */ face = (unsigned int *)face_array; for (sf_tri = sf_ctx.fillfacebase.first; sf_tri; sf_tri = sf_tri->next) { *(face++) = sf_tri->v3->tmp.u; *(face++) = sf_tri->v2->tmp.u; *(face++) = sf_tri->v1->tmp.u; *(face++) = TRI_VERT; face_index++; FACE_ASSERT(face - 4, sf_vert_tot); } /* start of feather faces... if we have this set, * 'face_index' is kept from loop above */ BLI_assert(face_index == sf_tri_tot); if (tot_feather_quads) { ScanFillEdge *sf_edge; for (sf_edge = sf_ctx.filledgebase.first; sf_edge; sf_edge = sf_edge->next) { if (sf_edge->tmp.c == SF_EDGE_IS_BOUNDARY) { *(face++) = sf_edge->v1->tmp.u; *(face++) = sf_edge->v2->tmp.u; *(face++) = sf_edge->v2->keyindex; *(face++) = sf_edge->v1->keyindex; face_index++; FACE_ASSERT(face - 4, sf_vert_tot); #ifdef USE_SCANFILL_EDGE_WORKAROUND tot_boundary_found++; #endif } } } #ifdef USE_SCANFILL_EDGE_WORKAROUND if (tot_boundary_found != tot_boundary_used) { BLI_assert(tot_boundary_found < tot_boundary_used); } #endif /* feather only splines */ while (open_spline_index > 0) { const unsigned int vertex_offset = open_spline_ranges[--open_spline_index].vertex_offset; unsigned int vertex_total = open_spline_ranges[ open_spline_index].vertex_total; unsigned int vertex_total_cap_head = open_spline_ranges[ open_spline_index].vertex_total_cap_head; unsigned int vertex_total_cap_tail = open_spline_ranges[ open_spline_index].vertex_total_cap_tail; unsigned int k, j; j = vertex_offset; /* subtract one since we reference next vertex triple */ for (k = 0; k < vertex_total - 1; k++, j += 3) { BLI_assert(j == vertex_offset + (k * 3)); *(face++) = j + 3; /* next span */ /* z 1 */ *(face++) = j + 0; /* z 1 */ *(face++) = j + 1; /* z 0 */ *(face++) = j + 4; /* next span */ /* z 0 */ face_index++; FACE_ASSERT(face - 4, sf_vert_tot); *(face++) = j + 0; /* z 1 */ *(face++) = j + 3; /* next span */ /* z 1 */ *(face++) = j + 5; /* next span */ /* z 0 */ *(face++) = j + 2; /* z 0 */ face_index++; FACE_ASSERT(face - 4, sf_vert_tot); } if (open_spline_ranges[open_spline_index].is_cyclic) { *(face++) = vertex_offset + 0; /* next span */ /* z 1 */ *(face++) = j + 0; /* z 1 */ *(face++) = j + 1; /* z 0 */ *(face++) = vertex_offset + 1; /* next span */ /* z 0 */ face_index++; FACE_ASSERT(face - 4, sf_vert_tot); *(face++) = j + 0; /* z 1 */ *(face++) = vertex_offset + 0; /* next span */ /* z 1 */ *(face++) = vertex_offset + 2; /* next span */ /* z 0 */ *(face++) = j + 2; /* z 0 */ face_index++; FACE_ASSERT(face - 4, sf_vert_tot); } else { unsigned int midvidx = vertex_offset; /*************** * cap end 'a' */ j = midvidx + (vertex_total * 3); for (k = 0; k < vertex_total_cap_head - 2; k++, j++) { *(face++) = midvidx + 0; /* z 1 */ *(face++) = midvidx + 0; /* z 1 */ *(face++) = j + 0; /* z 0 */ *(face++) = j + 1; /* z 0 */ face_index++; FACE_ASSERT(face - 4, sf_vert_tot); } j = vertex_offset + (vertex_total * 3); /* 2 tris that join the original */ *(face++) = midvidx + 0; /* z 1 */ *(face++) = midvidx + 0; /* z 1 */ *(face++) = midvidx + 1; /* z 0 */ *(face++) = j + 0; /* z 0 */ face_index++; FACE_ASSERT(face - 4, sf_vert_tot); *(face++) = midvidx + 0; /* z 1 */ *(face++) = midvidx + 0; /* z 1 */ *(face++) = j + vertex_total_cap_head - 2; /* z 0 */ *(face++) = midvidx + 2; /* z 0 */ face_index++; FACE_ASSERT(face - 4, sf_vert_tot); /*************** * cap end 'b' */ /* ... same as previous but v 2-3 flipped, and different initial offsets */ j = vertex_offset + (vertex_total * 3) + (vertex_total_cap_head - 1); midvidx = vertex_offset + (vertex_total * 3) - 3; for (k = 0; k < vertex_total_cap_tail - 2; k++, j++) { *(face++) = midvidx; /* z 1 */ *(face++) = midvidx; /* z 1 */ *(face++) = j + 1; /* z 0 */ *(face++) = j + 0; /* z 0 */ face_index++; FACE_ASSERT(face - 4, sf_vert_tot); } j = vertex_offset + (vertex_total * 3) + (vertex_total_cap_head - 1); /* 2 tris that join the original */ *(face++) = midvidx + 0; /* z 1 */ *(face++) = midvidx + 0; /* z 1 */ *(face++) = j + 0; /* z 0 */ *(face++) = midvidx + 1; /* z 0 */ face_index++; FACE_ASSERT(face - 4, sf_vert_tot); *(face++) = midvidx + 0; /* z 1 */ *(face++) = midvidx + 0; /* z 1 */ *(face++) = midvidx + 2; /* z 0 */ *(face++) = j + vertex_total_cap_tail - 2; /* z 0 */ face_index++; FACE_ASSERT(face - 4, sf_vert_tot); } } MEM_freeN(open_spline_ranges); // fprintf(stderr, "%u %u (%u %u), %u\n", face_index, sf_tri_tot + tot_feather_quads, sf_tri_tot, tot_feather_quads, tot_boundary_used - tot_boundary_found); #ifdef USE_SCANFILL_EDGE_WORKAROUND BLI_assert(face_index + (tot_boundary_used - tot_boundary_found) == sf_tri_tot + tot_feather_quads); #else BLI_assert(face_index == sf_tri_tot + tot_feather_quads); #endif { MaskRasterLayer *layer = &mr_handle->layers[masklay_index]; if (BLI_rctf_isect(&default_bounds, &bounds, &bounds)) { #ifdef USE_SCANFILL_EDGE_WORKAROUND layer->face_tot = (sf_tri_tot + tot_feather_quads) - (tot_boundary_used - tot_boundary_found); #else layer->face_tot = (sf_tri_tot + tot_feather_quads); #endif layer->face_coords = face_coords; layer->face_array = face_array; layer->bounds = bounds; layer_bucket_init(layer, pixel_size); BLI_rctf_union(&mr_handle->bounds, &bounds); } else { MEM_freeN(face_coords); MEM_freeN(face_array); layer_bucket_init_dummy(layer); } /* copy as-is */ layer->alpha = masklay->alpha; layer->blend = masklay->blend; layer->blend_flag = masklay->blend_flag; layer->falloff = masklay->falloff; } /* printf("tris %d, feather tris %d\n", sf_tri_tot, tot_feather_quads); */ } /* add trianges */ BLI_scanfill_end_arena(&sf_ctx, sf_arena); } BLI_memarena_free(sf_arena); }
int BLI_edgefill_ex(ScanFillContext *sf_ctx, const short do_quad_tri_speedup, const float nor_proj[3]) { /* * - fill works with its own lists, so create that first (no faces!) * - for vertices, put in ->tmp.v the old pointer * - struct elements xs en ys are not used here: don't hide stuff in it * - edge flag ->f becomes 2 when it's a new edge * - mode: & 1 is check for crossings, then create edges (TO DO ) * - returns number of triangle faces added. */ ListBase tempve, temped; ScanFillVert *eve; ScanFillEdge *eed, *nexted; PolyFill *pflist, *pf; float *min_xy_p, *max_xy_p; short a, c, poly = 0, ok = 0, toggle = 0; int totfaces = 0; /* total faces added */ int co_x, co_y; /* reset variables */ eve = sf_ctx->fillvertbase.first; a = 0; while (eve) { eve->f = 0; eve->poly_nr = 0; eve->h = 0; eve = eve->next; a += 1; } if (do_quad_tri_speedup && (a == 3)) { eve = sf_ctx->fillvertbase.first; addfillface(sf_ctx, eve, eve->next, eve->next->next); return 1; } else if (do_quad_tri_speedup && (a == 4)) { float vec1[3], vec2[3]; eve = sf_ctx->fillvertbase.first; /* no need to check 'eve->next->next->next' is valid, already counted */ /* use shortest diagonal for quad */ sub_v3_v3v3(vec1, eve->co, eve->next->next->co); sub_v3_v3v3(vec2, eve->next->co, eve->next->next->next->co); if (dot_v3v3(vec1, vec1) < dot_v3v3(vec2, vec2)) { addfillface(sf_ctx, eve, eve->next, eve->next->next); addfillface(sf_ctx, eve->next->next, eve->next->next->next, eve); } else { addfillface(sf_ctx, eve->next, eve->next->next, eve->next->next->next); addfillface(sf_ctx, eve->next->next->next, eve, eve->next); } return 2; } /* first test vertices if they are in edges */ /* including resetting of flags */ eed = sf_ctx->filledgebase.first; while (eed) { eed->poly_nr = 0; eed->v1->f = SF_VERT_UNKNOWN; eed->v2->f = SF_VERT_UNKNOWN; eed = eed->next; } eve = sf_ctx->fillvertbase.first; while (eve) { if (eve->f & SF_VERT_UNKNOWN) { ok = 1; break; } eve = eve->next; } if (ok == 0) { return 0; } else { float n[3]; if (nor_proj) { copy_v3_v3(n, nor_proj); } else { /* define projection: with 'best' normal */ /* Newell's Method */ /* Similar code used elsewhere, but this checks for double ups * which historically this function supports so better not change */ float *v_prev; zero_v3(n); eve = sf_ctx->fillvertbase.last; v_prev = eve->co; for (eve = sf_ctx->fillvertbase.first; eve; eve = eve->next) { if (LIKELY(!compare_v3v3(v_prev, eve->co, SF_EPSILON))) { add_newell_cross_v3_v3v3(n, v_prev, eve->co); v_prev = eve->co; } } } if (UNLIKELY(normalize_v3(n) == 0.0f)) { return 0; } axis_dominant_v3(&co_x, &co_y, n); } /* STEP 1: COUNT POLYS */ eve = sf_ctx->fillvertbase.first; while (eve) { eve->xy[0] = eve->co[co_x]; eve->xy[1] = eve->co[co_y]; /* get first vertex with no poly number */ if (eve->poly_nr == 0) { poly++; /* now a sort of select connected */ ok = 1; eve->poly_nr = poly; while (ok) { ok = 0; toggle++; if (toggle & 1) eed = sf_ctx->filledgebase.first; else eed = sf_ctx->filledgebase.last; while (eed) { if (eed->v1->poly_nr == 0 && eed->v2->poly_nr == poly) { eed->v1->poly_nr = poly; eed->poly_nr = poly; ok = 1; } else if (eed->v2->poly_nr == 0 && eed->v1->poly_nr == poly) { eed->v2->poly_nr = poly; eed->poly_nr = poly; ok = 1; } else if (eed->poly_nr == 0) { if (eed->v1->poly_nr == poly && eed->v2->poly_nr == poly) { eed->poly_nr = poly; ok = 1; } } if (toggle & 1) eed = eed->next; else eed = eed->prev; } } } eve = eve->next; } /* printf("amount of poly's: %d\n",poly); */ /* STEP 2: remove loose edges and strings of edges */ eed = sf_ctx->filledgebase.first; while (eed) { if (eed->v1->h++ > 250) break; if (eed->v2->h++ > 250) break; eed = eed->next; } if (eed) { /* otherwise it's impossible to be sure you can clear vertices */ callLocalErrorCallBack("No vertices with 250 edges allowed!"); return 0; } /* does it only for vertices with ->h==1 */ testvertexnearedge(sf_ctx); ok = 1; while (ok) { ok = 0; toggle++; if (toggle & 1) eed = sf_ctx->filledgebase.first; else eed = sf_ctx->filledgebase.last; while (eed) { if (toggle & 1) nexted = eed->next; else nexted = eed->prev; if (eed->v1->h == 1) { eed->v2->h--; BLI_remlink(&sf_ctx->fillvertbase, eed->v1); BLI_remlink(&sf_ctx->filledgebase, eed); ok = 1; } else if (eed->v2->h == 1) { eed->v1->h--; BLI_remlink(&sf_ctx->fillvertbase, eed->v2); BLI_remlink(&sf_ctx->filledgebase, eed); ok = 1; } eed = nexted; } } if (sf_ctx->filledgebase.first == 0) { /* printf("All edges removed\n"); */ return 0; } /* CURRENT STATUS: * - eve->f :1= availalble in edges * - eve->xs :polynumber * - eve->h :amount of edges connected to vertex * - eve->tmp.v :store! original vertex number * * - eed->f :1= boundary edge (optionally set by caller) * - eed->poly_nr :poly number */ /* STEP 3: MAKE POLYFILL STRUCT */ pflist = (PolyFill *)MEM_callocN(poly * sizeof(PolyFill), "edgefill"); pf = pflist; for (a = 1; a <= poly; a++) { pf->nr = a; pf->min_xy[0] = pf->min_xy[1] = 1.0e20; pf->max_xy[0] = pf->max_xy[1] = -1.0e20; pf++; } eed = sf_ctx->filledgebase.first; while (eed) { pflist[eed->poly_nr - 1].edges++; eed = eed->next; } eve = sf_ctx->fillvertbase.first; while (eve) { pflist[eve->poly_nr - 1].verts++; min_xy_p = pflist[eve->poly_nr - 1].min_xy; max_xy_p = pflist[eve->poly_nr - 1].max_xy; min_xy_p[0] = (min_xy_p[0]) < (eve->xy[0]) ? (min_xy_p[0]) : (eve->xy[0]); min_xy_p[1] = (min_xy_p[1]) < (eve->xy[1]) ? (min_xy_p[1]) : (eve->xy[1]); max_xy_p[0] = (max_xy_p[0]) > (eve->xy[0]) ? (max_xy_p[0]) : (eve->xy[0]); max_xy_p[1] = (max_xy_p[1]) > (eve->xy[1]) ? (max_xy_p[1]) : (eve->xy[1]); if (eve->h > 2) pflist[eve->poly_nr - 1].f = 1; eve = eve->next; } /* STEP 4: FIND HOLES OR BOUNDS, JOIN THEM * ( bounds just to divide it in pieces for optimization, * the edgefill itself has good auto-hole detection) * WATCH IT: ONLY WORKS WITH SORTED POLYS!!! */ if (poly > 1) { short *polycache, *pc; /* so, sort first */ qsort(pflist, poly, sizeof(PolyFill), vergpoly); #if 0 pf = pflist; for (a = 1; a <= poly; a++) { printf("poly:%d edges:%d verts:%d flag: %d\n", a, pf->edges, pf->verts, pf->f); PRINT2(f, f, pf->min[0], pf->min[1]); pf++; } #endif polycache = pc = MEM_callocN(sizeof(short) * poly, "polycache"); pf = pflist; for (a = 0; a < poly; a++, pf++) { for (c = a + 1; c < poly; c++) { /* if 'a' inside 'c': join (bbox too) * Careful: 'a' can also be inside another poly. */ if (boundisect(pf, pflist + c)) { *pc = c; pc++; } /* only for optimize! */ /* else if (pf->max_xy[0] < (pflist+c)->min[cox]) break; */ } while (pc != polycache) { pc--; mergepolysSimp(sf_ctx, pf, pflist + *pc); } } MEM_freeN(polycache); } #if 0 printf("after merge\n"); pf = pflist; for (a = 1; a <= poly; a++) { printf("poly:%d edges:%d verts:%d flag: %d\n", a, pf->edges, pf->verts, pf->f); pf++; } #endif /* STEP 5: MAKE TRIANGLES */ tempve.first = sf_ctx->fillvertbase.first; tempve.last = sf_ctx->fillvertbase.last; temped.first = sf_ctx->filledgebase.first; temped.last = sf_ctx->filledgebase.last; sf_ctx->fillvertbase.first = sf_ctx->fillvertbase.last = NULL; sf_ctx->filledgebase.first = sf_ctx->filledgebase.last = NULL; pf = pflist; for (a = 0; a < poly; a++) { if (pf->edges > 1) { splitlist(sf_ctx, &tempve, &temped, pf->nr); totfaces += scanfill(sf_ctx, pf); } pf++; } BLI_movelisttolist(&sf_ctx->fillvertbase, &tempve); BLI_movelisttolist(&sf_ctx->filledgebase, &temped); /* FREE */ MEM_freeN(pflist); return totfaces; }
static int gp_duplicate_exec(bContext *C, wmOperator *op) { bGPdata *gpd = ED_gpencil_data_get_active(C); if (gpd == NULL) { BKE_report(op->reports, RPT_ERROR, "No Grease Pencil data"); return OPERATOR_CANCELLED; } /* for each visible (and editable) layer's selected strokes, * copy the strokes into a temporary buffer, then append * once all done */ CTX_DATA_BEGIN(C, bGPDlayer *, gpl, editable_gpencil_layers) { ListBase new_strokes = {NULL, NULL}; bGPDframe *gpf = gpl->actframe; bGPDstroke *gps; if (gpf == NULL) continue; /* make copies of selected strokes, and deselect these once we're done */ for (gps = gpf->strokes.first; gps; gps = gps->next) { /* skip strokes that are invalid for current view */ if (ED_gpencil_stroke_can_use(C, gps) == false) continue; if (gps->flag & GP_STROKE_SELECT) { if (gps->totpoints == 1) { /* Special Case: If there's just a single point in this stroke... */ bGPDstroke *gpsd; /* make direct copies of the stroke and its points */ gpsd = MEM_dupallocN(gps); gpsd->points = MEM_dupallocN(gps->points); /* triangle information - will be calculated on next redraw */ gpsd->flag |= GP_STROKE_RECALC_CACHES; gpsd->triangles = NULL; /* add to temp buffer */ gpsd->next = gpsd->prev = NULL; BLI_addtail(&new_strokes, gpsd); } else { /* delegate to a helper, as there's too much to fit in here (for copying subsets)... */ gp_duplicate_points(gps, &new_strokes); } /* deselect original stroke, or else the originals get moved too * (when using the copy + move macro) */ gps->flag &= ~GP_STROKE_SELECT; } } /* add all new strokes in temp buffer to the frame (preventing double-copies) */ BLI_movelisttolist(&gpf->strokes, &new_strokes); BLI_assert(new_strokes.first == NULL); }