From ed84388e7d46214bbc5c4f8a28420f56e44cfa4d Mon Sep 17 00:00:00 2001 From: Howard Trickey Date: Wed, 7 Jun 2017 08:53:10 -0400 Subject: Bevel Op (internal and API) now returns new edges and verts as well as faces. This was requested by script writers. Especially needed if beveling wire edges with vertex_only. Should be backward compatible as just adds two new keys to returned dict in python ('edges' and 'verts'). --- source/blender/bmesh/intern/bmesh_opdefines.c | 2 + source/blender/bmesh/operators/bmo_bevel.c | 2 + source/blender/bmesh/tools/bmesh_bevel.c | 80 +++++++++++++++++++++++---- 3 files changed, 73 insertions(+), 11 deletions(-) (limited to 'source') diff --git a/source/blender/bmesh/intern/bmesh_opdefines.c b/source/blender/bmesh/intern/bmesh_opdefines.c index d2f0fc1721c..200a31b1a57 100644 --- a/source/blender/bmesh/intern/bmesh_opdefines.c +++ b/source/blender/bmesh/intern/bmesh_opdefines.c @@ -1741,6 +1741,8 @@ static BMOpDefine bmo_bevel_def = { }, /* slots_out */ {{"faces.out", BMO_OP_SLOT_ELEMENT_BUF, {BM_FACE}}, /* output faces */ + {"edges.out", BMO_OP_SLOT_ELEMENT_BUF, {BM_EDGE}}, /* output edges */ + {"verts.out", BMO_OP_SLOT_ELEMENT_BUF, {BM_VERT}}, /* output verts */ {{'\0'}}, }, diff --git a/source/blender/bmesh/operators/bmo_bevel.c b/source/blender/bmesh/operators/bmo_bevel.c index d5afb39d7b7..2ae87b64286 100644 --- a/source/blender/bmesh/operators/bmo_bevel.c +++ b/source/blender/bmesh/operators/bmo_bevel.c @@ -66,5 +66,7 @@ void bmo_bevel_exec(BMesh *bm, BMOperator *op) BM_mesh_bevel(bm, offset, offset_type, seg, profile, vonly, false, clamp_overlap, NULL, -1, material, loop_slide); BMO_slot_buffer_from_enabled_hflag(bm, op, op->slots_out, "faces.out", BM_FACE, BM_ELEM_TAG); + BMO_slot_buffer_from_enabled_hflag(bm, op, op->slots_out, "edges.out", BM_EDGE, BM_ELEM_TAG); + BMO_slot_buffer_from_enabled_hflag(bm, op, op->slots_out, "verts.out", BM_VERT, BM_ELEM_TAG); } } diff --git a/source/blender/bmesh/tools/bmesh_bevel.c b/source/blender/bmesh/tools/bmesh_bevel.c index 74841dc2756..6673c5d25cf 100644 --- a/source/blender/bmesh/tools/bmesh_bevel.c +++ b/source/blender/bmesh/tools/bmesh_bevel.c @@ -205,6 +205,29 @@ static int bev_debug_flags = 0; #define DEBUG_OLD_PROJ_TO_PERP_PLANE (bev_debug_flags & 2) #define DEBUG_OLD_FLAT_MID (bev_debug_flags & 4) +/* this flag values will get set on geom we want to return in 'out' slots for edges and verts */ +#define EDGE_OUT 4 +#define VERT_OUT 8 + +/* If we're called from the modifier, tool flags aren't available, but don't need output geometry */ +static void flag_out_edge(BMesh *bm, BMEdge *bme) +{ + if (bm->use_toolflags) + BMO_edge_flag_enable(bm, bme, EDGE_OUT); +} + +static void flag_out_vert(BMesh *bm, BMVert *bmv) +{ + if (bm->use_toolflags) + BMO_vert_flag_enable(bm, bmv, VERT_OUT); +} + +static void disable_flag_out_edge(BMesh *bm, BMEdge *bme) +{ + if (bm->use_toolflags) + BMO_edge_flag_disable(bm, bme, EDGE_OUT); +} + /* Are d1 and d2 parallel or nearly so? */ static bool nearly_parallel(const float d1[3], const float d2[3]) { @@ -262,6 +285,7 @@ static void create_mesh_bmvert(BMesh *bm, VMesh *vm, int i, int j, int k, BMVert NewVert *nv = mesh_vert(vm, i, j, k); nv->v = BM_vert_create(bm, nv->co, eg, BM_CREATE_NOP); BM_elem_flag_disable(nv->v, BM_ELEM_TAG); + flag_out_vert(bm, nv->v); } static void copy_mesh_vert( @@ -504,9 +528,12 @@ static BMFace *bev_create_ngon( } /* not essential for bevels own internal logic, - * this is done so the operator can select newly created faces */ + * this is done so the operator can select newly created geometry */ if (f) { BM_elem_flag_enable(f, BM_ELEM_TAG); + BM_ITER_ELEM(bme, &iter, f, BM_EDGES_OF_FACE) { + flag_out_edge(bm, bme); + } } if (mat_nr >= 0) @@ -3213,6 +3240,7 @@ static void bevel_build_trifan(BevelParams *bp, BMesh *bm, BevVert *bv) BMFace *f_new; BLI_assert(v_fan == l_fan->v); f_new = BM_face_split(bm, f, l_fan, l_fan->next->next, &l_new, NULL, false); + flag_out_edge(bm, l_new->e); if (f_new->len > f->len) { f = f_new; @@ -3259,6 +3287,7 @@ static void bevel_build_quadstrip(BevelParams *bp, BMesh *bm, BevVert *bv) else { BM_face_split(bm, f, l_a, l_b, &l_new, NULL, false); f = l_new->f; + flag_out_edge(bm, l_new->e); /* walk around the new face to get the next verts to split */ l_a = l_new->prev; @@ -3278,7 +3307,7 @@ static void bevel_vert_two_edges(BevelParams *bp, BMesh *bm, BevVert *bv) { VMesh *vm = bv->vmesh; BMVert *v1, *v2; - BMEdge *e_eg; + BMEdge *e_eg, *bme; Profile *pro; float co[3]; BoundVert *bndv; @@ -3320,7 +3349,9 @@ static void bevel_vert_two_edges(BevelParams *bp, BMesh *bm, BevVert *bv) v1 = mesh_vert(vm, 0, 0, k)->v; v2 = mesh_vert(vm, 0, 0, k + 1)->v; BLI_assert(v1 != NULL && v2 != NULL); - BM_edge_create(bm, v1, v2, e_eg, BM_CREATE_NO_DOUBLE); + bme = BM_edge_create(bm, v1, v2, e_eg, BM_CREATE_NO_DOUBLE); + if (bme) + flag_out_edge(bm, bme); } } } @@ -3901,7 +3932,7 @@ static BevVert *bevel_vert_construct(BMesh *bm, BevelParams *bp, BMVert *v) /* Face f has at least one beveled vertex. Rebuild f */ static bool bev_rebuild_polygon(BMesh *bm, BevelParams *bp, BMFace *f) { - BMIter liter; + BMIter liter, eiter, fiter; BMLoop *l, *lprev; BevVert *bv; BoundVert *v, *vstart, *vend; @@ -3909,10 +3940,10 @@ static bool bev_rebuild_polygon(BMesh *bm, BevelParams *bp, BMFace *f) VMesh *vm; int i, k, n; bool do_rebuild = false; - bool go_ccw, corner3special; + bool go_ccw, corner3special, keep; BMVert *bmv; BMEdge *bme, *bme_new, *bme_prev; - BMFace *f_new; + BMFace *f_new, *f_other; BMVert **vv = NULL; BMVert **vv_fix = NULL; BMEdge **ee = NULL; @@ -4050,9 +4081,21 @@ static bool bev_rebuild_polygon(BMesh *bm, BevelParams *bp, BMFace *f) } } - /* don't select newly created boundary faces... */ + /* don't select newly or return created boundary faces... */ if (f_new) { BM_elem_flag_disable(f_new, BM_ELEM_TAG); + /* Also don't want new edges that aren't part of a new bevel face */ + BM_ITER_ELEM(bme, &eiter, f_new, BM_EDGES_OF_FACE) { + keep = false; + BM_ITER_ELEM(f_other, &fiter, bme, BM_FACES_OF_EDGE) { + if (BM_elem_flag_test(f_other, BM_ELEM_TAG)) { + keep = true; + break; + } + } + if (!keep) + disable_flag_out_edge(bm, bme); + } } } @@ -4134,8 +4177,9 @@ static void bevel_reattach_wires(BMesh *bm, BevelParams *bp, BMVert *v) } } } while ((bndv = bndv->next) != bv->vmesh->boundstart); - if (vclosest) + if (vclosest) { BM_edge_create(bm, vclosest, votherclosest, e, BM_CREATE_NO_DOUBLE); + } } } @@ -4539,9 +4583,9 @@ static float bevel_limit_offset(BMesh *bm, BevelParams *bp) /** * - Currently only bevels BM_ELEM_TAG'd verts and edges. * - * - Newly created faces are BM_ELEM_TAG'd too, - * the caller needs to ensure this is cleared before calling - * if its going to use this face tag. + * - Newly created faces, edges, and verts are BM_ELEM_TAG'd too, + * the caller needs to ensure these are cleared before calling + * if its going to use this tag. * * - If limit_offset is set, adjusts offset down if necessary * to avoid geometry collisions. @@ -4633,6 +4677,20 @@ void BM_mesh_bevel( } } + /* When called from operator (as opposed to modifier), bm->use_toolflags + * will be set, and we to transfer the oflags to BM_ELEM_TAGs */ + if (bm->use_toolflags) { + BM_ITER_MESH (v, &iter, bm, BM_VERTS_OF_MESH) { + if (BMO_vert_flag_test(bm, v, VERT_OUT)) + BM_elem_flag_enable(v, BM_ELEM_TAG); + } + BM_ITER_MESH (e, &iter, bm, BM_EDGES_OF_MESH) { + if (BMO_edge_flag_test(bm, e, EDGE_OUT)) { + BM_elem_flag_enable(e, BM_ELEM_TAG); + } + } + } + /* primary free */ BLI_ghash_free(bp.vert_hash, NULL, NULL); BLI_memarena_free(bp.mem_arena); -- cgit v1.2.3 From 89d4164f54d49e5ffeff6ce0ec46556a3b638a8d Mon Sep 17 00:00:00 2001 From: Joshua Leung Date: Wed, 7 Jun 2017 19:07:21 +1200 Subject: GPencil Copy/Paste Fix: Copying/Pasting strokes between datablocks would crash The problem was that newly pasted strokes were still using colours from the original datablock. As a result, you'd either get an immediate crash, or if you managed to save the file before it crashed, each stroke would get reloaded with a dummy colour. This commit fixes makes it possible to copy/paste strokes between datablocks again. However, there are still problems when trying to paste across file boundaries (i.e. copy strokes in one file, paste in another), which the next commit will address. --- source/blender/editors/gpencil/gpencil_edit.c | 62 ++++++++++++++++++++++++++- 1 file changed, 61 insertions(+), 1 deletion(-) (limited to 'source') diff --git a/source/blender/editors/gpencil/gpencil_edit.c b/source/blender/editors/gpencil/gpencil_edit.c index 2df4b2cae54..f2a0476a9e7 100644 --- a/source/blender/editors/gpencil/gpencil_edit.c +++ b/source/blender/editors/gpencil/gpencil_edit.c @@ -38,8 +38,11 @@ #include "MEM_guardedalloc.h" -#include "BLI_math.h" #include "BLI_blenlib.h" +#include "BLI_ghash.h" +#include "BLI_math.h" +#include "BLI_string.h" +#include "BLI_string_utils.h" #include "BLI_utildefines.h" #include "BLT_translation.h" @@ -455,6 +458,7 @@ static int gp_strokes_paste_exec(bContext *C, wmOperator *op) Scene *scene = CTX_data_scene(C); bGPdata *gpd = ED_gpencil_data_get_active(C); bGPDlayer *gpl = CTX_data_active_gpencil_layer(C); /* only use active for copy merge */ + bGPDpalette *palette = CTX_data_active_gpencil_palette(C); bGPDframe *gpf; eGP_PasteMode type = RNA_enum_get(op->ptr, "type"); @@ -515,6 +519,48 @@ static int gp_strokes_paste_exec(bContext *C, wmOperator *op) } CTX_DATA_END; + + /* First Pass Over Buffer: Identifiy the colors needed */ + GHash *src_colors = BLI_ghash_str_new("GPencil Paste Src Colors"); + GHash *dst_colors = BLI_ghash_str_new("GPencil Paste Dst Colors"); + GHashIterator gh_iter; + + for (bGPDstroke *gps = gp_strokes_copypastebuf.first; gps; gps = gps->next) { + if (ED_gpencil_stroke_can_use(C, gps)) { + if (BLI_ghash_haskey(src_colors, gps->colorname) == false) { + BLI_ghash_insert(src_colors, gps->colorname, gps->palcolor); + } + } + } + + /* Ensure that all the necessary colors exist */ + if (palette == NULL) { + palette = BKE_gpencil_palette_addnew(gpd, "Pasted Palette", true); + } + + GHASH_ITER(gh_iter, src_colors) { + bGPDpalettecolor *palcolor; + char *name = BLI_ghashIterator_getKey(&gh_iter); + + /* Look for existing color to map to */ + /* XXX: What to do if same name but different color? Behaviour here should depend on a property? */ + palcolor = BKE_gpencil_palettecolor_getbyname(palette, name); + if (palcolor == NULL) { + /* Doesn't Exist - Create new matching color for this palette */ + /* XXX: This still doesn't fix the pasting across file boundaries problem... */ + bGPDpalettecolor *src_color = BLI_ghashIterator_getValue(&gh_iter); + + palcolor = MEM_dupallocN(src_color); + BLI_addtail(&palette->colors, palcolor); + + BLI_uniquename(&palette->colors, palcolor, DATA_("GP Color"), '.', offsetof(bGPDpalettecolor, info), sizeof(palcolor->info)); + } + + /* Store this mapping (for use later when pasting) */ + BLI_ghash_insert(dst_colors, name, palcolor); + } + + /* Second Pass Over Buffer: Actually copy over the strokes (and adjust the colors) */ for (bGPDstroke *gps = gp_strokes_copypastebuf.first; gps; gps = gps->next) { if (ED_gpencil_stroke_can_use(C, gps)) { /* Need to verify if layer exists */ @@ -533,6 +579,7 @@ static int gp_strokes_paste_exec(bContext *C, wmOperator *op) */ gpf = BKE_gpencil_layer_getframe(gpl, CFRA, true); if (gpf) { + /* Create new stroke */ bGPDstroke *new_stroke = MEM_dupallocN(gps); new_stroke->tmp_layerinfo[0] = '\0'; @@ -543,10 +590,23 @@ static int gp_strokes_paste_exec(bContext *C, wmOperator *op) new_stroke->next = new_stroke->prev = NULL; BLI_addtail(&gpf->strokes, new_stroke); + + /* Fix color references */ + BLI_assert(new_stroke->colorname[0] != '\0'); + new_stroke->palcolor = BLI_ghash_lookup(dst_colors, new_stroke->colorname); + + BLI_assert(new_stroke->palcolor != NULL); + BLI_strncpy(new_stroke->colorname, new_stroke->palcolor->info, sizeof(new_stroke->colorname)); + + /*new_stroke->flag |= GP_STROKE_RECALC_COLOR; */ } } } + /* free temp data*/ + BLI_ghash_free(src_colors, NULL, NULL); + BLI_ghash_free(dst_colors, NULL, NULL); + /* updates */ WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_EDITED, NULL); -- cgit v1.2.3 From 7667040dd023469f8feaf42467c6237177565cd2 Mon Sep 17 00:00:00 2001 From: Joshua Leung Date: Wed, 7 Jun 2017 19:07:57 +1200 Subject: GP Copy/Paste Fix: Paste button doesn't update after copying strokes using Ctrl-C --- source/blender/editors/gpencil/gpencil_edit.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) (limited to 'source') diff --git a/source/blender/editors/gpencil/gpencil_edit.c b/source/blender/editors/gpencil/gpencil_edit.c index f2a0476a9e7..c90faba3761 100644 --- a/source/blender/editors/gpencil/gpencil_edit.c +++ b/source/blender/editors/gpencil/gpencil_edit.c @@ -416,7 +416,10 @@ static int gp_strokes_copy_exec(bContext *C, wmOperator *op) } CTX_DATA_END; - /* done - no updates needed */ + /* updates (to ensure operator buttons are refreshed, when used via hotkeys) */ + WM_event_add_notifier(C, NC_GPENCIL | ND_DATA, NULL); // XXX? + + /* done */ return OPERATOR_FINISHED; } -- cgit v1.2.3 From 2bd49785f9d8bad39bd5687dfff022022cccc63f Mon Sep 17 00:00:00 2001 From: Joshua Leung Date: Thu, 8 Jun 2017 02:42:04 +1200 Subject: Fix: Pasting GP strokes files between files (or when the original colors were deleted) would crash The problem was that the strokes in the copy-paste buffer could be keeping dangling pointers to colors that were already freed. Therefore, this commit makes it so that when copying the strokes, we now make copies of the colors and put them in a hashtable beside the stroke buffer. This is convenient, as it saves us having to look up what colours need to be copied over each time when pasting. --- source/blender/editors/gpencil/gpencil_edit.c | 62 ++++++++++++++++++--------- 1 file changed, 42 insertions(+), 20 deletions(-) (limited to 'source') diff --git a/source/blender/editors/gpencil/gpencil_edit.c b/source/blender/editors/gpencil/gpencil_edit.c index c90faba3761..697095ffb85 100644 --- a/source/blender/editors/gpencil/gpencil_edit.c +++ b/source/blender/editors/gpencil/gpencil_edit.c @@ -338,11 +338,27 @@ void GPENCIL_OT_duplicate(wmOperatorType *ot) /* NOTE: is exposed within the editors/gpencil module so that other tools can use it too */ ListBase gp_strokes_copypastebuf = {NULL, NULL}; +/* Hash for hanging on to all the palette colors used by strokes in the buffer + * + * This is needed to prevent dangling and unsafe pointers when pasting across datablocks, + * or after a color used by a stroke in the buffer gets deleted (via user action or undo). + */ +GHash *gp_strokes_copypastebuf_colors = NULL; + /* Free copy/paste buffer data */ void ED_gpencil_strokes_copybuf_free(void) { bGPDstroke *gps, *gpsn; + /* Free the palettes buffer + * NOTE: This is done before the strokes so that the name ptrs (keys) are still safe + */ + if (gp_strokes_copypastebuf_colors) { + BLI_ghash_free(gp_strokes_copypastebuf_colors, NULL, MEM_freeN); + gp_strokes_copypastebuf_colors = NULL; + } + + /* Free the stroke buffer */ for (gps = gp_strokes_copypastebuf.first; gps; gps = gpsn) { gpsn = gps->next; @@ -416,6 +432,22 @@ static int gp_strokes_copy_exec(bContext *C, wmOperator *op) } CTX_DATA_END; + /* Build up hash of colors used in these strokes, making copies of these to protect against dangling pointers */ + if (gp_strokes_copypastebuf.first) { + gp_strokes_copypastebuf_colors = BLI_ghash_str_new("GPencil CopyBuf Colors"); + + for (bGPDstroke *gps = gp_strokes_copypastebuf.first; gps; gps = gps->next) { + if (ED_gpencil_stroke_can_use(C, gps)) { + if (BLI_ghash_haskey(gp_strokes_copypastebuf_colors, gps->colorname) == false) { + bGPDpalettecolor *color = MEM_dupallocN(gps->palcolor); + + BLI_ghash_insert(gp_strokes_copypastebuf_colors, gps->colorname, color); + gps->palcolor = color; + } + } + } + } + /* updates (to ensure operator buttons are refreshed, when used via hotkeys) */ WM_event_add_notifier(C, NC_GPENCIL | ND_DATA, NULL); // XXX? @@ -464,6 +496,8 @@ static int gp_strokes_paste_exec(bContext *C, wmOperator *op) bGPDpalette *palette = CTX_data_active_gpencil_palette(C); bGPDframe *gpf; + GHash *new_colors; + GHashIterator gh_iter; eGP_PasteMode type = RNA_enum_get(op->ptr, "type"); /* check for various error conditions */ @@ -523,25 +557,14 @@ static int gp_strokes_paste_exec(bContext *C, wmOperator *op) CTX_DATA_END; - /* First Pass Over Buffer: Identifiy the colors needed */ - GHash *src_colors = BLI_ghash_str_new("GPencil Paste Src Colors"); - GHash *dst_colors = BLI_ghash_str_new("GPencil Paste Dst Colors"); - GHashIterator gh_iter; - - for (bGPDstroke *gps = gp_strokes_copypastebuf.first; gps; gps = gps->next) { - if (ED_gpencil_stroke_can_use(C, gps)) { - if (BLI_ghash_haskey(src_colors, gps->colorname) == false) { - BLI_ghash_insert(src_colors, gps->colorname, gps->palcolor); - } - } - } - /* Ensure that all the necessary colors exist */ + // TODO: Split this out into a helper function if (palette == NULL) { palette = BKE_gpencil_palette_addnew(gpd, "Pasted Palette", true); } + new_colors = BLI_ghash_str_new("GPencil Paste Dst Colors"); - GHASH_ITER(gh_iter, src_colors) { + GHASH_ITER(gh_iter, gp_strokes_copypastebuf_colors) { bGPDpalettecolor *palcolor; char *name = BLI_ghashIterator_getKey(&gh_iter); @@ -560,10 +583,10 @@ static int gp_strokes_paste_exec(bContext *C, wmOperator *op) } /* Store this mapping (for use later when pasting) */ - BLI_ghash_insert(dst_colors, name, palcolor); + BLI_ghash_insert(new_colors, name, palcolor); } - /* Second Pass Over Buffer: Actually copy over the strokes (and adjust the colors) */ + /* Copy over the strokes from the buffer (and adjust the colors) */ for (bGPDstroke *gps = gp_strokes_copypastebuf.first; gps; gps = gps->next) { if (ED_gpencil_stroke_can_use(C, gps)) { /* Need to verify if layer exists */ @@ -596,7 +619,7 @@ static int gp_strokes_paste_exec(bContext *C, wmOperator *op) /* Fix color references */ BLI_assert(new_stroke->colorname[0] != '\0'); - new_stroke->palcolor = BLI_ghash_lookup(dst_colors, new_stroke->colorname); + new_stroke->palcolor = BLI_ghash_lookup(new_colors, new_stroke->colorname); BLI_assert(new_stroke->palcolor != NULL); BLI_strncpy(new_stroke->colorname, new_stroke->palcolor->info, sizeof(new_stroke->colorname)); @@ -606,9 +629,8 @@ static int gp_strokes_paste_exec(bContext *C, wmOperator *op) } } - /* free temp data*/ - BLI_ghash_free(src_colors, NULL, NULL); - BLI_ghash_free(dst_colors, NULL, NULL); + /* free temp data */ + BLI_ghash_free(new_colors, NULL, NULL); /* updates */ WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_EDITED, NULL); -- cgit v1.2.3 From d25ab3fcc44ef2e58a1cca197948f3f362b4cc80 Mon Sep 17 00:00:00 2001 From: Joshua Leung Date: Thu, 8 Jun 2017 03:07:57 +1200 Subject: Fix: GP Clone brush was not correcting color references for pasted strokes either --- source/blender/editors/gpencil/gpencil_brush.c | 21 ++++++++ source/blender/editors/gpencil/gpencil_edit.c | 72 ++++++++++++++----------- source/blender/editors/gpencil/gpencil_intern.h | 5 ++ 3 files changed, 68 insertions(+), 30 deletions(-) (limited to 'source') diff --git a/source/blender/editors/gpencil/gpencil_brush.c b/source/blender/editors/gpencil/gpencil_brush.c index 8ddf3a22517..5332bec0c64 100644 --- a/source/blender/editors/gpencil/gpencil_brush.c +++ b/source/blender/editors/gpencil/gpencil_brush.c @@ -773,6 +773,9 @@ typedef struct tGPSB_CloneBrushData { /* for "stamp" mode, the currently pasted brushes */ bGPDstroke **new_strokes; + + /* mapping from colors referenced per stroke, to the new colours in the "pasted" strokes */ + GHash *new_colors; } tGPSB_CloneBrushData; /* Initialise "clone" brush data */ @@ -816,6 +819,11 @@ static void gp_brush_clone_init(bContext *C, tGP_BrushEditData *gso) if (1 /*gso->brush->mode == GP_EDITBRUSH_CLONE_MODE_STAMP*/) { data->new_strokes = MEM_callocN(sizeof(bGPDstroke *) * data->totitems, "cloned strokes ptr array"); } + + /* Init colormap for mapping between the pasted stroke's source colour(names) + * and the final colours that will be used here instead... + */ + data->new_colors = gp_copybuf_validate_colormap(gso->gpd); } /* Free custom data used for "clone" brush */ @@ -829,6 +837,12 @@ static void gp_brush_clone_free(tGP_BrushEditData *gso) data->new_strokes = NULL; } + /* free copybuf colormap */ + if (data->new_colors) { + BLI_ghash_free(data->new_colors, NULL, NULL); + data->new_colors = NULL; + } + /* free the customdata itself */ MEM_freeN(data); gso->customdata = NULL; @@ -869,6 +883,13 @@ static void gp_brush_clone_add(bContext *C, tGP_BrushEditData *gso) new_stroke->next = new_stroke->prev = NULL; BLI_addtail(&gpf->strokes, new_stroke); + /* Fix color references */ + BLI_assert(new_stroke->colorname[0] != '\0'); + new_stroke->palcolor = BLI_ghash_lookup(data->new_colors, new_stroke->colorname); + + BLI_assert(new_stroke->palcolor != NULL); + BLI_strncpy(new_stroke->colorname, new_stroke->palcolor->info, sizeof(new_stroke->colorname)); + /* Adjust all the stroke's points, so that the strokes * get pasted relative to where the cursor is now */ diff --git a/source/blender/editors/gpencil/gpencil_edit.c b/source/blender/editors/gpencil/gpencil_edit.c index 697095ffb85..4b0ba6942e4 100644 --- a/source/blender/editors/gpencil/gpencil_edit.c +++ b/source/blender/editors/gpencil/gpencil_edit.c @@ -371,6 +371,46 @@ void ED_gpencil_strokes_copybuf_free(void) gp_strokes_copypastebuf.first = gp_strokes_copypastebuf.last = NULL; } +/* Ensure that destination datablock has all the colours the pasted strokes need + * Helper function for copy-pasting strokes + */ +GHash *gp_copybuf_validate_colormap(bGPdata *gpd) +{ + GHash *new_colors = BLI_ghash_str_new("GPencil Paste Dst Colors"); + GHashIterator gh_iter; + + /* If there's no active palette yet (i.e. new datablock), add one */ + bGPDpalette *palette = BKE_gpencil_palette_getactive(gpd); + if (palette == NULL) { + palette = BKE_gpencil_palette_addnew(gpd, "Pasted Palette", true); + } + + /* For each color, figure out what to map to... */ + GHASH_ITER(gh_iter, gp_strokes_copypastebuf_colors) { + bGPDpalettecolor *palcolor; + char *name = BLI_ghashIterator_getKey(&gh_iter); + + /* Look for existing color to map to */ + /* XXX: What to do if same name but different color? Behaviour here should depend on a property? */ + palcolor = BKE_gpencil_palettecolor_getbyname(palette, name); + if (palcolor == NULL) { + /* Doesn't Exist - Create new matching color for this palette */ + /* XXX: This still doesn't fix the pasting across file boundaries problem... */ + bGPDpalettecolor *src_color = BLI_ghashIterator_getValue(&gh_iter); + + palcolor = MEM_dupallocN(src_color); + BLI_addtail(&palette->colors, palcolor); + + BLI_uniquename(&palette->colors, palcolor, DATA_("GP Color"), '.', offsetof(bGPDpalettecolor, info), sizeof(palcolor->info)); + } + + /* Store this mapping (for use later when pasting) */ + BLI_ghash_insert(new_colors, name, palcolor); + } + + return new_colors; +} + /* --------------------- */ /* Copy selected strokes */ @@ -496,9 +536,8 @@ static int gp_strokes_paste_exec(bContext *C, wmOperator *op) bGPDpalette *palette = CTX_data_active_gpencil_palette(C); bGPDframe *gpf; - GHash *new_colors; - GHashIterator gh_iter; eGP_PasteMode type = RNA_enum_get(op->ptr, "type"); + GHash *new_colors; /* check for various error conditions */ if (gpd == NULL) { @@ -556,36 +595,9 @@ static int gp_strokes_paste_exec(bContext *C, wmOperator *op) } CTX_DATA_END; - /* Ensure that all the necessary colors exist */ - // TODO: Split this out into a helper function - if (palette == NULL) { - palette = BKE_gpencil_palette_addnew(gpd, "Pasted Palette", true); - } - new_colors = BLI_ghash_str_new("GPencil Paste Dst Colors"); - - GHASH_ITER(gh_iter, gp_strokes_copypastebuf_colors) { - bGPDpalettecolor *palcolor; - char *name = BLI_ghashIterator_getKey(&gh_iter); + new_colors = gp_copybuf_validate_colormap(gpd); - /* Look for existing color to map to */ - /* XXX: What to do if same name but different color? Behaviour here should depend on a property? */ - palcolor = BKE_gpencil_palettecolor_getbyname(palette, name); - if (palcolor == NULL) { - /* Doesn't Exist - Create new matching color for this palette */ - /* XXX: This still doesn't fix the pasting across file boundaries problem... */ - bGPDpalettecolor *src_color = BLI_ghashIterator_getValue(&gh_iter); - - palcolor = MEM_dupallocN(src_color); - BLI_addtail(&palette->colors, palcolor); - - BLI_uniquename(&palette->colors, palcolor, DATA_("GP Color"), '.', offsetof(bGPDpalettecolor, info), sizeof(palcolor->info)); - } - - /* Store this mapping (for use later when pasting) */ - BLI_ghash_insert(new_colors, name, palcolor); - } - /* Copy over the strokes from the buffer (and adjust the colors) */ for (bGPDstroke *gps = gp_strokes_copypastebuf.first; gps; gps = gps->next) { if (ED_gpencil_stroke_can_use(C, gps)) { diff --git a/source/blender/editors/gpencil/gpencil_intern.h b/source/blender/editors/gpencil/gpencil_intern.h index 5c7c9b84adb..13af06fd4fd 100644 --- a/source/blender/editors/gpencil/gpencil_intern.h +++ b/source/blender/editors/gpencil/gpencil_intern.h @@ -40,6 +40,8 @@ struct bGPdata; struct bGPDstroke; struct bGPDspoint; +struct GHash; + struct ARegion; struct View2D; struct wmOperatorType; @@ -155,6 +157,9 @@ int gp_brush_crt_presets_poll(bContext *C); extern ListBase gp_strokes_copypastebuf; +/* Build a map for converting between old colornames and destination-color-refs */ +struct GHash *gp_copybuf_validate_colormap(bGPdata *gpd); + /* Stroke Editing ------------------------------------ */ void gp_stroke_delete_tagged_points(bGPDframe *gpf, bGPDstroke *gps, bGPDstroke *next_stroke, int tag_flags); -- cgit v1.2.3 From ff6b2490468ed7103685d5c06d73393f361a7950 Mon Sep 17 00:00:00 2001 From: Bastien Montagne Date: Thu, 8 Jun 2017 09:17:08 +0200 Subject: Simplify Array modifier 'relative offset' handling. Was looping three times over the source mesh's vertices to get min/max along all three axes... Nothing critical, but still! --- source/blender/modifiers/intern/MOD_array.c | 38 ++++++++++++----------------- 1 file changed, 15 insertions(+), 23 deletions(-) (limited to 'source') diff --git a/source/blender/modifiers/intern/MOD_array.c b/source/blender/modifiers/intern/MOD_array.c index 2b739dc0093..5731ceb4382 100644 --- a/source/blender/modifiers/intern/MOD_array.c +++ b/source/blender/modifiers/intern/MOD_array.c @@ -50,6 +50,7 @@ #include "BKE_curve.h" #include "BKE_library_query.h" #include "BKE_modifier.h" +#include "BKE_mesh.h" #include "MOD_util.h" @@ -157,25 +158,6 @@ static void updateDepsgraph(ModifierData *md, } } -static float vertarray_size(const MVert *mvert, int numVerts, int axis) -{ - int i; - float min_co, max_co; - - /* if there are no vertices, width is 0 */ - if (numVerts == 0) return 0; - - /* find the minimum and maximum coordinates on the desired axis */ - min_co = max_co = mvert->co[axis]; - mvert++; - for (i = 1; i < numVerts; ++i, ++mvert) { - if (mvert->co[axis] < min_co) min_co = mvert->co[axis]; - if (mvert->co[axis] > max_co) max_co = mvert->co[axis]; - } - - return max_co - min_co; -} - BLI_INLINE float sum_v3(const float v[3]) { return v[0] + v[1] + v[2]; @@ -472,12 +454,22 @@ static DerivedMesh *arrayModifier_doArray( unit_m4(offset); src_mvert = dm->getVertArray(dm); - if (amd->offset_type & MOD_ARR_OFF_CONST) - add_v3_v3v3(offset[3], offset[3], amd->offset); + if (amd->offset_type & MOD_ARR_OFF_CONST) { + add_v3_v3(offset[3], amd->offset); + } if (amd->offset_type & MOD_ARR_OFF_RELATIVE) { - for (j = 0; j < 3; j++) - offset[3][j] += amd->scale[j] * vertarray_size(src_mvert, chunk_nverts, j); + float min[3], max[3]; + const MVert *src_mv; + + INIT_MINMAX(min, max); + for (src_mv = src_mvert, j = chunk_nverts; j--; src_mv++) { + minmax_v3v3_v3(min, max, src_mv->co); + } + + for (j = 3; j--; ) { + offset[3][j] += amd->scale[j] * (max[j] - min[j]); + } } if (use_offset_ob) { -- cgit v1.2.3 From c71e160c022abf0cc8425970e31c09ded15763a2 Mon Sep 17 00:00:00 2001 From: Bastien Montagne Date: Thu, 8 Jun 2017 14:02:49 +0200 Subject: Fix T51733: 3d print toolbox checks report false positives. Colinear vertices in a same face would not be handled correctly. --- source/blender/bmesh/intern/bmesh_queries.c | 26 ++++++++++++++++++++------ 1 file changed, 20 insertions(+), 6 deletions(-) (limited to 'source') diff --git a/source/blender/bmesh/intern/bmesh_queries.c b/source/blender/bmesh/intern/bmesh_queries.c index a774f278c98..f5c14304ea3 100644 --- a/source/blender/bmesh/intern/bmesh_queries.c +++ b/source/blender/bmesh/intern/bmesh_queries.c @@ -1515,16 +1515,30 @@ float BM_loop_calc_face_angle(const BMLoop *l) */ void BM_loop_calc_face_normal(const BMLoop *l, float r_normal[3]) { - if (normal_tri_v3(r_normal, - l->prev->v->co, - l->v->co, - l->next->v->co) != 0.0f) - { - /* pass */ +#define FEPSILON 1e-5f + + /* Note: we cannot use result of normal_tri_v3 here to detect colinear vectors (vertex on a straight line) + * from zero value, because it does not normalize both vectors before making crossproduct. + * Instead of adding two costly normalize computations, just check ourselves for colinear case. */ + /* Note: FEPSILON might need some finer tweaking at some point? Seems to be working OK for now though. */ + float v1[3], v2[3], v_tmp[3]; + sub_v3_v3v3(v1, l->prev->v->co, l->v->co); + sub_v3_v3v3(v2, l->next->v->co, l->v->co); + + const float fac = (v2[0] == 0.0f) ? ((v2[1] == 0.0f) ? ((v2[2] == 0.0f) ? 0.0f : v1[2] / v2[2]) : v1[1] / v2[1]) : v1[0] / v2[0]; + + mul_v3_v3fl(v_tmp, v2, fac); + sub_v3_v3(v_tmp, v1); + if (fac != 0.0f && !is_zero_v3(v1) && len_manhattan_v3(v_tmp) > FEPSILON) { + /* Not co-linear, we can compute crossproduct and normalize it into normal. */ + cross_v3_v3v3(r_normal, v1, v2); + normalize_v3(r_normal); } else { copy_v3_v3(r_normal, l->f->no); } + +#undef FEPSILON } /** -- cgit v1.2.3 From 46c073e4ac38fae5209b82284313b740b132fd66 Mon Sep 17 00:00:00 2001 From: Campbell Barton Date: Fri, 9 Jun 2017 06:45:21 +1000 Subject: Cleanup: cmake indentation, missing include --- source/blender/depsgraph/CMakeLists.txt | 1 + source/blender/editors/gpencil/gpencil_edit.c | 1 - source/blender/nodes/composite/nodes/node_composite_image.c | 2 +- 3 files changed, 2 insertions(+), 2 deletions(-) (limited to 'source') diff --git a/source/blender/depsgraph/CMakeLists.txt b/source/blender/depsgraph/CMakeLists.txt index b664af570b1..33a7628c68d 100644 --- a/source/blender/depsgraph/CMakeLists.txt +++ b/source/blender/depsgraph/CMakeLists.txt @@ -87,6 +87,7 @@ set(SRC intern/depsgraph_intern.h intern/depsgraph_types.h + util/deg_util_foreach.h util/deg_util_function.h ) diff --git a/source/blender/editors/gpencil/gpencil_edit.c b/source/blender/editors/gpencil/gpencil_edit.c index 4b0ba6942e4..174feec0e22 100644 --- a/source/blender/editors/gpencil/gpencil_edit.c +++ b/source/blender/editors/gpencil/gpencil_edit.c @@ -533,7 +533,6 @@ static int gp_strokes_paste_exec(bContext *C, wmOperator *op) Scene *scene = CTX_data_scene(C); bGPdata *gpd = ED_gpencil_data_get_active(C); bGPDlayer *gpl = CTX_data_active_gpencil_layer(C); /* only use active for copy merge */ - bGPDpalette *palette = CTX_data_active_gpencil_palette(C); bGPDframe *gpf; eGP_PasteMode type = RNA_enum_get(op->ptr, "type"); diff --git a/source/blender/nodes/composite/nodes/node_composite_image.c b/source/blender/nodes/composite/nodes/node_composite_image.c index a95a99449fd..8139e29bade 100644 --- a/source/blender/nodes/composite/nodes/node_composite_image.c +++ b/source/blender/nodes/composite/nodes/node_composite_image.c @@ -117,7 +117,7 @@ static void cmp_node_image_add_pass_output(bNodeTree *ntree, bNode *node, else { sock = BLI_findlink(&node->outputs, sock_index); NodeImageLayer *sockdata = sock->storage; - if(sockdata) { + if (sockdata) { BLI_strncpy(sockdata->pass_name, passname, sizeof(sockdata->pass_name)); } } -- cgit v1.2.3 From 8a757bf34ad2d01104e300e0ce529d01f2965e09 Mon Sep 17 00:00:00 2001 From: Campbell Barton Date: Fri, 9 Jun 2017 07:13:35 +1000 Subject: RNA: remove static strings from registration * Static strings aren't needed anymore, use stack memory. * Fix obscure leak on failed macro registration. * Use prefix for wrappers exported from bpy module. --- source/blender/makesrna/intern/rna_wm.c | 241 ++++++++++++---------- source/blender/python/intern/bpy_operator_wrap.c | 4 +- source/blender/python/intern/bpy_operator_wrap.h | 4 +- source/blenderplayer/bad_level_call_stubs/stubs.c | 4 +- 4 files changed, 135 insertions(+), 118 deletions(-) (limited to 'source') diff --git a/source/blender/makesrna/intern/rna_wm.c b/source/blender/makesrna/intern/rna_wm.c index b458b2e69d5..b5ecaf739c7 100644 --- a/source/blender/makesrna/intern/rna_wm.c +++ b/source/blender/makesrna/intern/rna_wm.c @@ -939,35 +939,8 @@ static void rna_wmClipboard_set(PointerRNA *UNUSED(ptr), const char *value) } #ifdef WITH_PYTHON -static void rna_Operator_unregister(struct Main *bmain, StructRNA *type) -{ - const char *idname; - wmOperatorType *ot = RNA_struct_blender_type_get(type); - wmWindowManager *wm; - - if (!ot) - return; - - /* update while blender is running */ - wm = bmain->wm.first; - if (wm) { - WM_operator_stack_clear(wm); - WM_operator_handlers_clear(wm, ot); - } - WM_main_add_notifier(NC_SCREEN | NA_EDITED, NULL); - - RNA_struct_free_extension(type, &ot->ext); - - idname = ot->idname; - WM_operatortype_remove_ptr(ot); - MEM_freeN((void *)idname); - - /* not to be confused with the RNA_struct_free that WM_operatortype_remove calls, they are 2 different srna's */ - RNA_struct_free(&BLENDER_RNA, type); -} - -static int operator_poll(bContext *C, wmOperatorType *ot) +static int rna_operator_poll_cb(bContext *C, wmOperatorType *ot) { extern FunctionRNA rna_Operator_poll_func; @@ -992,7 +965,7 @@ static int operator_poll(bContext *C, wmOperatorType *ot) return visible; } -static int operator_execute(bContext *C, wmOperator *op) +static int rna_operator_execute_cb(bContext *C, wmOperator *op) { extern FunctionRNA rna_Operator_execute_func; @@ -1018,7 +991,7 @@ static int operator_execute(bContext *C, wmOperator *op) } /* same as execute() but no return value */ -static bool operator_check(bContext *C, wmOperator *op) +static bool rna_operator_check_cb(bContext *C, wmOperator *op) { extern FunctionRNA rna_Operator_check_func; @@ -1043,7 +1016,7 @@ static bool operator_check(bContext *C, wmOperator *op) return result; } -static int operator_invoke(bContext *C, wmOperator *op, const wmEvent *event) +static int rna_operator_invoke_cb(bContext *C, wmOperator *op, const wmEvent *event) { extern FunctionRNA rna_Operator_invoke_func; @@ -1070,7 +1043,7 @@ static int operator_invoke(bContext *C, wmOperator *op, const wmEvent *event) } /* same as invoke */ -static int operator_modal(bContext *C, wmOperator *op, const wmEvent *event) +static int rna_operator_modal_cb(bContext *C, wmOperator *op, const wmEvent *event) { extern FunctionRNA rna_Operator_modal_func; @@ -1096,7 +1069,7 @@ static int operator_modal(bContext *C, wmOperator *op, const wmEvent *event) return result; } -static void operator_draw(bContext *C, wmOperator *op) +static void rna_operator_draw_cb(bContext *C, wmOperator *op) { extern FunctionRNA rna_Operator_draw_func; @@ -1115,7 +1088,7 @@ static void operator_draw(bContext *C, wmOperator *op) } /* same as exec(), but call cancel */ -static void operator_cancel(bContext *C, wmOperator *op) +static void rna_operator_cancel_cb(bContext *C, wmOperator *op) { extern FunctionRNA rna_Operator_cancel_func; @@ -1133,35 +1106,42 @@ static void operator_cancel(bContext *C, wmOperator *op) RNA_parameter_list_free(&list); } -void operator_wrapper(wmOperatorType *ot, void *userdata); -void macro_wrapper(wmOperatorType *ot, void *userdata); +static void rna_Operator_unregister(struct Main *bmain, StructRNA *type); -static char _operator_idname[OP_MAX_TYPENAME]; -static char _operator_name[OP_MAX_TYPENAME]; -static char _operator_descr[RNA_DYN_DESCR_MAX]; -static char _operator_ctxt[RNA_DYN_DESCR_MAX]; -static char _operator_undo_group[OP_MAX_TYPENAME]; -static StructRNA *rna_Operator_register(Main *bmain, ReportList *reports, void *data, const char *identifier, - StructValidateFunc validate, StructCallbackFunc call, StructFreeFunc free) +/* bpy_operator_wrap.c */ +extern void BPY_RNA_operator_wrapper(wmOperatorType *ot, void *userdata); +extern void BPY_RNA_operator_macro_wrapper(wmOperatorType *ot, void *userdata); + +static StructRNA *rna_Operator_register( + Main *bmain, ReportList *reports, void *data, const char *identifier, + StructValidateFunc validate, StructCallbackFunc call, StructFreeFunc free) { wmOperatorType dummyot = {NULL}; wmOperator dummyop = {NULL}; PointerRNA dummyotr; int have_function[7]; + struct { + char idname[OP_MAX_TYPENAME]; + char name[OP_MAX_TYPENAME]; + char descr[RNA_DYN_DESCR_MAX]; + char ctxt[RNA_DYN_DESCR_MAX]; + char undo_group[OP_MAX_TYPENAME]; + } temp_buffers; + /* setup dummy operator & operator type to store static properties in */ dummyop.type = &dummyot; - dummyot.idname = _operator_idname; /* only assigne the pointer, string is NULL'd */ - dummyot.name = _operator_name; /* only assigne the pointer, string is NULL'd */ - dummyot.description = _operator_descr; /* only assigne the pointer, string is NULL'd */ - dummyot.translation_context = _operator_ctxt; /* only assigne the pointer, string is NULL'd */ - dummyot.undo_group = _operator_undo_group; /* only assigne the pointer, string is NULL'd */ + dummyot.idname = temp_buffers.idname; /* only assigne the pointer, string is NULL'd */ + dummyot.name = temp_buffers.name; /* only assigne the pointer, string is NULL'd */ + dummyot.description = temp_buffers.descr; /* only assigne the pointer, string is NULL'd */ + dummyot.translation_context = temp_buffers.ctxt; /* only assigne the pointer, string is NULL'd */ + dummyot.undo_group = temp_buffers.undo_group; /* only assigne the pointer, string is NULL'd */ RNA_pointer_create(NULL, &RNA_Operator, &dummyop, &dummyotr); /* clear in case they are left unset */ - _operator_idname[0] = _operator_name[0] = _operator_descr[0] = _operator_undo_group[0] = '\0'; + temp_buffers.idname[0] = temp_buffers.name[0] = temp_buffers.descr[0] = temp_buffers.undo_group[0] = '\0'; /* We have to set default op context! */ - strcpy(_operator_ctxt, BLT_I18NCONTEXT_OPERATOR_DEFAULT); + strcpy(temp_buffers.ctxt, BLT_I18NCONTEXT_OPERATOR_DEFAULT); /* validate the python class */ if (validate(&dummyotr, data, have_function) != 0) @@ -1172,7 +1152,7 @@ static StructRNA *rna_Operator_register(Main *bmain, ReportList *reports, void * /* inconveniently long name sanity check */ { - char *ch = _operator_idname; + char *ch = temp_buffers.idname; int i; int dot = 0; for (i = 0; *ch; i++) { @@ -1185,7 +1165,7 @@ static StructRNA *rna_Operator_register(Main *bmain, ReportList *reports, void * else { BKE_reportf(reports, RPT_ERROR, "Registering operator class: '%s', invalid bl_idname '%s', at position %d", - identifier, _operator_idname, i); + identifier, temp_buffers.idname, i); return NULL; } @@ -1194,7 +1174,7 @@ static StructRNA *rna_Operator_register(Main *bmain, ReportList *reports, void * if (i > ((int)sizeof(dummyop.idname)) - 3) { BKE_reportf(reports, RPT_ERROR, "Registering operator class: '%s', invalid bl_idname '%s', " - "is too long, maximum length is %d", identifier, _operator_idname, + "is too long, maximum length is %d", identifier, temp_buffers.idname, (int)sizeof(dummyop.idname) - 3); return NULL; } @@ -1202,34 +1182,34 @@ static StructRNA *rna_Operator_register(Main *bmain, ReportList *reports, void * if (dot != 1) { BKE_reportf(reports, RPT_ERROR, "Registering operator class: '%s', invalid bl_idname '%s', must contain 1 '.' character", - identifier, _operator_idname); + identifier, temp_buffers.idname); return NULL; } } /* end sanity check */ { - int idlen = strlen(_operator_idname) + 4; - int namelen = strlen(_operator_name) + 1; - int desclen = strlen(_operator_descr) + 1; - int ctxtlen = strlen(_operator_ctxt) + 1; - int ugrouplen = strlen(_operator_undo_group) + 1; - char *ch; + const uint idname_len = strlen(temp_buffers.idname) + 4; + const uint name_len = strlen(temp_buffers.name) + 1; + const uint desc_len = strlen(temp_buffers.descr) + 1; + const uint ctxt_len = strlen(temp_buffers.ctxt) + 1; + const uint undo_group_len = strlen(temp_buffers.undo_group) + 1; /* 2 terminators and 3 to convert a.b -> A_OT_b */ - ch = MEM_callocN(sizeof(char) * (idlen + namelen + desclen + ctxtlen + ugrouplen), "_operator_idname"); - WM_operator_bl_idname(ch, _operator_idname); /* convert the idname from python */ + char *ch = MEM_mallocN( + sizeof(char) * (idname_len + name_len + desc_len + ctxt_len + undo_group_len), __func__); + WM_operator_bl_idname(ch, temp_buffers.idname); /* convert the idname from python */ dummyot.idname = ch; - ch += idlen; - strcpy(ch, _operator_name); + ch += idname_len; + memcpy(ch, temp_buffers.name, name_len); dummyot.name = ch; - ch += namelen; - strcpy(ch, _operator_descr); + ch += name_len; + memcpy(ch, temp_buffers.descr, desc_len); dummyot.description = ch; - ch += desclen; - strcpy(ch, _operator_ctxt); + ch += desc_len; + memcpy(ch, temp_buffers.ctxt, ctxt_len); dummyot.translation_context = ch; - ch += ctxtlen; - strcpy(ch, _operator_undo_group); + ch += ctxt_len; + memcpy(ch, temp_buffers.undo_group, undo_group_len); dummyot.undo_group = ch; } } @@ -1252,14 +1232,14 @@ static StructRNA *rna_Operator_register(Main *bmain, ReportList *reports, void * dummyot.ext.call = call; dummyot.ext.free = free; - dummyot.pyop_poll = (have_function[0]) ? operator_poll : NULL; - dummyot.exec = (have_function[1]) ? operator_execute : NULL; - dummyot.check = (have_function[2]) ? operator_check : NULL; - dummyot.invoke = (have_function[3]) ? operator_invoke : NULL; - dummyot.modal = (have_function[4]) ? operator_modal : NULL; - dummyot.ui = (have_function[5]) ? operator_draw : NULL; - dummyot.cancel = (have_function[6]) ? operator_cancel : NULL; - WM_operatortype_append_ptr(operator_wrapper, (void *)&dummyot); + dummyot.pyop_poll = (have_function[0]) ? rna_operator_poll_cb : NULL; + dummyot.exec = (have_function[1]) ? rna_operator_execute_cb : NULL; + dummyot.check = (have_function[2]) ? rna_operator_check_cb : NULL; + dummyot.invoke = (have_function[3]) ? rna_operator_invoke_cb : NULL; + dummyot.modal = (have_function[4]) ? rna_operator_modal_cb : NULL; + dummyot.ui = (have_function[5]) ? rna_operator_draw_cb : NULL; + dummyot.cancel = (have_function[6]) ? rna_operator_cancel_cb : NULL; + WM_operatortype_append_ptr(BPY_RNA_operator_wrapper, (void *)&dummyot); /* update while blender is running */ WM_main_add_notifier(NC_SCREEN | NA_EDITED, NULL); @@ -1267,70 +1247,107 @@ static StructRNA *rna_Operator_register(Main *bmain, ReportList *reports, void * return dummyot.ext.srna; } +static void rna_Operator_unregister(struct Main *bmain, StructRNA *type) +{ + const char *idname; + wmOperatorType *ot = RNA_struct_blender_type_get(type); + wmWindowManager *wm; + + if (!ot) + return; + + /* update while blender is running */ + wm = bmain->wm.first; + if (wm) { + WM_operator_stack_clear(wm); + + WM_operator_handlers_clear(wm, ot); + } + WM_main_add_notifier(NC_SCREEN | NA_EDITED, NULL); + + RNA_struct_free_extension(type, &ot->ext); + + idname = ot->idname; + WM_operatortype_remove_ptr(ot); + MEM_freeN((void *)idname); + + /* not to be confused with the RNA_struct_free that WM_operatortype_remove calls, they are 2 different srna's */ + RNA_struct_free(&BLENDER_RNA, type); +} + static void **rna_Operator_instance(PointerRNA *ptr) { wmOperator *op = ptr->data; return &op->py_instance; } -static StructRNA *rna_MacroOperator_register(Main *bmain, ReportList *reports, void *data, const char *identifier, - StructValidateFunc validate, StructCallbackFunc call, StructFreeFunc free) +static StructRNA *rna_MacroOperator_register( + Main *bmain, ReportList *reports, void *data, const char *identifier, + StructValidateFunc validate, StructCallbackFunc call, StructFreeFunc free) { wmOperatorType dummyot = {NULL}; wmOperator dummyop = {NULL}; PointerRNA dummyotr; int have_function[4]; + struct { + char idname[OP_MAX_TYPENAME]; + char name[OP_MAX_TYPENAME]; + char descr[RNA_DYN_DESCR_MAX]; + char ctxt[RNA_DYN_DESCR_MAX]; + char undo_group[OP_MAX_TYPENAME]; + } temp_buffers; + /* setup dummy operator & operator type to store static properties in */ dummyop.type = &dummyot; - dummyot.idname = _operator_idname; /* only assigne the pointer, string is NULL'd */ - dummyot.name = _operator_name; /* only assigne the pointer, string is NULL'd */ - dummyot.description = _operator_descr; /* only assigne the pointer, string is NULL'd */ - dummyot.translation_context = _operator_ctxt; /* only assigne the pointer, string is NULL'd */ - dummyot.undo_group = _operator_undo_group; /* only assigne the pointer, string is NULL'd */ + dummyot.idname = temp_buffers.idname; /* only assigne the pointer, string is NULL'd */ + dummyot.name = temp_buffers.name; /* only assigne the pointer, string is NULL'd */ + dummyot.description = temp_buffers.descr; /* only assigne the pointer, string is NULL'd */ + dummyot.translation_context = temp_buffers.ctxt; /* only assigne the pointer, string is NULL'd */ + dummyot.undo_group = temp_buffers.undo_group; /* only assigne the pointer, string is NULL'd */ RNA_pointer_create(NULL, &RNA_Macro, &dummyop, &dummyotr); /* clear in case they are left unset */ - _operator_idname[0] = _operator_name[0] = _operator_descr[0] = _operator_undo_group[0] = '\0'; + temp_buffers.idname[0] = temp_buffers.name[0] = temp_buffers.descr[0] = temp_buffers.undo_group[0] = '\0'; /* We have to set default op context! */ - strcpy(_operator_ctxt, BLT_I18NCONTEXT_OPERATOR_DEFAULT); + strcpy(temp_buffers.ctxt, BLT_I18NCONTEXT_OPERATOR_DEFAULT); /* validate the python class */ if (validate(&dummyotr, data, have_function) != 0) return NULL; + if (strlen(identifier) >= sizeof(dummyop.idname)) { + BKE_reportf(reports, RPT_ERROR, "Registering operator class: '%s' is too long, maximum length is %d", + identifier, (int)sizeof(dummyop.idname)); + return NULL; + } + { /* convert foo.bar to FOO_OT_bar * allocate the description and the idname in 1 go */ - int idlen = strlen(_operator_idname) + 4; - int namelen = strlen(_operator_name) + 1; - int desclen = strlen(_operator_descr) + 1; - int ctxtlen = strlen(_operator_ctxt) + 1; - int ugrouplen = strlen(_operator_undo_group) + 1; - char *ch; + const uint idname_len = strlen(temp_buffers.idname) + 4; + const uint name_len = strlen(temp_buffers.name) + 1; + const uint desc_len = strlen(temp_buffers.descr) + 1; + const uint ctxt_len = strlen(temp_buffers.ctxt) + 1; + const uint undo_group_len = strlen(temp_buffers.undo_group) + 1; /* 2 terminators and 3 to convert a.b -> A_OT_b */ - ch = MEM_callocN(sizeof(char) * (idlen + namelen + desclen + ctxtlen + ugrouplen), "_operator_idname"); - WM_operator_bl_idname(ch, _operator_idname); /* convert the idname from python */ + char *ch = MEM_mallocN( + sizeof(char) * (idname_len + name_len + desc_len + ctxt_len + undo_group_len), __func__); + WM_operator_bl_idname(ch, temp_buffers.idname); /* convert the idname from python */ dummyot.idname = ch; - ch += idlen; - strcpy(ch, _operator_name); + ch += idname_len; + memcpy(ch, temp_buffers.name, name_len); dummyot.name = ch; - ch += namelen; - strcpy(ch, _operator_descr); + ch += name_len; + memcpy(ch, temp_buffers.descr, desc_len); dummyot.description = ch; - ch += desclen; - strcpy(ch, _operator_ctxt); + ch += desc_len; + memcpy(ch, temp_buffers.ctxt, ctxt_len); dummyot.translation_context = ch; - ch += ctxtlen; - strcpy(ch, _operator_undo_group); + ch += ctxt_len; + memcpy(ch, temp_buffers.undo_group, undo_group_len); dummyot.undo_group = ch; } - if (strlen(identifier) >= sizeof(dummyop.idname)) { - BKE_reportf(reports, RPT_ERROR, "Registering operator class: '%s' is too long, maximum length is %d", - identifier, (int)sizeof(dummyop.idname)); - return NULL; - } - /* check if we have registered this operator type before, and remove it */ { wmOperatorType *ot = WM_operatortype_find(dummyot.idname, true); @@ -1348,10 +1365,10 @@ static StructRNA *rna_MacroOperator_register(Main *bmain, ReportList *reports, v dummyot.ext.call = call; dummyot.ext.free = free; - dummyot.pyop_poll = (have_function[0]) ? operator_poll : NULL; - dummyot.ui = (have_function[3]) ? operator_draw : NULL; + dummyot.pyop_poll = (have_function[0]) ? rna_operator_poll_cb : NULL; + dummyot.ui = (have_function[3]) ? rna_operator_draw_cb : NULL; - WM_operatortype_append_macro_ptr(macro_wrapper, (void *)&dummyot); + WM_operatortype_append_macro_ptr(BPY_RNA_operator_macro_wrapper, (void *)&dummyot); /* update while blender is running */ WM_main_add_notifier(NC_SCREEN | NA_EDITED, NULL); diff --git a/source/blender/python/intern/bpy_operator_wrap.c b/source/blender/python/intern/bpy_operator_wrap.c index 11e27ca3e3c..90719905a79 100644 --- a/source/blender/python/intern/bpy_operator_wrap.c +++ b/source/blender/python/intern/bpy_operator_wrap.c @@ -118,7 +118,7 @@ static void operator_properties_init(wmOperatorType *ot) } -void operator_wrapper(wmOperatorType *ot, void *userdata) +void BPY_RNA_operator_wrapper(wmOperatorType *ot, void *userdata) { /* take care not to overwrite anything set in * WM_operatortype_append_ptr before opfunc() is called */ @@ -134,7 +134,7 @@ void operator_wrapper(wmOperatorType *ot, void *userdata) operator_properties_init(ot); } -void macro_wrapper(wmOperatorType *ot, void *userdata) +void BPY_RNA_operator_macro_wrapper(wmOperatorType *ot, void *userdata) { wmOperatorType *data = (wmOperatorType *)userdata; diff --git a/source/blender/python/intern/bpy_operator_wrap.h b/source/blender/python/intern/bpy_operator_wrap.h index 05a566a1485..0828c58e2bd 100644 --- a/source/blender/python/intern/bpy_operator_wrap.h +++ b/source/blender/python/intern/bpy_operator_wrap.h @@ -33,7 +33,7 @@ struct wmOperatorType; PyObject *PYOP_wrap_macro_define(PyObject *self, PyObject *args); /* exposed to rna/wm api */ -void operator_wrapper(struct wmOperatorType *ot, void *userdata); -void macro_wrapper(struct wmOperatorType *ot, void *userdata); +void BPY_RNA_operator_wrapper(struct wmOperatorType *ot, void *userdata); +void BPY_RNA_operator_macro_wrapper(struct wmOperatorType *ot, void *userdata); #endif diff --git a/source/blenderplayer/bad_level_call_stubs/stubs.c b/source/blenderplayer/bad_level_call_stubs/stubs.c index 82a3527a655..ad79b316111 100644 --- a/source/blenderplayer/bad_level_call_stubs/stubs.c +++ b/source/blenderplayer/bad_level_call_stubs/stubs.c @@ -750,7 +750,8 @@ int collada_export(struct Scene *sce, void ED_mesh_calc_tessface(struct Mesh *mesh, bool free_mpoly) RET_NONE /* bpy/python internal api */ -void operator_wrapper(struct wmOperatorType *ot, void *userdata) RET_NONE +void BPY_RNA_operator_wrapper(struct wmOperatorType *ot, void *userdata) RET_NONE +void BPY_RNA_operator_macro_wrapper(struct wmOperatorType *ot, void *userdata) RET_NONE void BPY_text_free_code(struct Text *text) RET_NONE void BPY_id_release(struct ID *id) RET_NONE int BPY_context_member_get(struct bContext *C, const char *member, struct bContextDataResult *result) RET_ZERO @@ -758,7 +759,6 @@ void BPY_pyconstraint_target(struct bPythonConstraint *con, struct bConstraintTa float BPY_driver_exec(PathResolvedRNA *anim_rna, struct ChannelDriver *driver, const float evaltime) RET_ZERO /* might need this one! */ void BPY_DECREF(void *pyob_ptr) RET_NONE void BPY_pyconstraint_exec(struct bPythonConstraint *con, struct bConstraintOb *cob, struct ListBase *targets) RET_NONE -void macro_wrapper(struct wmOperatorType *ot, void *userdata) RET_NONE bool pyrna_id_FromPyObject(struct PyObject *obj, struct ID **id) RET_ZERO struct PyObject *pyrna_id_CreatePyObject(struct ID *id) RET_NULL bool pyrna_id_CheckPyObject(struct PyObject *obj) RET_ZERO -- cgit v1.2.3