Changeset View
Changeset View
Standalone View
Standalone View
source/blender/editors/gpencil/gpencil_data.c
| Show First 20 Lines • Show All 1,315 Lines • ▼ Show 20 Lines | void GPENCIL_OT_layer_isolate(wmOperatorType *ot) | ||||
| RNA_def_boolean(ot->srna, | RNA_def_boolean(ot->srna, | ||||
| "affect_visibility", | "affect_visibility", | ||||
| false, | false, | ||||
| "Affect Visibility", | "Affect Visibility", | ||||
| "In addition to toggling the editability, also affect the visibility"); | "In addition to toggling the editability, also affect the visibility"); | ||||
| } | } | ||||
| /* ********************** Merge Layer with the next layer **************************** */ | /* ********************** Merge Layer with the next layer **************************** */ | ||||
| enum { | |||||
| GP_LAYER_MERGE_ACTIVE = 0, | |||||
| GP_LAYER_MERGE_ALL = 1, | |||||
| }; | |||||
| static int gpencil_merge_layer_exec(bContext *C, wmOperator *op) | static int gpencil_merge_layer_exec(bContext *C, wmOperator *op) | ||||
| { | { | ||||
| bGPdata *gpd = ED_gpencil_data_get_active(C); | bGPdata *gpd = ED_gpencil_data_get_active(C); | ||||
| bGPDlayer *gpl_src = BKE_gpencil_layer_active_get(gpd); | bGPDlayer *gpl_src = BKE_gpencil_layer_active_get(gpd); | ||||
| bGPDlayer *gpl_dst = gpl_src->prev; | bGPDlayer *gpl_dst = gpl_src->prev; | ||||
| const int mode = RNA_enum_get(op->ptr, "mode"); | |||||
| if (mode == GP_LAYER_MERGE_ACTIVE) { | |||||
| if (ELEM(NULL, gpd, gpl_dst, gpl_src)) { | if (ELEM(NULL, gpd, gpl_dst, gpl_src)) { | ||||
| BKE_report(op->reports, RPT_ERROR, "No layers to merge"); | BKE_report(op->reports, RPT_ERROR, "No layers to merge"); | ||||
| return OPERATOR_CANCELLED; | return OPERATOR_CANCELLED; | ||||
| } | } | ||||
| /* Collect frames of gpl_dst in hash table to avoid O(n^2) lookups. */ | |||||
| GHash *gh_frames_dst = BLI_ghash_int_new_ex(__func__, 64); | |||||
| LISTBASE_FOREACH (bGPDframe *, gpf_dst, &gpl_dst->frames) { | |||||
| BLI_ghash_insert(gh_frames_dst, POINTER_FROM_INT(gpf_dst->framenum), gpf_dst); | |||||
| } | } | ||||
| else { | |||||
| /* Read all frames from merge layer and add any missing in destination layer, | if (ELEM(NULL, gpd, gpl_src)) { | ||||
| * copying all previous strokes to keep the image equals. | BKE_report(op->reports, RPT_ERROR, "No layers to flatten"); | ||||
| * Need to do it in a separated loop to avoid strokes accumulation. */ | return OPERATOR_CANCELLED; | ||||
| LISTBASE_FOREACH (bGPDframe *, gpf_src, &gpl_src->frames) { | |||||
| /* Try to find frame in destination layer hash table. */ | |||||
| bGPDframe *gpf_dst = BLI_ghash_lookup(gh_frames_dst, POINTER_FROM_INT(gpf_src->framenum)); | |||||
| if (!gpf_dst) { | |||||
| gpf_dst = BKE_gpencil_layer_frame_get(gpl_dst, gpf_src->framenum, GP_GETFRAME_ADD_COPY); | |||||
| /* Use same frame type. */ | |||||
| gpf_dst->key_type = gpf_src->key_type; | |||||
| BLI_ghash_insert(gh_frames_dst, POINTER_FROM_INT(gpf_src->framenum), gpf_dst); | |||||
| } | } | ||||
| } | } | ||||
| /* Read all frames from merge layer and add strokes. */ | if (mode == GP_LAYER_MERGE_ACTIVE) { | ||||
| LISTBASE_FOREACH (bGPDframe *, gpf_src, &gpl_src->frames) { | ED_gpencil_layer_merge(gpd, gpl_src, gpl_dst, false); | ||||
| /* Try to find frame in destination layer hash table. */ | |||||
| bGPDframe *gpf_dst = BLI_ghash_lookup(gh_frames_dst, POINTER_FROM_INT(gpf_src->framenum)); | |||||
| /* Apply layer transformation. */ | |||||
| LISTBASE_FOREACH (bGPDstroke *, gps_src, &gpf_src->strokes) { | |||||
| for (int p = 0; p < gps_src->totpoints; p++) { | |||||
| bGPDspoint *pt = &gps_src->points[p]; | |||||
| mul_v3_m4v3(&pt->x, gpl_src->layer_mat, &pt->x); | |||||
| } | } | ||||
| else if (mode == GP_LAYER_MERGE_ALL) { | |||||
| gpl_dst = gpl_src; | |||||
| /* Merge layers on top of active layer. */ | |||||
| if (gpd->layers.last != gpl_dst) { | |||||
| LISTBASE_FOREACH_BACKWARD_MUTABLE (bGPDlayer *, gpl, &gpd->layers) { | |||||
| if (gpl == gpl_dst) { | |||||
| break; | |||||
| } | } | ||||
| ED_gpencil_layer_merge(gpd, gpl, gpl->prev, false); | |||||
| /* Add to tail all strokes. */ | |||||
| if (gpf_dst) { | |||||
| BLI_movelisttolist(&gpf_dst->strokes, &gpf_src->strokes); | |||||
| } | } | ||||
| } | } | ||||
| /* Merge layers below active layer. */ | |||||
| /* Add Masks to destination layer. */ | LISTBASE_FOREACH_BACKWARD_MUTABLE (bGPDlayer *, gpl, &gpd->layers) { | ||||
| LISTBASE_FOREACH (bGPDlayer_Mask *, mask, &gpl_src->mask_layers) { | if (gpl == gpl_dst) { | ||||
| /* Don't add merged layers or missing layer names. */ | |||||
| if (!BKE_gpencil_layer_named_get(gpd, mask->name) || STREQ(mask->name, gpl_src->info) || | |||||
| STREQ(mask->name, gpl_dst->info)) { | |||||
| continue; | continue; | ||||
| } | } | ||||
| if (!BKE_gpencil_layer_mask_named_get(gpl_dst, mask->name)) { | ED_gpencil_layer_merge(gpd, gpl, gpl_dst, true); | ||||
| bGPDlayer_Mask *mask_new = MEM_dupallocN(mask); | |||||
| BLI_addtail(&gpl_dst->mask_layers, mask_new); | |||||
| gpl_dst->act_mask++; | |||||
| } | } | ||||
| } | } | ||||
| /* Set destination layer as active. */ | else { | ||||
| BKE_gpencil_layer_active_set(gpd, gpl_dst); | return OPERATOR_CANCELLED; | ||||
| } | |||||
| /* Now delete next layer */ | |||||
| BKE_gpencil_layer_delete(gpd, gpl_src); | |||||
| BLI_ghash_free(gh_frames_dst, NULL, NULL); | |||||
| /* Reorder masking. */ | /* Clear any invalid mask. Some other layer could be using the merged layer. */ | ||||
| BKE_gpencil_layer_mask_sort(gpd, gpl_dst); | LISTBASE_FOREACH (bGPDlayer *, gpl, &gpd->layers) { | ||||
| BKE_gpencil_layer_mask_cleanup(gpd, gpl); | |||||
| } | |||||
| /* notifiers */ | /* notifiers */ | ||||
| DEG_id_tag_update(&gpd->id, ID_RECALC_TRANSFORM | ID_RECALC_GEOMETRY); | DEG_id_tag_update(&gpd->id, ID_RECALC_TRANSFORM | ID_RECALC_GEOMETRY); | ||||
| WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_EDITED, NULL); | WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_EDITED, NULL); | ||||
| WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_SELECTED, NULL); | WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_SELECTED, NULL); | ||||
| return OPERATOR_FINISHED; | return OPERATOR_FINISHED; | ||||
| } | } | ||||
| void GPENCIL_OT_layer_merge(wmOperatorType *ot) | void GPENCIL_OT_layer_merge(wmOperatorType *ot) | ||||
| { | { | ||||
| static const EnumPropertyItem merge_modes[] = { | |||||
| {GP_LAYER_MERGE_ACTIVE, "ACTIVE", 0, "Active", "Merge active layer with the layer below"}, | |||||
| {GP_LAYER_MERGE_ALL, "ALL", 0, "Flatten", "Flatten all layers in the current active"}, | |||||
| {0, NULL, 0, NULL, NULL}, | |||||
| }; | |||||
| /* identifiers */ | /* identifiers */ | ||||
| ot->name = "Merge Down"; | ot->name = "Merge Down"; | ||||
| ot->idname = "GPENCIL_OT_layer_merge"; | ot->idname = "GPENCIL_OT_layer_merge"; | ||||
| ot->description = "Merge the current layer with the layer below"; | ot->description = "Merge the current layer with the layer below or flatten all in active"; | ||||
| /* callbacks */ | /* callbacks */ | ||||
| ot->exec = gpencil_merge_layer_exec; | ot->exec = gpencil_merge_layer_exec; | ||||
| ot->poll = gpencil_active_layer_poll; | ot->poll = gpencil_active_layer_poll; | ||||
| /* flags */ | /* flags */ | ||||
| ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; | ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; | ||||
| ot->prop = RNA_def_enum(ot->srna, "mode", merge_modes, GP_LAYER_MERGE_ACTIVE, "Mode", ""); | |||||
| } | } | ||||
| /* ********************** Change Layer ***************************** */ | /* ********************** Change Layer ***************************** */ | ||||
| static int gpencil_layer_change_invoke(bContext *C, wmOperator *op, const wmEvent *UNUSED(event)) | static int gpencil_layer_change_invoke(bContext *C, wmOperator *op, const wmEvent *UNUSED(event)) | ||||
| { | { | ||||
| uiPopupMenu *pup; | uiPopupMenu *pup; | ||||
| uiLayout *layout; | uiLayout *layout; | ||||
| ▲ Show 20 Lines • Show All 2,446 Lines • Show Last 20 Lines | |||||