/* * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software Foundation, * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * * The Original Code is Copyright (C) 2001-2002 by NaN Holding BV. * All rights reserved. */ /** \file * \ingroup edlattice */ #include "MEM_guardedalloc.h" #include "BLI_math.h" #include "BLI_utildefines.h" #include "DNA_curve_types.h" #include "DNA_lattice_types.h" #include "DNA_object_types.h" #include "DNA_scene_types.h" #include "RNA_access.h" #include "RNA_define.h" #include "BKE_context.h" #include "BKE_lattice.h" #include "BKE_layer.h" #include "DEG_depsgraph.h" #include "ED_screen.h" #include "WM_api.h" #include "WM_types.h" #include "lattice_intern.h" /** \} */ /* -------------------------------------------------------------------- */ /** \name Make Regular Operator * \{ */ static bool make_regular_poll(bContext *C) { Object *ob; if (ED_operator_editlattice(C)) { return 1; } ob = CTX_data_active_object(C); return (ob && ob->type == OB_LATTICE); } static int make_regular_exec(bContext *C, wmOperator *UNUSED(op)) { ViewLayer *view_layer = CTX_data_view_layer(C); View3D *v3d = CTX_wm_view3d(C); const bool is_editmode = CTX_data_edit_object(C) != NULL; if (is_editmode) { uint objects_len; Object **objects = BKE_view_layer_array_from_objects_in_edit_mode_unique_data( view_layer, CTX_wm_view3d(C), &objects_len); for (uint ob_index = 0; ob_index < objects_len; ob_index++) { Object *ob = objects[ob_index]; Lattice *lt = ob->data; if (lt->editlatt->latt == NULL) { continue; } BKE_lattice_resize(lt->editlatt->latt, lt->pntsu, lt->pntsv, lt->pntsw, NULL); DEG_id_tag_update(&ob->id, ID_RECALC_GEOMETRY); WM_event_add_notifier(C, NC_GEOM | ND_DATA, ob->data); } MEM_freeN(objects); } else { FOREACH_SELECTED_OBJECT_BEGIN (view_layer, v3d, ob) { if (ob->type != OB_LATTICE) { continue; } Lattice *lt = ob->data; BKE_lattice_resize(lt, lt->pntsu, lt->pntsv, lt->pntsw, NULL); DEG_id_tag_update(&ob->id, ID_RECALC_GEOMETRY); WM_event_add_notifier(C, NC_GEOM | ND_DATA, ob->data); } FOREACH_SELECTED_OBJECT_END; } return OPERATOR_FINISHED; } void LATTICE_OT_make_regular(wmOperatorType *ot) { /* identifiers */ ot->name = "Make Regular"; ot->description = "Set UVW control points a uniform distance apart"; ot->idname = "LATTICE_OT_make_regular"; /* api callbacks */ ot->exec = make_regular_exec; ot->poll = make_regular_poll; /* flags */ ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; } /** \} */ /* -------------------------------------------------------------------- */ /** \name Flip Verts Operator * \{ */ /* flipping options */ typedef enum eLattice_FlipAxes { LATTICE_FLIP_U = 0, LATTICE_FLIP_V = 1, LATTICE_FLIP_W = 2, } eLattice_FlipAxes; /** * Flip midpoint value so that relative distances between midpoint and neighbor-pair is maintained. * Assumes that uvw <=> xyz (i.e. axis-aligned index-axes with coordinate-axes). * - Helper for #lattice_flip_exec() */ static void lattice_flip_point_value( Lattice *lt, int u, int v, int w, float mid, eLattice_FlipAxes axis) { BPoint *bp; float diff; /* just the point in the middle (unpaired) */ bp = <->def[BKE_lattice_index_from_uvw(lt, u, v, w)]; /* flip over axis */ diff = mid - bp->vec[axis]; bp->vec[axis] = mid + diff; } /** * Swap pairs of lattice points along a specified axis. * - Helper for #lattice_flip_exec() */ static void lattice_swap_point_pairs( Lattice *lt, int u, int v, int w, float mid, eLattice_FlipAxes axis) { BPoint *bpA, *bpB; int numU = lt->pntsu; int numV = lt->pntsv; int numW = lt->pntsw; int u0 = u, u1 = u; int v0 = v, v1 = v; int w0 = w, w1 = w; /* get pair index by just overriding the relevant pair-value * - "-1" else buffer overflow */ switch (axis) { case LATTICE_FLIP_U: u1 = numU - u - 1; break; case LATTICE_FLIP_V: v1 = numV - v - 1; break; case LATTICE_FLIP_W: w1 = numW - w - 1; break; } /* get points to operate on */ bpA = <->def[BKE_lattice_index_from_uvw(lt, u0, v0, w0)]; bpB = <->def[BKE_lattice_index_from_uvw(lt, u1, v1, w1)]; /* Swap all coordinates, so that flipped coordinates belong to * the indices on the correct side of the lattice. * * Coords: (-2 4) |0| (3 4) --> (3 4) |0| (-2 4) * Indices: (0,L) (1,R) --> (0,L) (1,R) */ swap_v3_v3(bpA->vec, bpB->vec); /* However, we need to mirror the coordinate values on the axis we're dealing with, * otherwise we'd have effectively only rotated the points around. If we don't do this, * we'd just be reimplementing the naive mirroring algorithm, which causes unwanted deforms * such as flipped normals, etc. * * Coords: (3 4) |0| (-2 4) --\ * \-> (-3 4) |0| (2 4) * Indices: (0,L) (1,R) --> (0,L) (1,R) */ lattice_flip_point_value(lt, u0, v0, w0, mid, axis); lattice_flip_point_value(lt, u1, v1, w1, mid, axis); } static int lattice_flip_exec(bContext *C, wmOperator *op) { ViewLayer *view_layer = CTX_data_view_layer(C); uint objects_len; bool changed = false; const eLattice_FlipAxes axis = RNA_enum_get(op->ptr, "axis"); Object **objects = BKE_view_layer_array_from_objects_in_edit_mode_unique_data( view_layer, CTX_wm_view3d(C), &objects_len); for (uint ob_index = 0; ob_index < objects_len; ob_index++) { Object *obedit = objects[ob_index]; Lattice *lt; int numU, numV, numW; int totP; float mid = 0.0f; short isOdd = 0; /* get lattice - we need the "edit lattice" from the lattice... confusing... */ lt = (Lattice *)obedit->data; lt = lt->editlatt->latt; numU = lt->pntsu; numV = lt->pntsv; numW = lt->pntsw; totP = numU * numV * numW; /* First Pass: determine midpoint - used for flipping center verts if there * are odd number of points on axis */ switch (axis) { case LATTICE_FLIP_U: isOdd = numU & 1; break; case LATTICE_FLIP_V: isOdd = numV & 1; break; case LATTICE_FLIP_W: isOdd = numW & 1; break; default: printf("lattice_flip(): Unknown flipping axis (%u)\n", axis); return OPERATOR_CANCELLED; } if (isOdd) { BPoint *bp; float avgInv = 1.0f / (float)totP; int i; /* midpoint calculation - assuming that u/v/w are axis-aligned */ for (i = 0, bp = lt->def; i < totP; i++, bp++) { mid += bp->vec[axis] * avgInv; } } /* Second Pass: swap pairs of vertices per axis, assuming they are all sorted */ switch (axis) { case LATTICE_FLIP_U: { int u, v, w; /* v/w strips - front to back, top to bottom */ for (w = 0; w < numW; w++) { for (v = 0; v < numV; v++) { /* swap coordinates of pairs of vertices on u */ for (u = 0; u < (numU / 2); u++) { lattice_swap_point_pairs(lt, u, v, w, mid, axis); } /* flip u-coordinate of midpoint (i.e. unpaired point on u) */ if (isOdd) { u = (numU / 2); lattice_flip_point_value(lt, u, v, w, mid, axis); } } } break; } case LATTICE_FLIP_V: { int u, v, w; /* u/w strips - front to back, left to right */ for (w = 0; w < numW; w++) { for (u = 0; u < numU; u++) { /* swap coordinates of pairs of vertices on v */ for (v = 0; v < (numV / 2); v++) { lattice_swap_point_pairs(lt, u, v, w, mid, axis); } /* flip v-coordinate of midpoint (i.e. unpaired point on v) */ if (isOdd) { v = (numV / 2); lattice_flip_point_value(lt, u, v, w, mid, axis); } } } break; } case LATTICE_FLIP_W: { int u, v, w; for (v = 0; v < numV; v++) { for (u = 0; u < numU; u++) { /* swap coordinates of pairs of vertices on w */ for (w = 0; w < (numW / 2); w++) { lattice_swap_point_pairs(lt, u, v, w, mid, axis); } /* flip w-coordinate of midpoint (i.e. unpaired point on w) */ if (isOdd) { w = (numW / 2); lattice_flip_point_value(lt, u, v, w, mid, axis); } } } break; } default: /* shouldn't happen, but just in case */ break; } /* updates */ DEG_id_tag_update(&obedit->id, ID_RECALC_GEOMETRY); WM_event_add_notifier(C, NC_GEOM | ND_DATA, obedit->data); changed = true; } MEM_freeN(objects); return changed ? OPERATOR_FINISHED : OPERATOR_CANCELLED; } void LATTICE_OT_flip(wmOperatorType *ot) { static const EnumPropertyItem flip_items[] = { {LATTICE_FLIP_U, "U", 0, "U (X) Axis", ""}, {LATTICE_FLIP_V, "V", 0, "V (Y) Axis", ""}, {LATTICE_FLIP_W, "W", 0, "W (Z) Axis", ""}, {0, NULL, 0, NULL, NULL}, }; /* identifiers */ ot->name = "Flip (Distortion Free)"; ot->description = "Mirror all control points without inverting the lattice deform"; ot->idname = "LATTICE_OT_flip"; /* api callbacks */ ot->poll = ED_operator_editlattice; ot->invoke = WM_menu_invoke; ot->exec = lattice_flip_exec; /* flags */ ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; /* properties */ ot->prop = RNA_def_enum(ot->srna, "axis", flip_items, LATTICE_FLIP_U, "Flip Axis", "Coordinates along this axis get flipped"); } /** \} */