457 lines
14 KiB
C
457 lines
14 KiB
C
/* SPDX-License-Identifier: GPL-2.0-or-later
|
|
* Copyright 2001-2002 NaN Holding BV. All rights reserved. */
|
|
|
|
/** \file
|
|
* \ingroup edtransform
|
|
*/
|
|
|
|
#include "DNA_curve_types.h"
|
|
|
|
#include "MEM_guardedalloc.h"
|
|
|
|
#include "BLI_listbase.h"
|
|
#include "BLI_math.h"
|
|
|
|
#include "BKE_context.h"
|
|
#include "BKE_curve.h"
|
|
|
|
#include "transform.h"
|
|
#include "transform_snap.h"
|
|
|
|
/* Own include. */
|
|
#include "transform_convert.h"
|
|
#include "transform_orientations.h"
|
|
|
|
/* -------------------------------------------------------------------- */
|
|
/** \name Curve/Surfaces Transform Creation
|
|
* \{ */
|
|
|
|
/**
|
|
* For the purpose of transform code we need to behave as if handles are selected,
|
|
* even when they aren't (see special case below).
|
|
*/
|
|
static int bezt_select_to_transform_triple_flag(const BezTriple *bezt, const bool hide_handles)
|
|
{
|
|
int flag = 0;
|
|
|
|
if (hide_handles) {
|
|
if (bezt->f2 & SELECT) {
|
|
flag = (1 << 0) | (1 << 1) | (1 << 2);
|
|
}
|
|
}
|
|
else {
|
|
flag = (((bezt->f1 & SELECT) ? (1 << 0) : 0) | ((bezt->f2 & SELECT) ? (1 << 1) : 0) |
|
|
((bezt->f3 & SELECT) ? (1 << 2) : 0));
|
|
}
|
|
|
|
/* Special case for auto & aligned handles:
|
|
* When a center point is being moved without the handles,
|
|
* leaving the handles stationary makes no sense and only causes strange behavior,
|
|
* where one handle is arbitrarily anchored, the other one is aligned and lengthened
|
|
* based on where the center point is moved. Also a bug when canceling, see: #52007.
|
|
*
|
|
* A more 'correct' solution could be to store handle locations in 'TransDataCurveHandleFlags'.
|
|
* However that doesn't resolve odd behavior, so best transform the handles in this case.
|
|
*/
|
|
if ((flag != ((1 << 0) | (1 << 1) | (1 << 2))) && (flag & (1 << 1))) {
|
|
if (ELEM(bezt->h1, HD_AUTO, HD_ALIGN) && ELEM(bezt->h2, HD_AUTO, HD_ALIGN)) {
|
|
flag = (1 << 0) | (1 << 1) | (1 << 2);
|
|
}
|
|
}
|
|
|
|
return flag;
|
|
}
|
|
|
|
static void createTransCurveVerts(bContext *UNUSED(C), TransInfo *t)
|
|
{
|
|
|
|
#define SEL_F1 (1 << 0)
|
|
#define SEL_F2 (1 << 1)
|
|
#define SEL_F3 (1 << 2)
|
|
|
|
t->data_len_all = 0;
|
|
|
|
/* Count control points (one per #BezTriple) if any number of handles are selected.
|
|
* Needed for #transform_around_single_fallback_ex. */
|
|
int data_len_all_pt = 0;
|
|
|
|
FOREACH_TRANS_DATA_CONTAINER (t, tc) {
|
|
Curve *cu = tc->obedit->data;
|
|
BLI_assert(cu->editnurb != NULL);
|
|
BezTriple *bezt;
|
|
BPoint *bp;
|
|
int a;
|
|
int count = 0, countsel = 0;
|
|
int count_pt = 0, countsel_pt = 0;
|
|
const bool is_prop_edit = (t->flag & T_PROP_EDIT) != 0;
|
|
const bool is_prop_connected = (t->flag & T_PROP_CONNECTED) != 0;
|
|
View3D *v3d = t->view;
|
|
short hide_handles = (v3d != NULL) ? (v3d->overlay.handle_display == CURVE_HANDLE_NONE) :
|
|
false;
|
|
|
|
/* count total of vertices, check identical as in 2nd loop for making transdata! */
|
|
ListBase *nurbs = BKE_curve_editNurbs_get(cu);
|
|
LISTBASE_FOREACH (Nurb *, nu, nurbs) {
|
|
if (nu->type == CU_BEZIER) {
|
|
for (a = 0, bezt = nu->bezt; a < nu->pntsu; a++, bezt++) {
|
|
if (bezt->hide == 0) {
|
|
const int bezt_tx = bezt_select_to_transform_triple_flag(bezt, hide_handles);
|
|
if (bezt_tx & (SEL_F1 | SEL_F2 | SEL_F3)) {
|
|
if (bezt_tx & SEL_F1) {
|
|
countsel++;
|
|
}
|
|
if (bezt_tx & SEL_F2) {
|
|
countsel++;
|
|
}
|
|
if (bezt_tx & SEL_F3) {
|
|
countsel++;
|
|
}
|
|
countsel_pt++;
|
|
}
|
|
if (is_prop_edit) {
|
|
count += 3;
|
|
count_pt++;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
else {
|
|
for (a = nu->pntsu * nu->pntsv, bp = nu->bp; a > 0; a--, bp++) {
|
|
if (bp->hide == 0) {
|
|
if (bp->f1 & SELECT) {
|
|
countsel++;
|
|
countsel_pt++;
|
|
}
|
|
if (is_prop_edit) {
|
|
count++;
|
|
count_pt++;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/* Support other objects using proportional editing to adjust these, unless connected is
|
|
* enabled. */
|
|
if (((is_prop_edit && !is_prop_connected) ? count : countsel) == 0) {
|
|
tc->data_len = 0;
|
|
continue;
|
|
}
|
|
|
|
int data_len_pt = 0;
|
|
|
|
if (is_prop_edit) {
|
|
tc->data_len = count;
|
|
data_len_pt = count_pt;
|
|
}
|
|
else {
|
|
tc->data_len = countsel;
|
|
data_len_pt = countsel_pt;
|
|
}
|
|
tc->data = MEM_callocN(tc->data_len * sizeof(TransData), "TransObData(Curve EditMode)");
|
|
|
|
t->data_len_all += tc->data_len;
|
|
data_len_all_pt += data_len_pt;
|
|
}
|
|
|
|
transform_around_single_fallback_ex(t, data_len_all_pt);
|
|
t->data_len_all = -1;
|
|
|
|
FOREACH_TRANS_DATA_CONTAINER (t, tc) {
|
|
if (tc->data_len == 0) {
|
|
continue;
|
|
}
|
|
|
|
Curve *cu = tc->obedit->data;
|
|
BezTriple *bezt;
|
|
BPoint *bp;
|
|
int a;
|
|
const bool is_prop_edit = (t->flag & T_PROP_EDIT) != 0;
|
|
View3D *v3d = t->view;
|
|
short hide_handles = (v3d != NULL) ? (v3d->overlay.handle_display == CURVE_HANDLE_NONE) :
|
|
false;
|
|
|
|
bool use_around_origins_for_handles_test = ((t->around == V3D_AROUND_LOCAL_ORIGINS) &&
|
|
transform_mode_use_local_origins(t));
|
|
float mtx[3][3], smtx[3][3];
|
|
|
|
copy_m3_m4(mtx, tc->obedit->object_to_world);
|
|
pseudoinverse_m3_m3(smtx, mtx, PSEUDOINVERSE_EPSILON);
|
|
|
|
TransData *td = tc->data;
|
|
ListBase *nurbs = BKE_curve_editNurbs_get(cu);
|
|
LISTBASE_FOREACH (Nurb *, nu, nurbs) {
|
|
TransData *head, *tail;
|
|
head = tail = td;
|
|
bool has_any_selected = false;
|
|
if (nu->type == CU_BEZIER) {
|
|
for (a = 0, bezt = nu->bezt; a < nu->pntsu; a++, bezt++) {
|
|
if (bezt->hide == 0) {
|
|
TransDataCurveHandleFlags *hdata = NULL;
|
|
float axismtx[3][3];
|
|
|
|
if (t->around == V3D_AROUND_LOCAL_ORIGINS) {
|
|
float normal[3], plane[3];
|
|
|
|
BKE_nurb_bezt_calc_normal(nu, bezt, normal);
|
|
BKE_nurb_bezt_calc_plane(nu, bezt, plane);
|
|
|
|
if (createSpaceNormalTangent(axismtx, normal, plane)) {
|
|
/* pass */
|
|
}
|
|
else {
|
|
normalize_v3(normal);
|
|
axis_dominant_v3_to_m3(axismtx, normal);
|
|
invert_m3(axismtx);
|
|
}
|
|
}
|
|
|
|
/* Elements that will be transform (not always a match to selection). */
|
|
const int bezt_tx = bezt_select_to_transform_triple_flag(bezt, hide_handles);
|
|
has_any_selected |= bezt_tx != 0;
|
|
|
|
if (is_prop_edit || bezt_tx & SEL_F1) {
|
|
copy_v3_v3(td->iloc, bezt->vec[0]);
|
|
td->loc = bezt->vec[0];
|
|
copy_v3_v3(td->center,
|
|
bezt->vec[(hide_handles || (t->around == V3D_AROUND_LOCAL_ORIGINS) ||
|
|
(bezt->f2 & SELECT)) ?
|
|
1 :
|
|
0]);
|
|
if (hide_handles) {
|
|
if (bezt->f2 & SELECT) {
|
|
td->flag = TD_SELECTED;
|
|
}
|
|
else {
|
|
td->flag = 0;
|
|
}
|
|
}
|
|
else {
|
|
if (bezt->f1 & SELECT) {
|
|
td->flag = TD_SELECTED;
|
|
}
|
|
else {
|
|
td->flag = 0;
|
|
}
|
|
}
|
|
td->ext = NULL;
|
|
td->val = NULL;
|
|
|
|
hdata = initTransDataCurveHandles(td, bezt);
|
|
|
|
copy_m3_m3(td->smtx, smtx);
|
|
copy_m3_m3(td->mtx, mtx);
|
|
if (t->around == V3D_AROUND_LOCAL_ORIGINS) {
|
|
copy_m3_m3(td->axismtx, axismtx);
|
|
}
|
|
|
|
td++;
|
|
tail++;
|
|
}
|
|
|
|
/* This is the Curve Point, the other two are handles */
|
|
if (is_prop_edit || bezt_tx & SEL_F2) {
|
|
copy_v3_v3(td->iloc, bezt->vec[1]);
|
|
td->loc = bezt->vec[1];
|
|
copy_v3_v3(td->center, td->loc);
|
|
if (bezt->f2 & SELECT) {
|
|
td->flag = TD_SELECTED;
|
|
}
|
|
else {
|
|
td->flag = 0;
|
|
}
|
|
td->ext = NULL;
|
|
|
|
/* TODO: make points scale. */
|
|
if (t->mode == TFM_CURVE_SHRINKFATTEN /* `|| t->mode == TFM_RESIZE` */) {
|
|
td->val = &(bezt->radius);
|
|
td->ival = bezt->radius;
|
|
}
|
|
else if (t->mode == TFM_TILT) {
|
|
td->val = &(bezt->tilt);
|
|
td->ival = bezt->tilt;
|
|
}
|
|
else {
|
|
td->val = NULL;
|
|
}
|
|
|
|
copy_m3_m3(td->smtx, smtx);
|
|
copy_m3_m3(td->mtx, mtx);
|
|
if (t->around == V3D_AROUND_LOCAL_ORIGINS) {
|
|
copy_m3_m3(td->axismtx, axismtx);
|
|
}
|
|
|
|
if ((bezt_tx & SEL_F1) == 0 && (bezt_tx & SEL_F3) == 0) {
|
|
/* If the middle is selected but the sides aren't, this is needed. */
|
|
if (hdata == NULL) {
|
|
/* if the handle was not saved by the previous handle */
|
|
hdata = initTransDataCurveHandles(td, bezt);
|
|
}
|
|
}
|
|
|
|
td++;
|
|
tail++;
|
|
}
|
|
if (is_prop_edit || bezt_tx & SEL_F3) {
|
|
copy_v3_v3(td->iloc, bezt->vec[2]);
|
|
td->loc = bezt->vec[2];
|
|
copy_v3_v3(td->center,
|
|
bezt->vec[(hide_handles || (t->around == V3D_AROUND_LOCAL_ORIGINS) ||
|
|
(bezt->f2 & SELECT)) ?
|
|
1 :
|
|
2]);
|
|
if (hide_handles) {
|
|
if (bezt->f2 & SELECT) {
|
|
td->flag = TD_SELECTED;
|
|
}
|
|
else {
|
|
td->flag = 0;
|
|
}
|
|
}
|
|
else {
|
|
if (bezt->f3 & SELECT) {
|
|
td->flag = TD_SELECTED;
|
|
}
|
|
else {
|
|
td->flag = 0;
|
|
}
|
|
}
|
|
td->ext = NULL;
|
|
td->val = NULL;
|
|
|
|
if (hdata == NULL) {
|
|
/* if the handle was not saved by the previous handle */
|
|
hdata = initTransDataCurveHandles(td, bezt);
|
|
}
|
|
|
|
copy_m3_m3(td->smtx, smtx);
|
|
copy_m3_m3(td->mtx, mtx);
|
|
if (t->around == V3D_AROUND_LOCAL_ORIGINS) {
|
|
copy_m3_m3(td->axismtx, axismtx);
|
|
}
|
|
|
|
td++;
|
|
tail++;
|
|
}
|
|
|
|
(void)hdata; /* quiet warning */
|
|
}
|
|
}
|
|
}
|
|
else {
|
|
for (a = nu->pntsu * nu->pntsv, bp = nu->bp; a > 0; a--, bp++) {
|
|
if (bp->hide == 0) {
|
|
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);
|
|
if (bp->f1 & SELECT) {
|
|
td->flag = TD_SELECTED;
|
|
has_any_selected |= true;
|
|
}
|
|
else {
|
|
td->flag = 0;
|
|
}
|
|
td->ext = NULL;
|
|
|
|
if (ELEM(t->mode, TFM_CURVE_SHRINKFATTEN, TFM_RESIZE)) {
|
|
td->val = &(bp->radius);
|
|
td->ival = bp->radius;
|
|
}
|
|
else {
|
|
td->val = &(bp->tilt);
|
|
td->ival = bp->tilt;
|
|
}
|
|
|
|
copy_m3_m3(td->smtx, smtx);
|
|
copy_m3_m3(td->mtx, mtx);
|
|
|
|
if (t->around == V3D_AROUND_LOCAL_ORIGINS) {
|
|
if (nu->pntsv == 1) {
|
|
float normal[3], plane[3];
|
|
|
|
BKE_nurb_bpoint_calc_normal(nu, bp, normal);
|
|
BKE_nurb_bpoint_calc_plane(nu, bp, plane);
|
|
|
|
if (createSpaceNormalTangent(td->axismtx, normal, plane)) {
|
|
/* pass */
|
|
}
|
|
else {
|
|
normalize_v3(normal);
|
|
axis_dominant_v3_to_m3(td->axismtx, normal);
|
|
invert_m3(td->axismtx);
|
|
}
|
|
}
|
|
}
|
|
|
|
td++;
|
|
tail++;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
if (is_prop_edit && head != tail) {
|
|
tail -= 1;
|
|
if (!has_any_selected) {
|
|
for (td = head; td <= tail; td++) {
|
|
td->flag |= TD_NOTCONNECTED;
|
|
}
|
|
}
|
|
bool cyclic = (nu->flagu & CU_NURB_CYCLIC) != 0;
|
|
calc_distanceCurveVerts(head, tail, cyclic);
|
|
}
|
|
|
|
/* 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 ((nu->type == CU_BEZIER) &&
|
|
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, use_around_origins_for_handles_test);
|
|
}
|
|
}
|
|
}
|
|
#undef SEL_F1
|
|
#undef SEL_F2
|
|
#undef SEL_F3
|
|
}
|
|
|
|
static void recalcData_curve(TransInfo *t)
|
|
{
|
|
if (t->state != TRANS_CANCEL) {
|
|
transform_snap_project_individual_apply(t);
|
|
}
|
|
|
|
FOREACH_TRANS_DATA_CONTAINER (t, tc) {
|
|
Curve *cu = tc->obedit->data;
|
|
ListBase *nurbs = BKE_curve_editNurbs_get(cu);
|
|
Nurb *nu = nurbs->first;
|
|
|
|
DEG_id_tag_update(tc->obedit->data, ID_RECALC_GEOMETRY);
|
|
|
|
if (t->state == TRANS_CANCEL) {
|
|
while (nu) {
|
|
/* Can't do testhandlesNurb here, it messes up the h1 and h2 flags */
|
|
BKE_nurb_handles_calc(nu);
|
|
nu = nu->next;
|
|
}
|
|
}
|
|
else {
|
|
/* Apply clipping after so we never project past the clip plane #25423. */
|
|
transform_convert_clip_mirror_modifier_apply(tc);
|
|
|
|
/* Normal updating. */
|
|
BKE_curve_dimension_update(cu);
|
|
}
|
|
}
|
|
}
|
|
|
|
/** \} */
|
|
|
|
TransConvertTypeInfo TransConvertType_Curve = {
|
|
/*flags*/ (T_EDIT | T_POINTS),
|
|
/*createTransData*/ createTransCurveVerts,
|
|
/*recalcData*/ recalcData_curve,
|
|
/*special_aftertrans_update*/ NULL,
|
|
};
|