Add Chromium-only Blender WebEngine parity work
This commit is contained in:
59
blender-5.2.0/source/blender/editors/uvedit/CMakeLists.txt
Normal file
59
blender-5.2.0/source/blender/editors/uvedit/CMakeLists.txt
Normal file
@@ -0,0 +1,59 @@
|
||||
# SPDX-FileCopyrightText: 2023 Blender Authors
|
||||
#
|
||||
# SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
set(INC
|
||||
../include
|
||||
../../makesrna
|
||||
../../../../intern/eigen
|
||||
# RNA_prototypes.hh
|
||||
${CMAKE_BINARY_DIR}/source/blender/makesrna
|
||||
)
|
||||
|
||||
set(INC_SYS
|
||||
)
|
||||
|
||||
set(SRC
|
||||
uvedit_buttons.cc
|
||||
uvedit_clipboard.cc
|
||||
uvedit_clipboard_graph_iso.cc
|
||||
uvedit_draw.cc
|
||||
uvedit_islands.cc
|
||||
uvedit_ops.cc
|
||||
uvedit_path.cc
|
||||
uvedit_rip.cc
|
||||
uvedit_select.cc
|
||||
uvedit_smart_stitch.cc
|
||||
uvedit_unwrap_ops.cc
|
||||
|
||||
uvedit_clipboard_graph_iso.hh
|
||||
uvedit_intern.hh
|
||||
)
|
||||
|
||||
set(LIB
|
||||
PRIVATE bf::blenkernel
|
||||
PRIVATE bf::blenlib
|
||||
PRIVATE bf::blentranslation
|
||||
PRIVATE bf::bmesh
|
||||
PRIVATE bf::depsgraph
|
||||
PRIVATE bf::dna
|
||||
PRIVATE bf::geometry
|
||||
PRIVATE bf::gpu
|
||||
PRIVATE bf::imbuf
|
||||
PRIVATE bf::intern::guardedalloc
|
||||
PRIVATE bf::nodes
|
||||
PRIVATE bf::windowmanager
|
||||
)
|
||||
|
||||
if(WITH_UV_SLIM)
|
||||
list(APPEND LIB
|
||||
bf_intern_slim
|
||||
)
|
||||
add_definitions(-DWITH_UV_SLIM)
|
||||
endif()
|
||||
|
||||
|
||||
blender_add_lib(bf_editor_uvedit "${SRC}" "${INC}" "${INC_SYS}" "${LIB}")
|
||||
|
||||
# RNA_prototypes.hh
|
||||
add_dependencies(bf_editor_uvedit bf_rna)
|
||||
261
blender-5.2.0/source/blender/editors/uvedit/uvedit_buttons.cc
Normal file
261
blender-5.2.0/source/blender/editors/uvedit/uvedit_buttons.cc
Normal file
@@ -0,0 +1,261 @@
|
||||
/* SPDX-FileCopyrightText: 2001-2002 NaN Holding BV. All rights reserved.
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
/** \file
|
||||
* \ingroup eduv
|
||||
*/
|
||||
|
||||
#include <cstring>
|
||||
|
||||
#include "MEM_guardedalloc.h"
|
||||
|
||||
#include "DNA_object_types.h"
|
||||
#include "DNA_scene_types.h"
|
||||
#include "DNA_screen_types.h"
|
||||
#include "DNA_space_types.h"
|
||||
|
||||
#include "BLI_listbase.h"
|
||||
#include "BLI_math_vector.h"
|
||||
#include "BLI_string_utf8.h"
|
||||
#include "BLI_utildefines.h"
|
||||
|
||||
#include "BLT_translation.hh"
|
||||
|
||||
#include "BKE_context.hh"
|
||||
#include "BKE_customdata.hh"
|
||||
#include "BKE_editmesh.hh"
|
||||
#include "BKE_layer.hh"
|
||||
#include "BKE_screen.hh"
|
||||
|
||||
#include "DEG_depsgraph.hh"
|
||||
|
||||
#include "ED_image.hh"
|
||||
#include "ED_uvedit.hh"
|
||||
|
||||
#include "UI_interface.hh"
|
||||
#include "UI_interface_layout.hh"
|
||||
|
||||
#include "WM_api.hh"
|
||||
#include "WM_types.hh"
|
||||
|
||||
namespace blender {
|
||||
|
||||
#define B_UVEDIT_VERTEX 3
|
||||
|
||||
/* UV Utilities */
|
||||
|
||||
static int uvedit_center(Scene *scene, const Span<Object *> objects, float center[2])
|
||||
{
|
||||
BMFace *f;
|
||||
BMLoop *l;
|
||||
BMIter iter, liter;
|
||||
float *luv;
|
||||
int tot = 0;
|
||||
|
||||
zero_v2(center);
|
||||
|
||||
for (Object *obedit : objects) {
|
||||
BMEditMesh *em = BKE_editmesh_from_object(obedit);
|
||||
const BMUVOffsets offsets = BM_uv_map_offsets_get(em->bm);
|
||||
|
||||
BM_ITER_MESH (f, &iter, em->bm, BM_FACES_OF_MESH) {
|
||||
if (!uvedit_face_visible_test(scene, f)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
BM_ITER_ELEM (l, &liter, f, BM_LOOPS_OF_FACE) {
|
||||
if (uvedit_uv_select_test(scene, em->bm, l, offsets)) {
|
||||
luv = BM_ELEM_CD_GET_FLOAT_P(l, offsets.uv);
|
||||
add_v2_v2(center, luv);
|
||||
tot++;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (tot > 0) {
|
||||
center[0] /= tot;
|
||||
center[1] /= tot;
|
||||
}
|
||||
|
||||
return tot;
|
||||
}
|
||||
|
||||
static void uvedit_translate(Scene *scene, const Span<Object *> objects, const float delta[2])
|
||||
{
|
||||
BMFace *f;
|
||||
BMLoop *l;
|
||||
BMIter iter, liter;
|
||||
float *luv;
|
||||
|
||||
for (Object *obedit : objects) {
|
||||
BMEditMesh *em = BKE_editmesh_from_object(obedit);
|
||||
|
||||
const BMUVOffsets offsets = BM_uv_map_offsets_get(em->bm);
|
||||
|
||||
BM_ITER_MESH (f, &iter, em->bm, BM_FACES_OF_MESH) {
|
||||
if (!uvedit_face_visible_test(scene, f)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
BM_ITER_ELEM (l, &liter, f, BM_LOOPS_OF_FACE) {
|
||||
if (uvedit_uv_select_test(scene, em->bm, l, offsets)) {
|
||||
luv = BM_ELEM_CD_GET_FLOAT_P(l, offsets.uv);
|
||||
add_v2_v2(luv, delta);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Button Functions, using an evil static variable */
|
||||
|
||||
static float uvedit_old_center[2];
|
||||
|
||||
static void uvedit_vertex_buttons(const bContext *C, ui::Block *block)
|
||||
{
|
||||
SpaceImage *sima = CTX_wm_space_image(C);
|
||||
const Main *bmain = CTX_data_main(C);
|
||||
Scene *scene = CTX_data_scene(C);
|
||||
float center[2];
|
||||
int imx, imy, step, digits;
|
||||
Vector<Object *> objects = BKE_view_layer_array_from_objects_in_edit_mode_unique_data_with_uvs(
|
||||
*bmain, scene, CTX_data_view_layer(C), CTX_wm_view3d(C));
|
||||
|
||||
ED_space_image_get_size(sima, &imx, &imy);
|
||||
|
||||
if (uvedit_center(scene, objects, center)) {
|
||||
float range_xy[2][2] = {
|
||||
{-10.0f, 10.0f},
|
||||
{-10.0f, 10.0f},
|
||||
};
|
||||
|
||||
copy_v2_v2(uvedit_old_center, center);
|
||||
|
||||
/* expand UI range by center */
|
||||
CLAMP_MAX(range_xy[0][0], uvedit_old_center[0]);
|
||||
CLAMP_MIN(range_xy[0][1], uvedit_old_center[0]);
|
||||
CLAMP_MAX(range_xy[1][0], uvedit_old_center[1]);
|
||||
CLAMP_MIN(range_xy[1][1], uvedit_old_center[1]);
|
||||
|
||||
if (!(sima->flag & SI_COORDFLOATS)) {
|
||||
uvedit_old_center[0] *= imx;
|
||||
uvedit_old_center[1] *= imy;
|
||||
|
||||
mul_v2_fl(range_xy[0], imx);
|
||||
mul_v2_fl(range_xy[1], imy);
|
||||
}
|
||||
|
||||
if (sima->flag & SI_COORDFLOATS) {
|
||||
step = 1;
|
||||
digits = 3;
|
||||
}
|
||||
else {
|
||||
step = 100;
|
||||
digits = 2;
|
||||
}
|
||||
|
||||
ui::Button *but;
|
||||
|
||||
int y = 0;
|
||||
block_align_begin(block);
|
||||
but = uiDefButV(block,
|
||||
ui::ButtonType::Num,
|
||||
IFACE_("X:"),
|
||||
0,
|
||||
y -= UI_UNIT_Y,
|
||||
200,
|
||||
UI_UNIT_Y,
|
||||
&uvedit_old_center[0],
|
||||
UNPACK2(range_xy[0]),
|
||||
"");
|
||||
button_retval_set(but, B_UVEDIT_VERTEX);
|
||||
button_number_step_size_set(but, step);
|
||||
button_number_precision_set(but, digits);
|
||||
but = uiDefButV(block,
|
||||
ui::ButtonType::Num,
|
||||
IFACE_("Y:"),
|
||||
0,
|
||||
y -= UI_UNIT_Y,
|
||||
200,
|
||||
UI_UNIT_Y,
|
||||
&uvedit_old_center[1],
|
||||
UNPACK2(range_xy[1]),
|
||||
"");
|
||||
button_retval_set(but, B_UVEDIT_VERTEX);
|
||||
button_number_step_size_set(but, step);
|
||||
button_number_precision_set(but, digits);
|
||||
block_align_end(block);
|
||||
}
|
||||
}
|
||||
|
||||
static void do_uvedit_vertex(bContext *C, void * /*arg*/, int event)
|
||||
{
|
||||
SpaceImage *sima = CTX_wm_space_image(C);
|
||||
const Main *bmain = CTX_data_main(C);
|
||||
Scene *scene = CTX_data_scene(C);
|
||||
float center[2], delta[2];
|
||||
int imx, imy;
|
||||
|
||||
if (event != B_UVEDIT_VERTEX) {
|
||||
return;
|
||||
}
|
||||
|
||||
Vector<Object *> objects = BKE_view_layer_array_from_objects_in_edit_mode_unique_data_with_uvs(
|
||||
*bmain, scene, CTX_data_view_layer(C), CTX_wm_view3d(C));
|
||||
|
||||
ED_space_image_get_size(sima, &imx, &imy);
|
||||
uvedit_center(scene, objects, center);
|
||||
|
||||
if (sima->flag & SI_COORDFLOATS) {
|
||||
delta[0] = uvedit_old_center[0] - center[0];
|
||||
delta[1] = uvedit_old_center[1] - center[1];
|
||||
}
|
||||
else {
|
||||
delta[0] = uvedit_old_center[0] / imx - center[0];
|
||||
delta[1] = uvedit_old_center[1] / imy - center[1];
|
||||
}
|
||||
|
||||
uvedit_translate(scene, objects, delta);
|
||||
|
||||
WM_event_add_notifier(C, NC_IMAGE, sima->image);
|
||||
for (Object *obedit : objects) {
|
||||
DEG_id_tag_update(obedit->data, ID_RECALC_GEOMETRY);
|
||||
}
|
||||
}
|
||||
|
||||
/* Panels */
|
||||
|
||||
static bool image_panel_uv_poll(const bContext *C, PanelType * /*pt*/)
|
||||
{
|
||||
SpaceImage *sima = CTX_wm_space_image(C);
|
||||
if (sima->mode != SI_MODE_UV) {
|
||||
return false;
|
||||
}
|
||||
Object *obedit = CTX_data_edit_object(C);
|
||||
return ED_uvedit_test(obedit);
|
||||
}
|
||||
|
||||
static void image_panel_uv(const bContext *C, Panel *panel)
|
||||
{
|
||||
ui::Block *block = panel->layout->absolute().block();
|
||||
block_func_handle_set(block, do_uvedit_vertex, nullptr);
|
||||
|
||||
uvedit_vertex_buttons(C, block);
|
||||
}
|
||||
|
||||
void ED_uvedit_buttons_register(ARegionType *art)
|
||||
{
|
||||
PanelType *pt = MEM_new_zeroed<PanelType>(__func__);
|
||||
|
||||
STRNCPY_UTF8(pt->idname, "IMAGE_PT_uv");
|
||||
STRNCPY_UTF8(pt->label, N_("UV Vertex")); /* XXX C panels unavailable through RNA bpy.types! */
|
||||
/* Could be 'Item' matching 3D view, avoid new tab for two buttons. */
|
||||
STRNCPY_UTF8(pt->category, "Image");
|
||||
pt->draw = image_panel_uv;
|
||||
pt->poll = image_panel_uv_poll;
|
||||
BLI_addtail(&art->paneltypes, pt);
|
||||
}
|
||||
|
||||
} // namespace blender
|
||||
406
blender-5.2.0/source/blender/editors/uvedit/uvedit_clipboard.cc
Normal file
406
blender-5.2.0/source/blender/editors/uvedit/uvedit_clipboard.cc
Normal file
@@ -0,0 +1,406 @@
|
||||
/* SPDX-FileCopyrightText: 2022 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
/** \file
|
||||
* \ingroup eduv
|
||||
*
|
||||
* Attempt to find a graph isomorphism between the topology of two different UV islands.
|
||||
*
|
||||
* \note On terminology, for the purposes of this file:
|
||||
* * An iso_graph is a "Graph" in Graph Theory.
|
||||
* * An iso_graph has an unordered set of iso_verts.
|
||||
* * An iso_graph has an unordered set of iso_edges.
|
||||
* * An iso_vert is a "Vertex" in Graph Theory
|
||||
* * Each iso_vert has a label.
|
||||
* * An iso_edge is an "Edge" in Graph Theory
|
||||
* * Each iso_edge connects two iso_verts.
|
||||
* * An iso_edge is undirected.
|
||||
*/
|
||||
|
||||
#include "BKE_context.hh"
|
||||
#include "BKE_customdata.hh"
|
||||
#include "BKE_editmesh.hh"
|
||||
#include "BKE_layer.hh"
|
||||
#include "BKE_mesh_mapping.hh" /* UvElementMap */
|
||||
#include "BKE_report.hh"
|
||||
|
||||
#include "DEG_depsgraph.hh"
|
||||
|
||||
#include "ED_mesh.hh"
|
||||
#include "ED_screen.hh"
|
||||
|
||||
#include "WM_api.hh"
|
||||
|
||||
#include "uvedit_clipboard_graph_iso.hh"
|
||||
#include "uvedit_intern.hh" /* Own include. */
|
||||
|
||||
namespace blender {
|
||||
|
||||
void UV_clipboard_free();
|
||||
|
||||
class UV_ClipboardBuffer {
|
||||
public:
|
||||
~UV_ClipboardBuffer();
|
||||
|
||||
void append(UvElementMap *element_map, const int cd_loop_uv_offset);
|
||||
/**
|
||||
* \return True when found.
|
||||
*/
|
||||
bool find_isomorphism(UvElementMap *dest_element_map,
|
||||
int island_index,
|
||||
int cd_loop_uv_offset,
|
||||
Vector<int> &r_label,
|
||||
bool *r_search_abandoned);
|
||||
|
||||
void write_uvs(UvElementMap *element_map,
|
||||
int island_index,
|
||||
const int cd_loop_uv_offset,
|
||||
const Vector<int> &label);
|
||||
|
||||
private:
|
||||
Vector<GraphISO *> graph;
|
||||
Vector<int> offset;
|
||||
Vector<std::pair<float, float>> uv;
|
||||
};
|
||||
|
||||
static UV_ClipboardBuffer *uv_clipboard = nullptr;
|
||||
|
||||
UV_ClipboardBuffer::~UV_ClipboardBuffer()
|
||||
{
|
||||
for (const int64_t index : graph.index_range()) {
|
||||
delete graph[index];
|
||||
}
|
||||
graph.clear();
|
||||
offset.clear();
|
||||
uv.clear();
|
||||
}
|
||||
|
||||
/* Given a `BMLoop`, possibly belonging to an island in a `UvElementMap`,
|
||||
* return the `iso_index` corresponding to it's representation
|
||||
* in the `iso_graph`.
|
||||
*
|
||||
* If the `BMLoop` is not part of the `iso_graph`, return -1.
|
||||
*/
|
||||
static int iso_index_for_loop(const BMLoop *loop,
|
||||
UvElementMap *element_map,
|
||||
const int island_index)
|
||||
{
|
||||
UvElement *element = BM_uv_element_get(element_map, loop);
|
||||
if (!element) {
|
||||
return -1; /* Either unselected, or a different island. */
|
||||
}
|
||||
const int index = BM_uv_element_get_unique_index(element_map, element);
|
||||
const int base_index = BM_uv_element_get_unique_index(
|
||||
element_map, element_map->storage + element_map->island_indices[island_index]);
|
||||
return index - base_index;
|
||||
}
|
||||
|
||||
/* Add an `iso_edge` to an `iso_graph` between two BMLoops.
|
||||
*/
|
||||
static void add_iso_edge(
|
||||
GraphISO *graph, BMLoop *loop_v, BMLoop *loop_w, UvElementMap *element_map, int island_index)
|
||||
{
|
||||
BLI_assert(loop_v->f == loop_w->f); /* Ensure on the same face. */
|
||||
const int index_v = iso_index_for_loop(loop_v, element_map, island_index);
|
||||
const int index_w = iso_index_for_loop(loop_w, element_map, island_index);
|
||||
BLI_assert(index_v != index_w);
|
||||
if (index_v == -1 || index_w == -1) {
|
||||
return; /* Unselected. */
|
||||
}
|
||||
|
||||
BLI_assert(0 <= index_v && index_v < graph->n);
|
||||
BLI_assert(0 <= index_w && index_w < graph->n);
|
||||
|
||||
graph->add_edge(index_v, index_w);
|
||||
}
|
||||
|
||||
/* Build an `iso_graph` representation of an island of a `UvElementMap`.
|
||||
*/
|
||||
static GraphISO *build_iso_graph(UvElementMap *element_map,
|
||||
const int island_index,
|
||||
int /*cd_loop_uv_offset*/)
|
||||
{
|
||||
GraphISO *g = new GraphISO(element_map->island_total_unique_uvs[island_index]);
|
||||
for (int i = 0; i < g->n; i++) {
|
||||
g->label[i] = i;
|
||||
}
|
||||
|
||||
const int i0 = element_map->island_indices[island_index];
|
||||
const int i1 = i0 + element_map->island_total_uvs[island_index];
|
||||
|
||||
/* Add iso_edges. */
|
||||
for (int i = i0; i < i1; i++) {
|
||||
const UvElement *element = element_map->storage + i;
|
||||
/* Look forward around the current face. */
|
||||
add_iso_edge(g, element->l, element->l->next, element_map, island_index);
|
||||
|
||||
/* Look backward around the current face.
|
||||
* (Required for certain vertex selection cases.)
|
||||
*/
|
||||
add_iso_edge(g, element->l->prev, element->l, element_map, island_index);
|
||||
}
|
||||
|
||||
/* TODO: call g->sort_vertices_by_degree() */
|
||||
|
||||
return g;
|
||||
}
|
||||
|
||||
/* Convert each island inside an `element_map` into an `iso_graph`, and append them to the
|
||||
* clipboard buffer. */
|
||||
void UV_ClipboardBuffer::append(UvElementMap *element_map, const int cd_loop_uv_offset)
|
||||
{
|
||||
for (int island_index = 0; island_index < element_map->total_islands; island_index++) {
|
||||
offset.append(uv.size());
|
||||
graph.append(build_iso_graph(element_map, island_index, cd_loop_uv_offset));
|
||||
|
||||
/* TODO: Consider iterating over `BM_uv_element_map_ensure_unique_index` instead. */
|
||||
for (int j = 0; j < element_map->island_total_uvs[island_index]; j++) {
|
||||
UvElement *element = element_map->storage + element_map->island_indices[island_index] + j;
|
||||
if (!element->separate) {
|
||||
continue;
|
||||
}
|
||||
float *luv = BM_ELEM_CD_GET_FLOAT_P(element->l, cd_loop_uv_offset);
|
||||
uv.append(std::make_pair(luv[0], luv[1]));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Write UVs back to an island. */
|
||||
void UV_ClipboardBuffer::write_uvs(UvElementMap *element_map,
|
||||
int island_index,
|
||||
const int cd_loop_uv_offset,
|
||||
const Vector<int> &label)
|
||||
{
|
||||
BLI_assert(label.size() == element_map->island_total_unique_uvs[island_index]);
|
||||
|
||||
/* TODO: Consider iterating over `BM_uv_element_map_ensure_unique_index` instead. */
|
||||
int unique_uv = 0;
|
||||
for (int j = 0; j < element_map->island_total_uvs[island_index]; j++) {
|
||||
int k = element_map->island_indices[island_index] + j;
|
||||
UvElement *element = element_map->storage + k;
|
||||
if (!element->separate) {
|
||||
continue;
|
||||
}
|
||||
BLI_assert(0 <= unique_uv);
|
||||
BLI_assert(unique_uv < label.size());
|
||||
const std::pair<float, float> &source_uv = uv_clipboard->uv[label[unique_uv]];
|
||||
while (element) {
|
||||
float *luv = BM_ELEM_CD_GET_FLOAT_P(element->l, cd_loop_uv_offset);
|
||||
luv[0] = source_uv.first;
|
||||
luv[1] = source_uv.second;
|
||||
element = element->next;
|
||||
if (!element || element->separate) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
unique_uv++;
|
||||
}
|
||||
BLI_assert(unique_uv == label.size());
|
||||
}
|
||||
|
||||
/**
|
||||
* Call the external isomorphism solver.
|
||||
* \return True when found.
|
||||
*/
|
||||
static bool find_isomorphism(UvElementMap *dest,
|
||||
const int dest_island_index,
|
||||
GraphISO *graph_source,
|
||||
const int cd_loop_uv_offset,
|
||||
Vector<int> &r_label,
|
||||
bool *r_search_abandoned)
|
||||
{
|
||||
|
||||
const int island_total_unique_uvs = dest->island_total_unique_uvs[dest_island_index];
|
||||
if (island_total_unique_uvs != graph_source->n) {
|
||||
return false; /* Isomorphisms can't differ in |iso_vert|. */
|
||||
}
|
||||
r_label.resize(island_total_unique_uvs);
|
||||
|
||||
GraphISO *graph_dest = build_iso_graph(dest, dest_island_index, cd_loop_uv_offset);
|
||||
|
||||
int (*solution)[2] = MEM_new_array_uninitialized<int[2]>(graph_source->n, __func__);
|
||||
int solution_length = 0;
|
||||
const bool found = ED_uvedit_clipboard_maximum_common_subgraph(
|
||||
graph_source, graph_dest, solution, &solution_length, r_search_abandoned);
|
||||
|
||||
/* TODO: Implement "Best Effort" / "Nearest Match" paste functionality here. */
|
||||
|
||||
if (found) {
|
||||
BLI_assert(solution_length == dest->island_total_unique_uvs[dest_island_index]);
|
||||
for (int i = 0; i < solution_length; i++) {
|
||||
int index_s = solution[i][0];
|
||||
int index_t = solution[i][1];
|
||||
BLI_assert(0 <= index_s && index_s < solution_length);
|
||||
BLI_assert(0 <= index_t && index_t < solution_length);
|
||||
r_label[index_t] = index_s;
|
||||
}
|
||||
}
|
||||
|
||||
MEM_SAFE_DELETE(solution);
|
||||
delete graph_dest;
|
||||
return found;
|
||||
}
|
||||
|
||||
bool UV_ClipboardBuffer::find_isomorphism(UvElementMap *dest_element_map,
|
||||
const int dest_island_index,
|
||||
const int cd_loop_uv_offset,
|
||||
Vector<int> &r_label,
|
||||
bool *r_search_abandoned)
|
||||
{
|
||||
for (const int64_t source_island_index : graph.index_range()) {
|
||||
if (blender::find_isomorphism(dest_element_map,
|
||||
dest_island_index,
|
||||
graph[source_island_index],
|
||||
cd_loop_uv_offset,
|
||||
r_label,
|
||||
r_search_abandoned))
|
||||
{
|
||||
const int island_total_unique_uvs =
|
||||
dest_element_map->island_total_unique_uvs[dest_island_index];
|
||||
const int island_offset = offset[source_island_index];
|
||||
BLI_assert(island_total_unique_uvs == r_label.size());
|
||||
for (int i = 0; i < island_total_unique_uvs; i++) {
|
||||
r_label[i] += island_offset; /* TODO: (minor optimization) Defer offset. */
|
||||
}
|
||||
|
||||
/* TODO: There may be more than one match. How to choose between them? */
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
static wmOperatorStatus uv_copy_exec(bContext *C, wmOperator * /*op*/)
|
||||
{
|
||||
UV_clipboard_free();
|
||||
uv_clipboard = new UV_ClipboardBuffer();
|
||||
|
||||
const Main *bmain = CTX_data_main(C);
|
||||
ViewLayer *view_layer = CTX_data_view_layer(C);
|
||||
Scene *scene = CTX_data_scene(C);
|
||||
|
||||
Vector<Object *> objects = BKE_view_layer_array_from_objects_in_edit_mode_unique_data_with_uvs(
|
||||
*bmain, scene, view_layer, nullptr);
|
||||
|
||||
for (Object *ob : objects) {
|
||||
BMEditMesh *em = BKE_editmesh_from_object(ob);
|
||||
|
||||
const bool use_seams = false;
|
||||
UvElementMap *element_map = BM_uv_element_map_create(
|
||||
em->bm, scene, true, false, use_seams, true);
|
||||
if (element_map) {
|
||||
const int cd_loop_uv_offset = CustomData_get_offset(&em->bm->ldata, CD_PROP_FLOAT2);
|
||||
uv_clipboard->append(element_map, cd_loop_uv_offset);
|
||||
}
|
||||
BM_uv_element_map_free(element_map);
|
||||
}
|
||||
|
||||
/* TODO: Serialize `UvClipboard` to system clipboard. */
|
||||
|
||||
return OPERATOR_FINISHED;
|
||||
}
|
||||
|
||||
static wmOperatorStatus uv_paste_exec(bContext *C, wmOperator *op)
|
||||
{
|
||||
/* TODO: Restore `UvClipboard` from system clipboard. */
|
||||
if (!uv_clipboard) {
|
||||
return OPERATOR_FINISHED; /* Nothing to do. */
|
||||
}
|
||||
const Main *bmain = CTX_data_main(C);
|
||||
ViewLayer *view_layer = CTX_data_view_layer(C);
|
||||
Scene *scene = CTX_data_scene(C);
|
||||
|
||||
Vector<Object *> objects = BKE_view_layer_array_from_objects_in_edit_mode_unique_data_with_uvs(
|
||||
*bmain, scene, view_layer, nullptr);
|
||||
|
||||
bool changed_multi = false;
|
||||
int complicated_search = 0;
|
||||
int total_search = 0;
|
||||
for (Object *ob : objects) {
|
||||
BMEditMesh *em = BKE_editmesh_from_object(ob);
|
||||
|
||||
const bool use_seams = false;
|
||||
const int cd_loop_uv_offset = CustomData_get_offset(&em->bm->ldata, CD_PROP_FLOAT2);
|
||||
|
||||
UvElementMap *dest_element_map = BM_uv_element_map_create(
|
||||
em->bm, scene, true, false, use_seams, true);
|
||||
|
||||
if (!dest_element_map) {
|
||||
continue;
|
||||
}
|
||||
|
||||
bool changed = false;
|
||||
|
||||
for (int i = 0; i < dest_element_map->total_islands; i++) {
|
||||
total_search++;
|
||||
Vector<int> label;
|
||||
bool search_abandoned = false;
|
||||
const bool found = uv_clipboard->find_isomorphism(
|
||||
dest_element_map, i, cd_loop_uv_offset, label, &search_abandoned);
|
||||
if (!found) {
|
||||
if (search_abandoned) {
|
||||
complicated_search++;
|
||||
}
|
||||
continue; /* No source UVs can be found that is isomorphic to this island. */
|
||||
}
|
||||
|
||||
uv_clipboard->write_uvs(dest_element_map, i, cd_loop_uv_offset, label);
|
||||
changed = true; /* UVs were moved. */
|
||||
}
|
||||
|
||||
BM_uv_element_map_free(dest_element_map);
|
||||
|
||||
if (changed) {
|
||||
changed_multi = true;
|
||||
|
||||
DEG_id_tag_update(ob->data, ID_RECALC_GEOMETRY);
|
||||
WM_event_add_notifier(C, NC_GEOM | ND_DATA, ob->data);
|
||||
}
|
||||
}
|
||||
|
||||
if (complicated_search) {
|
||||
BKE_reportf(op->reports,
|
||||
RPT_WARNING,
|
||||
"Skipped %d of %d island(s), geometry was too complicated to detect a match",
|
||||
complicated_search,
|
||||
total_search);
|
||||
}
|
||||
|
||||
return changed_multi ? OPERATOR_FINISHED : OPERATOR_CANCELLED;
|
||||
}
|
||||
|
||||
void UV_OT_copy(wmOperatorType *ot)
|
||||
{
|
||||
/* identifiers */
|
||||
ot->name = "Copy UVs";
|
||||
ot->description = "Copy selected UV vertices";
|
||||
ot->idname = "UV_OT_copy";
|
||||
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
|
||||
|
||||
/* API callbacks. */
|
||||
ot->exec = uv_copy_exec;
|
||||
ot->poll = ED_operator_uvedit;
|
||||
}
|
||||
|
||||
void UV_OT_paste(wmOperatorType *ot)
|
||||
{
|
||||
/* identifiers */
|
||||
ot->name = "Paste UVs";
|
||||
ot->description = "Paste selected UV vertices";
|
||||
ot->idname = "UV_OT_paste";
|
||||
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
|
||||
|
||||
/* API callbacks. */
|
||||
ot->exec = uv_paste_exec;
|
||||
ot->poll = ED_operator_uvedit;
|
||||
}
|
||||
|
||||
void UV_clipboard_free()
|
||||
{
|
||||
delete uv_clipboard;
|
||||
uv_clipboard = nullptr;
|
||||
}
|
||||
|
||||
} // namespace blender
|
||||
@@ -0,0 +1,444 @@
|
||||
/* SPDX-FileCopyrightText: 2019 Stefano Quer
|
||||
* SPDX-FileCopyrightText: 2022 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-3.0-or-later
|
||||
*
|
||||
* Originally 6846114 from https://github.com/stefanoquer/graphISO/blob/master/v3
|
||||
* graphISO: Tools to compute the Maximum Common Subgraph between two graphs.
|
||||
*/
|
||||
|
||||
#include "uvedit_clipboard_graph_iso.hh"
|
||||
|
||||
#include "BLI_assert.h"
|
||||
|
||||
#include "MEM_guardedalloc.h"
|
||||
|
||||
#include <algorithm>
|
||||
#include <climits>
|
||||
|
||||
namespace blender {
|
||||
|
||||
#define L 0
|
||||
#define R 1
|
||||
#define LL 2
|
||||
#define RL 3
|
||||
#define ADJ 4
|
||||
#define P 5
|
||||
#define W 6
|
||||
#define IRL 7
|
||||
|
||||
#define BDS 8
|
||||
|
||||
GraphISO::GraphISO(int n)
|
||||
{
|
||||
this->n = n;
|
||||
label = MEM_new_array_uninitialized<uint>(n, __func__);
|
||||
adjmat = MEM_new_array_uninitialized<uint8_t *>(n, __func__);
|
||||
|
||||
/* \note Allocation of `n * n` bytes total! */
|
||||
|
||||
for (int i = 0; i < n; i++) {
|
||||
/* Caution, are you trying to change the representation of adjmat?
|
||||
* Consider `Vector<std::pair<int, int>> adjmat;` instead.
|
||||
* Better still is to use a different algorithm. See for example:
|
||||
* https://www.uni-ulm.de/fileadmin/website_uni_ulm/iui.inst.190/Mitarbeiter/toran/beatcs09.pdf
|
||||
*/
|
||||
adjmat[i] = MEM_new_array_zeroed<uint8_t>(n * sizeof *adjmat[i], __func__);
|
||||
}
|
||||
degree = nullptr;
|
||||
}
|
||||
|
||||
GraphISO::~GraphISO()
|
||||
{
|
||||
for (int i = 0; i < n; i++) {
|
||||
MEM_delete(adjmat[i]);
|
||||
}
|
||||
MEM_delete(adjmat);
|
||||
MEM_delete(label);
|
||||
if (degree) {
|
||||
MEM_delete(degree);
|
||||
}
|
||||
}
|
||||
|
||||
void GraphISO::add_edge(int v, int w)
|
||||
{
|
||||
BLI_assert(v != w);
|
||||
adjmat[v][w] = 1;
|
||||
adjmat[w][v] = 1;
|
||||
}
|
||||
|
||||
void GraphISO::calculate_degrees() const
|
||||
{
|
||||
if (degree) {
|
||||
return;
|
||||
}
|
||||
degree = MEM_new_array_uninitialized<uint>(n, __func__);
|
||||
for (int v = 0; v < n; v++) {
|
||||
int row_count = 0;
|
||||
for (int w = 0; w < n; w++) {
|
||||
if (adjmat[v][w]) {
|
||||
row_count++;
|
||||
}
|
||||
}
|
||||
degree[v] = row_count;
|
||||
}
|
||||
}
|
||||
|
||||
class GraphISO_DegreeCompare {
|
||||
public:
|
||||
GraphISO_DegreeCompare(const GraphISO *g)
|
||||
{
|
||||
this->g = g;
|
||||
}
|
||||
const GraphISO *g;
|
||||
|
||||
bool operator()(int i, int j) const
|
||||
{
|
||||
return g->degree[i] < g->degree[j];
|
||||
}
|
||||
};
|
||||
|
||||
GraphISO *GraphISO::sort_vertices_by_degree() const
|
||||
{
|
||||
calculate_degrees();
|
||||
|
||||
int *vv = MEM_new_array_uninitialized<int>(n, __func__);
|
||||
for (int i = 0; i < n; i++) {
|
||||
vv[i] = i;
|
||||
}
|
||||
/* Currently ordering iso_verts by degree.
|
||||
* Instead should order iso_verts by frequency of degree. */
|
||||
GraphISO_DegreeCompare compare_obj(this);
|
||||
std::sort(vv, vv + n, compare_obj);
|
||||
|
||||
GraphISO *subg = new GraphISO(n);
|
||||
for (int i = 0; i < n; i++) {
|
||||
for (int j = 0; j < n; j++) {
|
||||
subg->adjmat[i][j] = adjmat[vv[i]][vv[j]];
|
||||
}
|
||||
}
|
||||
for (int i = 0; i < n; i++) {
|
||||
subg->label[i] = label[vv[i]];
|
||||
}
|
||||
subg->calculate_degrees();
|
||||
|
||||
MEM_delete(vv);
|
||||
return subg;
|
||||
}
|
||||
|
||||
static void update_incumbent(uint8_t cur[][2], int inc[][2], int cur_pos, int *inc_pos)
|
||||
{
|
||||
if (cur_pos > *inc_pos) {
|
||||
*inc_pos = cur_pos;
|
||||
for (int i = 0; i < cur_pos; i++) {
|
||||
inc[i][L] = cur[i][L];
|
||||
inc[i][R] = cur[i][R];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void add_bidomain(uint8_t domains[][BDS],
|
||||
int *bd_pos,
|
||||
uint8_t left_i,
|
||||
uint8_t right_i,
|
||||
uint8_t left_len,
|
||||
uint8_t right_len,
|
||||
uint8_t is_adjacent,
|
||||
uint8_t cur_pos)
|
||||
{
|
||||
domains[*bd_pos][L] = left_i;
|
||||
domains[*bd_pos][R] = right_i;
|
||||
domains[*bd_pos][LL] = left_len;
|
||||
domains[*bd_pos][RL] = right_len;
|
||||
domains[*bd_pos][ADJ] = is_adjacent;
|
||||
domains[*bd_pos][P] = cur_pos;
|
||||
domains[*bd_pos][W] = UINT8_MAX;
|
||||
domains[*bd_pos][IRL] = right_len;
|
||||
(*bd_pos)++;
|
||||
}
|
||||
|
||||
static int calc_bound(const uint8_t domains[][BDS], int bd_pos, int cur_pos)
|
||||
{
|
||||
int bound = 0;
|
||||
for (int i = bd_pos - 1; i >= 0 && domains[i][P] == cur_pos; i--) {
|
||||
bound += std::min(domains[i][LL], domains[i][IRL]);
|
||||
}
|
||||
return bound;
|
||||
}
|
||||
|
||||
static int partition(uint8_t *arr, int start, int len, const uint8_t *adjrow)
|
||||
{
|
||||
int i = 0;
|
||||
for (int j = 0; j < len; j++) {
|
||||
if (adjrow[arr[start + j]]) {
|
||||
std::swap(arr[start + i], arr[start + j]);
|
||||
i++;
|
||||
}
|
||||
}
|
||||
return i;
|
||||
}
|
||||
|
||||
static void generate_next_domains(uint8_t domains[][BDS],
|
||||
int *bd_pos,
|
||||
int cur_pos,
|
||||
uint8_t *left,
|
||||
uint8_t *right,
|
||||
uint8_t v,
|
||||
uint8_t w,
|
||||
int inc_pos,
|
||||
uint8_t **adjmat0,
|
||||
uint8_t **adjmat1)
|
||||
{
|
||||
int i;
|
||||
int bd_backup = *bd_pos;
|
||||
int bound = 0;
|
||||
uint8_t *bd;
|
||||
for (i = *bd_pos - 1, bd = &domains[i][L]; i >= 0 && bd[P] == cur_pos - 1;
|
||||
i--, bd = &domains[i][L])
|
||||
{
|
||||
|
||||
uint8_t l_len = partition(left, bd[L], bd[LL], adjmat0[v]);
|
||||
uint8_t r_len = partition(right, bd[R], bd[RL], adjmat1[w]);
|
||||
|
||||
if (bd[LL] - l_len && bd[RL] - r_len) {
|
||||
add_bidomain(domains,
|
||||
bd_pos,
|
||||
bd[L] + l_len,
|
||||
bd[R] + r_len,
|
||||
bd[LL] - l_len,
|
||||
bd[RL] - r_len,
|
||||
bd[ADJ],
|
||||
uint8_t(cur_pos));
|
||||
bound += std::min(bd[LL] - l_len, bd[RL] - r_len);
|
||||
}
|
||||
if (l_len && r_len) {
|
||||
add_bidomain(domains, bd_pos, bd[L], bd[R], l_len, r_len, true, uint8_t(cur_pos));
|
||||
bound += std::min(l_len, r_len);
|
||||
}
|
||||
}
|
||||
if (cur_pos + bound <= inc_pos) {
|
||||
*bd_pos = bd_backup;
|
||||
}
|
||||
}
|
||||
|
||||
static uint8_t select_next_v(uint8_t *left, uint8_t *bd)
|
||||
{
|
||||
uint8_t min = UINT8_MAX;
|
||||
uint8_t idx = UINT8_MAX;
|
||||
if (bd[RL] != bd[IRL]) {
|
||||
return left[bd[L] + bd[LL]];
|
||||
}
|
||||
for (uint8_t i = 0; i < bd[LL]; i++) {
|
||||
if (left[bd[L] + i] < min) {
|
||||
min = left[bd[L] + i];
|
||||
idx = i;
|
||||
}
|
||||
}
|
||||
std::swap(left[bd[L] + idx], left[bd[L] + bd[LL] - 1]);
|
||||
bd[LL]--;
|
||||
bd[RL]--;
|
||||
return min;
|
||||
}
|
||||
|
||||
static uint8_t find_min_value(const uint8_t *arr, uint8_t start_idx, uint8_t len)
|
||||
{
|
||||
uint8_t min_v = UINT8_MAX;
|
||||
for (int i = 0; i < len; i++) {
|
||||
min_v = std::min(arr[start_idx + i], min_v);
|
||||
}
|
||||
return min_v;
|
||||
}
|
||||
|
||||
static void select_bidomain(uint8_t domains[][BDS],
|
||||
int bd_pos,
|
||||
const uint8_t *left,
|
||||
int current_matching_size,
|
||||
bool connected)
|
||||
{
|
||||
int i;
|
||||
int min_size = INT_MAX;
|
||||
int min_tie_breaker = INT_MAX;
|
||||
int best = INT_MAX;
|
||||
uint8_t *bd;
|
||||
for (i = bd_pos - 1, bd = &domains[i][L]; i >= 0 && bd[P] == current_matching_size;
|
||||
i--, bd = &domains[i][L])
|
||||
{
|
||||
if (connected && current_matching_size > 0 && !bd[ADJ]) {
|
||||
continue;
|
||||
}
|
||||
int len = bd[LL] > bd[RL] ? bd[LL] : bd[RL];
|
||||
if (len < min_size) {
|
||||
min_size = len;
|
||||
min_tie_breaker = find_min_value(left, bd[L], bd[LL]);
|
||||
best = i;
|
||||
}
|
||||
else if (len == min_size) {
|
||||
int tie_breaker = find_min_value(left, bd[L], bd[LL]);
|
||||
if (tie_breaker < min_tie_breaker) {
|
||||
min_tie_breaker = tie_breaker;
|
||||
best = i;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (best != INT_MAX && best != bd_pos - 1) {
|
||||
uint8_t tmp[BDS];
|
||||
for (i = 0; i < BDS; i++) {
|
||||
tmp[i] = domains[best][i];
|
||||
}
|
||||
for (i = 0; i < BDS; i++) {
|
||||
domains[best][i] = domains[bd_pos - 1][i];
|
||||
}
|
||||
for (i = 0; i < BDS; i++) {
|
||||
domains[bd_pos - 1][i] = tmp[i];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static uint8_t select_next_w(const uint8_t *right, uint8_t *bd)
|
||||
{
|
||||
uint8_t min = UINT8_MAX;
|
||||
uint8_t idx = UINT8_MAX;
|
||||
for (uint8_t i = 0; i < bd[RL] + 1; i++) {
|
||||
if ((right[bd[R] + i] > bd[W] || bd[W] == UINT8_MAX) && right[bd[R] + i] < min) {
|
||||
min = right[bd[R] + i];
|
||||
idx = i;
|
||||
}
|
||||
}
|
||||
if (idx == UINT8_MAX) {
|
||||
bd[RL]++;
|
||||
}
|
||||
return idx;
|
||||
}
|
||||
|
||||
static void maximum_common_subgraph_internal(int incumbent[][2],
|
||||
int *inc_pos,
|
||||
uint8_t **adjmat0,
|
||||
int n0,
|
||||
uint8_t **adjmat1,
|
||||
int n1,
|
||||
bool *r_search_abandoned)
|
||||
{
|
||||
int min = std::min(n0, n1);
|
||||
|
||||
uint8_t (*cur)[2] = MEM_new_array_uninitialized<uint8_t[2]>(min, __func__);
|
||||
uint8_t (*domains)[BDS] = MEM_new_array_uninitialized<uint8_t[BDS]>(min * min, __func__);
|
||||
uint8_t *left = MEM_new_array_uninitialized<uint8_t>(n0, __func__);
|
||||
uint8_t *right = MEM_new_array_uninitialized<uint8_t>(n1, __func__);
|
||||
|
||||
uint8_t v, w, *bd;
|
||||
int bd_pos = 0;
|
||||
for (int i = 0; i < n0; i++) {
|
||||
left[i] = i;
|
||||
}
|
||||
for (int i = 0; i < n1; i++) {
|
||||
right[i] = i;
|
||||
}
|
||||
add_bidomain(domains, &bd_pos, 0, 0, n0, n1, 0, 0);
|
||||
|
||||
int iteration_count = 0;
|
||||
|
||||
while (bd_pos > 0) {
|
||||
if (iteration_count++ > 10000000) {
|
||||
/* Unlikely to find a solution, may as well give up.
|
||||
* Can occur with moderate sized inputs where the graph has lots of symmetry, e.g. a cube
|
||||
* subdivided 3x times.
|
||||
*/
|
||||
*r_search_abandoned = true;
|
||||
*inc_pos = 0;
|
||||
break;
|
||||
}
|
||||
bd = &domains[bd_pos - 1][L];
|
||||
if (calc_bound(domains, bd_pos, bd[P]) + bd[P] <= *inc_pos ||
|
||||
(bd[LL] == 0 && bd[RL] == bd[IRL]))
|
||||
{
|
||||
bd_pos--;
|
||||
}
|
||||
else {
|
||||
const bool connected = false;
|
||||
select_bidomain(domains, bd_pos, left, domains[bd_pos - 1][P], connected);
|
||||
v = select_next_v(left, bd);
|
||||
if ((bd[W] = select_next_w(right, bd)) != UINT8_MAX) {
|
||||
w = right[bd[R] + bd[W]]; /* Swap the W after the bottom of the current right domain. */
|
||||
right[bd[R] + bd[W]] = right[bd[R] + bd[RL]];
|
||||
right[bd[R] + bd[RL]] = w;
|
||||
bd[W] = w; /* Store the W used for this iteration. */
|
||||
cur[bd[P]][L] = v;
|
||||
cur[bd[P]][R] = w;
|
||||
update_incumbent(cur, incumbent, bd[P] + uint8_t(1), inc_pos);
|
||||
generate_next_domains(
|
||||
domains, &bd_pos, bd[P] + 1, left, right, v, w, *inc_pos, adjmat0, adjmat1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
MEM_delete(cur);
|
||||
MEM_delete(domains);
|
||||
MEM_delete(right);
|
||||
MEM_delete(left);
|
||||
}
|
||||
|
||||
static bool check_automorphism(const GraphISO *g0,
|
||||
const GraphISO *g1,
|
||||
int solution[][2],
|
||||
int *solution_length)
|
||||
{
|
||||
if (g0->n != g1->n) {
|
||||
return false;
|
||||
}
|
||||
for (int i = 0; i < g0->n; i++) {
|
||||
if (g0->label[i] != g1->label[i]) {
|
||||
return false;
|
||||
}
|
||||
for (int j = 0; j < g0->n; j++) {
|
||||
if (g0->adjmat[i][j] != g1->adjmat[i][j]) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
solution[i][0] = i;
|
||||
solution[i][1] = i;
|
||||
}
|
||||
*solution_length = g0->n;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool ED_uvedit_clipboard_maximum_common_subgraph(GraphISO *g0_input,
|
||||
GraphISO *g1_input,
|
||||
int solution[][2],
|
||||
int *solution_length,
|
||||
bool *r_search_abandoned)
|
||||
{
|
||||
if (check_automorphism(g0_input, g1_input, solution, solution_length)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
int n0 = g0_input->n;
|
||||
int n1 = g1_input->n;
|
||||
|
||||
int min_size = std::min(n0, n1);
|
||||
if (min_size >= UINT8_MAX - 2) {
|
||||
return false;
|
||||
}
|
||||
|
||||
GraphISO *g0 = g0_input->sort_vertices_by_degree();
|
||||
GraphISO *g1 = g1_input->sort_vertices_by_degree();
|
||||
|
||||
int sol_len = 0;
|
||||
maximum_common_subgraph_internal(
|
||||
solution, &sol_len, g0->adjmat, n0, g1->adjmat, n1, r_search_abandoned);
|
||||
*solution_length = sol_len;
|
||||
|
||||
bool result = (sol_len == n0);
|
||||
if (result) {
|
||||
for (int i = 0; i < sol_len; i++) {
|
||||
solution[i][0] = g0->label[solution[i][0]]; /* Index from input. */
|
||||
solution[i][1] = g1->label[solution[i][1]];
|
||||
}
|
||||
}
|
||||
|
||||
delete g1;
|
||||
delete g0;
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
} // namespace blender
|
||||
@@ -0,0 +1,45 @@
|
||||
/* SPDX-FileCopyrightText: 2019 Stefano Quer
|
||||
* SPDX-FileCopyrightText: 2022 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-3.0-or-later
|
||||
*
|
||||
* Originally 6846114 from https://github.com/stefanoquer/graphISO/blob/master/v3
|
||||
* graphISO: Tools to compute the Maximum Common Subgraph between two graphs.
|
||||
*/
|
||||
|
||||
/** \file
|
||||
* \ingroup eduv
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "BLI_sys_types.h"
|
||||
|
||||
namespace blender {
|
||||
|
||||
/* A thin representation of a "Graph" in graph theory. */
|
||||
class GraphISO {
|
||||
public:
|
||||
GraphISO(int n);
|
||||
~GraphISO();
|
||||
int n;
|
||||
uint8_t **adjmat;
|
||||
uint *label;
|
||||
mutable uint *degree;
|
||||
|
||||
void add_edge(int v, int w);
|
||||
GraphISO *sort_vertices_by_degree() const;
|
||||
|
||||
private:
|
||||
void calculate_degrees() const;
|
||||
};
|
||||
|
||||
/**
|
||||
* Find the maximum common subgraph between two graphs.
|
||||
* (Can be used to find graph ismorphism.)
|
||||
* \return True when found.
|
||||
*/
|
||||
bool ED_uvedit_clipboard_maximum_common_subgraph(
|
||||
GraphISO *, GraphISO *, int solution[][2], int *solution_length, bool *r_search_abandoned);
|
||||
|
||||
} // namespace blender
|
||||
97
blender-5.2.0/source/blender/editors/uvedit/uvedit_draw.cc
Normal file
97
blender-5.2.0/source/blender/editors/uvedit/uvedit_draw.cc
Normal file
@@ -0,0 +1,97 @@
|
||||
/* SPDX-FileCopyrightText: 2001-2002 NaN Holding BV. All rights reserved.
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
/** \file
|
||||
* \ingroup eduv
|
||||
*/
|
||||
|
||||
#include "BLI_math_vector.h"
|
||||
|
||||
#include "DNA_screen_types.h"
|
||||
#include "DNA_userdef_types.h"
|
||||
|
||||
#include "GPU_immediate.hh"
|
||||
#include "GPU_matrix.hh"
|
||||
#include "GPU_state.hh"
|
||||
|
||||
#include "UI_view2d.hh"
|
||||
|
||||
#include "ED_uvedit.hh"
|
||||
|
||||
namespace blender {
|
||||
|
||||
/* ------------------------- */
|
||||
|
||||
void ED_image_draw_cursor(ARegion *region, const float cursor[2])
|
||||
{
|
||||
float zoom[2], x_fac, y_fac;
|
||||
|
||||
ui::view2d_scale_get_inverse(®ion->v2d, &zoom[0], &zoom[1]);
|
||||
|
||||
mul_v2_fl(zoom, 256.0f * UI_SCALE_FAC);
|
||||
x_fac = zoom[0];
|
||||
y_fac = zoom[1];
|
||||
|
||||
GPU_line_width(1.0f);
|
||||
|
||||
GPU_matrix_translate_2fv(cursor);
|
||||
|
||||
const uint shdr_pos = GPU_vertformat_attr_add(
|
||||
immVertexFormat(), "pos", gpu::VertAttrType::SFLOAT_32_32);
|
||||
|
||||
immBindBuiltinProgram(GPU_SHADER_3D_LINE_DASHED_UNIFORM_COLOR);
|
||||
|
||||
float viewport_size[4];
|
||||
GPU_viewport_size_get_f(viewport_size);
|
||||
immUniform2f("viewport_size", viewport_size[2] / UI_SCALE_FAC, viewport_size[3] / UI_SCALE_FAC);
|
||||
|
||||
immUniform1i("colors_len", 2); /* "advanced" mode */
|
||||
immUniform4f("color", 1.0f, 0.0f, 0.0f, 1.0f);
|
||||
immUniform4f("color2", 1.0f, 1.0f, 1.0f, 1.0f);
|
||||
immUniform1f("dash_width", 8.0f);
|
||||
immUniform1f("udash_factor", 0.5f);
|
||||
|
||||
immBegin(GPU_PRIM_LINES, 8);
|
||||
|
||||
immVertex2f(shdr_pos, -0.05f * x_fac, 0.0f);
|
||||
immVertex2f(shdr_pos, 0.0f, 0.05f * y_fac);
|
||||
|
||||
immVertex2f(shdr_pos, 0.0f, 0.05f * y_fac);
|
||||
immVertex2f(shdr_pos, 0.05f * x_fac, 0.0f);
|
||||
|
||||
immVertex2f(shdr_pos, 0.05f * x_fac, 0.0f);
|
||||
immVertex2f(shdr_pos, 0.0f, -0.05f * y_fac);
|
||||
|
||||
immVertex2f(shdr_pos, 0.0f, -0.05f * y_fac);
|
||||
immVertex2f(shdr_pos, -0.05f * x_fac, 0.0f);
|
||||
|
||||
immEnd();
|
||||
|
||||
immUniform4f("color", 1.0f, 1.0f, 1.0f, 1.0f);
|
||||
immUniform4f("color2", 0.0f, 0.0f, 0.0f, 1.0f);
|
||||
immUniform1f("dash_width", 2.0f);
|
||||
immUniform1f("udash_factor", 0.5f);
|
||||
|
||||
immBegin(GPU_PRIM_LINES, 8);
|
||||
|
||||
immVertex2f(shdr_pos, -0.020f * x_fac, 0.0f);
|
||||
immVertex2f(shdr_pos, -0.1f * x_fac, 0.0f);
|
||||
|
||||
immVertex2f(shdr_pos, 0.1f * x_fac, 0.0f);
|
||||
immVertex2f(shdr_pos, 0.020f * x_fac, 0.0f);
|
||||
|
||||
immVertex2f(shdr_pos, 0.0f, -0.020f * y_fac);
|
||||
immVertex2f(shdr_pos, 0.0f, -0.1f * y_fac);
|
||||
|
||||
immVertex2f(shdr_pos, 0.0f, 0.1f * y_fac);
|
||||
immVertex2f(shdr_pos, 0.0f, 0.020f * y_fac);
|
||||
|
||||
immEnd();
|
||||
|
||||
immUnbindProgram();
|
||||
|
||||
GPU_matrix_translate_2f(-cursor[0], -cursor[1]);
|
||||
}
|
||||
|
||||
} // namespace blender
|
||||
215
blender-5.2.0/source/blender/editors/uvedit/uvedit_intern.hh
Normal file
215
blender-5.2.0/source/blender/editors/uvedit/uvedit_intern.hh
Normal file
@@ -0,0 +1,215 @@
|
||||
/* SPDX-FileCopyrightText: 2008 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
/** \file
|
||||
* \ingroup eduv
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "BKE_customdata.hh"
|
||||
|
||||
namespace blender {
|
||||
|
||||
struct BMVert;
|
||||
struct BMEdge;
|
||||
struct BMFace;
|
||||
struct BMLoop;
|
||||
struct Object;
|
||||
struct Scene;
|
||||
struct SpaceImage;
|
||||
struct ToolSettings;
|
||||
struct wmOperatorType;
|
||||
struct View2D;
|
||||
|
||||
/* find nearest */
|
||||
|
||||
struct UvNearestHit {
|
||||
/** Only for `*_multi(..)` versions of functions. */
|
||||
Object *ob;
|
||||
/** Always set if we have a hit. */
|
||||
BMFace *efa;
|
||||
BMLoop *l;
|
||||
/**
|
||||
* Needs to be set before calling nearest functions.
|
||||
*
|
||||
* \note When #uv_nearest_hit_init_dist_px or #uv_nearest_hit_init_max are used,
|
||||
* this value is pixels squared.
|
||||
*/
|
||||
float dist_sq;
|
||||
|
||||
/** Scale the UVs to account for aspect ratio from the image view. */
|
||||
float scale[2];
|
||||
};
|
||||
|
||||
UvNearestHit uv_nearest_hit_init_dist_px(const View2D *v2d, float dist_px);
|
||||
UvNearestHit uv_nearest_hit_init_max(const View2D *v2d);
|
||||
UvNearestHit uv_nearest_hit_init_max_default();
|
||||
|
||||
/**
|
||||
* A utility to set the vertex or edge in #UvNearestHit, useful when face-select
|
||||
* is used as a fallback, but the caller expects to be able to access an element
|
||||
* that would be "picked" based on the current selection mode.
|
||||
*
|
||||
* - Does nothing when `uv_selectmode` is #UV_SELECT_FACE.
|
||||
* - Only call this when #UvNearestHit::efa has been set.
|
||||
*/
|
||||
void uv_nearest_hit_elem_set_from_face(const float co[2], UvNearestHit *hit, short uv_selectmode);
|
||||
|
||||
bool uv_find_nearest_vert(
|
||||
Scene *scene, Object *obedit, const float co[2], float penalty_dist, UvNearestHit *hit);
|
||||
bool uv_find_nearest_vert_multi(Scene *scene,
|
||||
Span<Object *> objects,
|
||||
const float co[2],
|
||||
float penalty_dist,
|
||||
UvNearestHit *hit);
|
||||
|
||||
bool uv_find_nearest_edge(
|
||||
Scene *scene, Object *obedit, const float co[2], float penalty, UvNearestHit *hit);
|
||||
bool uv_find_nearest_edge_multi(
|
||||
Scene *scene, Span<Object *> objects, const float co[2], float penalty, UvNearestHit *hit);
|
||||
|
||||
/**
|
||||
* \param only_in_face: when true, only hit faces which `co` is inside.
|
||||
* This gives users a result they might expect, especially when zoomed in.
|
||||
*
|
||||
* \note Concave faces can cause odd behavior, although in practice this isn't often an issue.
|
||||
* The center can be outside the face, in this case the distance to the center
|
||||
* could cause the face to be considered too far away.
|
||||
* If this becomes an issue we could track the distance to the faces closest edge.
|
||||
*/
|
||||
bool uv_find_nearest_face_ex(
|
||||
Scene *scene, Object *obedit, const float co[2], UvNearestHit *hit, bool only_in_face);
|
||||
bool uv_find_nearest_face(Scene *scene, Object *obedit, const float co[2], UvNearestHit *hit);
|
||||
bool uv_find_nearest_face_multi_ex(
|
||||
Scene *scene, Span<Object *> objects, const float co[2], UvNearestHit *hit, bool only_in_face);
|
||||
bool uv_find_nearest_face_multi(Scene *scene,
|
||||
Span<Object *> objects,
|
||||
const float co[2],
|
||||
UvNearestHit *hit);
|
||||
|
||||
BMLoop *uv_find_nearest_loop_from_vert(Scene *scene, Object *obedit, BMVert *v, const float co[2]);
|
||||
BMLoop *uv_find_nearest_loop_from_edge(Scene *scene, Object *obedit, BMEdge *e, const float co[2]);
|
||||
|
||||
bool uvedit_vert_is_edge_select_any_other(const ToolSettings *ts,
|
||||
const BMesh *bm,
|
||||
const BMLoop *l,
|
||||
const BMUVOffsets &offsets);
|
||||
bool uvedit_vert_is_face_select_any_other(const ToolSettings *ts,
|
||||
const BMesh *bm,
|
||||
const BMLoop *l,
|
||||
const BMUVOffsets &offsets);
|
||||
bool uvedit_edge_is_face_select_any_other(const ToolSettings *ts,
|
||||
const BMesh *bm,
|
||||
const BMLoop *l,
|
||||
const BMUVOffsets &offsets);
|
||||
|
||||
bool uvedit_vert_is_all_other_faces_selected(const ToolSettings *ts,
|
||||
const BMesh *bm,
|
||||
const BMLoop *l,
|
||||
const BMUVOffsets &offsets);
|
||||
|
||||
[[nodiscard]] bool uvedit_vert_select_get_no_sync(const ToolSettings *ts,
|
||||
const BMesh *bm,
|
||||
const BMLoop *l);
|
||||
[[nodiscard]] bool uvedit_edge_select_get_no_sync(const ToolSettings *ts,
|
||||
const BMesh *bm,
|
||||
const BMLoop *l);
|
||||
[[nodiscard]] bool uvedit_face_select_get_no_sync(const ToolSettings *ts,
|
||||
const BMesh *bm,
|
||||
const BMFace *f);
|
||||
|
||||
void uvedit_vert_select_set_no_sync(const ToolSettings *ts,
|
||||
const BMesh *bm,
|
||||
BMLoop *l,
|
||||
bool select);
|
||||
void uvedit_edge_select_set_no_sync(const ToolSettings *ts,
|
||||
const BMesh *bm,
|
||||
BMLoop *l,
|
||||
bool select);
|
||||
void uvedit_face_select_set_no_sync(const ToolSettings *ts,
|
||||
const BMesh *bm,
|
||||
BMFace *f,
|
||||
bool select);
|
||||
|
||||
/* utility tool functions */
|
||||
|
||||
void uvedit_live_unwrap_update(SpaceImage *sima, Scene *scene, Object *obedit);
|
||||
|
||||
/* operators */
|
||||
|
||||
void UV_OT_average_islands_scale(wmOperatorType *ot);
|
||||
void UV_OT_cube_project(wmOperatorType *ot);
|
||||
void UV_OT_cylinder_project(wmOperatorType *ot);
|
||||
void UV_OT_project_from_view(wmOperatorType *ot);
|
||||
void UV_OT_minimize_stretch(wmOperatorType *ot);
|
||||
void UV_OT_pack_islands(wmOperatorType *ot);
|
||||
void UV_OT_reset(wmOperatorType *ot);
|
||||
void UV_OT_sphere_project(wmOperatorType *ot);
|
||||
void UV_OT_unwrap(wmOperatorType *ot);
|
||||
void UV_OT_rip(wmOperatorType *ot);
|
||||
void UV_OT_stitch(wmOperatorType *ot);
|
||||
void UV_OT_smart_project(wmOperatorType *ot);
|
||||
void UV_OT_copy_mirrored_faces(wmOperatorType *ot);
|
||||
|
||||
/* uvedit_copy_paste.cc */
|
||||
void UV_OT_copy(wmOperatorType *ot);
|
||||
void UV_OT_paste(wmOperatorType *ot);
|
||||
|
||||
/* `uvedit_path.cc` */
|
||||
|
||||
void UV_OT_shortest_path_pick(wmOperatorType *ot);
|
||||
void UV_OT_shortest_path_select(wmOperatorType *ot);
|
||||
|
||||
/* `uvedit_select.cc` */
|
||||
|
||||
void uvedit_select_prepare_custom_data(const Scene *scene, BMesh *bm);
|
||||
void uvedit_select_prepare_sync_select(const Scene *scene, BMesh *bm);
|
||||
|
||||
void uvedit_select_prepare(const Scene *scene, BMesh *bm);
|
||||
|
||||
bool uvedit_select_is_any_selected(const Scene *scene, BMesh *bm);
|
||||
bool uvedit_select_is_any_selected_multi(const Scene *scene, Span<Object *> objects);
|
||||
/**
|
||||
* \warning This returns first selected UV,
|
||||
* not ideal in many cases since there could be multiple.
|
||||
*/
|
||||
const float *uvedit_first_selected_uv_from_vertex(Scene *scene,
|
||||
const BMesh *bm,
|
||||
BMVert *eve,
|
||||
const BMUVOffsets &offsets);
|
||||
|
||||
void UV_OT_select_all(wmOperatorType *ot);
|
||||
void UV_OT_select(wmOperatorType *ot);
|
||||
void UV_OT_select_loop(wmOperatorType *ot);
|
||||
void UV_OT_select_edge_ring(wmOperatorType *ot);
|
||||
void UV_OT_select_linked(wmOperatorType *ot);
|
||||
void UV_OT_select_linked_pick(wmOperatorType *ot);
|
||||
void UV_OT_select_split(wmOperatorType *ot);
|
||||
void UV_OT_select_pinned(wmOperatorType *ot);
|
||||
void UV_OT_select_box(wmOperatorType *ot);
|
||||
void UV_OT_select_lasso(wmOperatorType *ot);
|
||||
void UV_OT_select_circle(wmOperatorType *ot);
|
||||
void UV_OT_select_more(wmOperatorType *ot);
|
||||
void UV_OT_select_less(wmOperatorType *ot);
|
||||
void UV_OT_select_overlap(wmOperatorType *ot);
|
||||
void UV_OT_select_by_winding(wmOperatorType *ot);
|
||||
void UV_OT_select_similar(wmOperatorType *ot);
|
||||
void UV_OT_select_tile(wmOperatorType *ot);
|
||||
|
||||
void UV_OT_custom_region_set(wmOperatorType *ot);
|
||||
|
||||
/* Used only when UV sync select is disabled. */
|
||||
void UV_OT_select_mode(wmOperatorType *ot);
|
||||
|
||||
/**
|
||||
* Stitch the selected UV islands together for the unwrap "Original Bounds" option.
|
||||
* Uses a fixed configuration: vertex mode, snap islands, only selected UVs,
|
||||
* ignore fully seam-bounded islands, no distance limit, and no draw preview.
|
||||
*
|
||||
* \return false if stitching could not be initialized.
|
||||
*/
|
||||
bool uv_stitch_selected_islands_for_original_bounds(const Scene *scene, Span<Object *> objects);
|
||||
|
||||
} // namespace blender
|
||||
176
blender-5.2.0/source/blender/editors/uvedit/uvedit_islands.cc
Normal file
176
blender-5.2.0/source/blender/editors/uvedit/uvedit_islands.cc
Normal file
@@ -0,0 +1,176 @@
|
||||
/* SPDX-FileCopyrightText: 2023 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
/** \file
|
||||
* \ingroup eduv
|
||||
*
|
||||
* Utilities for manipulating UV islands.
|
||||
*
|
||||
* \note This is similar to `GEO_uv_parametrizer.hh`,
|
||||
* however the data structures there don't support arbitrary topology
|
||||
* such as an edge with 3 or more faces using it.
|
||||
* This API uses #BMesh data structures and doesn't have limitations for manifold meshes.
|
||||
*/
|
||||
|
||||
#include "BLI_listbase.h"
|
||||
#include "BLI_math_vector.h"
|
||||
|
||||
#include "BKE_editmesh.hh"
|
||||
|
||||
#include "DNA_image_types.h"
|
||||
|
||||
#include "ED_uvedit.hh" /* Own include. */
|
||||
|
||||
namespace blender {
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name UDIM packing helper functions
|
||||
* \{ */
|
||||
|
||||
bool uv_coords_isect_udim(const Image *image, const int udim_grid[2], const float coords[2])
|
||||
{
|
||||
const float coords_floor[2] = {floorf(coords[0]), floorf(coords[1])};
|
||||
const bool is_tiled_image = image && (image->source == IMA_SRC_TILED);
|
||||
|
||||
if (coords[0] < udim_grid[0] && coords[0] > 0 && coords[1] < udim_grid[1] && coords[1] > 0) {
|
||||
return true;
|
||||
}
|
||||
/* Check if selection lies on a valid UDIM image tile. */
|
||||
if (is_tiled_image) {
|
||||
for (const ImageTile &tile : image->tiles) {
|
||||
const int tile_index = tile.tile_number - 1001;
|
||||
const int target_x = (tile_index % 10);
|
||||
const int target_y = (tile_index / 10);
|
||||
if (coords_floor[0] == target_x && coords_floor[1] == target_y) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
/* Probably not required since UDIM grid checks for 1001. */
|
||||
else if (image && !is_tiled_image) {
|
||||
if (is_zero_v2(coords_floor)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/** \} */
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name Calculate UV Islands
|
||||
* \{ */
|
||||
|
||||
struct SharedUVLoopData {
|
||||
BMUVOffsets offsets;
|
||||
bool use_seams;
|
||||
};
|
||||
|
||||
static bool bm_loop_uv_shared_edge_check(const BMLoop *l_a, const BMLoop *l_b, void *user_data)
|
||||
{
|
||||
const SharedUVLoopData *data = static_cast<const SharedUVLoopData *>(user_data);
|
||||
|
||||
if (data->use_seams) {
|
||||
if (BM_elem_flag_test(l_a->e, BM_ELEM_SEAM)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return BM_loop_uv_share_edge_check(
|
||||
const_cast<BMLoop *>(l_a), const_cast<BMLoop *>(l_b), data->offsets.uv);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if `efa` is able to be affected by a packing operation, given various parameters.
|
||||
*
|
||||
* Checks if it's (not) hidden, and optionally selected, and/or UV selected.
|
||||
*
|
||||
* Will eventually be superseded by `BM_uv_element_map_create()`.
|
||||
*
|
||||
* Loosely based on `uvedit_is_face_affected`, but "bug-compatible" with previous code.
|
||||
*/
|
||||
static bool uvedit_is_face_affected_for_calc_uv_islands(const Scene *scene,
|
||||
const BMesh *bm,
|
||||
BMFace *efa,
|
||||
const bool only_selected_faces,
|
||||
const bool only_selected_uvs)
|
||||
{
|
||||
if (BM_elem_flag_test(efa, BM_ELEM_HIDDEN)) {
|
||||
return false;
|
||||
}
|
||||
if (only_selected_faces) {
|
||||
if (only_selected_uvs) {
|
||||
return BM_elem_flag_test(efa, BM_ELEM_SELECT) && uvedit_face_select_test(scene, bm, efa);
|
||||
}
|
||||
return BM_elem_flag_test(efa, BM_ELEM_SELECT);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
int bm_mesh_calc_uv_islands(const Scene *scene,
|
||||
BMesh *bm,
|
||||
ListBaseT<FaceIsland> *island_list,
|
||||
const bool only_selected_faces,
|
||||
const bool only_selected_uvs,
|
||||
const bool use_seams,
|
||||
const float aspect_y,
|
||||
const BMUVOffsets &uv_offsets)
|
||||
{
|
||||
BLI_assert(uv_offsets.uv >= 0);
|
||||
int island_added = 0;
|
||||
BM_mesh_elem_table_ensure(bm, BM_FACE);
|
||||
|
||||
int *groups_array = MEM_new_array_uninitialized<int>(bm->totface, __func__);
|
||||
|
||||
int (*group_index)[2];
|
||||
|
||||
/* Set the tag for `BM_mesh_calc_face_groups`. */
|
||||
BMFace *f;
|
||||
BMIter iter;
|
||||
BM_ITER_MESH (f, &iter, bm, BM_FACES_OF_MESH) {
|
||||
const bool face_affected = uvedit_is_face_affected_for_calc_uv_islands(
|
||||
scene, bm, f, only_selected_faces, only_selected_uvs);
|
||||
BM_elem_flag_set(f, BM_ELEM_TAG, face_affected);
|
||||
}
|
||||
|
||||
SharedUVLoopData user_data = {{0}};
|
||||
user_data.offsets = uv_offsets;
|
||||
user_data.use_seams = use_seams;
|
||||
|
||||
const int group_len = BM_mesh_calc_face_groups(bm,
|
||||
groups_array,
|
||||
&group_index,
|
||||
nullptr,
|
||||
bm_loop_uv_shared_edge_check,
|
||||
&user_data,
|
||||
BM_ELEM_TAG,
|
||||
BM_EDGE);
|
||||
|
||||
for (int i = 0; i < group_len; i++) {
|
||||
const int faces_start = group_index[i][0];
|
||||
const int faces_len = group_index[i][1];
|
||||
BMFace **faces = MEM_new_array_uninitialized<BMFace *>(faces_len, __func__);
|
||||
|
||||
for (int j = 0; j < faces_len; j++) {
|
||||
faces[j] = BM_face_at_index(bm, groups_array[faces_start + j]);
|
||||
}
|
||||
|
||||
FaceIsland *island = MEM_new_zeroed<FaceIsland>(__func__);
|
||||
island->faces = faces;
|
||||
island->faces_len = faces_len;
|
||||
island->offsets = uv_offsets;
|
||||
island->aspect_y = aspect_y;
|
||||
BLI_addtail(island_list, island);
|
||||
island_added += 1;
|
||||
}
|
||||
|
||||
MEM_delete(groups_array);
|
||||
MEM_delete(group_index);
|
||||
return island_added;
|
||||
}
|
||||
|
||||
/** \} */
|
||||
|
||||
} // namespace blender
|
||||
3009
blender-5.2.0/source/blender/editors/uvedit/uvedit_ops.cc
Normal file
3009
blender-5.2.0/source/blender/editors/uvedit/uvedit_ops.cc
Normal file
File diff suppressed because it is too large
Load Diff
897
blender-5.2.0/source/blender/editors/uvedit/uvedit_path.cc
Normal file
897
blender-5.2.0/source/blender/editors/uvedit/uvedit_path.cc
Normal file
@@ -0,0 +1,897 @@
|
||||
/* SPDX-FileCopyrightText: 2023 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
/** \file
|
||||
* \ingroup eduv
|
||||
*
|
||||
* \note The logic in this file closely follows `editmesh_path.cc`.
|
||||
*/
|
||||
|
||||
#include <cstdlib>
|
||||
#include <cstring>
|
||||
|
||||
#include "BLI_linklist.h"
|
||||
#include "BLI_math_vector.h"
|
||||
#include "BLI_utildefines.h"
|
||||
|
||||
#include "MEM_guardedalloc.h"
|
||||
|
||||
#include "DNA_mesh_types.h"
|
||||
#include "DNA_object_types.h"
|
||||
#include "DNA_scene_types.h"
|
||||
#include "DNA_windowmanager_types.h"
|
||||
|
||||
#include "BKE_context.hh"
|
||||
#include "BKE_customdata.hh"
|
||||
#include "BKE_editmesh.hh"
|
||||
#include "BKE_layer.hh"
|
||||
#include "BKE_mesh.hh"
|
||||
#include "BKE_report.hh"
|
||||
|
||||
#include "DEG_depsgraph.hh"
|
||||
#include "DEG_depsgraph_query.hh"
|
||||
|
||||
#include "ED_object.hh"
|
||||
#include "ED_screen.hh"
|
||||
#include "ED_uvedit.hh"
|
||||
|
||||
#include "RNA_access.hh"
|
||||
#include "RNA_define.hh"
|
||||
|
||||
#include "WM_api.hh"
|
||||
#include "WM_types.hh"
|
||||
|
||||
#include "UI_view2d.hh"
|
||||
|
||||
#include "intern/bmesh_marking.hh"
|
||||
#include "uvedit_intern.hh"
|
||||
|
||||
#include "bmesh_tools.hh"
|
||||
|
||||
namespace blender {
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name Path Select Struct & Properties
|
||||
* \{ */
|
||||
|
||||
namespace {
|
||||
|
||||
struct PathSelectParams {
|
||||
/** ensure the active element is the last selected item (handy for picking) */
|
||||
bool track_active;
|
||||
bool use_topology_distance;
|
||||
bool use_face_step;
|
||||
bool use_fill;
|
||||
CheckerIntervalParams interval_params;
|
||||
};
|
||||
|
||||
struct UserData_UV {
|
||||
Scene *scene;
|
||||
BMesh *bm;
|
||||
BMUVOffsets offsets;
|
||||
};
|
||||
|
||||
} // namespace
|
||||
|
||||
static void path_select_properties(wmOperatorType *ot)
|
||||
{
|
||||
RNA_def_boolean(ot->srna,
|
||||
"use_face_step",
|
||||
false,
|
||||
"Face Stepping",
|
||||
"Traverse connected faces (includes diagonals and edge-rings)");
|
||||
RNA_def_boolean(ot->srna,
|
||||
"use_topology_distance",
|
||||
false,
|
||||
"Topology Distance",
|
||||
"Find the minimum number of steps, ignoring spatial distance");
|
||||
RNA_def_boolean(ot->srna,
|
||||
"use_fill",
|
||||
false,
|
||||
"Fill Region",
|
||||
"Select all paths between the source/destination elements");
|
||||
|
||||
WM_operator_properties_checker_interval(ot, true);
|
||||
}
|
||||
|
||||
static void path_select_params_from_op(wmOperator *op, PathSelectParams *op_params)
|
||||
{
|
||||
op_params->track_active = false;
|
||||
op_params->use_face_step = RNA_boolean_get(op->ptr, "use_face_step");
|
||||
op_params->use_fill = RNA_boolean_get(op->ptr, "use_fill");
|
||||
op_params->use_topology_distance = RNA_boolean_get(op->ptr, "use_topology_distance");
|
||||
WM_operator_properties_checker_interval_from_op(op, &op_params->interval_params);
|
||||
}
|
||||
|
||||
/** \} */
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name UV Vert Path
|
||||
* \{ */
|
||||
|
||||
/* callbacks */
|
||||
static bool verttag_filter_cb(BMLoop *l, void *user_data_v)
|
||||
{
|
||||
UserData_UV *user_data = static_cast<UserData_UV *>(user_data_v);
|
||||
return uvedit_face_visible_test(user_data->scene, l->f);
|
||||
}
|
||||
static bool verttag_test_cb(BMLoop *l, void *user_data_v)
|
||||
{
|
||||
/* All connected loops are selected or we return false. */
|
||||
UserData_UV *user_data = static_cast<UserData_UV *>(user_data_v);
|
||||
const Scene *scene = user_data->scene;
|
||||
const int cd_loop_uv_offset = user_data->offsets.uv;
|
||||
const float *luv = BM_ELEM_CD_GET_FLOAT_P(l, cd_loop_uv_offset);
|
||||
BMIter iter;
|
||||
BMLoop *l_iter;
|
||||
BM_ITER_ELEM (l_iter, &iter, l->v, BM_LOOPS_OF_VERT) {
|
||||
if (verttag_filter_cb(l_iter, user_data)) {
|
||||
const float *luv_iter = BM_ELEM_CD_GET_FLOAT_P(l_iter, cd_loop_uv_offset);
|
||||
if (equals_v2v2(luv, luv_iter)) {
|
||||
if (!uvedit_uv_select_test(scene, user_data->bm, l_iter, user_data->offsets)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
static void verttag_set_cb(BMLoop *l, bool val, void *user_data_v)
|
||||
{
|
||||
UserData_UV *user_data = static_cast<UserData_UV *>(user_data_v);
|
||||
const Scene *scene = user_data->scene;
|
||||
BMesh *bm = user_data->bm;
|
||||
const uint cd_loop_uv_offset = user_data->offsets.uv;
|
||||
const float *luv = BM_ELEM_CD_GET_FLOAT_P(l, cd_loop_uv_offset);
|
||||
BMIter iter;
|
||||
BMLoop *l_iter;
|
||||
BM_ITER_ELEM (l_iter, &iter, l->v, BM_LOOPS_OF_VERT) {
|
||||
if (verttag_filter_cb(l_iter, user_data)) {
|
||||
const float *luv_iter = BM_ELEM_CD_GET_FLOAT_P(l_iter, cd_loop_uv_offset);
|
||||
if (equals_v2v2(luv, luv_iter)) {
|
||||
uvedit_uv_select_set(scene, bm, l_iter, val);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static int mouse_mesh_uv_shortest_path_vert(Scene *scene,
|
||||
Object *obedit,
|
||||
const PathSelectParams *op_params,
|
||||
BMLoop *l_src,
|
||||
BMLoop *l_dst,
|
||||
const float aspect_y,
|
||||
const BMUVOffsets &offsets)
|
||||
{
|
||||
BMEditMesh *em = BKE_editmesh_from_object(obedit);
|
||||
BMesh *bm = em->bm;
|
||||
int flush = 0;
|
||||
|
||||
UserData_UV user_data = {};
|
||||
user_data.scene = scene;
|
||||
user_data.bm = bm;
|
||||
user_data.offsets = offsets;
|
||||
|
||||
BMCalcPathUVParams params{};
|
||||
params.use_topology_distance = op_params->use_topology_distance;
|
||||
params.use_step_face = op_params->use_face_step;
|
||||
params.aspect_y = aspect_y;
|
||||
params.cd_loop_uv_offset = offsets.uv;
|
||||
|
||||
LinkNode *path = nullptr;
|
||||
bool is_path_ordered = false;
|
||||
|
||||
if (l_src != l_dst) {
|
||||
if (op_params->use_fill) {
|
||||
path = BM_mesh_calc_path_uv_region_vert(bm,
|
||||
reinterpret_cast<BMElem *>(l_src),
|
||||
reinterpret_cast<BMElem *>(l_dst),
|
||||
params.cd_loop_uv_offset,
|
||||
verttag_filter_cb,
|
||||
&user_data);
|
||||
}
|
||||
else {
|
||||
is_path_ordered = true;
|
||||
path = BM_mesh_calc_path_uv_vert(bm, l_src, l_dst, ¶ms, verttag_filter_cb, &user_data);
|
||||
}
|
||||
}
|
||||
|
||||
BMLoop *l_dst_last = l_dst;
|
||||
|
||||
if (path) {
|
||||
/* toggle the flag */
|
||||
bool all_set = true;
|
||||
LinkNode *node = path;
|
||||
do {
|
||||
if (!verttag_test_cb(static_cast<BMLoop *>(node->link), &user_data)) {
|
||||
all_set = false;
|
||||
break;
|
||||
}
|
||||
} while ((node = node->next));
|
||||
|
||||
int depth = -1;
|
||||
node = path;
|
||||
do {
|
||||
if ((is_path_ordered == false) ||
|
||||
WM_operator_properties_checker_interval_test(&op_params->interval_params, depth))
|
||||
{
|
||||
verttag_set_cb(static_cast<BMLoop *>(node->link), !all_set, &user_data);
|
||||
if (is_path_ordered) {
|
||||
l_dst_last = static_cast<BMLoop *>(node->link);
|
||||
}
|
||||
}
|
||||
} while ((void)depth++, (node = node->next));
|
||||
|
||||
BLI_linklist_free(path, nullptr);
|
||||
flush = all_set ? -1 : 1;
|
||||
}
|
||||
else {
|
||||
const bool is_act = !verttag_test_cb(l_dst, &user_data);
|
||||
verttag_set_cb(l_dst, is_act, &user_data); /* switch the face option */
|
||||
}
|
||||
|
||||
if (op_params->track_active) {
|
||||
ED_uvedit_active_vert_loop_set(bm, l_dst_last);
|
||||
}
|
||||
return flush;
|
||||
}
|
||||
|
||||
/** \} */
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name UV Edge Path
|
||||
* \{ */
|
||||
|
||||
/* callbacks */
|
||||
static bool edgetag_filter_cb(BMLoop *l, void *user_data_v)
|
||||
{
|
||||
UserData_UV *user_data = static_cast<UserData_UV *>(user_data_v);
|
||||
return uvedit_face_visible_test(user_data->scene, l->f);
|
||||
}
|
||||
static bool edgetag_test_cb(BMLoop *l, void *user_data_v)
|
||||
{
|
||||
/* All connected loops (UV) are selected or we return false. */
|
||||
UserData_UV *user_data = static_cast<UserData_UV *>(user_data_v);
|
||||
const Scene *scene = user_data->scene;
|
||||
BMIter iter;
|
||||
BMLoop *l_iter;
|
||||
BM_ITER_ELEM (l_iter, &iter, l->e, BM_LOOPS_OF_EDGE) {
|
||||
if (edgetag_filter_cb(l_iter, user_data)) {
|
||||
if (BM_loop_uv_share_edge_check(l, l_iter, user_data->offsets.uv)) {
|
||||
if (!uvedit_edge_select_test(scene, user_data->bm, l_iter, user_data->offsets)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
static void edgetag_set_cb(BMLoop *l, bool val, void *user_data_v)
|
||||
{
|
||||
UserData_UV *user_data = static_cast<UserData_UV *>(user_data_v);
|
||||
const Scene *scene = user_data->scene;
|
||||
BMesh *bm = user_data->bm;
|
||||
uvedit_edge_select_set_with_sticky(scene, bm, l, val, user_data->offsets);
|
||||
}
|
||||
|
||||
static int mouse_mesh_uv_shortest_path_edge(Scene *scene,
|
||||
Object *obedit,
|
||||
const PathSelectParams *op_params,
|
||||
BMLoop *l_src,
|
||||
BMLoop *l_dst,
|
||||
const float aspect_y,
|
||||
const BMUVOffsets &offsets)
|
||||
{
|
||||
BMEditMesh *em = BKE_editmesh_from_object(obedit);
|
||||
BMesh *bm = em->bm;
|
||||
int flush = 0;
|
||||
|
||||
UserData_UV user_data = {};
|
||||
user_data.scene = scene;
|
||||
user_data.bm = bm;
|
||||
user_data.offsets = offsets;
|
||||
|
||||
BMCalcPathUVParams params = {};
|
||||
params.use_topology_distance = op_params->use_topology_distance;
|
||||
params.use_step_face = op_params->use_face_step;
|
||||
params.aspect_y = aspect_y;
|
||||
params.cd_loop_uv_offset = offsets.uv;
|
||||
|
||||
LinkNode *path = nullptr;
|
||||
bool is_path_ordered = false;
|
||||
|
||||
if (l_src != l_dst) {
|
||||
if (op_params->use_fill) {
|
||||
path = BM_mesh_calc_path_uv_region_edge(bm,
|
||||
reinterpret_cast<BMElem *>(l_src),
|
||||
reinterpret_cast<BMElem *>(l_dst),
|
||||
params.cd_loop_uv_offset,
|
||||
edgetag_filter_cb,
|
||||
&user_data);
|
||||
}
|
||||
else {
|
||||
is_path_ordered = true;
|
||||
path = BM_mesh_calc_path_uv_edge(bm, l_src, l_dst, ¶ms, edgetag_filter_cb, &user_data);
|
||||
}
|
||||
}
|
||||
|
||||
BMLoop *l_dst_last = l_dst;
|
||||
|
||||
if (path) {
|
||||
/* toggle the flag */
|
||||
bool all_set = true;
|
||||
LinkNode *node = path;
|
||||
do {
|
||||
if (!edgetag_test_cb(static_cast<BMLoop *>(node->link), &user_data)) {
|
||||
all_set = false;
|
||||
break;
|
||||
}
|
||||
} while ((node = node->next));
|
||||
|
||||
int depth = -1;
|
||||
node = path;
|
||||
do {
|
||||
if ((is_path_ordered == false) ||
|
||||
WM_operator_properties_checker_interval_test(&op_params->interval_params, depth))
|
||||
{
|
||||
edgetag_set_cb(static_cast<BMLoop *>(node->link), !all_set, &user_data);
|
||||
if (is_path_ordered) {
|
||||
l_dst_last = static_cast<BMLoop *>(node->link);
|
||||
}
|
||||
}
|
||||
} while ((void)depth++, (node = node->next));
|
||||
|
||||
BLI_linklist_free(path, nullptr);
|
||||
flush = all_set ? -1 : 1;
|
||||
}
|
||||
else {
|
||||
const bool is_act = !edgetag_test_cb(l_dst, &user_data);
|
||||
edgetag_set_cb(l_dst, is_act, &user_data); /* switch the face option */
|
||||
}
|
||||
|
||||
if (op_params->track_active) {
|
||||
ED_uvedit_active_edge_loop_set(bm, l_dst_last);
|
||||
}
|
||||
return flush;
|
||||
}
|
||||
|
||||
/** \} */
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name UV Face Path
|
||||
* \{ */
|
||||
|
||||
/* callbacks */
|
||||
static bool facetag_filter_cb(BMFace *f, void *user_data_v)
|
||||
{
|
||||
UserData_UV *user_data = static_cast<UserData_UV *>(user_data_v);
|
||||
return uvedit_face_visible_test(user_data->scene, f);
|
||||
}
|
||||
static bool facetag_test_cb(BMFace *f, void *user_data_v)
|
||||
{
|
||||
/* All connected loops are selected or we return false. */
|
||||
UserData_UV *user_data = static_cast<UserData_UV *>(user_data_v);
|
||||
const Scene *scene = user_data->scene;
|
||||
BMIter iter;
|
||||
BMLoop *l_iter;
|
||||
BM_ITER_ELEM (l_iter, &iter, f, BM_LOOPS_OF_FACE) {
|
||||
if (!uvedit_edge_select_test(scene, user_data->bm, l_iter, user_data->offsets)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
static void facetag_set_cb(BMFace *f, bool val, void *user_data_v)
|
||||
{
|
||||
UserData_UV *user_data = static_cast<UserData_UV *>(user_data_v);
|
||||
const Scene *scene = user_data->scene;
|
||||
BMesh *bm = user_data->bm;
|
||||
uvedit_face_select_set_with_sticky(scene, bm, f, val, user_data->offsets);
|
||||
}
|
||||
|
||||
static int mouse_mesh_uv_shortest_path_face(Scene *scene,
|
||||
Object *obedit,
|
||||
const PathSelectParams *op_params,
|
||||
BMFace *f_src,
|
||||
BMFace *f_dst,
|
||||
const float aspect_y,
|
||||
const BMUVOffsets &offsets)
|
||||
{
|
||||
BMEditMesh *em = BKE_editmesh_from_object(obedit);
|
||||
BMesh *bm = em->bm;
|
||||
int flush = 0;
|
||||
|
||||
UserData_UV user_data = {};
|
||||
user_data.scene = scene;
|
||||
user_data.bm = bm;
|
||||
user_data.offsets = offsets;
|
||||
|
||||
BMCalcPathUVParams params = {};
|
||||
params.use_topology_distance = op_params->use_topology_distance;
|
||||
params.use_step_face = op_params->use_face_step;
|
||||
params.aspect_y = aspect_y;
|
||||
params.cd_loop_uv_offset = offsets.uv;
|
||||
|
||||
LinkNode *path = nullptr;
|
||||
bool is_path_ordered = false;
|
||||
|
||||
if (f_src != f_dst) {
|
||||
if (op_params->use_fill) {
|
||||
path = BM_mesh_calc_path_uv_region_face(bm,
|
||||
reinterpret_cast<BMElem *>(f_src),
|
||||
reinterpret_cast<BMElem *>(f_dst),
|
||||
params.cd_loop_uv_offset,
|
||||
facetag_filter_cb,
|
||||
&user_data);
|
||||
}
|
||||
else {
|
||||
is_path_ordered = true;
|
||||
path = BM_mesh_calc_path_uv_face(bm, f_src, f_dst, ¶ms, facetag_filter_cb, &user_data);
|
||||
}
|
||||
}
|
||||
|
||||
BMFace *f_dst_last = f_dst;
|
||||
|
||||
if (path) {
|
||||
/* toggle the flag */
|
||||
bool all_set = true;
|
||||
LinkNode *node = path;
|
||||
do {
|
||||
if (!facetag_test_cb(static_cast<BMFace *>(node->link), &user_data)) {
|
||||
all_set = false;
|
||||
break;
|
||||
}
|
||||
} while ((node = node->next));
|
||||
|
||||
int depth = -1;
|
||||
node = path;
|
||||
do {
|
||||
if ((is_path_ordered == false) ||
|
||||
WM_operator_properties_checker_interval_test(&op_params->interval_params, depth))
|
||||
{
|
||||
facetag_set_cb(static_cast<BMFace *>(node->link), !all_set, &user_data);
|
||||
if (is_path_ordered) {
|
||||
f_dst_last = static_cast<BMFace *>(node->link);
|
||||
}
|
||||
}
|
||||
} while ((void)depth++, (node = node->next));
|
||||
|
||||
BLI_linklist_free(path, nullptr);
|
||||
flush = all_set ? -1 : 1;
|
||||
}
|
||||
else {
|
||||
const bool is_act = !facetag_test_cb(f_dst, &user_data);
|
||||
facetag_set_cb(f_dst, is_act, &user_data); /* switch the face option */
|
||||
}
|
||||
|
||||
if (op_params->track_active) {
|
||||
/* Unlike other types, we can track active without it being selected. */
|
||||
BM_mesh_active_face_set(bm, f_dst_last);
|
||||
}
|
||||
return flush;
|
||||
}
|
||||
|
||||
/** \} */
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name Main Operator for vert/edge/face tag
|
||||
* \{ */
|
||||
|
||||
static wmOperatorStatus uv_shortest_path_pick_exec(bContext *C, wmOperator *op);
|
||||
|
||||
static bool uv_shortest_path_pick_ex(Scene *scene,
|
||||
Depsgraph *depsgraph,
|
||||
Object *obedit,
|
||||
const PathSelectParams *op_params,
|
||||
BMElem *ele_src,
|
||||
BMElem *ele_dst,
|
||||
const float aspect_y,
|
||||
const BMUVOffsets &offsets)
|
||||
{
|
||||
const ToolSettings *ts = scene->toolsettings;
|
||||
const char uv_selectmode = ED_uvedit_select_mode_get(scene);
|
||||
bool ok = false;
|
||||
int flush = 0;
|
||||
|
||||
if (ELEM(nullptr, ele_src, ele_dst) || (ele_src->head.htype != ele_dst->head.htype)) {
|
||||
/* pass */
|
||||
}
|
||||
else if (ele_src->head.htype == BM_FACE) {
|
||||
flush = mouse_mesh_uv_shortest_path_face(scene,
|
||||
obedit,
|
||||
op_params,
|
||||
reinterpret_cast<BMFace *>(ele_src),
|
||||
reinterpret_cast<BMFace *>(ele_dst),
|
||||
aspect_y,
|
||||
offsets);
|
||||
ok = true;
|
||||
}
|
||||
else if (ele_src->head.htype == BM_LOOP) {
|
||||
if (uv_selectmode & UV_SELECT_EDGE) {
|
||||
flush = mouse_mesh_uv_shortest_path_edge(scene,
|
||||
obedit,
|
||||
op_params,
|
||||
reinterpret_cast<BMLoop *>(ele_src),
|
||||
reinterpret_cast<BMLoop *>(ele_dst),
|
||||
aspect_y,
|
||||
offsets);
|
||||
}
|
||||
else {
|
||||
flush = mouse_mesh_uv_shortest_path_vert(scene,
|
||||
obedit,
|
||||
op_params,
|
||||
reinterpret_cast<BMLoop *>(ele_src),
|
||||
reinterpret_cast<BMLoop *>(ele_dst),
|
||||
aspect_y,
|
||||
offsets);
|
||||
}
|
||||
ok = true;
|
||||
}
|
||||
|
||||
if (ok) {
|
||||
if (flush != 0) {
|
||||
const bool select = (flush == 1);
|
||||
BMesh *bm = BKE_editmesh_from_object(obedit)->bm;
|
||||
if (ts->uv_flag & UV_FLAG_SELECT_SYNC) {
|
||||
ED_uvedit_select_sync_flush(scene->toolsettings, bm, select);
|
||||
}
|
||||
else {
|
||||
ED_uvedit_selectmode_flush(scene, bm);
|
||||
}
|
||||
}
|
||||
|
||||
if (ts->uv_flag & UV_FLAG_SELECT_SYNC) {
|
||||
DEG_id_tag_update(obedit->data, ID_RECALC_SELECT);
|
||||
}
|
||||
else {
|
||||
Object *obedit_eval = DEG_get_evaluated(depsgraph, obedit);
|
||||
BKE_mesh_batch_cache_dirty_tag(id_cast<Mesh *>(obedit_eval->data),
|
||||
BKE_MESH_BATCH_DIRTY_UVEDIT_SELECT);
|
||||
}
|
||||
/* Only for region redraw. */
|
||||
WM_main_add_notifier(NC_GEOM | ND_SELECT, obedit->data);
|
||||
}
|
||||
|
||||
return ok;
|
||||
}
|
||||
|
||||
static wmOperatorStatus uv_shortest_path_pick_invoke(bContext *C,
|
||||
wmOperator *op,
|
||||
const wmEvent *event)
|
||||
{
|
||||
const Main *bmain = CTX_data_main(C);
|
||||
Scene *scene = CTX_data_scene(C);
|
||||
const ToolSettings *ts = scene->toolsettings;
|
||||
const char uv_selectmode = ED_uvedit_select_mode_get(scene);
|
||||
|
||||
/* We could support this, it needs further testing. */
|
||||
if (RNA_struct_property_is_set(op->ptr, "index")) {
|
||||
return uv_shortest_path_pick_exec(C, op);
|
||||
}
|
||||
|
||||
PathSelectParams op_params;
|
||||
path_select_params_from_op(op, &op_params);
|
||||
|
||||
/* Set false if we support edge tagging. */
|
||||
op_params.track_active = true;
|
||||
|
||||
Depsgraph *depsgraph = CTX_data_ensure_evaluated_depsgraph(C);
|
||||
ViewLayer *view_layer = CTX_data_view_layer(C);
|
||||
Vector<Object *> objects = BKE_view_layer_array_from_objects_in_edit_mode_unique_data_with_uvs(
|
||||
*bmain, scene, view_layer, nullptr);
|
||||
|
||||
float co[2];
|
||||
|
||||
const ARegion *region = CTX_wm_region(C);
|
||||
|
||||
ui::view2d_region_to_view(®ion->v2d, event->mval[0], event->mval[1], &co[0], &co[1]);
|
||||
|
||||
BMElem *ele_src = nullptr, *ele_dst = nullptr;
|
||||
|
||||
/* Detect the hit. */
|
||||
UvNearestHit hit = uv_nearest_hit_init_max(®ion->v2d);
|
||||
bool hit_found = false;
|
||||
if (uv_selectmode == UV_SELECT_FACE) {
|
||||
if (uv_find_nearest_face_multi(scene, objects, co, &hit)) {
|
||||
hit_found = true;
|
||||
}
|
||||
}
|
||||
else if (uv_selectmode & UV_SELECT_EDGE) {
|
||||
if (uv_find_nearest_edge_multi(scene, objects, co, 0.0f, &hit)) {
|
||||
hit_found = true;
|
||||
}
|
||||
}
|
||||
else {
|
||||
if (uv_find_nearest_vert_multi(scene, objects, co, 0.0f, &hit)) {
|
||||
hit_found = true;
|
||||
}
|
||||
}
|
||||
|
||||
bool changed = false;
|
||||
if (hit_found) {
|
||||
/* This may not be the active object. */
|
||||
Object *obedit = hit.ob;
|
||||
BMEditMesh *em = BKE_editmesh_from_object(obedit);
|
||||
BMesh *bm = em->bm;
|
||||
const BMUVOffsets offsets = BM_uv_map_offsets_get(bm);
|
||||
|
||||
/* Respond to the hit. */
|
||||
if (uv_selectmode == UV_SELECT_FACE) {
|
||||
/* Face selection. */
|
||||
BMFace *f_src = BM_mesh_active_face_get(bm, false, false);
|
||||
/* Check selection? */
|
||||
ele_src = reinterpret_cast<BMElem *>(f_src);
|
||||
ele_dst = reinterpret_cast<BMElem *>(hit.efa);
|
||||
}
|
||||
else if (uv_selectmode & UV_SELECT_EDGE) {
|
||||
/* Edge selection. */
|
||||
BMLoop *l_src = nullptr;
|
||||
if ((ts->uv_flag & UV_FLAG_SELECT_SYNC) && (bm->uv_select_sync_valid == false)) {
|
||||
BMEdge *e_src = BM_mesh_active_edge_get(bm);
|
||||
if (e_src != nullptr) {
|
||||
l_src = uv_find_nearest_loop_from_edge(scene, obedit, e_src, co);
|
||||
}
|
||||
}
|
||||
else {
|
||||
l_src = ED_uvedit_active_edge_loop_get(ts, bm);
|
||||
if (l_src != nullptr) {
|
||||
if (!uvedit_uv_select_test(scene, bm, l_src, offsets) &&
|
||||
!uvedit_uv_select_test(scene, bm, l_src->next, offsets))
|
||||
{
|
||||
l_src = nullptr;
|
||||
}
|
||||
ele_src = reinterpret_cast<BMElem *>(l_src);
|
||||
}
|
||||
}
|
||||
ele_src = reinterpret_cast<BMElem *>(l_src);
|
||||
ele_dst = reinterpret_cast<BMElem *>(hit.l);
|
||||
}
|
||||
else {
|
||||
/* Vertex selection. */
|
||||
BMLoop *l_src = nullptr;
|
||||
if ((ts->uv_flag & UV_FLAG_SELECT_SYNC) && (bm->uv_select_sync_valid == false)) {
|
||||
BMVert *v_src = BM_mesh_active_vert_get(bm);
|
||||
if (v_src != nullptr) {
|
||||
l_src = uv_find_nearest_loop_from_vert(scene, obedit, v_src, co);
|
||||
}
|
||||
}
|
||||
else {
|
||||
l_src = ED_uvedit_active_vert_loop_get(ts, bm);
|
||||
if (l_src != nullptr) {
|
||||
if (!uvedit_uv_select_test(scene, bm, l_src, offsets)) {
|
||||
l_src = nullptr;
|
||||
}
|
||||
}
|
||||
}
|
||||
ele_src = reinterpret_cast<BMElem *>(l_src);
|
||||
ele_dst = reinterpret_cast<BMElem *>(hit.l);
|
||||
}
|
||||
|
||||
if (ele_src && ele_dst) {
|
||||
/* Always use the active object, not `obedit` as the active defines the UV display. */
|
||||
const float aspect_y = ED_uvedit_get_aspect_y(CTX_data_edit_object(C));
|
||||
uv_shortest_path_pick_ex(
|
||||
scene, depsgraph, obedit, &op_params, ele_src, ele_dst, aspect_y, offsets);
|
||||
|
||||
/* Store the object and it's index so redo is possible. */
|
||||
int index;
|
||||
if (uv_selectmode & UV_SELECT_FACE) {
|
||||
BM_mesh_elem_index_ensure(bm, BM_FACE);
|
||||
index = BM_elem_index_get(ele_dst);
|
||||
}
|
||||
else if (uv_selectmode & UV_SELECT_EDGE) {
|
||||
BM_mesh_elem_index_ensure(bm, BM_LOOP);
|
||||
index = BM_elem_index_get(ele_dst);
|
||||
}
|
||||
else {
|
||||
BM_mesh_elem_index_ensure(bm, BM_LOOP);
|
||||
index = BM_elem_index_get(ele_dst);
|
||||
}
|
||||
|
||||
const int object_index = ed::object::object_in_mode_to_index(
|
||||
*bmain, scene, view_layer, OB_MODE_EDIT, obedit);
|
||||
BLI_assert(object_index != -1);
|
||||
RNA_int_set(op->ptr, "object_index", object_index);
|
||||
RNA_int_set(op->ptr, "index", index);
|
||||
changed = true;
|
||||
}
|
||||
}
|
||||
|
||||
return changed ? OPERATOR_FINISHED : OPERATOR_CANCELLED;
|
||||
}
|
||||
|
||||
static wmOperatorStatus uv_shortest_path_pick_exec(bContext *C, wmOperator *op)
|
||||
{
|
||||
Depsgraph *depsgraph = CTX_data_ensure_evaluated_depsgraph(C);
|
||||
const Main *bmain = CTX_data_main(C);
|
||||
Scene *scene = CTX_data_scene(C);
|
||||
const ToolSettings *ts = scene->toolsettings;
|
||||
ViewLayer *view_layer = CTX_data_view_layer(C);
|
||||
const char uv_selectmode = ED_uvedit_select_mode_get(scene);
|
||||
|
||||
const int object_index = RNA_int_get(op->ptr, "object_index");
|
||||
const int index = RNA_int_get(op->ptr, "index");
|
||||
if (object_index == -1) {
|
||||
return OPERATOR_CANCELLED;
|
||||
}
|
||||
|
||||
Object *obedit = ed::object::object_in_mode_from_index(
|
||||
*bmain, scene, view_layer, OB_MODE_EDIT, object_index);
|
||||
if (obedit == nullptr) {
|
||||
return OPERATOR_CANCELLED;
|
||||
}
|
||||
|
||||
BMEditMesh *em = BKE_editmesh_from_object(obedit);
|
||||
BMesh *bm = em->bm;
|
||||
const BMUVOffsets offsets = BM_uv_map_offsets_get(bm);
|
||||
|
||||
BMElem *ele_src, *ele_dst;
|
||||
|
||||
/* NOLINTBEGIN: bugprone-assignment-in-if-condition */
|
||||
if (uv_selectmode & UV_SELECT_FACE) {
|
||||
if (index < 0 || index >= bm->totface) {
|
||||
return OPERATOR_CANCELLED;
|
||||
}
|
||||
if (!(ele_src = reinterpret_cast<BMElem *>(BM_mesh_active_face_get(bm, false, false))) ||
|
||||
!(ele_dst = reinterpret_cast<BMElem *>(BM_face_at_index_find_or_table(bm, index))))
|
||||
{
|
||||
return OPERATOR_CANCELLED;
|
||||
}
|
||||
}
|
||||
else if (uv_selectmode & UV_SELECT_EDGE) {
|
||||
if (index < 0 || index >= bm->totloop) {
|
||||
return OPERATOR_CANCELLED;
|
||||
}
|
||||
if (!(ele_src = reinterpret_cast<BMElem *>(ED_uvedit_active_edge_loop_get(ts, bm))) ||
|
||||
!(ele_dst = reinterpret_cast<BMElem *>(BM_loop_at_index_find(bm, index))))
|
||||
{
|
||||
return OPERATOR_CANCELLED;
|
||||
}
|
||||
}
|
||||
else {
|
||||
if (index < 0 || index >= bm->totloop) {
|
||||
return OPERATOR_CANCELLED;
|
||||
}
|
||||
if (!(ele_src = reinterpret_cast<BMElem *>(ED_uvedit_active_vert_loop_get(ts, bm))) ||
|
||||
!(ele_dst = reinterpret_cast<BMElem *>(BM_loop_at_index_find(bm, index))))
|
||||
{
|
||||
return OPERATOR_CANCELLED;
|
||||
}
|
||||
}
|
||||
/* NOLINTEND: bugprone-assignment-in-if-condition */
|
||||
|
||||
/* Always use the active object, not `obedit` as the active defines the UV display. */
|
||||
const float aspect_y = ED_uvedit_get_aspect_y(CTX_data_edit_object(C));
|
||||
|
||||
PathSelectParams op_params;
|
||||
path_select_params_from_op(op, &op_params);
|
||||
op_params.track_active = true;
|
||||
|
||||
if (!uv_shortest_path_pick_ex(
|
||||
scene, depsgraph, obedit, &op_params, ele_src, ele_dst, aspect_y, offsets))
|
||||
{
|
||||
return OPERATOR_CANCELLED;
|
||||
}
|
||||
|
||||
return OPERATOR_FINISHED;
|
||||
}
|
||||
|
||||
void UV_OT_shortest_path_pick(wmOperatorType *ot)
|
||||
{
|
||||
PropertyRNA *prop;
|
||||
|
||||
/* identifiers */
|
||||
ot->name = "Pick Shortest Path";
|
||||
ot->idname = "UV_OT_shortest_path_pick";
|
||||
ot->description = "Select shortest path between two selections";
|
||||
|
||||
/* API callbacks. */
|
||||
ot->invoke = uv_shortest_path_pick_invoke;
|
||||
ot->exec = uv_shortest_path_pick_exec;
|
||||
ot->poll = ED_operator_uvedit_space_image;
|
||||
|
||||
/* flags */
|
||||
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
|
||||
|
||||
/* properties */
|
||||
path_select_properties(ot);
|
||||
|
||||
/* use for redo */
|
||||
prop = RNA_def_int(ot->srna, "object_index", -1, -1, INT_MAX, "", "", 0, INT_MAX);
|
||||
RNA_def_property_flag(prop, PROP_HIDDEN | PROP_SKIP_SAVE);
|
||||
prop = RNA_def_int(ot->srna, "index", -1, -1, INT_MAX, "", "", 0, INT_MAX);
|
||||
RNA_def_property_flag(prop, PROP_HIDDEN | PROP_SKIP_SAVE);
|
||||
}
|
||||
|
||||
/** \} */
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name Select Path Between Existing Selection
|
||||
* \{ */
|
||||
|
||||
static wmOperatorStatus uv_shortest_path_select_exec(bContext *C, wmOperator *op)
|
||||
{
|
||||
Depsgraph *depsgraph = CTX_data_ensure_evaluated_depsgraph(C);
|
||||
const Main *bmain = CTX_data_main(C);
|
||||
Scene *scene = CTX_data_scene(C);
|
||||
const char uv_selectmode = ED_uvedit_select_mode_get(scene);
|
||||
bool found_valid_elements = false;
|
||||
|
||||
const float aspect_y = ED_uvedit_get_aspect_y(CTX_data_edit_object(C));
|
||||
|
||||
ViewLayer *view_layer = CTX_data_view_layer(C);
|
||||
Vector<Object *> objects = BKE_view_layer_array_from_objects_in_edit_mode_unique_data_with_uvs(
|
||||
*bmain, scene, view_layer, nullptr);
|
||||
for (Object *obedit : objects) {
|
||||
BMesh *bm = BKE_editmesh_from_object(obedit)->bm;
|
||||
|
||||
const BMUVOffsets offsets = BM_uv_map_offsets_get(bm);
|
||||
|
||||
BMElem *ele_src = nullptr, *ele_dst = nullptr;
|
||||
|
||||
/* Find 2x elements. */
|
||||
{
|
||||
BMElem **ele_array = nullptr;
|
||||
int ele_array_len = 0;
|
||||
if (uv_selectmode & UV_SELECT_FACE) {
|
||||
ele_array = reinterpret_cast<BMElem **>(
|
||||
ED_uvedit_selected_faces(scene, bm, 3, &ele_array_len));
|
||||
}
|
||||
else if (uv_selectmode & UV_SELECT_EDGE) {
|
||||
ele_array = reinterpret_cast<BMElem **>(
|
||||
ED_uvedit_selected_edges(scene, bm, 3, &ele_array_len));
|
||||
}
|
||||
else {
|
||||
ele_array = reinterpret_cast<BMElem **>(
|
||||
ED_uvedit_selected_verts(scene, bm, 3, &ele_array_len));
|
||||
}
|
||||
|
||||
if (ele_array_len == 2) {
|
||||
ele_src = ele_array[0];
|
||||
ele_dst = ele_array[1];
|
||||
}
|
||||
MEM_delete(ele_array);
|
||||
}
|
||||
|
||||
if (ele_src && ele_dst) {
|
||||
PathSelectParams op_params;
|
||||
path_select_params_from_op(op, &op_params);
|
||||
|
||||
uv_shortest_path_pick_ex(
|
||||
scene, depsgraph, obedit, &op_params, ele_src, ele_dst, aspect_y, offsets);
|
||||
|
||||
found_valid_elements = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (!found_valid_elements) {
|
||||
BKE_report(
|
||||
op->reports, RPT_WARNING, "Path selection requires two matching elements to be selected");
|
||||
return OPERATOR_CANCELLED;
|
||||
}
|
||||
|
||||
return OPERATOR_FINISHED;
|
||||
}
|
||||
|
||||
void UV_OT_shortest_path_select(wmOperatorType *ot)
|
||||
{
|
||||
/* identifiers */
|
||||
ot->name = "Select Shortest Path";
|
||||
ot->idname = "UV_OT_shortest_path_select";
|
||||
ot->description = "Select shortest path between two vertices/edges/faces";
|
||||
|
||||
/* API callbacks. */
|
||||
ot->exec = uv_shortest_path_select_exec;
|
||||
ot->poll = ED_operator_uvedit_space_image;
|
||||
|
||||
/* flags */
|
||||
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
|
||||
|
||||
/* properties */
|
||||
path_select_properties(ot);
|
||||
}
|
||||
|
||||
/** \} */
|
||||
|
||||
} // namespace blender
|
||||
1028
blender-5.2.0/source/blender/editors/uvedit/uvedit_rip.cc
Normal file
1028
blender-5.2.0/source/blender/editors/uvedit/uvedit_rip.cc
Normal file
File diff suppressed because it is too large
Load Diff
7611
blender-5.2.0/source/blender/editors/uvedit/uvedit_select.cc
Normal file
7611
blender-5.2.0/source/blender/editors/uvedit/uvedit_select.cc
Normal file
File diff suppressed because it is too large
Load Diff
2961
blender-5.2.0/source/blender/editors/uvedit/uvedit_smart_stitch.cc
Normal file
2961
blender-5.2.0/source/blender/editors/uvedit/uvedit_smart_stitch.cc
Normal file
File diff suppressed because it is too large
Load Diff
4442
blender-5.2.0/source/blender/editors/uvedit/uvedit_unwrap_ops.cc
Normal file
4442
blender-5.2.0/source/blender/editors/uvedit/uvedit_unwrap_ops.cc
Normal file
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user