diff options
Diffstat (limited to 'source/blender/editors/transform/transform_conversions.c')
-rw-r--r-- | source/blender/editors/transform/transform_conversions.c | 1435 |
1 files changed, 1064 insertions, 371 deletions
diff --git a/source/blender/editors/transform/transform_conversions.c b/source/blender/editors/transform/transform_conversions.c index b7098085ac2..c12418f1a0f 100644 --- a/source/blender/editors/transform/transform_conversions.c +++ b/source/blender/editors/transform/transform_conversions.c @@ -31,6 +31,7 @@ #include <string.h> #include <math.h> +#include <limits.h> #include "DNA_anim_types.h" #include "DNA_brush_types.h" @@ -54,11 +55,11 @@ #include "BLI_math.h" #include "BLI_utildefines.h" -#include "BLI_smallhash.h" #include "BLI_listbase.h" #include "BLI_linklist_stack.h" #include "BLI_string.h" #include "BLI_bitmap.h" +#include "BLI_rect.h" #include "BKE_DerivedMesh.h" #include "BKE_action.h" @@ -90,7 +91,6 @@ #include "BKE_editmesh.h" #include "BKE_tracking.h" #include "BKE_mask.h" -#include "BKE_lattice.h" #include "BIK_api.h" @@ -107,6 +107,7 @@ #include "ED_uvedit.h" #include "ED_clip.h" #include "ED_mask.h" +#include "ED_gpencil.h" #include "WM_api.h" /* for WM_event_add_notifier to deal with stabilization nodes */ #include "WM_types.h" @@ -218,6 +219,9 @@ static void set_prop_dist(TransInfo *t, const bool with_dist) float _proj_vec[3]; const float *proj_vec = NULL; + /* support for face-islands */ + const bool use_island = transdata_check_local_islands(t, t->around); + if (t->flag & T_PROP_PROJECTED) { if (t->spacetype == SPACE_VIEW3D && t->ar && t->ar->regiontype == RGN_TYPE_WINDOW) { RegionView3D *rv3d = t->ar->regiondata; @@ -239,7 +243,12 @@ static void set_prop_dist(TransInfo *t, const bool with_dist) for (i = 0, td = t->data; i < t->total; i++, td++) { if (td->flag & TD_SELECTED) { - sub_v3_v3v3(vec, tob->center, td->center); + if (use_island) { + sub_v3_v3v3(vec, tob->iloc, td->iloc); + } + else { + sub_v3_v3v3(vec, tob->center, td->center); + } mul_m3_v3(tob->mtx, vec); if (proj_vec) { @@ -249,8 +258,12 @@ static void set_prop_dist(TransInfo *t, const bool with_dist) } dist_sq = len_squared_v3(vec); - if ((tob->rdist == -1.0f) || (dist_sq < (tob->rdist * tob->rdist))) { + if ((tob->rdist == -1.0f) || (dist_sq < SQUARE(tob->rdist))) { tob->rdist = sqrtf(dist_sq); + if (use_island) { + copy_v3_v3(tob->center, td->center); + copy_m3_m3(tob->axismtx, td->axismtx); + } } } else { @@ -329,20 +342,20 @@ static void createTransEdge(TransInfo *t) BMIter iter; float mtx[3][3], smtx[3][3]; int count = 0, countsel = 0; - int propmode = t->flag & T_PROP_EDIT; + const bool is_prop_edit = (t->flag & T_PROP_EDIT) != 0; int cd_edge_float_offset; BM_ITER_MESH (eed, &iter, em->bm, BM_EDGES_OF_MESH) { if (!BM_elem_flag_test(eed, BM_ELEM_HIDDEN)) { if (BM_elem_flag_test(eed, BM_ELEM_SELECT)) countsel++; - if (propmode) count++; + if (is_prop_edit) count++; } } if (countsel == 0) return; - if (propmode) { + if (is_prop_edit) { t->total = count; } else { @@ -368,7 +381,7 @@ static void createTransEdge(TransInfo *t) BLI_assert(cd_edge_float_offset != -1); BM_ITER_MESH (eed, &iter, em->bm, BM_EDGES_OF_MESH) { - if (!BM_elem_flag_test(eed, BM_ELEM_HIDDEN) && (BM_elem_flag_test(eed, BM_ELEM_SELECT) || propmode)) { + if (!BM_elem_flag_test(eed, BM_ELEM_HIDDEN) && (BM_elem_flag_test(eed, BM_ELEM_SELECT) || is_prop_edit)) { float *fl_ptr; /* need to set center for center calculations */ mid_v3_v3v3(td->center, eed->v1->co, eed->v2->co); @@ -616,10 +629,10 @@ static void add_pose_transdata(TransInfo *t, bPoseChannel *pchan, Object *ob, Tr mul_m3_m3m3(td->axismtx, omat, pmat); normalize_m3(td->axismtx); - if (t->mode == TFM_BONESIZE) { + if (ELEM(t->mode, TFM_BONESIZE, TFM_BONE_ENVELOPE_DIST)) { bArmature *arm = t->poseobj->data; - if (arm->drawtype == ARM_ENVELOPE) { + if ((t->mode == TFM_BONE_ENVELOPE_DIST) || (arm->drawtype == ARM_ENVELOPE)) { td->loc = NULL; td->val = &bone->dist; td->ival = bone->dist; @@ -706,7 +719,7 @@ int count_set_pose_transflags(int *out_mode, short around, Object *ob) /* make sure no bone can be transformed when a parent is transformed */ /* since pchans are depsgraph sorted, the parents are in beginning of list */ - if (mode != TFM_BONESIZE) { + if (!ELEM(mode, TFM_BONESIZE, TFM_BONE_ENVELOPE_DIST)) { for (pchan = ob->pose->chanbase.first; pchan; pchan = pchan->next) { bone = pchan->bone; if (bone->flag & BONE_TRANSFORM) @@ -1056,11 +1069,13 @@ static void createTransPose(TransInfo *t, Object *ob) void restoreBones(TransInfo *t) { + bArmature *arm = t->obedit->data; BoneInitData *bid = t->customData; EditBone *ebo; while (bid->bone) { ebo = bid->bone; + ebo->dist = bid->dist; ebo->rad_tail = bid->rad_tail; ebo->roll = bid->roll; @@ -1068,7 +1083,26 @@ void restoreBones(TransInfo *t) ebo->zwidth = bid->zwidth; copy_v3_v3(ebo->head, bid->head); copy_v3_v3(ebo->tail, bid->tail); + + if (arm->flag & ARM_MIRROR_EDIT) { + EditBone *ebo_child; + + /* Also move connected ebo_child, in case ebo_child's name aren't mirrored properly */ + for (ebo_child = arm->edbo->first; ebo_child; ebo_child = ebo_child->next) { + if ((ebo_child->flag & BONE_CONNECTED) && (ebo_child->parent == ebo)) { + copy_v3_v3(ebo_child->head, ebo->tail); + ebo_child->rad_head = ebo->rad_tail; + } + } + /* Also move connected parent, in case parent's name isn't mirrored properly */ + if ((ebo->flag & BONE_CONNECTED) && ebo->parent) { + EditBone *parent = ebo->parent; + copy_v3_v3(parent->tail, ebo->head); + parent->rad_tail = ebo->rad_head; + } + } + bid++; } } @@ -1092,7 +1126,7 @@ static void createTransArmatureVerts(TransInfo *t) oldtot = t->total; if (EBONE_VISIBLE(arm, ebo) && !(ebo->flag & BONE_EDITMODE_LOCKED)) { - if (t->mode == TFM_BONESIZE) { + if (ELEM(t->mode, TFM_BONESIZE, TFM_BONE_ENVELOPE_DIST)) { if (ebo->flag & BONE_SELECTED) t->total++; } @@ -1170,9 +1204,9 @@ static void createTransArmatureVerts(TransInfo *t) } } - else if (t->mode == TFM_BONESIZE) { + else if (ELEM(t->mode, TFM_BONESIZE, TFM_BONE_ENVELOPE_DIST)) { if (ebo->flag & BONE_SELECTED) { - if (arm->drawtype == ARM_ENVELOPE) { + if ((t->mode == TFM_BONE_ENVELOPE_DIST) || (arm->drawtype == ARM_ENVELOPE)) { td->loc = NULL; td->val = &ebo->dist; td->ival = ebo->dist; @@ -1218,7 +1252,13 @@ static void createTransArmatureVerts(TransInfo *t) else { if (ebo->flag & BONE_TIPSEL) { copy_v3_v3(td->iloc, ebo->tail); - copy_v3_v3(td->center, (t->around == V3D_LOCAL) ? ebo->head : td->iloc); + + /* don't allow single selected tips to have a modified center, + * causes problem with snapping T45974 */ + copy_v3_v3(td->center, + ((t->around == V3D_LOCAL) && + (ebo->flag & BONE_ROOTSEL)) ? ebo->head : td->iloc); + td->loc = ebo->tail; td->flag = TD_SELECTED; if (ebo->flag & BONE_EDITMODE_LOCKED) @@ -1297,18 +1337,18 @@ static void createTransMBallVerts(TransInfo *t) TransDataExtension *tx; float mtx[3][3], smtx[3][3]; int count = 0, countsel = 0; - int propmode = t->flag & T_PROP_EDIT; + const bool is_prop_edit = (t->flag & T_PROP_EDIT) != 0; /* count totals */ for (ml = mb->editelems->first; ml; ml = ml->next) { if (ml->flag & SELECT) countsel++; - if (propmode) count++; + if (is_prop_edit) count++; } /* note: in prop mode we need at least 1 selected */ if (countsel == 0) return; - if (propmode) t->total = count; + if (is_prop_edit) t->total = count; else t->total = countsel; td = t->data = MEM_callocN(t->total * sizeof(TransData), "TransObData(MBall EditMode)"); @@ -1318,7 +1358,7 @@ static void createTransMBallVerts(TransInfo *t) pseudoinverse_m3_m3(smtx, mtx, PSEUDOINVERSE_EPSILON); for (ml = mb->editelems->first; ml; ml = ml->next) { - if (propmode || (ml->flag & SELECT)) { + if (is_prop_edit || (ml->flag & SELECT)) { td->loc = &ml->x; copy_v3_v3(td->iloc, td->loc); copy_v3_v3(td->center, td->loc); @@ -1431,7 +1471,7 @@ static void createTransCurveVerts(TransInfo *t) float mtx[3][3], smtx[3][3]; int a; int count = 0, countsel = 0; - int propmode = t->flag & T_PROP_EDIT; + const bool is_prop_edit = (t->flag & T_PROP_EDIT) != 0; short hide_handles = (cu->drawflag & CU_HIDE_HANDLES); ListBase *nurbs; @@ -1446,13 +1486,13 @@ static void createTransCurveVerts(TransInfo *t) if (bezt->hide == 0) { if (hide_handles) { if (bezt->f2 & SELECT) countsel += 3; - if (propmode) count += 3; + if (is_prop_edit) count += 3; } else { if (bezt->f1 & SELECT) countsel++; if (bezt->f2 & SELECT) countsel++; if (bezt->f3 & SELECT) countsel++; - if (propmode) count += 3; + if (is_prop_edit) count += 3; } } } @@ -1460,7 +1500,7 @@ static void createTransCurveVerts(TransInfo *t) else { for (a = nu->pntsu * nu->pntsv, bp = nu->bp; a > 0; a--, bp++) { if (bp->hide == 0) { - if (propmode) count++; + if (is_prop_edit) count++; if (bp->f1 & SELECT) countsel++; } } @@ -1469,7 +1509,7 @@ static void createTransCurveVerts(TransInfo *t) /* note: in prop mode we need at least 1 selected */ if (countsel == 0) return; - if (propmode) t->total = count; + if (is_prop_edit) t->total = count; else t->total = countsel; t->data = MEM_callocN(t->total * sizeof(TransData), "TransObData(Curve EditMode)"); @@ -1504,7 +1544,7 @@ static void createTransCurveVerts(TransInfo *t) } } - if (propmode || + if (is_prop_edit || ((bezt->f2 & SELECT) && hide_handles) || ((bezt->f1 & SELECT) && hide_handles == 0)) { @@ -1538,7 +1578,7 @@ static void createTransCurveVerts(TransInfo *t) } /* This is the Curve Point, the other two are handles */ - if (propmode || (bezt->f2 & SELECT)) { + if (is_prop_edit || (bezt->f2 & SELECT)) { copy_v3_v3(td->iloc, bezt->vec[1]); td->loc = bezt->vec[1]; copy_v3_v3(td->center, td->loc); @@ -1574,7 +1614,7 @@ static void createTransCurveVerts(TransInfo *t) count++; tail++; } - if (propmode || + if (is_prop_edit || ((bezt->f2 & SELECT) && hide_handles) || ((bezt->f3 & SELECT) && hide_handles == 0)) { @@ -1611,17 +1651,17 @@ static void createTransCurveVerts(TransInfo *t) (void)hdata; /* quiet warning */ } - else if (propmode && head != tail) { + else if (is_prop_edit && head != tail) { calc_distanceCurveVerts(head, tail - 1); head = tail; } } - if (propmode && head != tail) + if (is_prop_edit && head != tail) calc_distanceCurveVerts(head, tail - 1); /* TODO - in the case of tilt and radius we can also avoid allocating the initTransDataCurveHandles * but for now just don't change handle types */ - if (ELEM(t->mode, TFM_CURVE_SHRINKFATTEN, TFM_TILT) == 0) { + if (ELEM(t->mode, TFM_CURVE_SHRINKFATTEN, TFM_TILT, TFM_DUMMY) == 0) { /* sets the handles based on their selection, do this after the data is copied to the TransData */ BKE_nurb_handles_test(nu, !hide_handles); } @@ -1631,7 +1671,7 @@ static void createTransCurveVerts(TransInfo *t) head = tail = td; for (a = nu->pntsu * nu->pntsv, bp = nu->bp; a > 0; a--, bp++) { if (bp->hide == 0) { - if (propmode || (bp->f1 & SELECT)) { + if (is_prop_edit || (bp->f1 & SELECT)) { copy_v3_v3(td->iloc, bp->vec); td->loc = bp->vec; copy_v3_v3(td->center, td->loc); @@ -1656,12 +1696,12 @@ static void createTransCurveVerts(TransInfo *t) tail++; } } - else if (propmode && head != tail) { + else if (is_prop_edit && head != tail) { calc_distanceCurveVerts(head, tail - 1); head = tail; } } - if (propmode && head != tail) + if (is_prop_edit && head != tail) calc_distanceCurveVerts(head, tail - 1); } } @@ -1677,14 +1717,14 @@ static void createTransLatticeVerts(TransInfo *t) float mtx[3][3], smtx[3][3]; int a; int count = 0, countsel = 0; - int propmode = t->flag & T_PROP_EDIT; + const bool is_prop_edit = (t->flag & T_PROP_EDIT) != 0; bp = latt->def; a = latt->pntsu * latt->pntsv * latt->pntsw; while (a--) { if (bp->hide == 0) { if (bp->f1 & SELECT) countsel++; - if (propmode) count++; + if (is_prop_edit) count++; } bp++; } @@ -1692,7 +1732,7 @@ static void createTransLatticeVerts(TransInfo *t) /* note: in prop mode we need at least 1 selected */ if (countsel == 0) return; - if (propmode) t->total = count; + if (is_prop_edit) t->total = count; else t->total = countsel; t->data = MEM_callocN(t->total * sizeof(TransData), "TransObData(Lattice EditMode)"); @@ -1703,7 +1743,7 @@ static void createTransLatticeVerts(TransInfo *t) bp = latt->def; a = latt->pntsu * latt->pntsv * latt->pntsw; while (a--) { - if (propmode || (bp->f1 & SELECT)) { + if (is_prop_edit || (bp->f1 & SELECT)) { if (bp->hide == 0) { copy_v3_v3(td->iloc, bp->vec); td->loc = bp->vec; @@ -1711,7 +1751,9 @@ static void createTransLatticeVerts(TransInfo *t) if (bp->f1 & SELECT) { td->flag = TD_SELECTED; } - else td->flag = 0; + else { + td->flag = 0; + } copy_m3_m3(td->smtx, smtx); copy_m3_m3(td->mtx, mtx); @@ -1742,7 +1784,7 @@ static void createTransParticleVerts(bContext *C, TransInfo *t) float mat[4][4]; int i, k, transformparticle; int count = 0, hasselected = 0; - int propmode = t->flag & T_PROP_EDIT; + const bool is_prop_edit = (t->flag & T_PROP_EDIT) != 0; if (edit == NULL || t->settings->particle.selectmode == SCE_SELECT_PATH) return; @@ -1764,7 +1806,7 @@ static void createTransParticleVerts(bContext *C, TransInfo *t) hasselected = 1; transformparticle = 1; } - else if (propmode) + else if (is_prop_edit) transformparticle = 1; } } @@ -1814,7 +1856,7 @@ static void createTransParticleVerts(bContext *C, TransInfo *t) if (key->flag & PEK_SELECT) td->flag |= TD_SELECTED; - else if (!propmode) + else if (!is_prop_edit) td->flag |= TD_SKIP; unit_m3(td->mtx); @@ -1843,7 +1885,7 @@ static void createTransParticleVerts(bContext *C, TransInfo *t) tx++; tail++; } - if (propmode && head != tail) + if (is_prop_edit && head != tail) calc_distanceCurveVerts(head, tail - 1); } } @@ -1859,7 +1901,8 @@ void flushTransParticles(TransInfo *t) PTCacheEditKey *key; TransData *td; float mat[4][4], imat[4][4], co[3]; - int i, k, propmode = t->flag & T_PROP_EDIT; + int i, k; + const bool is_prop_edit = (t->flag & T_PROP_EDIT) != 0; if (psys) psmd = psys_get_modifier(ob, psys); @@ -1880,7 +1923,7 @@ void flushTransParticles(TransInfo *t) /* optimization for proportional edit */ - if (!propmode || !compare_v3v3(key->co, co, 0.0001f)) { + if (!is_prop_edit || !compare_v3v3(key->co, co, 0.0001f)) { copy_v3_v3(key->co, co); point->flag |= PEP_EDIT_RECALC; } @@ -1938,17 +1981,19 @@ static void editmesh_set_connectivity_distance(BMesh *bm, float mtx[3][3], float int i; BM_ITER_MESH_INDEX (v, &viter, bm, BM_VERTS_OF_MESH, i) { + float dist; BM_elem_index_set(v, i); /* set_inline */ BM_elem_flag_disable(v, BM_ELEM_TAG); if (BM_elem_flag_test(v, BM_ELEM_SELECT) == 0 || BM_elem_flag_test(v, BM_ELEM_HIDDEN)) { - dists[i] = FLT_MAX; + dist = FLT_MAX; } else { BLI_LINKSTACK_PUSH(queue, v); - - dists[i] = 0.0f; + dist = 0.0f; } + + dists[i] = dists_prev[i] = dist; } bm->elem_index_dirty &= ~BM_VERT; } @@ -1957,55 +2002,77 @@ static void editmesh_set_connectivity_distance(BMesh *bm, float mtx[3][3], float BMVert *v; LinkNode *lnk; + /* this is correct but slow to do each iteration, + * instead sync the dist's while clearing BM_ELEM_TAG (below) */ +#if 0 memcpy(dists_prev, dists, sizeof(float) * bm->totvert); +#endif while ((v = BLI_LINKSTACK_POP(queue))) { - /* quick checks */ - bool has_edges = (v->e != NULL); - bool has_faces = false; + BLI_assert(dists[BM_elem_index_get(v)] != FLT_MAX); /* connected edge-verts */ - if (has_edges) { - BMIter iter; - BMEdge *e; + if (v->e != NULL) { + BMEdge *e_iter, *e_first; + + e_iter = e_first = v->e; - BM_ITER_ELEM (e, &iter, v, BM_EDGES_OF_VERT) { - has_faces |= (BM_edge_is_wire(e) == false); + /* would normally use BM_EDGES_OF_VERT, but this runs so often, + * its faster to iterate on the data directly */ + do { - if (BM_elem_flag_test(e, BM_ELEM_HIDDEN) == 0) { - BMVert *v_other = BM_edge_other_vert(e, v); + if (BM_elem_flag_test(e_iter, BM_ELEM_HIDDEN) == 0) { + + /* edge distance */ + BMVert *v_other = BM_edge_other_vert(e_iter, v); if (bmesh_test_dist_add(v, v_other, dists, dists_prev, mtx)) { if (BM_elem_flag_test(v_other, BM_ELEM_TAG) == 0) { BM_elem_flag_enable(v_other, BM_ELEM_TAG); BLI_LINKSTACK_PUSH(queue_next, v_other); } } - } - } - } - - /* imaginary edge diagonally across quad */ - if (has_faces) { - BMIter iter; - BMLoop *l; - BM_ITER_ELEM (l, &iter, v, BM_LOOPS_OF_VERT) { - if ((BM_elem_flag_test(l->f, BM_ELEM_HIDDEN) == 0) && (l->f->len == 4)) { - BMVert *v_other = l->next->next->v; - if (bmesh_test_dist_add(v, v_other, dists, dists_prev, mtx)) { - if (BM_elem_flag_test(v_other, BM_ELEM_TAG) == 0) { - BM_elem_flag_enable(v_other, BM_ELEM_TAG); - BLI_LINKSTACK_PUSH(queue_next, v_other); - } + /* face distance */ + if (e_iter->l) { + BMLoop *l_iter_radial, *l_first_radial; + /** + * imaginary edge diagonally across quad, + * \note, this takes advantage of the rules of winding that we + * know 2 or more of a verts edges wont reference the same face twice. + * Also, if the edge is hidden, the face will be hidden too. + */ + l_iter_radial = l_first_radial = e_iter->l; + + do { + if ((l_iter_radial->v == v) && + (l_iter_radial->f->len == 4) && + (BM_elem_flag_test(l_iter_radial->f, BM_ELEM_HIDDEN) == 0)) + { + BMVert *v_other = l_iter_radial->next->next->v; + if (bmesh_test_dist_add(v, v_other, dists, dists_prev, mtx)) { + if (BM_elem_flag_test(v_other, BM_ELEM_TAG) == 0) { + BM_elem_flag_enable(v_other, BM_ELEM_TAG); + BLI_LINKSTACK_PUSH(queue_next, v_other); + } + } + } + } while ((l_iter_radial = l_iter_radial->radial_next) != l_first_radial); } } - } + } while ((e_iter = BM_DISK_EDGE_NEXT(e_iter, v)) != e_first); } } + /* clear for the next loop */ for (lnk = queue_next; lnk; lnk = lnk->next) { - BM_elem_flag_disable((BMVert *)lnk->link, BM_ELEM_TAG); + BMVert *v = lnk->link; + const int i = BM_elem_index_get(v); + + BM_elem_flag_disable(v, BM_ELEM_TAG); + + /* keep in sync, avoid having to do full memcpy each iteration */ + dists_prev[i] = dists[i]; } BLI_LINKSTACK_SWAP(queue, queue_next); @@ -2063,7 +2130,7 @@ static struct TransIslandData *editmesh_islands_info_calc(BMEditMesh *em, int *r vert_map = MEM_mallocN(sizeof(*vert_map) * bm->totvert, __func__); /* we shouldn't need this, but with incorrect selection flushing * its possible we have a selected vertex thats not in a face, for now best not crash in that case. */ - fill_vn_i(vert_map, bm->totvert, -1); + copy_vn_i(vert_map, bm->totvert, -1); BM_mesh_elem_table_ensure(bm, htype); ele_array = (htype == BM_FACE) ? (void **)bm->ftable : (void **)bm->etable; @@ -2144,6 +2211,7 @@ static void VertsToTransData(TransInfo *t, TransData *td, TransDataExtension *tx BMEditMesh *em, BMVert *eve, float *bweight, struct TransIslandData *v_island) { + float *no, _no[3]; BLI_assert(BM_elem_flag_test(eve, BM_ELEM_HIDDEN) == 0); td->flag = 0; @@ -2153,19 +2221,30 @@ static void VertsToTransData(TransInfo *t, TransData *td, TransDataExtension *tx td->loc = eve->co; copy_v3_v3(td->iloc, td->loc); + if ((t->mode == TFM_SHRINKFATTEN) && + (em->selectmode & SCE_SELECT_FACE) && + BM_elem_flag_test(eve, BM_ELEM_SELECT) && + (BM_vert_normal_update_ex(eve, BM_ELEM_SELECT, _no))) + { + no = _no; + } + else { + no = eve->no; + } + if (v_island) { copy_v3_v3(td->center, v_island->co); copy_m3_m3(td->axismtx, v_island->axismtx); } else if (t->around == V3D_LOCAL) { copy_v3_v3(td->center, td->loc); - createSpaceNormal(td->axismtx, eve->no); + createSpaceNormal(td->axismtx, no); } else { copy_v3_v3(td->center, td->loc); /* Setting normals */ - copy_v3_v3(td->axismtx[2], eve->no); + copy_v3_v3(td->axismtx[2], no); td->axismtx[0][0] = td->axismtx[0][1] = td->axismtx[0][2] = @@ -2194,7 +2273,7 @@ static void VertsToTransData(TransInfo *t, TransData *td, TransDataExtension *tx } else if (t->mode == TFM_SHRINKFATTEN) { td->ext = tx; - tx->isize[0] = BM_vert_calc_shell_factor_ex(eve, BM_ELEM_SELECT); + tx->isize[0] = BM_vert_calc_shell_factor_ex(eve, no, BM_ELEM_SELECT); } } @@ -2211,7 +2290,7 @@ static void createTransEditVerts(TransInfo *t) float mtx[3][3], smtx[3][3], (*defmats)[3][3] = NULL, (*defcos)[3] = NULL; float *dists = NULL; int a; - int propmode = (t->flag & T_PROP_EDIT) ? (t->flag & T_PROP_EDIT_ALL) : 0; + const int prop_mode = (t->flag & T_PROP_EDIT) ? (t->flag & T_PROP_EDIT_ALL) : 0; int mirror = 0; int cd_vert_bweight_offset = -1; bool use_topology = (me->editflag & ME_EDIT_MIRROR_TOPO) != 0; @@ -2251,7 +2330,7 @@ static void createTransEditVerts(TransInfo *t) cd_vert_bweight_offset = CustomData_get_offset(&bm->vdata, CD_BWEIGHT); } - if (propmode) { + if (prop_mode) { unsigned int count = 0; BM_ITER_MESH (eve, &iter, bm, BM_VERTS_OF_MESH) { if (!BM_elem_flag_test(eve, BM_ELEM_HIDDEN)) { @@ -2262,7 +2341,7 @@ static void createTransEditVerts(TransInfo *t) t->total = count; /* allocating scratch arrays */ - if (propmode & T_PROP_CONNECTED) + if (prop_mode & T_PROP_CONNECTED) dists = MEM_mallocN(em->bm->totvert * sizeof(float), "scratch nears"); } else { @@ -2284,7 +2363,7 @@ static void createTransEditVerts(TransInfo *t) * matrix inversion still works and we can still moving along the other */ pseudoinverse_m3_m3(smtx, mtx, PSEUDOINVERSE_EPSILON); - if (propmode & T_PROP_CONNECTED) { + if (prop_mode & T_PROP_CONNECTED) { editmesh_set_connectivity_distance(em->bm, mtx, dists); } @@ -2298,7 +2377,7 @@ static void createTransEditVerts(TransInfo *t) if (modifiers_isCorrectableDeformed(t->scene, t->obedit)) { /* check if we can use deform matrices for modifier from the * start up to stack, they are more accurate than quats */ - totleft = editbmesh_get_first_deform_matrices(t->scene, t->obedit, em, &defmats, &defcos); + totleft = BKE_crazyspace_get_first_deform_matrices_editbmesh(t->scene, t->obedit, em, &defmats, &defcos); } /* if we still have more modifiers, also do crazyspace @@ -2313,7 +2392,7 @@ static void createTransEditVerts(TransInfo *t) { mappedcos = BKE_crazyspace_get_mapped_editverts(t->scene, t->obedit); quats = MEM_mallocN(em->bm->totvert * sizeof(*quats), "crazy quats"); - BKE_crazyspace_set_quats_editmesh(em, defcos, mappedcos, quats, !propmode); + BKE_crazyspace_set_quats_editmesh(em, defcos, mappedcos, quats, !prop_mode); if (mappedcos) MEM_freeN(mappedcos); } @@ -2338,7 +2417,7 @@ static void createTransEditVerts(TransInfo *t) BM_ITER_MESH_INDEX (eve, &iter, bm, BM_VERTS_OF_MESH, a) { if (!BM_elem_flag_test(eve, BM_ELEM_HIDDEN)) { - if (propmode || BM_elem_flag_test(eve, BM_ELEM_SELECT)) { + if (prop_mode || BM_elem_flag_test(eve, BM_ELEM_SELECT)) { struct TransIslandData *v_island = (island_info && island_vert_map[a] != -1) ? &island_info[island_vert_map[a]] : NULL; float *bweight = (cd_vert_bweight_offset != -1) ? BM_ELEM_CD_GET_VOID_P(eve, cd_vert_bweight_offset) : NULL; @@ -2351,8 +2430,8 @@ static void createTransEditVerts(TransInfo *t) if (BM_elem_flag_test(eve, BM_ELEM_SELECT)) tob->flag |= TD_SELECTED; - if (propmode) { - if (propmode & T_PROP_CONNECTED) { + if (prop_mode) { + if (prop_mode & T_PROP_CONNECTED) { tob->dist = dists[a]; } else { @@ -2604,24 +2683,23 @@ void flushTransSeq(TransInfo *t) /* ********************* UV ****************** */ -static void UVsToTransData(SpaceImage *sima, TransData *td, TransData2D *td2d, float *uv, int selected) +static void UVsToTransData( + const float aspect[2], TransData *td, TransData2D *td2d, + float *uv, const float *center, bool selected) { - float aspx, aspy; - - ED_space_image_get_uv_aspect(sima, &aspx, &aspy); - /* uv coords are scaled by aspects. this is needed for rotations and * proportional editing to be consistent with the stretched uv coords * that are displayed. this also means that for display and numinput, - * and when the the uv coords are flushed, these are converted each time */ - td2d->loc[0] = uv[0] * aspx; - td2d->loc[1] = uv[1] * aspy; + * and when the uv coords are flushed, these are converted each time */ + td2d->loc[0] = uv[0] * aspect[0]; + td2d->loc[1] = uv[1] * aspect[1]; td2d->loc[2] = 0.0f; td2d->loc2d = uv; td->flag = 0; td->loc = td2d->loc; - copy_v3_v3(td->center, td->loc); + copy_v2_v2(td->center, center ? center : td->loc); + td->center[2] = 0.0f; copy_v3_v3(td->iloc, td->loc); memset(td->axismtx, 0, sizeof(td->axismtx)); @@ -2648,36 +2726,43 @@ static void createTransUVs(bContext *C, TransInfo *t) ToolSettings *ts = CTX_data_tool_settings(C); TransData *td = NULL; TransData2D *td2d = NULL; - MTexPoly *tf; - MLoopUV *luv; BMEditMesh *em = BKE_editmesh_from_object(t->obedit); BMFace *efa; - BMLoop *l; BMIter iter, liter; UvElementMap *elementmap = NULL; BLI_bitmap *island_enabled = NULL; + struct { float co[2]; int co_num; } *island_center = NULL; int count = 0, countsel = 0, count_rejected = 0; - const bool propmode = (t->flag & T_PROP_EDIT) != 0; - const bool propconnected = (t->flag & T_PROP_CONNECTED) != 0; - + const bool is_prop_edit = (t->flag & T_PROP_EDIT) != 0; + const bool is_prop_connected = (t->flag & T_PROP_CONNECTED) != 0; + const bool is_island_center = (t->around == V3D_LOCAL); const int cd_loop_uv_offset = CustomData_get_offset(&em->bm->ldata, CD_MLOOPUV); + const int cd_poly_tex_offset = CustomData_get_offset(&em->bm->pdata, CD_MTEXPOLY); - if (!ED_space_image_show_uvedit(sima, t->obedit)) return; + if (!ED_space_image_show_uvedit(sima, t->obedit)) + return; /* count */ - if (propconnected) { + if (is_prop_connected || is_island_center) { /* create element map with island information */ - if (ts->uv_flag & UV_SYNC_SELECTION) { - elementmap = BM_uv_element_map_create(em->bm, false, true); + const bool use_facesel = (ts->uv_flag & UV_SYNC_SELECTION) == 0; + elementmap = BM_uv_element_map_create(em->bm, use_facesel, false, true); + if (elementmap == NULL) { + return; } - else { - elementmap = BM_uv_element_map_create(em->bm, true, true); + + if (is_prop_connected) { + island_enabled = BLI_BITMAP_NEW(elementmap->totalIslands, "TransIslandData(UV Editing)"); + } + + if (is_island_center) { + island_center = MEM_callocN(sizeof(*island_center) * elementmap->totalIslands, __func__); } - island_enabled = BLI_BITMAP_NEW(elementmap->totalIslands, "TransIslandData(UV Editing)"); } BM_ITER_MESH (efa, &iter, em->bm, BM_FACES_OF_MESH) { - tf = CustomData_bmesh_get(&em->bm->pdata, efa->head.data, CD_MTEXPOLY); + MTexPoly *tf = BM_ELEM_CD_GET_VOID_P(efa, cd_poly_tex_offset); + BMLoop *l; if (!uvedit_face_visible_test(scene, ima, efa, tf)) { BM_elem_flag_disable(efa, BM_ELEM_TAG); @@ -2689,14 +2774,25 @@ static void createTransUVs(bContext *C, TransInfo *t) if (uvedit_uv_select_test(scene, l, cd_loop_uv_offset)) { countsel++; - if (propconnected) { + if (is_prop_connected || island_center) { UvElement *element = BM_uv_element_get(elementmap, efa, l); - BLI_BITMAP_ENABLE(island_enabled, element->island); - } + if (is_prop_connected) { + BLI_BITMAP_ENABLE(island_enabled, element->island); + } + + if (is_island_center) { + if (element->flag == false) { + MLoopUV *luv = BM_ELEM_CD_GET_VOID_P(l, cd_loop_uv_offset); + add_v2_v2(island_center[element->island].co, luv->uv); + island_center[element->island].co_num++; + element->flag = true; + } + } + } } - if (propmode) { + if (is_prop_edit) { count++; } } @@ -2704,13 +2800,19 @@ static void createTransUVs(bContext *C, TransInfo *t) /* note: in prop mode we need at least 1 selected */ if (countsel == 0) { - if (propconnected) { - MEM_freeN(island_enabled); + goto finally; + } + + if (is_island_center) { + int i; + + for (i = 0; i < elementmap->totalIslands; i++) { + mul_v2_fl(island_center[i].co, 1.0f / island_center[i].co_num); + mul_v2_v2(island_center[i].co, t->aspect); } - return; } - t->total = (propmode) ? count : countsel; + t->total = (is_prop_edit) ? count : countsel; t->data = MEM_callocN(t->total * sizeof(TransData), "TransObData(UV Editing)"); /* for each 2d uv coord a 3d vector is allocated, so that they can be * treated just as if they were 3d verts */ @@ -2723,56 +2825,88 @@ static void createTransUVs(bContext *C, TransInfo *t) td2d = t->data2d; BM_ITER_MESH (efa, &iter, em->bm, BM_FACES_OF_MESH) { + BMLoop *l; + if (!BM_elem_flag_test(efa, BM_ELEM_TAG)) continue; BM_ITER_ELEM (l, &liter, efa, BM_LOOPS_OF_FACE) { - if (!propmode && !uvedit_uv_select_test(scene, l, cd_loop_uv_offset)) + const bool selected = uvedit_uv_select_test(scene, l, cd_loop_uv_offset); + MLoopUV *luv; + const float *center = NULL; + + if (!is_prop_edit && !selected) continue; - if (propconnected) { + if (is_prop_connected || is_island_center) { UvElement *element = BM_uv_element_get(elementmap, efa, l); - if (!BLI_BITMAP_TEST(island_enabled, element->island)) { - count_rejected++; - continue; + + if (is_prop_connected) { + if (!BLI_BITMAP_TEST(island_enabled, element->island)) { + count_rejected++; + continue; + } + } + + if (is_island_center) { + center = island_center[element->island].co; } } + BM_elem_flag_enable(l, BM_ELEM_TAG); luv = BM_ELEM_CD_GET_VOID_P(l, cd_loop_uv_offset); - UVsToTransData(sima, td++, td2d++, luv->uv, uvedit_uv_select_test(scene, l, cd_loop_uv_offset)); + UVsToTransData(t->aspect, td++, td2d++, luv->uv, center, selected); } } - if (propconnected) { + if (is_prop_connected) { t->total -= count_rejected; - BM_uv_element_map_free(elementmap); - MEM_freeN(island_enabled); } if (sima->flag & SI_LIVE_UNWRAP) ED_uvedit_live_unwrap_begin(t->scene, t->obedit); + + +finally: + if (is_prop_connected || is_island_center) { + BM_uv_element_map_free(elementmap); + + if (is_prop_connected) { + MEM_freeN(island_enabled); + } + + if (island_center) { + MEM_freeN(island_center); + } + } } void flushTransUVs(TransInfo *t) { SpaceImage *sima = t->sa->spacedata.first; TransData2D *td; - int a, width, height; - float aspx, aspy, invx, invy; + int a; + float aspect_inv[2], size[2]; + const bool use_pixel_snap = ((sima->flag & SI_PIXELSNAP) && (t->state != TRANS_CANCEL)); + + aspect_inv[0] = 1.0f / t->aspect[0]; + aspect_inv[1] = 1.0f / t->aspect[1]; - ED_space_image_get_uv_aspect(sima, &aspx, &aspy); - ED_space_image_get_size(sima, &width, &height); - invx = 1.0f / aspx; - invy = 1.0f / aspy; + if (use_pixel_snap) { + int size_i[2]; + ED_space_image_get_size(sima, &size_i[0], &size_i[1]); + size[0] = size_i[0]; + size[1] = size_i[1]; + } /* flush to 2d vector from internally used 3d vector */ for (a = 0, td = t->data2d; a < t->total; a++, td++) { - td->loc2d[0] = td->loc[0] * invx; - td->loc2d[1] = td->loc[1] * invy; + td->loc2d[0] = td->loc[0] * aspect_inv[0]; + td->loc2d[1] = td->loc[1] * aspect_inv[1]; - if ((sima->flag & SI_PIXELSNAP) && (t->state != TRANS_CANCEL)) { - td->loc2d[0] = (float)floor(width * td->loc2d[0] + 0.5f) / width; - td->loc2d[1] = (float)floor(height * td->loc2d[1] + 0.5f) / height; + if (use_pixel_snap) { + td->loc2d[0] = roundf(td->loc2d[0] * size[0]) / size[0]; + td->loc2d[1] = roundf(td->loc2d[1] * size[1]) / size[1]; } } } @@ -2780,44 +2914,45 @@ void flushTransUVs(TransInfo *t) bool clipUVTransform(TransInfo *t, float vec[2], const bool resize) { TransData *td; - int a, clipx = 1, clipy = 1; - float aspx, aspy, min[2], max[2]; + int a; + bool clipx = true, clipy = true; + float min[2], max[2]; - ED_space_image_get_uv_aspect(t->sa->spacedata.first, &aspx, &aspy); min[0] = min[1] = 0.0f; - max[0] = aspx; max[1] = aspy; + max[0] = t->aspect[0]; + max[1] = t->aspect[1]; for (a = 0, td = t->data; a < t->total; a++, td++) { minmax_v2v2_v2(min, max, td->loc); } if (resize) { - if (min[0] < 0.0f && t->center[0] > 0.0f && t->center[0] < aspx * 0.5f) + if (min[0] < 0.0f && t->center[0] > 0.0f && t->center[0] < t->aspect[0] * 0.5f) vec[0] *= t->center[0] / (t->center[0] - min[0]); - else if (max[0] > aspx && t->center[0] < aspx) - vec[0] *= (t->center[0] - aspx) / (t->center[0] - max[0]); + else if (max[0] > t->aspect[0] && t->center[0] < t->aspect[0]) + vec[0] *= (t->center[0] - t->aspect[0]) / (t->center[0] - max[0]); else clipx = 0; - if (min[1] < 0.0f && t->center[1] > 0.0f && t->center[1] < aspy * 0.5f) + if (min[1] < 0.0f && t->center[1] > 0.0f && t->center[1] < t->aspect[1] * 0.5f) vec[1] *= t->center[1] / (t->center[1] - min[1]); - else if (max[1] > aspy && t->center[1] < aspy) - vec[1] *= (t->center[1] - aspy) / (t->center[1] - max[1]); + else if (max[1] > t->aspect[1] && t->center[1] < t->aspect[1]) + vec[1] *= (t->center[1] - t->aspect[1]) / (t->center[1] - max[1]); else clipy = 0; } else { if (min[0] < 0.0f) vec[0] -= min[0]; - else if (max[0] > aspx) - vec[0] -= max[0] - aspx; + else if (max[0] > t->aspect[0]) + vec[0] -= max[0] - t->aspect[0]; else clipx = 0; if (min[1] < 0.0f) vec[1] -= min[1]; - else if (max[1] > aspy) - vec[1] -= max[1] - aspy; + else if (max[1] > t->aspect[1]) + vec[1] -= max[1] - t->aspect[1]; else clipy = 0; } @@ -2829,9 +2964,6 @@ void clipUVData(TransInfo *t) { TransData *td = NULL; int a; - float aspx, aspy; - - ED_space_image_get_uv_aspect(t->sa->spacedata.first, &aspx, &aspy); for (a = 0, td = t->data; a < t->total; a++, td++) { if (td->flag & TD_NOACTION) @@ -2840,8 +2972,8 @@ void clipUVData(TransInfo *t) if ((td->flag & TD_SKIP) || (!td->loc)) continue; - td->loc[0] = min_ff(max_ff(0.0f, td->loc[0]), aspx); - td->loc[1] = min_ff(max_ff(0.0f, td->loc[1]), aspy); + td->loc[0] = min_ff(max_ff(0.0f, td->loc[0]), t->aspect[0]); + td->loc[1] = min_ff(max_ff(0.0f, td->loc[1]), t->aspect[1]); } } @@ -3115,7 +3247,7 @@ static void posttrans_gpd_clean(bGPdata *gpd) bGPDframe *gpf, *gpfn; bool is_double = false; - BLI_sortlist_r(&gpl->frames, &is_double, gpf_cmp_frame); + BLI_listbase_sort_r(&gpl->frames, gpf_cmp_frame, &is_double); if (is_double) { for (gpf = gpl->frames.first; gpf; gpf = gpfn) { @@ -3142,7 +3274,7 @@ static void posttrans_mask_clean(Mask *mask) MaskLayerShape *masklay_shape, *masklay_shape_next; bool is_double = false; - BLI_sortlist_r(&masklay->splines_shapes, &is_double, masklay_shape_cmp_frame); + BLI_listbase_sort_r(&masklay->splines_shapes, masklay_shape_cmp_frame, &is_double); if (is_double) { for (masklay_shape = masklay->splines_shapes.first; masklay_shape; masklay_shape = masklay_shape_next) { @@ -3164,7 +3296,7 @@ static void posttrans_mask_clean(Mask *mask) /* Called during special_aftertrans_update to make sure selected keyframes replace * any other keyframes which may reside on that frame (that is not selected). */ -static void posttrans_fcurve_clean(FCurve *fcu, const short use_handle) +static void posttrans_fcurve_clean(FCurve *fcu, const bool use_handle) { float *selcache; /* cache for frame numbers of selected frames (fcu->totvert*sizeof(float)) */ int len, index, i; /* number of frames in cache, item index */ @@ -3186,7 +3318,7 @@ static void posttrans_fcurve_clean(FCurve *fcu, const short use_handle) for (i = 0; i < fcu->totvert; i++) { BezTriple *bezt = &fcu->bezt[i]; - if (BEZSELECTED(bezt)) { + if (BEZT_ISSEL_ANY(bezt)) { selcache[index] = bezt->vec[1][0]; index++; len++; @@ -3200,7 +3332,7 @@ static void posttrans_fcurve_clean(FCurve *fcu, const short use_handle) for (i = fcu->totvert - 1; i >= 0; i--) { BezTriple *bezt = &fcu->bezt[i]; - if (BEZSELECTED(bezt) == 0) { + if (BEZT_ISSEL_ANY(bezt) == 0) { /* check beztriple should be removed according to cache */ for (index = 0; index < len; index++) { if (IS_EQF(bezt->vec[1][0], selcache[index])) { @@ -3258,76 +3390,90 @@ static void posttrans_action_clean(bAnimContext *ac, bAction *act) /* ----------------------------- */ /* fully select selected beztriples, but only include if it's on the right side of cfra */ -static int count_fcurve_keys(FCurve *fcu, char side, float cfra) +static int count_fcurve_keys(FCurve *fcu, char side, float cfra, bool is_prop_edit) { BezTriple *bezt; - int i, count = 0; + int i, count = 0, count_all = 0; if (ELEM(NULL, fcu, fcu->bezt)) return count; /* only include points that occur on the right side of cfra */ for (i = 0, bezt = fcu->bezt; i < fcu->totvert; i++, bezt++) { - if (bezt->f2 & SELECT) { + if (FrameOnMouseSide(side, bezt->vec[1][0], cfra)) { /* no need to adjust the handle selection since they are assumed * selected (like graph editor with SIPO_NOHANDLES) */ - if (FrameOnMouseSide(side, bezt->vec[1][0], cfra)) { - count += 1; - } + if (bezt->f2 & SELECT) + count++; + + count_all++; } } - return count; + if (is_prop_edit && count > 0) + return count_all; + else return count; } /* fully select selected beztriples, but only include if it's on the right side of cfra */ -static int count_gplayer_frames(bGPDlayer *gpl, char side, float cfra) +static int count_gplayer_frames(bGPDlayer *gpl, char side, float cfra, bool is_prop_edit) { bGPDframe *gpf; - int count = 0; + int count = 0, count_all = 0; if (gpl == NULL) return count; /* only include points that occur on the right side of cfra */ for (gpf = gpl->frames.first; gpf; gpf = gpf->next) { - if (gpf->flag & GP_FRAME_SELECT) { - if (FrameOnMouseSide(side, (float)gpf->framenum, cfra)) + if (FrameOnMouseSide(side, (float)gpf->framenum, cfra)) { + if (gpf->flag & GP_FRAME_SELECT) count++; + count_all++; } } - return count; + if (is_prop_edit && count > 0) + return count_all; + else + return count; } /* fully select selected beztriples, but only include if it's on the right side of cfra */ -static int count_masklayer_frames(MaskLayer *masklay, char side, float cfra) +static int count_masklayer_frames(MaskLayer *masklay, char side, float cfra, bool is_prop_edit) { MaskLayerShape *masklayer_shape; - int count = 0; + int count = 0, count_all = 0; if (masklay == NULL) return count; /* only include points that occur on the right side of cfra */ for (masklayer_shape = masklay->splines_shapes.first; masklayer_shape; masklayer_shape = masklayer_shape->next) { - if (masklayer_shape->flag & MASK_SHAPE_SELECT) { - if (FrameOnMouseSide(side, (float)masklayer_shape->frame, cfra)) + if (FrameOnMouseSide(side, (float)masklayer_shape->frame, cfra)) { + if (masklayer_shape->flag & MASK_SHAPE_SELECT) count++; + count_all++; } } - return count; + if (is_prop_edit && count > 0) + return count_all; + else + return count; } /* This function assigns the information to transdata */ -static void TimeToTransData(TransData *td, float *time, AnimData *adt) +static void TimeToTransData(TransData *td, float *time, AnimData *adt, float ypos) { /* memory is calloc'ed, so that should zero everything nicely for us */ td->val = time; td->ival = *(time); + td->center[0] = td->ival; + td->center[1] = ypos; + /* store the AnimData where this keyframe exists as a keyframe of the * active action as td->extra. */ @@ -3341,7 +3487,7 @@ static void TimeToTransData(TransData *td, float *time, AnimData *adt) * The 'side' argument is needed for the extend mode. 'B' = both sides, 'R'/'L' mean only data * on the named side are used. */ -static TransData *ActionFCurveToTransData(TransData *td, TransData2D **td2dv, FCurve *fcu, AnimData *adt, char side, float cfra) +static TransData *ActionFCurveToTransData(TransData *td, TransData2D **td2dv, FCurve *fcu, AnimData *adt, char side, float cfra, bool is_prop_edit, float ypos) { BezTriple *bezt; TransData2D *td2d = *td2dv; @@ -3352,11 +3498,14 @@ static TransData *ActionFCurveToTransData(TransData *td, TransData2D **td2dv, FC for (i = 0, bezt = fcu->bezt; i < fcu->totvert; i++, bezt++) { /* only add selected keyframes (for now, proportional edit is not enabled) */ - if (bezt->f2 & SELECT) { /* note this MUST match count_fcurve_keys(), so can't use BEZSELECTED() macro */ + if (is_prop_edit || (bezt->f2 & SELECT)) { /* note this MUST match count_fcurve_keys(), so can't use BEZT_ISSEL_ANY() macro */ /* only add if on the right 'side' of the current frame */ if (FrameOnMouseSide(side, bezt->vec[1][0], cfra)) { - TimeToTransData(td, bezt->vec[1], adt); + TimeToTransData(td, bezt->vec[1], adt, ypos); + if (bezt->f2 & SELECT) + td->flag |= TD_SELECTED; + /*set flags to move handles as necessary*/ td->flag |= TD_MOVEHANDLE1 | TD_MOVEHANDLE2; td2d->h1 = bezt->vec[0]; @@ -3407,19 +3556,22 @@ void flushTransIntFrameActionData(TransInfo *t) * The 'side' argument is needed for the extend mode. 'B' = both sides, 'R'/'L' mean only data * on the named side are used. */ -static int GPLayerToTransData(TransData *td, tGPFtransdata *tfd, bGPDlayer *gpl, char side, float cfra) +static int GPLayerToTransData(TransData *td, tGPFtransdata *tfd, bGPDlayer *gpl, char side, float cfra, bool is_prop_edit, float ypos) { bGPDframe *gpf; int count = 0; /* check for select frames on right side of current frame */ for (gpf = gpl->frames.first; gpf; gpf = gpf->next) { - if (gpf->flag & GP_FRAME_SELECT) { + if (is_prop_edit || (gpf->flag & GP_FRAME_SELECT)) { if (FrameOnMouseSide(side, (float)gpf->framenum, cfra)) { /* memory is calloc'ed, so that should zero everything nicely for us */ td->val = &tfd->val; td->ival = (float)gpf->framenum; + td->center[0] = td->ival; + td->center[1] = ypos; + tfd->val = (float)gpf->framenum; tfd->sdata = &gpf->framenum; @@ -3435,19 +3587,22 @@ static int GPLayerToTransData(TransData *td, tGPFtransdata *tfd, bGPDlayer *gpl, } /* refer to comment above #GPLayerToTransData, this is the same but for masks */ -static int MaskLayerToTransData(TransData *td, tGPFtransdata *tfd, MaskLayer *masklay, char side, float cfra) +static int MaskLayerToTransData(TransData *td, tGPFtransdata *tfd, MaskLayer *masklay, char side, float cfra, bool is_prop_edit, float ypos) { MaskLayerShape *masklay_shape; int count = 0; /* check for select frames on right side of current frame */ for (masklay_shape = masklay->splines_shapes.first; masklay_shape; masklay_shape = masklay_shape->next) { - if (masklay_shape->flag & MASK_SHAPE_SELECT) { + if (is_prop_edit || (masklay_shape->flag & MASK_SHAPE_SELECT)) { if (FrameOnMouseSide(side, (float)masklay_shape->frame, cfra)) { /* memory is calloc'ed, so that should zero everything nicely for us */ td->val = &tfd->val; td->ival = (float)masklay_shape->frame; + td->center[0] = td->ival; + td->center[1] = ypos; + tfd->val = (float)masklay_shape->frame; tfd->sdata = &masklay_shape->frame; @@ -3469,15 +3624,25 @@ static void createTransActionData(bContext *C, TransInfo *t) TransData *td = NULL; TransData2D *td2d = NULL; tGPFtransdata *tfd = NULL; - + + rcti *mask = &t->ar->v2d.mask; + rctf *datamask = &t->ar->v2d.cur; + + float xsize = BLI_rctf_size_x(datamask); + float ysize = BLI_rctf_size_y(datamask); + float xmask = BLI_rcti_size_x(mask); + float ymask = BLI_rcti_size_y(mask); + bAnimContext ac; ListBase anim_data = {NULL, NULL}; bAnimListElem *ale; int filter; - + const bool is_prop_edit = (t->flag & T_PROP_EDIT) != 0; + int count = 0; float cfra; - + float ypos = 1.0f / ((ysize / xsize) * (xmask / ymask)) * BLI_rctf_cent_y(&t->ar->v2d.cur); + /* determine what type of data we are operating on */ if (ANIM_animdata_get_context(C, &ac) == 0) return; @@ -3505,7 +3670,7 @@ static void createTransActionData(bContext *C, TransInfo *t) /* loop 1: fully select ipo-keys and count how many BezTriples are selected */ for (ale = anim_data.first; ale; ale = ale->next) { AnimData *adt = ANIM_nla_mapping_get(&ac, ale); - + int adt_count = 0; /* convert current-frame to action-time (slightly less accurate, especially under * higher scaling ratios, but is faster than converting all points) */ @@ -3514,14 +3679,19 @@ static void createTransActionData(bContext *C, TransInfo *t) else cfra = (float)CFRA; - if (ale->type == ANIMTYPE_FCURVE) - count += count_fcurve_keys(ale->key_data, t->frame_side, cfra); + if (ELEM(ale->type, ANIMTYPE_FCURVE, ANIMTYPE_NLACURVE)) + adt_count = count_fcurve_keys(ale->key_data, t->frame_side, cfra, is_prop_edit); else if (ale->type == ANIMTYPE_GPLAYER) - count += count_gplayer_frames(ale->data, t->frame_side, cfra); + adt_count = count_gplayer_frames(ale->data, t->frame_side, cfra, is_prop_edit); else if (ale->type == ANIMTYPE_MASKLAYER) - count += count_masklayer_frames(ale->data, t->frame_side, cfra); + adt_count = count_masklayer_frames(ale->data, t->frame_side, cfra, is_prop_edit); else BLI_assert(0); + + if (adt_count > 0) { + count += adt_count; + ale->tag = true; + } } /* stop if trying to build list if nothing selected */ @@ -3558,11 +3728,22 @@ static void createTransActionData(bContext *C, TransInfo *t) /* loop 2: build transdata array */ for (ale = anim_data.first; ale; ale = ale->next) { + AnimData *adt; + + if (is_prop_edit && !ale->tag) + continue; + + adt = ANIM_nla_mapping_get(&ac, ale); + if (adt) + cfra = BKE_nla_tweakedit_remap(adt, (float)CFRA, NLATIME_CONVERT_UNMAP); + else + cfra = (float)CFRA; + if (ale->type == ANIMTYPE_GPLAYER) { bGPDlayer *gpl = (bGPDlayer *)ale->data; int i; - i = GPLayerToTransData(td, tfd, gpl, t->frame_side, cfra); + i = GPLayerToTransData(td, tfd, gpl, t->frame_side, cfra, is_prop_edit, ypos); td += i; tfd += i; } @@ -3570,7 +3751,7 @@ static void createTransActionData(bContext *C, TransInfo *t) MaskLayer *masklay = (MaskLayer *)ale->data; int i; - i = MaskLayerToTransData(td, tfd, masklay, t->frame_side, cfra); + i = MaskLayerToTransData(td, tfd, masklay, t->frame_side, cfra, is_prop_edit, ypos); td += i; tfd += i; } @@ -3578,15 +3759,7 @@ static void createTransActionData(bContext *C, TransInfo *t) AnimData *adt = ANIM_nla_mapping_get(&ac, ale); FCurve *fcu = (FCurve *)ale->key_data; - /* convert current-frame to action-time (slightly less accurate, especially under - * higher scaling ratios, but is faster than converting all points) - */ - if (adt) - cfra = BKE_nla_tweakedit_remap(adt, (float)CFRA, NLATIME_CONVERT_UNMAP); - else - cfra = (float)CFRA; - - td = ActionFCurveToTransData(td, &td2d, fcu, adt, t->frame_side, cfra); + td = ActionFCurveToTransData(td, &td2d, fcu, adt, t->frame_side, cfra, is_prop_edit, ypos); } } @@ -3615,6 +3788,109 @@ static void createTransActionData(bContext *C, TransInfo *t) *((float *)(t->customData) + 1) = max; } + /* calculate distances for proportional editing */ + if (is_prop_edit) { + td = t->data; + + for (ale = anim_data.first; ale; ale = ale->next) { + AnimData *adt; + + /* F-Curve may not have any keyframes */ + if (!ale->tag) + continue; + + adt = ANIM_nla_mapping_get(&ac, ale); + if (adt) + cfra = BKE_nla_tweakedit_remap(adt, (float)CFRA, NLATIME_CONVERT_UNMAP); + else + cfra = (float)CFRA; + + if (ale->type == ANIMTYPE_GPLAYER) { + bGPDlayer *gpl = (bGPDlayer *)ale->data; + bGPDframe *gpf; + + for (gpf = gpl->frames.first; gpf; gpf = gpf->next) { + if (gpf->flag & GP_FRAME_SELECT) { + td->dist = td->rdist = 0.0f; + } + else { + bGPDframe *gpf_iter; + int min = INT_MAX; + for (gpf_iter = gpl->frames.first; gpf_iter; gpf_iter = gpf->next) { + if (gpf_iter->flag & GP_FRAME_SELECT) { + if (FrameOnMouseSide(t->frame_side, (float)gpf_iter->framenum, cfra)) { + int val = abs(gpf->framenum - gpf_iter->framenum); + if (val < min) { + min = val; + } + } + } + } + td->dist = td->rdist = min; + } + td++; + } + } + else if (ale->type == ANIMTYPE_MASKLAYER) { + MaskLayer *masklay = (MaskLayer *)ale->data; + MaskLayerShape *masklay_shape; + + for (masklay_shape = masklay->splines_shapes.first; masklay_shape; masklay_shape = masklay_shape->next) { + if (FrameOnMouseSide(t->frame_side, (float)masklay_shape->frame, cfra)) { + if (masklay_shape->flag & MASK_SHAPE_SELECT) { + td->dist = td->rdist = 0.0f; + } + else { + MaskLayerShape *masklay_iter; + int min = INT_MAX; + for (masklay_iter = masklay->splines_shapes.first; masklay_iter; masklay_iter = masklay_iter->next) { + if (masklay_iter->flag & MASK_SHAPE_SELECT) { + if (FrameOnMouseSide(t->frame_side, (float)masklay_iter->frame, cfra)) { + int val = abs(masklay_shape->frame - masklay_iter->frame); + if (val < min) { + min = val; + } + } + } + } + td->dist = td->rdist = min; + } + td++; + } + } + } + else { + FCurve *fcu = (FCurve *)ale->key_data; + BezTriple *bezt; + int i; + + for (i = 0, bezt = fcu->bezt; i < fcu->totvert; i++, bezt++) { + if (FrameOnMouseSide(t->frame_side, bezt->vec[1][0], cfra)) { + if (bezt->f2 & SELECT) { + td->dist = td->rdist = 0.0f; + } + else { + BezTriple *bezt_iter; + int j; + float min = FLT_MAX; + for (j = 0, bezt_iter = fcu->bezt; j < fcu->totvert; j++, bezt_iter++) { + if (bezt_iter->f2 & SELECT) { + if (FrameOnMouseSide(t->frame_side, (float)bezt_iter->vec[1][0], cfra)) { + float val = fabs(bezt->vec[1][0] - bezt_iter->vec[1][0]); + if (val < min) + min = val; + } + } + } + td->dist = td->rdist = min; + } + td++; + } + } + } + } + } + /* cleanup temp list */ ANIM_animdata_freelist(&anim_data); } @@ -3623,6 +3899,7 @@ static void createTransActionData(bContext *C, TransInfo *t) typedef struct TransDataGraph { float unit_scale; + float offset; } TransDataGraph; /* Helper function for createTransGraphEditData, which is responsible for associating @@ -3631,7 +3908,7 @@ typedef struct TransDataGraph { static void bezt_to_transdata(TransData *td, TransData2D *td2d, TransDataGraph *tdg, AnimData *adt, BezTriple *bezt, int bi, bool selected, bool ishandle, bool intvals, - float mtx[3][3], float smtx[3][3], float unit_scale) + float mtx[3][3], float smtx[3][3], float unit_scale, float offset) { float *loc = bezt->vec[bi]; const float *cent = bezt->vec[1]; @@ -3645,26 +3922,26 @@ static void bezt_to_transdata(TransData *td, TransData2D *td2d, TransDataGraph * if (adt) { td2d->loc[0] = BKE_nla_tweakedit_remap(adt, loc[0], NLATIME_CONVERT_MAP); - td2d->loc[1] = loc[1] * unit_scale; + td2d->loc[1] = (loc[1] + offset) * unit_scale; td2d->loc[2] = 0.0f; td2d->loc2d = loc; td->loc = td2d->loc; td->center[0] = BKE_nla_tweakedit_remap(adt, cent[0], NLATIME_CONVERT_MAP); - td->center[1] = cent[1] * unit_scale; + td->center[1] = (cent[1] + offset) * unit_scale; td->center[2] = 0.0f; copy_v3_v3(td->iloc, td->loc); } else { td2d->loc[0] = loc[0]; - td2d->loc[1] = loc[1] * unit_scale; + td2d->loc[1] = (loc[1] + offset) * unit_scale; td2d->loc[2] = 0.0f; td2d->loc2d = loc; td->loc = td2d->loc; copy_v3_v3(td->center, cent); - td->center[1] *= unit_scale; + td->center[1] = (td->center[1] + offset) * unit_scale; copy_v3_v3(td->iloc, td->loc); } @@ -3704,6 +3981,7 @@ static void bezt_to_transdata(TransData *td, TransData2D *td2d, TransDataGraph * copy_m3_m3(td->smtx, smtx); tdg->unit_scale = unit_scale; + tdg->offset = offset; } static bool graph_edit_is_translation_mode(TransInfo *t) @@ -3716,6 +3994,29 @@ static bool graph_edit_use_local_center(TransInfo *t) return (t->around == V3D_LOCAL) && !graph_edit_is_translation_mode(t); } + +static void graph_key_shortest_dist(TransInfo *t, FCurve *fcu, TransData *td_start, TransData *td, int cfra, bool use_handle) +{ + int j = 0; + TransData *td_iter = td_start; + + td->dist = FLT_MAX; + for (; j < fcu->totvert; j++) { + BezTriple *bezt = fcu->bezt + j; + if (FrameOnMouseSide(t->frame_side, bezt->vec[1][0], cfra)) { + const bool sel2 = (bezt->f2 & SELECT) != 0; + const bool sel1 = use_handle ? (bezt->f1 & SELECT) != 0 : sel2; + const bool sel3 = use_handle ? (bezt->f3 & SELECT) != 0 : sel2; + + if (sel1 || sel2 || sel3) { + td->dist = td->rdist = min_ff(td->dist, fabs(td_iter->center[0] - td->center[0])); + } + + td_iter += 3; + } + } +} + static void createTransGraphEditData(bContext *C, TransInfo *t) { SpaceIpo *sipo = (SpaceIpo *)t->sa->spacedata.first; @@ -3734,11 +4035,11 @@ static void createTransGraphEditData(bContext *C, TransInfo *t) BezTriple *bezt; int count = 0, i; - float cfra; float mtx[3][3], smtx[3][3]; const bool is_translation_mode = graph_edit_is_translation_mode(t); const bool use_handle = !(sipo->flag & SIPO_NOHANDLES); const bool use_local_center = graph_edit_use_local_center(t); + const bool is_prop_edit = (t->flag & T_PROP_EDIT) != 0; short anim_map_flag = ANIM_UNITCONV_ONLYSEL | ANIM_UNITCONV_SELVERTS; /* determine what type of data we are operating on */ @@ -3769,7 +4070,14 @@ static void createTransGraphEditData(bContext *C, TransInfo *t) for (ale = anim_data.first; ale; ale = ale->next) { AnimData *adt = ANIM_nla_mapping_get(&ac, ale); FCurve *fcu = (FCurve *)ale->key_data; - + float cfra; + int curvecount = 0; + bool selected = false; + + /* F-Curve may not have any keyframes */ + if (fcu->bezt == NULL) + continue; + /* convert current-frame to action-time (slightly less accurate, especially under * higher scaling ratios, but is faster than converting all points) */ @@ -3777,48 +4085,44 @@ static void createTransGraphEditData(bContext *C, TransInfo *t) cfra = BKE_nla_tweakedit_remap(adt, (float)CFRA, NLATIME_CONVERT_UNMAP); else cfra = (float)CFRA; - - /* F-Curve may not have any keyframes */ - if (fcu->bezt == NULL) - continue; - + /* only include BezTriples whose 'keyframe' occurs on the same side of the current frame as mouse */ for (i = 0, bezt = fcu->bezt; i < fcu->totvert; i++, bezt++) { if (FrameOnMouseSide(t->frame_side, bezt->vec[1][0], cfra)) { - const bool sel2 = bezt->f2 & SELECT; - const bool sel1 = use_handle ? bezt->f1 & SELECT : sel2; - const bool sel3 = use_handle ? bezt->f3 & SELECT : sel2; + const bool sel2 = (bezt->f2 & SELECT) != 0; + const bool sel1 = use_handle ? (bezt->f1 & SELECT) != 0 : sel2; + const bool sel3 = use_handle ? (bezt->f3 & SELECT) != 0 : sel2; + + if (is_prop_edit) { + curvecount += 3; + if (sel2 || sel1 || sel3) + selected = true; + } + else { + if (!is_translation_mode || !(sel2)) { + if (sel1) { + count++; + } - if (is_translation_mode) { - /* for 'normal' pivots - just include anything that is selected. - * this works a bit differently in translation modes */ - if (sel2) { - count++; - } - else { - if (sel1) count++; - if (sel3) count++; + if (sel3) { + count++; + } } - } - else if (use_local_center) { - /* for local-pivot we only need to count the number of selected handles only, - * so that centerpoints don't get moved wrong - */ - if (bezt->ipo == BEZT_IPO_BEZ) { - if (sel1) count++; - if (sel3) count++; + + /* only include main vert if selected */ + if (sel2 && !use_local_center) { + count++; } - /* else if (sel2) count++; // TODO: could this cause problems? */ - /* - yes this causes problems, because no td is created for the center point */ - } - else { - /* for 'normal' pivots - just include anything that is selected */ - if (sel1) count++; - if (sel2) count++; - if (sel3) count++; } } } + + if (is_prop_edit) { + if (selected) { + count += curvecount; + ale->tag = true; + } + } } /* stop if trying to build list if nothing selected */ @@ -3864,8 +4168,13 @@ static void createTransGraphEditData(bContext *C, TransInfo *t) for (ale = anim_data.first; ale; ale = ale->next) { AnimData *adt = ANIM_nla_mapping_get(&ac, ale); FCurve *fcu = (FCurve *)ale->key_data; - bool intvals = (fcu->flag & FCURVE_INT_VALUES); - float unit_scale; + bool intvals = (fcu->flag & FCURVE_INT_VALUES) != 0; + float unit_scale, offset; + float cfra; + + /* F-Curve may not have any keyframes */ + if (fcu->bezt == NULL || (is_prop_edit && ale->tag == 0)) + continue; /* convert current-frame to action-time (slightly less accurate, especially under * higher scaling ratios, but is faster than converting all points) @@ -3874,83 +4183,148 @@ static void createTransGraphEditData(bContext *C, TransInfo *t) cfra = BKE_nla_tweakedit_remap(adt, (float)CFRA, NLATIME_CONVERT_UNMAP); else cfra = (float)CFRA; - - /* F-Curve may not have any keyframes */ - if (fcu->bezt == NULL) - continue; - - unit_scale = ANIM_unit_mapping_get_factor(ac.scene, ale->id, ale->key_data, anim_map_flag); + + unit_scale = ANIM_unit_mapping_get_factor(ac.scene, ale->id, ale->key_data, anim_map_flag, &offset); /* only include BezTriples whose 'keyframe' occurs on the same side of the current frame as mouse (if applicable) */ for (i = 0, bezt = fcu->bezt; i < fcu->totvert; i++, bezt++) { if (FrameOnMouseSide(t->frame_side, bezt->vec[1][0], cfra)) { - const bool sel2 = bezt->f2 & SELECT; - const bool sel1 = use_handle ? bezt->f1 & SELECT : sel2; - const bool sel3 = use_handle ? bezt->f3 & SELECT : sel2; + const bool sel2 = (bezt->f2 & SELECT) != 0; + const bool sel1 = use_handle ? (bezt->f1 & SELECT) != 0 : sel2; + const bool sel3 = use_handle ? (bezt->f3 & SELECT) != 0 : sel2; TransDataCurveHandleFlags *hdata = NULL; /* short h1=1, h2=1; */ /* UNUSED */ - /* only include handles if selected, irrespective of the interpolation modes. - * also, only treat handles specially if the center point isn't selected. - */ - if (!is_translation_mode || !(sel2)) { - if (sel1) { - hdata = initTransDataCurveHandles(td, bezt); - bezt_to_transdata(td++, td2d++, tdg++, adt, bezt, 0, sel1, true, intvals, mtx, smtx, unit_scale); + if (is_prop_edit) { + bool is_sel = (sel2 || sel1 || sel3); + /* we always select all handles for proportional editing if central handle is selected */ + initTransDataCurveHandles(td, bezt); + bezt_to_transdata(td++, td2d++, tdg++, adt, bezt, 0, is_sel, true, intvals, mtx, smtx, unit_scale, offset); + initTransDataCurveHandles(td, bezt); + bezt_to_transdata(td++, td2d++, tdg++, adt, bezt, 1, is_sel, false, intvals, mtx, smtx, unit_scale, offset); + initTransDataCurveHandles(td, bezt); + bezt_to_transdata(td++, td2d++, tdg++, adt, bezt, 2, is_sel, true, intvals, mtx, smtx, unit_scale, offset); + } + else { + /* only include handles if selected, irrespective of the interpolation modes. + * also, only treat handles specially if the center point isn't selected. + */ + if (!is_translation_mode || !(sel2)) { + if (sel1) { + hdata = initTransDataCurveHandles(td, bezt); + bezt_to_transdata(td++, td2d++, tdg++, adt, bezt, 0, sel1, true, intvals, mtx, smtx, unit_scale, offset); + } + else { + /* h1 = 0; */ /* UNUSED */ + } + + if (sel3) { + if (hdata == NULL) + hdata = initTransDataCurveHandles(td, bezt); + bezt_to_transdata(td++, td2d++, tdg++, adt, bezt, 2, sel3, true, intvals, mtx, smtx, unit_scale, offset); + } + else { + /* h2 = 0; */ /* UNUSED */ + } } - else { - /* h1 = 0; */ /* UNUSED */ + + /* only include main vert if selected */ + if (sel2 && !use_local_center) { + /* move handles relative to center */ + if (is_translation_mode) { + if (sel1) td->flag |= TD_MOVEHANDLE1; + if (sel3) td->flag |= TD_MOVEHANDLE2; + } + + /* if handles were not selected, store their selection status */ + if (!(sel1) || !(sel3)) { + if (hdata == NULL) + hdata = initTransDataCurveHandles(td, bezt); + } + + bezt_to_transdata(td++, td2d++, tdg++, adt, bezt, 1, sel2, false, intvals, mtx, smtx, unit_scale, offset); + } - - if (sel3) { - if (hdata == NULL) - hdata = initTransDataCurveHandles(td, bezt); - bezt_to_transdata(td++, td2d++, tdg++, adt, bezt, 2, sel3, true, intvals, mtx, smtx, unit_scale); + /* special hack (must be done after initTransDataCurveHandles(), as that stores handle settings to restore...): + * - Check if we've got entire BezTriple selected and we're scaling/rotating that point, + * then check if we're using auto-handles. + * - If so, change them auto-handles to aligned handles so that handles get affected too + */ + if (ELEM(bezt->h1, HD_AUTO, HD_AUTO_ANIM) && + ELEM(bezt->h2, HD_AUTO, HD_AUTO_ANIM) && + ELEM(t->mode, TFM_ROTATION, TFM_RESIZE)) + { + if (hdata && (sel1) && (sel3)) { + bezt->h1 = HD_ALIGN; + bezt->h2 = HD_ALIGN; + } + } + } + } + } + + /* Sets handles based on the selection */ + testhandles_fcurve(fcu, use_handle); + } + + if (is_prop_edit) { + /* loop 2: build transdata arrays */ + td = t->data; + + for (ale = anim_data.first; ale; ale = ale->next) { + AnimData *adt = ANIM_nla_mapping_get(&ac, ale); + FCurve *fcu = (FCurve *)ale->key_data; + TransData *td_start = td; + float cfra; + + /* F-Curve may not have any keyframes */ + if (fcu->bezt == NULL || (ale->tag == 0)) + continue; + + /* convert current-frame to action-time (slightly less accurate, especially under + * higher scaling ratios, but is faster than converting all points) + */ + if (adt) + cfra = BKE_nla_tweakedit_remap(adt, (float)CFRA, NLATIME_CONVERT_UNMAP); + else + cfra = (float)CFRA; + + /* only include BezTriples whose 'keyframe' occurs on the same side of the current frame as mouse (if applicable) */ + for (i = 0, bezt = fcu->bezt; i < fcu->totvert; i++, bezt++) { + if (FrameOnMouseSide(t->frame_side, bezt->vec[1][0], cfra)) { + const bool sel2 = (bezt->f2 & SELECT) != 0; + const bool sel1 = use_handle ? (bezt->f1 & SELECT) != 0 : sel2; + const bool sel3 = use_handle ? (bezt->f3 & SELECT) != 0 : sel2; + + if (sel1 || sel2) { + td->dist = td->rdist = 0.0f; } else { - /* h2 = 0; */ /* UNUSED */ + graph_key_shortest_dist(t, fcu, td_start, td, cfra, use_handle); } - } - - /* only include main vert if selected */ - if (sel2 && !use_local_center) { - /* move handles relative to center */ - if (is_translation_mode) { - if (sel1) td->flag |= TD_MOVEHANDLE1; - if (sel3) td->flag |= TD_MOVEHANDLE2; + td++; + + if (sel2) { + td->dist = td->rdist = 0.0f; } - - /* if handles were not selected, store their selection status */ - if (!(sel1) || !(sel3)) { - if (hdata == NULL) - hdata = initTransDataCurveHandles(td, bezt); + else { + graph_key_shortest_dist(t, fcu, td_start, td, cfra, use_handle); } - - bezt_to_transdata(td++, td2d++, tdg++, adt, bezt, 1, sel2, false, intvals, mtx, smtx, unit_scale); - - } - /* special hack (must be done after initTransDataCurveHandles(), as that stores handle settings to restore...): - * - Check if we've got entire BezTriple selected and we're scaling/rotating that point, - * then check if we're using auto-handles. - * - If so, change them auto-handles to aligned handles so that handles get affected too - */ - if (ELEM(bezt->h1, HD_AUTO, HD_AUTO_ANIM) && - ELEM(bezt->h2, HD_AUTO, HD_AUTO_ANIM) && - ELEM(t->mode, TFM_ROTATION, TFM_RESIZE)) - { - if (hdata && (sel1) && (sel3)) { - bezt->h1 = HD_ALIGN; - bezt->h2 = HD_ALIGN; + td++; + + if (sel3 || sel2) { + td->dist = td->rdist = 0.0f; + } + else { + graph_key_shortest_dist(t, fcu, td_start, td, cfra, use_handle); } + td++; } } } - - /* Sets handles based on the selection */ - testhandles_fcurve(fcu, use_handle); } - + /* cleanup temp list */ ANIM_animdata_freelist(&anim_data); } @@ -4130,7 +4504,7 @@ void remake_graph_transdata(TransInfo *t, ListBase *anim_data) { SpaceIpo *sipo = (SpaceIpo *)t->sa->spacedata.first; bAnimListElem *ale; - const short use_handle = !(sipo->flag & SIPO_NOHANDLES); + const bool use_handle = (sipo->flag & SIPO_NOHANDLES) == 0; /* sort and reassign verts */ for (ale = anim_data->first; ale; ale = ale->next) { @@ -4234,7 +4608,7 @@ void flushTransGraphData(TransInfo *t) if (td->flag & TD_INTVALUES) td2d->loc2d[1] = floorf(td2d->loc[1] + 0.5f); else - td2d->loc2d[1] = td2d->loc[1] * inv_unit_scale; + td2d->loc2d[1] = td2d->loc[1] * inv_unit_scale - tdg->offset; if ((td->flag & TD_MOVEHANDLE1) && td2d->h1) { td2d->h1[0] = td2d->ih1[0] + td->loc[0] - td->iloc[0]; @@ -4493,10 +4867,47 @@ static int SeqToTransData_Recursive(TransInfo *t, ListBase *seqbase, TransData * } } } - return tot; } + +static void SeqTransDataBounds(TransInfo *t, ListBase *seqbase, TransSeq *ts) +{ + Sequence *seq; + int recursive, count, flag; + int max = INT32_MIN, min = INT32_MAX; + + for (seq = seqbase->first; seq; seq = seq->next) { + + /* just to get the flag since there are corner cases where this isn't totally obvious */ + SeqTransInfo(t, seq, &recursive, &count, &flag); + + /* use 'flag' which is derived from seq->flag but modified for special cases */ + if (flag & SELECT) { + if (flag & (SEQ_LEFTSEL | SEQ_RIGHTSEL)) { + if (flag & SEQ_LEFTSEL) { + min = min_ii(seq->startdisp, min); + max = max_ii(seq->startdisp, max); + } + if (flag & SEQ_RIGHTSEL) { + min = min_ii(seq->enddisp, min); + max = max_ii(seq->enddisp, max); + } + } + else { + min = min_ii(seq->startdisp, min); + max = max_ii(seq->enddisp, max); + } + } + } + + if (ts) { + ts->max = max; + ts->min = min; + } +} + + static void freeSeqData(TransInfo *t) { Editing *ed = BKE_sequencer_editing_get(t->scene, false); @@ -4659,6 +5070,8 @@ static void freeSeqData(TransInfo *t) } if ((t->customData != NULL) && (t->flag & T_FREE_CUSTOMDATA)) { + TransSeq *ts = t->customData; + MEM_freeN(ts->tdseq); MEM_freeN(t->customData); t->customData = NULL; } @@ -4678,6 +5091,8 @@ static void createTransSeqData(bContext *C, TransInfo *t) TransData *td = NULL; TransData2D *td2d = NULL; TransDataSeq *tdsq = NULL; + TransSeq *ts = NULL; + int xmouse; int count = 0; @@ -4688,12 +5103,11 @@ static void createTransSeqData(bContext *C, TransInfo *t) t->customFree = freeSeqData; + xmouse = (int)UI_view2d_region_to_view_x(v2d, t->imval[0]); + /* which side of the current frame should be allowed */ if (t->mode == TFM_TIME_EXTEND) { /* only side on which mouse is gets transformed */ - float xmouse, ymouse; - - UI_view2d_region_to_view(v2d, t->imval[0], t->imval[1], &xmouse, &ymouse); t->frame_side = (xmouse > CFRA) ? 'R' : 'L'; } else { @@ -4733,15 +5147,19 @@ static void createTransSeqData(bContext *C, TransInfo *t) return; } + t->customData = ts = MEM_mallocN(sizeof(TransSeq), "transseq"); td = t->data = MEM_callocN(t->total * sizeof(TransData), "TransSeq TransData"); td2d = t->data2d = MEM_callocN(t->total * sizeof(TransData2D), "TransSeq TransData2D"); - tdsq = t->customData = MEM_callocN(t->total * sizeof(TransDataSeq), "TransSeq TransDataSeq"); + ts->tdseq = tdsq = MEM_callocN(t->total * sizeof(TransDataSeq), "TransSeq TransDataSeq"); t->flag |= T_FREE_CUSTOMDATA; - - /* loop 2: build transdata array */ SeqToTransData_Recursive(t, ed->seqbasep, td, td2d, tdsq); + SeqTransDataBounds(t, ed->seqbasep, ts); + + /* set the snap mode based on how close the mouse is at the end/start points */ + if (abs(xmouse - ts->max) > abs(xmouse - ts->min)) + ts->snap_left = true; #undef XXX_DURIAN_ANIM_TX_HACK } @@ -4769,7 +5187,6 @@ static bool constraints_list_needinv(TransInfo *t, ListBase *list) /* (affirmative) returns for specific constraints here... */ /* constraints that require this regardless */ if (ELEM(con->type, - CONSTRAINT_TYPE_CHILDOF, CONSTRAINT_TYPE_FOLLOWPATH, CONSTRAINT_TYPE_CLAMPTO, CONSTRAINT_TYPE_OBJECTSOLVER, @@ -4779,7 +5196,14 @@ static bool constraints_list_needinv(TransInfo *t, ListBase *list) } /* constraints that require this only under special conditions */ - if (con->type == CONSTRAINT_TYPE_ROTLIKE) { + if (con->type == CONSTRAINT_TYPE_CHILDOF) { + /* ChildOf constraint only works when using all location components, see T42256. */ + bChildOfConstraint *data = (bChildOfConstraint *)con->data; + + if ((data->flag & CHILDOF_LOCX) && (data->flag & CHILDOF_LOCY) && (data->flag & CHILDOF_LOCZ)) + return true; + } + else if (con->type == CONSTRAINT_TYPE_ROTLIKE) { /* CopyRot constraint only does this when rotating, and offset is on */ bRotateLikeConstraint *data = (bRotateLikeConstraint *)con->data; @@ -4997,7 +5421,9 @@ static void set_trans_object_base_flags(TransInfo *t) } /* all recalc flags get flushed to all layers, so a layer flip later on works fine */ +#ifdef WITH_LEGACY_DEPSGRAPH DAG_scene_flush_update(G.main, t->scene, -1, 0); +#endif /* and we store them temporal in base (only used for transform code) */ /* this because after doing updates, the object->recalc is cleared */ @@ -5074,7 +5500,9 @@ static int count_proportional_objects(TransInfo *t) /* all recalc flags get flushed to all layers, so a layer flip later on works fine */ DAG_scene_relations_update(G.main, t->scene); +#ifdef WITH_LEGACY_DEPSGRAPH DAG_scene_flush_update(G.main, t->scene, -1, 0); +#endif /* and we store them temporal in base (only used for transform code) */ /* this because after doing updates, the object->recalc is cleared */ @@ -5271,7 +5699,7 @@ void autokeyframe_pose_cb_func(bContext *C, Scene *scene, View3D *v3d, Object *o /* only if bone name matches too... * NOTE: this will do constraints too, but those are ok to do here too? */ - if (pchanName && strcmp(pchanName, pchan->name) == 0) + if (pchanName && STREQ(pchanName, pchan->name)) insert_keyframe(reports, id, act, ((fcu->grp) ? (fcu->grp->name) : (NULL)), fcu->rna_path, fcu->array_index, cfra, flag); if (pchanName) MEM_freeN(pchanName); @@ -5514,6 +5942,12 @@ void special_aftertrans_update(bContext *C, TransInfo *t) * during cleanup - psy-fi */ freeEdgeSlideTempFaces(sld); } + else if (t->mode == TFM_VERT_SLIDE) { + /* as above */ + VertSlideData *sld = t->customData; + projectVertSlideData(t, true); + freeVertSlideTempFaces(sld); + } if (t->obedit->type == OB_MESH) { special_aftertrans_update__mesh(C, t); @@ -5526,12 +5960,21 @@ void special_aftertrans_update(bContext *C, TransInfo *t) sld->perc = 0.0; projectEdgeSlideData(t, false); } + else if (t->mode == TFM_VERT_SLIDE) { + VertSlideData *sld = t->customData; + + sld->perc = 0.0; + projectVertSlideData(t, false); + } } } } - if (t->spacetype == SPACE_SEQ) { + if (t->options & CTX_GPENCIL_STROKES) { + /* pass */ + } + else if (t->spacetype == SPACE_SEQ) { /* freeSeqData in transform_conversions.c does this * keep here so the else at the end wont run... */ @@ -5714,7 +6157,7 @@ void special_aftertrans_update(bContext *C, TransInfo *t) else if (t->spacetype == SPACE_IPO) { SpaceIpo *sipo = (SpaceIpo *)t->sa->spacedata.first; bAnimContext ac; - const short use_handle = !(sipo->flag & SIPO_NOHANDLES); + const bool use_handle = (sipo->flag & SIPO_NOHANDLES) == 0; /* initialize relevant anim-context 'context' data */ if (ANIM_animdata_get_context(C, &ac) == 0) @@ -5941,7 +6384,7 @@ static void createTransObject(bContext *C, TransInfo *t) TransData *td = NULL; TransDataExtension *tx; - int propmode = t->flag & T_PROP_EDIT; + const bool is_prop_edit = (t->flag & T_PROP_EDIT) != 0; set_trans_object_base_flags(t); @@ -5954,7 +6397,7 @@ static void createTransObject(bContext *C, TransInfo *t) return; } - if (propmode) { + if (is_prop_edit) { t->total += count_proportional_objects(t); } @@ -5987,7 +6430,7 @@ static void createTransObject(bContext *C, TransInfo *t) } CTX_DATA_END; - if (propmode) { + if (is_prop_edit) { View3D *v3d = t->view; Base *base; @@ -6141,22 +6584,22 @@ typedef struct TransDataTracking { static void markerToTransDataInit(TransData *td, TransData2D *td2d, TransDataTracking *tdt, MovieTrackingTrack *track, MovieTrackingMarker *marker, - int area, float loc[2], float rel[2], const float off[2], float aspx, float aspy) + int area, float loc[2], float rel[2], const float off[2], const float aspect[2]) { int anchor = area == TRACK_AREA_POINT && off; tdt->mode = transDataTracking_ModeTracks; if (anchor) { - td2d->loc[0] = rel[0] * aspx; /* hold original location */ - td2d->loc[1] = rel[1] * aspy; + td2d->loc[0] = rel[0] * aspect[0]; /* hold original location */ + td2d->loc[1] = rel[1] * aspect[1]; tdt->loc = loc; td2d->loc2d = loc; /* current location */ } else { - td2d->loc[0] = loc[0] * aspx; /* hold original location */ - td2d->loc[1] = loc[1] * aspy; + td2d->loc[0] = loc[0] * aspect[0]; /* hold original location */ + td2d->loc[1] = loc[1] * aspect[1]; td2d->loc2d = loc; /* current location */ } @@ -6171,8 +6614,8 @@ static void markerToTransDataInit(TransData *td, TransData2D *td2d, TransDataTra if (rel) { if (!anchor) { - td2d->loc[0] += rel[0] * aspx; - td2d->loc[1] += rel[1] * aspy; + td2d->loc[0] += rel[0] * aspect[0]; + td2d->loc[1] += rel[1] * aspect[1]; } copy_v2_v2(tdt->srelative, rel); @@ -6187,8 +6630,8 @@ static void markerToTransDataInit(TransData *td, TransData2D *td2d, TransDataTra //copy_v3_v3(td->center, td->loc); td->flag |= TD_INDIVIDUAL_SCALE; - td->center[0] = marker->pos[0] * aspx; - td->center[1] = marker->pos[1] * aspy; + td->center[0] = marker->pos[0] * aspect[0]; + td->center[1] = marker->pos[1] * aspect[1]; memset(td->axismtx, 0, sizeof(td->axismtx)); td->axismtx[2][2] = 1.0f; @@ -6203,8 +6646,9 @@ static void markerToTransDataInit(TransData *td, TransData2D *td2d, TransDataTra unit_m3(td->smtx); } -static void trackToTransData(const int framenr, TransData *td, TransData2D *td2d, - TransDataTracking *tdt, MovieTrackingTrack *track, float aspx, float aspy) +static void trackToTransData( + const int framenr, TransData *td, TransData2D *td2d, + TransDataTracking *tdt, MovieTrackingTrack *track, const float aspect[2]) { MovieTrackingMarker *marker = BKE_tracking_marker_ensure(track, framenr); @@ -6212,11 +6656,11 @@ static void trackToTransData(const int framenr, TransData *td, TransData2D *td2d marker->flag &= ~(MARKER_DISABLED | MARKER_TRACKED); markerToTransDataInit(td++, td2d++, tdt++, track, marker, TRACK_AREA_POINT, - track->offset, marker->pos, track->offset, aspx, aspy); + track->offset, marker->pos, track->offset, aspect); if (track->flag & SELECT) { markerToTransDataInit(td++, td2d++, tdt++, track, marker, TRACK_AREA_POINT, - marker->pos, NULL, NULL, aspx, aspy); + marker->pos, NULL, NULL, aspect); } if (track->pat_flag & SELECT) { @@ -6224,28 +6668,28 @@ static void trackToTransData(const int framenr, TransData *td, TransData2D *td2d for (a = 0; a < 4; a++) { markerToTransDataInit(td++, td2d++, tdt++, track, marker, TRACK_AREA_PAT, - marker->pattern_corners[a], marker->pos, NULL, aspx, aspy); + marker->pattern_corners[a], marker->pos, NULL, aspect); } } if (track->search_flag & SELECT) { markerToTransDataInit(td++, td2d++, tdt++, track, marker, TRACK_AREA_SEARCH, - marker->search_min, marker->pos, NULL, aspx, aspy); + marker->search_min, marker->pos, NULL, aspect); markerToTransDataInit(td++, td2d++, tdt++, track, marker, TRACK_AREA_SEARCH, - marker->search_max, marker->pos, NULL, aspx, aspy); + marker->search_max, marker->pos, NULL, aspect); } } static void planeMarkerToTransDataInit(TransData *td, TransData2D *td2d, TransDataTracking *tdt, MovieTrackingPlaneTrack *plane_track, float corner[2], - float aspx, float aspy) + const float aspect[2]) { tdt->mode = transDataTracking_ModePlaneTracks; tdt->plane_track = plane_track; - td2d->loc[0] = corner[0] * aspx; /* hold original location */ - td2d->loc[1] = corner[1] * aspy; + td2d->loc[0] = corner[0] * aspect[0]; /* hold original location */ + td2d->loc[1] = corner[1] * aspect[1]; td2d->loc2d = corner; /* current location */ td2d->loc[2] = 0.0f; @@ -6270,7 +6714,7 @@ static void planeMarkerToTransDataInit(TransData *td, TransData2D *td2d, TransDa static void planeTrackToTransData(const int framenr, TransData *td, TransData2D *td2d, TransDataTracking *tdt, MovieTrackingPlaneTrack *plane_track, - float aspx, float aspy) + const float aspect[2]) { MovieTrackingPlaneMarker *plane_marker = BKE_tracking_plane_marker_ensure(plane_track, framenr); int i; @@ -6279,7 +6723,7 @@ static void planeTrackToTransData(const int framenr, TransData *td, TransData2D plane_marker->flag &= ~PLANE_MARKER_TRACKED; for (i = 0; i < 4; i++) { - planeMarkerToTransDataInit(td++, td2d++, tdt++, plane_track, plane_marker->corners[i], aspx, aspy); + planeMarkerToTransDataInit(td++, td2d++, tdt++, plane_track, plane_marker->corners[i], aspect); } } @@ -6308,7 +6752,6 @@ static void createTransTrackingTracksData(bContext *C, TransInfo *t) MovieTrackingPlaneTrack *plane_track; TransDataTracking *tdt; int framenr = ED_space_clip_get_clip_frame_number(sc); - float aspx, aspy; /* count */ t->total = 0; @@ -6343,8 +6786,6 @@ static void createTransTrackingTracksData(bContext *C, TransInfo *t) if (t->total == 0) return; - ED_space_clip_get_aspect_dimension_aware(sc, &aspx, &aspy); - td = t->data = MEM_callocN(t->total * sizeof(TransData), "TransTracking TransData"); td2d = t->data2d = MEM_callocN(t->total * sizeof(TransData2D), "TransTracking TransData2D"); tdt = t->customData = MEM_callocN(t->total * sizeof(TransDataTracking), "TransTracking TransDataTracking"); @@ -6355,7 +6796,7 @@ static void createTransTrackingTracksData(bContext *C, TransInfo *t) track = tracksbase->first; while (track) { if (TRACK_VIEW_SELECTED(sc, track) && (track->flag & TRACK_LOCKED) == 0) { - trackToTransData(framenr, td, td2d, tdt, track, aspx, aspy); + trackToTransData(framenr, td, td2d, tdt, track, t->aspect); /* offset */ td++; @@ -6389,7 +6830,7 @@ static void createTransTrackingTracksData(bContext *C, TransInfo *t) plane_track = plane_track->next) { if (PLANE_TRACK_VIEW_SELECTED(plane_track)) { - planeTrackToTransData(framenr, td, td2d, tdt, plane_track, aspx, aspy); + planeTrackToTransData(framenr, td, td2d, tdt, plane_track, t->aspect); td += 4; td2d += 4; tdt += 4; @@ -6601,14 +7042,10 @@ static void cancelTransTracking(TransInfo *t) void flushTransTracking(TransInfo *t) { - SpaceClip *sc = t->sa->spacedata.first; TransData *td; TransData2D *td2d; TransDataTracking *tdt; int a; - float aspx, aspy; - - ED_space_clip_get_aspect_dimension_aware(sc, &aspx, &aspy); if (t->state == TRANS_CANCEL) cancelTransTracking(t); @@ -6622,8 +7059,8 @@ void flushTransTracking(TransInfo *t) continue; } - loc2d[0] = td2d->loc[0] / aspx; - loc2d[1] = td2d->loc[1] / aspy; + loc2d[0] = td2d->loc[0] / t->aspect[0]; + loc2d[1] = td2d->loc[1] / t->aspect[1]; if (t->flag & T_ALT_TRANSFORM) { if (t->mode == TFM_RESIZE) { @@ -6665,8 +7102,8 @@ void flushTransTracking(TransInfo *t) td2d->loc2d[tdt->coord] = tdt->prev_pos[tdt->coord] + td2d->loc[1] * tdt->scale; } else if (tdt->mode == transDataTracking_ModePlaneTracks) { - td2d->loc2d[0] = td2d->loc[0] / aspx; - td2d->loc2d[1] = td2d->loc[1] / aspy; + td2d->loc2d[0] = td2d->loc[0] / t->aspect[0]; + td2d->loc2d[1] = td2d->loc[1] / t->aspect[1]; } } } @@ -6744,9 +7181,10 @@ static void MaskHandleToTransData(MaskSplinePoint *point, eMaskWhichHandle which } } -static void MaskPointToTransData(Scene *scene, MaskSplinePoint *point, - TransData *td, TransData2D *td2d, TransDataMasking *tdm, - const int propmode, const float asp[2]) +static void MaskPointToTransData( + Scene *scene, MaskSplinePoint *point, + TransData *td, TransData2D *td2d, TransDataMasking *tdm, + const bool is_prop_edit, const float asp[2]) { BezTriple *bezt = &point->bezt; const bool is_sel_point = MASKPOINT_ISSEL_KNOT(point); @@ -6756,7 +7194,7 @@ static void MaskPointToTransData(Scene *scene, MaskSplinePoint *point, BKE_mask_point_parent_matrix_get(point, CFRA, parent_matrix); invert_m3_m3(parent_inverse_matrix, parent_matrix); - if (propmode || is_sel_point) { + if (is_prop_edit || is_sel_point) { int i; tdm->point = point; @@ -6769,7 +7207,7 @@ static void MaskPointToTransData(Scene *scene, MaskSplinePoint *point, /* CV coords are scaled by aspects. this is needed for rotations and * proportional editing to be consistent with the stretched CV coords * that are displayed. this also means that for display and numinput, - * and when the the CV coords are flushed, these are converted each time */ + * and when the CV coords are flushed, these are converted each time */ mul_v2_m3v2(td2d->loc, parent_matrix, bezt->vec[i]); td2d->loc[0] *= asp[0]; td2d->loc[1] *= asp[1]; @@ -6876,7 +7314,7 @@ static void createTransMaskingData(bContext *C, TransInfo *t) TransData2D *td2d = NULL; TransDataMasking *tdm = NULL; int count = 0, countsel = 0; - int propmode = t->flag & T_PROP_EDIT; + const bool is_prop_edit = (t->flag & T_PROP_EDIT); float asp[2]; t->total = 0; @@ -6926,7 +7364,7 @@ static void createTransMaskingData(bContext *C, TransInfo *t) } } - if (propmode) + if (is_prop_edit) count += 3; } } @@ -6939,7 +7377,7 @@ static void createTransMaskingData(bContext *C, TransInfo *t) ED_mask_get_aspect(t->sa, t->ar, &asp[0], &asp[1]); - t->total = (propmode) ? count : countsel; + t->total = (is_prop_edit) ? count : countsel; td = t->data = MEM_callocN(t->total * sizeof(TransData), "TransObData(Mask Editing)"); /* for each 2d uv coord a 3d vector is allocated, so that they can be * treated just as if they were 3d verts */ @@ -6962,10 +7400,10 @@ static void createTransMaskingData(bContext *C, TransInfo *t) for (i = 0; i < spline->tot_point; i++) { MaskSplinePoint *point = &spline->points[i]; - if (propmode || MASKPOINT_ISSEL_ANY(point)) { - MaskPointToTransData(scene, point, td, td2d, tdm, propmode, asp); + if (is_prop_edit || MASKPOINT_ISSEL_ANY(point)) { + MaskPointToTransData(scene, point, td, td2d, tdm, is_prop_edit, asp); - if (propmode || MASKPOINT_ISSEL_KNOT(point)) { + if (is_prop_edit || MASKPOINT_ISSEL_KNOT(point)) { td += 3; td2d += 3; tdm += 3; @@ -7198,6 +7636,246 @@ void flushTransPaintCurve(TransInfo *t) } +static void createTransGPencil(bContext *C, TransInfo *t) +{ + bGPdata *gpd = ED_gpencil_data_get_active(C); + bGPDlayer *gpl; + TransData *td = NULL; + float mtx[3][3], smtx[3][3]; + + const Scene *scene = CTX_data_scene(C); + const int cfra = CFRA; + + const bool is_prop_edit = (t->flag & T_PROP_EDIT) != 0; + const bool is_prop_edit_connected = (t->flag & T_PROP_CONNECTED) != 0; + + + /* == Grease Pencil Strokes to Transform Data == + * Grease Pencil stroke points can be a mixture of 2D (screen-space), + * or 3D coordinates. However, they're always saved as 3D points. + * For now, we just do these without creating TransData2D for the 2D + * strokes. This may cause issues in future though. + */ + t->total = 0; + + if (gpd == NULL) + return; + + /* First Pass: Count the number of datapoints required for the strokes, + * (and additional info about the configuration - e.g. 2D/3D?) + */ + for (gpl = gpd->layers.first; gpl; gpl = gpl->next) { + /* only editable and visible layers are considered */ + if ((gpl->flag & (GP_LAYER_HIDE | GP_LAYER_LOCKED)) == 0 && + (gpl->actframe != NULL)) + { + bGPDframe *gpf = gpl->actframe; + bGPDstroke *gps; + + 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 (is_prop_edit) { + /* Proportional Editing... */ + if (is_prop_edit_connected) { + /* connected only - so only if selected */ + if (gps->flag & GP_STROKE_SELECT) + t->total += gps->totpoints; + } + else { + /* everything goes - connection status doesn't matter */ + t->total += gps->totpoints; + } + } + else { + /* only selected stroke points are considered */ + if (gps->flag & GP_STROKE_SELECT) { + bGPDspoint *pt; + int i; + + // TODO: 2D vs 3D? + for (i = 0, pt = gps->points; i < gps->totpoints; i++, pt++) { + if (pt->flag & GP_SPOINT_SELECT) + t->total++; + } + } + } + } + } + } + + /* Stop trying if nothing selected */ + if (t->total == 0) { + return; + } + + /* Allocate memory for data */ + t->data = MEM_callocN(t->total * sizeof(TransData), "TransData(GPencil)"); + td = t->data; + + unit_m3(smtx); + unit_m3(mtx); + + /* Second Pass: Build transdata array */ + for (gpl = gpd->layers.first; gpl; gpl = gpl->next) { + /* only editable and visible layers are considered */ + if ((gpl->flag & (GP_LAYER_HIDE | GP_LAYER_LOCKED)) == 0 && + (gpl->actframe != NULL)) + { + bGPDframe *gpf = gpl->actframe; + bGPDstroke *gps; + + /* Make a new frame to work on if the layer's frame and the current scene frame don't match up + * - This is useful when animating as it saves that "uh-oh" moment when you realize you've + * spent too much time editing the wrong frame... + */ + // XXX: should this be allowed when framelock is enabled? + if (gpf->framenum != cfra) { + bGPDframe *new_frame = gpencil_frame_duplicate(gpf); + bGPDframe *gf; + bool found = false; + + /* Find frame to insert it before */ + for (gf = gpf->next; gf; gf = gf->next) { + if (gf->framenum > cfra) { + /* Add it here */ + BLI_insertlinkbefore(&gpl->frames, gf, new_frame); + + found = true; + break; + } + else if (gf->framenum == cfra) { + /* This only happens when we're editing with framelock on... + * - Delete the new frame and don't do anything else here... + */ + //printf("GP Frame convert to TransData - Copy aborted for frame %d -> %d\n", gpf->framenum, gf->framenum); + free_gpencil_strokes(new_frame); + MEM_freeN(new_frame); + new_frame = NULL; + + found = true; + break; + } + } + + if (found == false) { + /* Add new frame to the end */ + BLI_addtail(&gpl->frames, new_frame); + } + + /* Edit the new frame instead, if it did get created + added */ + if (new_frame) { + // TODO: tag this one as being "newly created" so that we can remove it if the edit is cancelled + new_frame->framenum = cfra; + + gpf = new_frame; + } + } + + /* Loop over strokes, adding TransData for points as needed... */ + for (gps = gpf->strokes.first; gps; gps = gps->next) { + TransData *head = td; + TransData *tail = td; + bool stroke_ok; + + /* skip strokes that are invalid for current view */ + if (ED_gpencil_stroke_can_use(C, gps) == false) { + continue; + } + + /* What we need to include depends on proportional editing settings... */ + if (is_prop_edit) { + if (is_prop_edit_connected) { + /* A) "Connected" - Only those in selected strokes */ + stroke_ok = (gps->flag & GP_STROKE_SELECT) != 0; + } + else { + /* B) All points, always */ + stroke_ok = true; + } + } + else { + /* C) Only selected points in selected strokes */ + stroke_ok = (gps->flag & GP_STROKE_SELECT) != 0; + } + + /* Do stroke... */ + if (stroke_ok && gps->totpoints) { + bGPDspoint *pt; + int i; + +#if 0 /* XXX: this isn't needed anymore; cannot calculate center this way or is_prop_edit breaks */ + const float ninv = 1.0f / gps->totpoints; + float center[3] = {0.0f}; + + /* compute midpoint of stroke */ + for (i = 0, pt = gps->points; i < gps->totpoints; i++, pt++) { + madd_v3_v3v3fl(center, center, &pt->x, ninv); + } +#endif + + /* add all necessary points... */ + for (i = 0, pt = gps->points; i < gps->totpoints; i++, pt++) { + bool point_ok; + + /* include point? */ + if (is_prop_edit) { + /* Always all points in strokes that get included */ + point_ok = true; + } + else { + /* Only selected points in selected strokes */ + point_ok = (pt->flag & GP_SPOINT_SELECT) != 0; + } + + /* do point... */ + if (point_ok) { + copy_v3_v3(td->iloc, &pt->x); + copy_v3_v3(td->center, &pt->x); // XXX: what about t->around == local? + + td->loc = &pt->x; + + td->flag = 0; + + if (pt->flag & GP_SPOINT_SELECT) + td->flag |= TD_SELECTED; + + /* for other transform modes (e.g. shrink-fatten), need to additional data */ + if (t->mode == TFM_GPENCIL_SHRINKFATTEN) { + td->val = &pt->pressure; + td->ival = pt->pressure; + } + + /* configure 2D points so that they don't play up... */ + if (gps->flag & (GP_STROKE_2DSPACE | GP_STROKE_2DIMAGE)) { + td->protectflag = OB_LOCK_LOCZ | OB_LOCK_ROTZ | OB_LOCK_SCALEZ; + // XXX: matrices may need to be different? + } + + copy_m3_m3(td->smtx, smtx); + copy_m3_m3(td->mtx, mtx); + unit_m3(td->axismtx); // XXX? + + td++; + tail++; + } + } + + /* March over these points, and calculate the proportional editing distances */ + if (is_prop_edit && (head != tail)) { + /* XXX: for now, we are similar enough that this works... */ + calc_distanceCurveVerts(head, tail - 1); + } + } + } + } + } +} + + void createTransData(bContext *C, TransInfo *t) { Scene *scene = t->scene; @@ -7218,6 +7896,16 @@ void createTransData(bContext *C, TransInfo *t) sort_trans_data_dist(t); } } + else if (t->options & CTX_GPENCIL_STROKES) { + t->flag |= T_POINTS; // XXX... + createTransGPencil(C, t); + + if (t->data && (t->flag & T_PROP_EDIT)) { + sort_trans_data(t); // makes selected become first in array + set_prop_dist(t, 1); + sort_trans_data_dist(t); + } + } else if (t->spacetype == SPACE_IMAGE) { t->flag |= T_POINTS | T_2D_EDIT; if (t->options & CTX_MASK) { @@ -7246,6 +7934,12 @@ void createTransData(bContext *C, TransInfo *t) else if (t->spacetype == SPACE_ACTION) { t->flag |= T_POINTS | T_2D_EDIT; createTransActionData(C, t); + + if (t->data && (t->flag & T_PROP_EDIT)) { + sort_trans_data(t); // makes selected become first in array + //set_prop_dist(t, false); /* don't do that, distance has been set in createTransActionData already */ + sort_trans_data_dist(t); + } } else if (t->spacetype == SPACE_NLA) { t->flag |= T_POINTS | T_2D_EDIT; @@ -7259,13 +7953,12 @@ void createTransData(bContext *C, TransInfo *t) else if (t->spacetype == SPACE_IPO) { t->flag |= T_POINTS | T_2D_EDIT; createTransGraphEditData(C, t); -#if 0 + if (t->data && (t->flag & T_PROP_EDIT)) { sort_trans_data(t); // makes selected become first in array - set_prop_dist(t, 1); + set_prop_dist(t, false); /* don't do that, distance has been set in createTransGraphEditData already */ sort_trans_data_dist(t); } -#endif } else if (t->spacetype == SPACE_NODE) { t->flag |= T_POINTS | T_2D_EDIT; |