Blender V4.3
editmesh_knife.cc
Go to the documentation of this file.
1/* SPDX-FileCopyrightText: 2007 Blender Authors
2 *
3 * SPDX-License-Identifier: GPL-2.0-or-later */
4
11#ifdef _MSC_VER
12# define _USE_MATH_DEFINES
13#endif
14
15#include <fmt/format.h>
16
17#include "MEM_guardedalloc.h"
18
19#include "BLF_api.hh"
20
21#include "BLI_alloca.h"
22#include "BLI_linklist.h"
23#include "BLI_listbase.h"
24#include "BLI_map.hh"
25#include "BLI_math_color.h"
26#include "BLI_math_matrix.h"
27#include "BLI_math_rotation.h"
29#include "BLI_memarena.h"
30#include "BLI_set.hh"
31#include "BLI_stack.h"
32#include "BLI_string.h"
33#include "BLI_vector.hh"
34
35#include "BLT_translation.hh"
36
37#include "BKE_bvhutils.hh"
38#include "BKE_context.hh"
39#include "BKE_editmesh.hh"
40#include "BKE_layer.hh"
41#include "BKE_report.hh"
42#include "BKE_scene.hh"
43#include "BKE_unit.hh"
44
45#include "GPU_immediate.hh"
46#include "GPU_matrix.hh"
47#include "GPU_state.hh"
48
49#include "ED_mesh.hh"
50#include "ED_numinput.hh"
51#include "ED_screen.hh"
52#include "ED_space_api.hh"
53#include "ED_transform.hh"
54#include "ED_view3d.hh"
55
56#include "WM_api.hh"
57#include "WM_types.hh"
58
59#include "DNA_object_types.h"
60
61#include "UI_interface.hh"
62#include "UI_resources.hh"
63
64#include "RNA_access.hh"
65#include "RNA_define.hh"
66
67#include "DEG_depsgraph.hh"
69
70#include "mesh_intern.hh" /* Own include. */
71
72using namespace blender;
73
74/* Detect isolated holes and fill them. */
75#define USE_NET_ISLAND_CONNECT
76
77#define KMAXDIST (10 * UI_SCALE_FAC) /* Max mouse distance from edge before not detecting it. */
78
79/* WARNING: Knife float precision is fragile:
80 * Be careful before making changes here see: (#43229, #42864, #42459, #41164).
81 */
82#define KNIFE_FLT_EPS 0.00001f
83#define KNIFE_FLT_EPS_SQUARED (KNIFE_FLT_EPS * KNIFE_FLT_EPS)
84#define KNIFE_FLT_EPSBIG 0.0005f
85
86#define KNIFE_FLT_EPS_PX_VERT 0.5f
87#define KNIFE_FLT_EPS_PX_EDGE 0.05f
88#define KNIFE_FLT_EPS_PX_FACE 0.05f
89
90#define KNIFE_DEFAULT_ANGLE_SNAPPING_INCREMENT 30.0f
91#define KNIFE_MIN_ANGLE_SNAPPING_INCREMENT 0.0f
92#define KNIFE_MAX_ANGLE_SNAPPING_INCREMENT 180.0f
93
107
108/* Knife-tool Operator. */
109struct KnifeVert {
110 BMVert *v; /* Non-null if this is an original vert. */
113
114 /* Index of the associated object.
115 * -1 represents the absence of an object. */
117
118 float co[3]; /* Vertex position in the original mesh. Equivalent to #BMVert::co[3]. */
119 float cageco[3]; /* Vertex position in the Cage mesh and in World Space. */
120 bool is_cut; /* Along a cut created by user input (will draw too). */
122 bool is_splitting; /* Created when an edge was split. */
123};
124
125struct KnifeEdge {
127 BMFace *basef; /* Face to restrict face fill to. */
129
130 BMEdge *e; /* Non-null if this is an original edge. */
131 bool is_cut; /* Along a cut created by user input (will draw too). */
133 int splits; /* Number of times this edge has been split. */
134};
135
137 float hit[3], cagehit[3];
138 float schit[2]; /* Screen coordinates for cagehit. */
139 float l; /* Lambda along cut line. */
140 float m; /* Depth front-to-back. */
141
142 /* Exactly one of kfe, v, or f should be non-null,
143 * saying whether cut line crosses and edge,
144 * is snapped to a vert, or is in the middle of some face. */
148
149 /* Index of the associated object.
150 * -1 represents the absence of an object. */
152};
153
156
157 /* At most one of vert, edge, or bmface should be non-null,
158 * saying whether the point is snapped to a vertex, edge, or in a face.
159 * If none are set, this point is in space and is_space should be true. */
163
164 /* Index of the associated object.
165 * -1 represents the absence of an object. */
167
168 float2 mval; /* Mouse screen position (may be non-integral if snapped to something). */
169
170 bool is_space() const
171 {
172 return this->ob_index == -1;
173 }
174};
175
177 float cage[3];
178 float mval[2];
180};
181
183 int cuts; /* Line hits cause multiple edges/cuts to be created at once. */
184 int splits; /* Number of edges split. */
185 KnifePosData pos; /* Store previous KnifePosData. */
187};
188
189struct KnifeBVH {
190 BVHTree *tree; /* Knife Custom BVH Tree. */
191 /* Used by #knife_bvh_raycast_cb to store the intersecting triangles. */
194
195 /* Use #bm_ray_cast_cb_elem_not_in_face_check. */
196 bool (*filter_cb)(BMFace *f, void *userdata);
198};
199
215
217
218/* struct for properties used while drawing */
220 ARegion *region; /* Region that knifetool was activated in. */
221 void *draw_handle; /* For drawing preview loop. */
222 ViewContext vc; /* NOTE: _don't_ use 'mval', instead use the one we define below. */
223 float mval[2]; /* Mouse value with snapping applied. */
224
226
227 /* Used for swapping current object when in multi-object edit mode. */
229
232
234
235 /* Reused for edge-net filling. */
236 struct {
237 /* Cleared each use. */
239#ifdef USE_NET_ISLAND_CONNECT
241#endif
243
248
250
253 bool no_cuts; /* A cut has not been made yet. */
254
256 BLI_Stack *splitstack; /* Store edge splits by #knife_split_edge. */
257
258 float vthresh;
259 float ethresh;
260
261 /* Used for drag-cutting. */
263
264 /* Data for mouse-position-derived data. */
265 KnifePosData curr; /* Current point under the cursor. */
266 KnifePosData prev; /* Last added cut (a line draws from the cursor to this). */
267 KnifePosData init; /* The first point in the cut-list, used for closing the loop. */
268
269 /* Number of knife edges `kedges`. */
271 /* Number of knife vertices, `kverts`. */
273
275
277
278 /* Run by the UI or not. */
280
281 /* Operator options. */
282 bool cut_through; /* Preference, can be modified at runtime (that feature may go). */
283 bool only_select; /* Set on initialization. */
284 bool select_result; /* Set on initialization. */
285
289
291
294
299
301 float angle_snapping_increment; /* Degrees */
302
303 /* Use to check if we're currently dragging an angle snapped line. */
307 float angle;
308 /* Relative angle snapping reference edge. */
311 int snap_edge; /* Used by #KNF_MODAL_CYCLE_ANGLE_SNAP_EDGE to choose an edge for snapping. */
312
316 char axis_string[2];
317
320 KnifeMeasureData mdata; /* Data for distance and angle drawing calculations. */
321
322 KnifeUndoFrame *undo; /* Current undo frame. */
324
326};
327
328enum {
348};
349
350enum {
355
356enum {
362
363enum {
368
369enum {
375
376/* -------------------------------------------------------------------- */
380static void knifetool_raycast_planes(const KnifeTool_OpData *kcd, float r_v1[3], float r_v2[3])
381{
382 float planes[4][4];
384 kcd->vc.rv3d->persmat, planes[2], planes[0], planes[1], planes[3], nullptr, nullptr);
385
386 /* Ray-cast all planes. */
387 float ray_dir[3];
388 float ray_hit_best[2][3] = {{UNPACK3(kcd->prev.cage)}, {UNPACK3(kcd->curr.cage)}};
389 float lambda_best[2] = {-FLT_MAX, FLT_MAX};
390 int i;
391
392 {
393 float curr_cage_adjust[3];
394 float co_depth[3];
395
396 copy_v3_v3(co_depth, kcd->prev.cage);
397 ED_view3d_win_to_3d(kcd->vc.v3d, kcd->region, co_depth, kcd->curr.mval, curr_cage_adjust);
398
399 sub_v3_v3v3(ray_dir, curr_cage_adjust, kcd->prev.cage);
400 }
401
402 for (i = 0; i < 4; i++) {
403 float ray_hit[3];
404 float lambda_test;
405 if (!isect_ray_plane_v3(kcd->prev.cage, ray_dir, planes[i], &lambda_test, false)) {
406 continue;
407 }
408
409 madd_v3_v3v3fl(ray_hit, kcd->prev.cage, ray_dir, lambda_test);
410 if (lambda_test < 0.0f) {
411 if (lambda_test > lambda_best[0]) {
412 copy_v3_v3(ray_hit_best[0], ray_hit);
413 lambda_best[0] = lambda_test;
414 }
415 }
416 else {
417 if (lambda_test < lambda_best[1]) {
418 copy_v3_v3(ray_hit_best[1], ray_hit);
419 lambda_best[1] = lambda_test;
420 }
421 }
422 }
423
424 copy_v3_v3(r_v1, ray_hit_best[0]);
425 copy_v3_v3(r_v2, ray_hit_best[1]);
426}
427
447
449{
450 if (compare_v3v3(kcd->prev.cage, kcd->curr.cage, KNIFE_FLT_EPSBIG)) {
451 return;
452 }
453
454 float v1[3], v2[3];
455
456 /* This is causing buggy behavior when `prev.cage` and `curr.cage` are too close together. */
458
461
462 switch (kcd->constrain_axis) {
465 break;
466 }
469 break;
470 }
473 break;
474 }
475 default: {
477 break;
478 }
479 }
480
481 GPU_line_width(2.0);
482
484 immVertex3fv(pos, v1);
486 immEnd();
487
489}
490
492{
497
500
501 char numstr[256];
502 float numstr_size[2];
503 float posit[2];
504 const float bg_margin = 4.0f * UI_SCALE_FAC;
505 const float font_size = 14.0f;
506 const int distance_precision = 4;
507
508 /* Calculate distance and convert to string. */
509 const float cut_len = len_v3v3(kcd->prev.cage, kcd->curr.cage);
510
511 const UnitSettings *unit = &kcd->scene->unit;
512 if (unit->system == USER_UNIT_NONE) {
513 SNPRINTF(numstr, "%.*f", distance_precision, cut_len);
514 }
515 else {
517 sizeof(numstr),
518 double(cut_len * unit->scale_length),
519 distance_precision,
521 unit,
522 false);
523 }
524
526 BLF_size(blf_mono_font, font_size * UI_SCALE_FAC);
528 BLF_width_and_height(blf_mono_font, numstr, sizeof(numstr), &numstr_size[0], &numstr_size[1]);
529
530 /* Center text. */
531 mid_v2_v2v2(posit, kcd->prev.mval, kcd->curr.mval);
532 posit[0] -= numstr_size[0] / 2.0f;
533 posit[1] -= numstr_size[1] / 2.0f;
534
535 /* Draw text background. */
536 float color_back[4] = {0.0f, 0.0f, 0.0f, 0.5f}; /* TODO: Replace with theme color. */
537 immUniformColor4fv(color_back);
538
541 posit[0] - bg_margin,
542 posit[1] - bg_margin,
543 posit[0] + bg_margin + numstr_size[0],
544 posit[1] + bg_margin + numstr_size[1]);
547
548 /* Draw text. */
549 uchar color_text[3];
550 UI_GetThemeColor3ubv(TH_TEXT, color_text);
551
552 BLF_color3ubv(blf_mono_font, color_text);
553 BLF_position(blf_mono_font, posit[0], posit[1], 0.0f);
554 BLF_draw(blf_mono_font, numstr, sizeof(numstr));
556
559}
560
562 const float start[3],
563 const float mid[3],
564 const float end[3],
565 const float start_ss[2],
566 const float mid_ss[2],
567 const float end_ss[2],
568 const float angle)
569{
570 const RegionView3D *rv3d = static_cast<const RegionView3D *>(kcd->region->regiondata);
571 const int arc_steps = 24;
572 const float arc_size = 64.0f * UI_SCALE_FAC;
573 const float bg_margin = 4.0f * UI_SCALE_FAC;
574 const float cap_size = 4.0f * UI_SCALE_FAC;
575 const float font_size = 14.0f;
576 const int angle_precision = 3;
577
578 /* Angle arc in 3d space. */
580
581 const uint pos_3d = GPU_vertformat_attr_add(
584
585 {
586 float dir_tmp[3];
587 float ar_coord[3];
588
589 float dir_a[3];
590 float dir_b[3];
591 float quat[4];
592 float axis[3];
593 float arc_angle;
594
595 Object *ob = kcd->objects[kcd->bvh.ob_index];
596 const float inverse_average_scale = 1 / (ob->object_to_world().ptr()[0][0] +
597 ob->object_to_world().ptr()[1][1] +
598 ob->object_to_world().ptr()[2][2]);
599
600 const float px_scale =
601 3.0f * inverse_average_scale *
603 min_fff(arc_size, len_v2v2(start_ss, mid_ss) / 2.0f, len_v2v2(end_ss, mid_ss) / 2.0f));
604
605 sub_v3_v3v3(dir_a, start, mid);
606 sub_v3_v3v3(dir_b, end, mid);
607 normalize_v3(dir_a);
608 normalize_v3(dir_b);
609
610 cross_v3_v3v3(axis, dir_a, dir_b);
611 arc_angle = angle_normalized_v3v3(dir_a, dir_b);
612
613 axis_angle_to_quat(quat, axis, arc_angle / arc_steps);
614
615 copy_v3_v3(dir_tmp, dir_a);
616
618 GPU_line_width(1.0);
619
620 immBegin(GPU_PRIM_LINE_STRIP, arc_steps + 1);
621 for (int j = 0; j <= arc_steps; j++) {
622 madd_v3_v3v3fl(ar_coord, mid, dir_tmp, px_scale);
623 mul_qt_v3(quat, dir_tmp);
624
625 immVertex3fv(pos_3d, ar_coord);
626 }
627 immEnd();
628 }
629
631
632 /* Angle text and background in 2d space. */
637
641
642 /* Angle as string. */
643 char numstr[256];
644 float numstr_size[2];
645 float posit[2];
646
647 const UnitSettings *unit = &kcd->scene->unit;
648 if (unit->system == USER_UNIT_NONE) {
649 SNPRINTF(numstr, "%.*f" BLI_STR_UTF8_DEGREE_SIGN, angle_precision, RAD2DEGF(angle));
650 }
651 else {
653 numstr, sizeof(numstr), double(angle), angle_precision, B_UNIT_ROTATION, unit, false);
654 }
655
657 BLF_size(blf_mono_font, font_size * UI_SCALE_FAC);
659 BLF_width_and_height(blf_mono_font, numstr, sizeof(numstr), &numstr_size[0], &numstr_size[1]);
660
661 posit[0] = mid_ss[0] + (cap_size * 2.0f);
662 posit[1] = mid_ss[1] - (numstr_size[1] / 2.0f);
663
664 /* Draw text background. */
665 float color_back[4] = {0.0f, 0.0f, 0.0f, 0.5f}; /* TODO: Replace with theme color. */
666 immUniformColor4fv(color_back);
667
669 immRectf(pos_2d,
670 posit[0] - bg_margin,
671 posit[1] - bg_margin,
672 posit[0] + bg_margin + numstr_size[0],
673 posit[1] + bg_margin + numstr_size[1]);
676
677 /* Draw text. */
678 uchar color_text[3];
679 UI_GetThemeColor3ubv(TH_TEXT, color_text);
680
681 BLF_color3ubv(blf_mono_font, color_text);
682 BLF_position(blf_mono_font, posit[0], posit[1], 0.0f);
684 BLF_draw(blf_mono_font, numstr, sizeof(numstr));
686
689
691}
692
694{
695 KnifeVert *kfv;
696 KnifeVert *tempkfv;
697 KnifeEdge *kfe;
698 KnifeEdge *tempkfe;
699
700 if (kcd->curr.vert) {
701 kfv = kcd->curr.vert;
702
703 float min_angle = FLT_MAX;
704 float angle = 0.0f;
705 float *end;
706
707 kfe = static_cast<KnifeEdge *>(((LinkData *)kfv->edges.first)->data);
708 LISTBASE_FOREACH (LinkData *, ref, &kfv->edges) {
709 tempkfe = static_cast<KnifeEdge *>(ref->data);
710 if (tempkfe->v1 != kfv) {
711 tempkfv = tempkfe->v1;
712 }
713 else {
714 tempkfv = tempkfe->v2;
715 }
716 angle = angle_v3v3v3(kcd->prev.cage, kcd->curr.cage, tempkfv->cageco);
717 if (angle < min_angle) {
718 min_angle = angle;
719 kfe = tempkfe;
720 end = tempkfv->cageco;
721 }
722 }
723
724 if (min_angle > KNIFE_FLT_EPSBIG) {
725 /* Last vertex in screen space. */
726 float end_ss[2];
728
730 kcd->prev.cage,
731 kcd->curr.cage,
732 end,
733 kcd->prev.mval,
734 kcd->curr.mval,
735 end_ss,
736 min_angle);
737 }
738 }
739 else if (kcd->curr.edge) {
740 kfe = kcd->curr.edge;
741
742 /* Check for most recent cut (if cage is part of previous cut). */
743 if (!compare_v3v3(kfe->v1->cageco, kcd->prev.cage, KNIFE_FLT_EPSBIG) &&
745 {
746 /* Determine acute angle. */
747 float angle1 = angle_v3v3v3(kcd->prev.cage, kcd->curr.cage, kfe->v1->cageco);
748 float angle2 = angle_v3v3v3(kcd->prev.cage, kcd->curr.cage, kfe->v2->cageco);
749
750 float angle;
751 float *end;
752 if (angle1 < angle2) {
753 angle = angle1;
754 end = kfe->v1->cageco;
755 }
756 else {
757 angle = angle2;
758 end = kfe->v2->cageco;
759 }
760
761 /* Last vertex in screen space. */
762 float end_ss[2];
764
766 kcd, kcd->prev.cage, kcd->curr.cage, end, kcd->prev.mval, kcd->curr.mval, end_ss, angle);
767 }
768 }
769
770 if (kcd->prev.vert) {
771 kfv = kcd->prev.vert;
772 float min_angle = FLT_MAX;
773 float angle = 0.0f;
774 float *end;
775
776 /* If using relative angle snapping, always draw angle to reference edge. */
778 kfe = kcd->snap_ref_edge;
779 if (kfe->v1 != kfv) {
780 tempkfv = kfe->v1;
781 }
782 else {
783 tempkfv = kfe->v2;
784 }
785 min_angle = angle_v3v3v3(kcd->curr.cage, kcd->prev.cage, tempkfv->cageco);
786 end = tempkfv->cageco;
787 }
788 else {
789 /* Choose minimum angle edge. */
790 kfe = static_cast<KnifeEdge *>(((LinkData *)kfv->edges.first)->data);
791 LISTBASE_FOREACH (LinkData *, ref, &kfv->edges) {
792 tempkfe = static_cast<KnifeEdge *>(ref->data);
793 if (tempkfe->v1 != kfv) {
794 tempkfv = tempkfe->v1;
795 }
796 else {
797 tempkfv = tempkfe->v2;
798 }
799 angle = angle_v3v3v3(kcd->curr.cage, kcd->prev.cage, tempkfv->cageco);
800 if (angle < min_angle) {
801 min_angle = angle;
802 kfe = tempkfe;
803 end = tempkfv->cageco;
804 }
805 }
806 }
807
808 if (min_angle > KNIFE_FLT_EPSBIG) {
809 /* Last vertex in screen space. */
810 float end_ss[2];
812
814 kcd->curr.cage,
815 kcd->prev.cage,
816 end,
817 kcd->curr.mval,
818 kcd->prev.mval,
819 end_ss,
820 min_angle);
821 }
822 }
823 else if (kcd->prev.edge) {
824 /* Determine acute angle. */
825 kfe = kcd->prev.edge;
826 float angle1 = angle_v3v3v3(kcd->curr.cage, kcd->prev.cage, kfe->v1->cageco);
827 float angle2 = angle_v3v3v3(kcd->curr.cage, kcd->prev.cage, kfe->v2->cageco);
828
829 float angle;
830 float *end;
831 /* kcd->prev.edge can have one vertex part of cut and one part of mesh? */
832 /* This never seems to happen for kcd->curr.edge. */
833 if ((!kcd->prev.vert || kcd->prev.vert->v == kfe->v1->v) || kfe->v1->is_cut) {
834 angle = angle2;
835 end = kfe->v2->cageco;
836 }
837 else if ((!kcd->prev.vert || kcd->prev.vert->v == kfe->v2->v) || kfe->v2->is_cut) {
838 angle = angle1;
839 end = kfe->v1->cageco;
840 }
841 else {
842 if (angle1 < angle2) {
843 angle = angle1;
844 end = kfe->v1->cageco;
845 }
846 else {
847 angle = angle2;
848 end = kfe->v2->cageco;
849 }
850 }
851
852 /* Last vertex in screen space. */
853 float end_ss[2];
855
857 kcd, kcd->curr.cage, kcd->prev.cage, end, kcd->curr.mval, kcd->prev.mval, end_ss, angle);
858 }
859 else if (kcd->mdata.is_stored && !kcd->prev.is_space()) {
860 float angle = angle_v3v3v3(kcd->curr.cage, kcd->prev.cage, kcd->mdata.cage);
862 kcd->curr.cage,
863 kcd->prev.cage,
864 kcd->mdata.cage,
865 kcd->curr.mval,
866 kcd->prev.mval,
867 kcd->mdata.mval,
868 angle);
869 }
870}
871
873{
874 switch (kcd->dist_angle_mode) {
878 break;
879 }
882 break;
883 }
886 break;
887 }
888 }
889}
890
891/* Modal loop selection drawing callback. */
892static void knifetool_draw(const bContext * /*C*/, ARegion * /*region*/, void *arg)
893{
894 const KnifeTool_OpData *kcd = static_cast<const KnifeTool_OpData *>(arg);
896
898 GPU_polygon_offset(1.0f, 1.0f);
899
902
904
905 if (kcd->mode == MODE_DRAGGING) {
907 GPU_line_width(2.0);
908
910 immVertex3fv(pos, kcd->prev.cage);
911 immVertex3fv(pos, kcd->curr.cage);
912 immEnd();
913 }
914
915 if (kcd->prev.vert) {
918
920 immVertex3fv(pos, kcd->prev.cage);
921 immEnd();
922 }
923
924 if (kcd->prev.bmface || kcd->prev.edge) {
927
929 immVertex3fv(pos, kcd->prev.cage);
930 immEnd();
931 }
932
933 if (kcd->curr.vert) {
936
938 immVertex3fv(pos, kcd->curr.cage);
939 immEnd();
940 }
941 else if (kcd->curr.edge) {
943 GPU_line_width(2.0);
944
948 immEnd();
949 }
950
951 if (kcd->curr.bmface || kcd->curr.edge) {
954
956 immVertex3fv(pos, kcd->curr.cage);
957 immEnd();
958 }
959
960 if (kcd->depth_test) {
962 }
963
964 if (kcd->totkedge > 0) {
965 BLI_mempool_iter iter;
966 KnifeEdge *kfe;
967
969 GPU_line_width(1.0);
970
972
973 BLI_mempool_iternew(kcd->kedges, &iter);
974 for (kfe = static_cast<KnifeEdge *>(BLI_mempool_iterstep(&iter)); kfe;
975 kfe = static_cast<KnifeEdge *>(BLI_mempool_iterstep(&iter)))
976 {
977 if (!kfe->is_cut || kfe->is_invalid) {
978 continue;
979 }
980
981 immVertex3fv(pos, kfe->v1->cageco);
982 immVertex3fv(pos, kfe->v2->cageco);
983 }
984
985 immEnd();
986
989 }
990
991 if (kcd->totkvert > 0) {
992 BLI_mempool_iter iter;
993 KnifeVert *kfv;
994
997
999
1000 BLI_mempool_iternew(kcd->kverts, &iter);
1001 for (kfv = static_cast<KnifeVert *>(BLI_mempool_iterstep(&iter)); kfv;
1002 kfv = static_cast<KnifeVert *>(BLI_mempool_iterstep(&iter)))
1003 {
1004 if (!kfv->is_cut || kfv->is_invalid) {
1005 continue;
1006 }
1007
1008 immVertex3fv(pos, kfv->cageco);
1009 }
1010
1011 immEnd();
1012
1015 }
1016
1017 /* Draw relative angle snapping reference edge. */
1020 GPU_line_width(2.0);
1021
1025 immEnd();
1026 }
1027
1028 const int64_t total_hits = kcd->linehits.size();
1029 if (total_hits > 0) {
1031
1033 GPU_vertbuf_data_alloc(*vert, total_hits);
1034
1035 int other_verts_count = 0;
1036 int snapped_verts_count = 0;
1037 for (const KnifeLineHit &hit : kcd->linehits) {
1038 if (hit.v) {
1039 GPU_vertbuf_attr_set(vert, pos, snapped_verts_count++, hit.cagehit);
1040 }
1041 else {
1042 GPU_vertbuf_attr_set(vert, pos, total_hits - 1 - other_verts_count++, hit.cagehit);
1043 }
1044 }
1045
1046 gpu::Batch *batch = GPU_batch_create_ex(GPU_PRIM_POINTS, vert, nullptr, GPU_BATCH_OWNS_VBO);
1048
1049 /* Draw any snapped verts first. */
1050 float fcol[4];
1052 GPU_batch_uniform_4fv(batch, "color", fcol);
1054 if (snapped_verts_count > 0) {
1055 GPU_batch_draw_range(batch, 0, snapped_verts_count);
1056 }
1057
1058 /* Now draw the rest. */
1060 GPU_batch_uniform_4fv(batch, "color", fcol);
1062 if (other_verts_count > 0) {
1063 GPU_batch_draw_range(batch, snapped_verts_count, other_verts_count);
1064 }
1065
1067
1069 }
1070
1072
1074
1075 if (kcd->mode == MODE_DRAGGING) {
1076 if (kcd->is_angle_snapping) {
1078 }
1079 else if (kcd->axis_constrained) {
1081 }
1082
1083 if (kcd->show_dist_angle) {
1085 }
1086 }
1087
1089
1090 /* Reset default. */
1092}
1093
1096/* -------------------------------------------------------------------- */
1101{
1102 auto get_modal_key_str = [&](int id) {
1103 return WM_modalkeymap_operator_items_to_string(op->type, id, true).value_or("");
1104 };
1105
1106 WorkspaceStatus status(C);
1107 status.opmodal(IFACE_("Cut"), op->type, KNF_MODAL_ADD_CUT);
1108 status.opmodal(IFACE_("Close"), op->type, KNF_MODAL_ADD_CUT_CLOSED);
1109 status.opmodal(IFACE_("Stop"), op->type, KNF_MODAL_NEW_CUT);
1110 status.opmodal(IFACE_("Confirm"), op->type, KNF_MODAL_CONFIRM);
1111 status.opmodal(IFACE_("Cancel"), op->type, KNF_MODAL_CANCEL);
1112 status.opmodal(IFACE_("Undo"), op->type, KNF_MODAL_UNDO);
1113 status.opmodal(IFACE_("Pan View"), op->type, KNF_MODAL_PANNING);
1114 status.opmodal(IFACE_("Midpoint Snap"), op->type, KNF_MODAL_MIDPOINT_ON, kcd->snap_midpoints);
1115 status.opmodal(
1116 IFACE_("Ignore Snap"), op->type, KNF_MODAL_IGNORE_SNAP_ON, kcd->ignore_edge_snapping);
1117 status.opmodal(IFACE_("Cut Through"), op->type, KNF_MODAL_CUT_THROUGH_TOGGLE, kcd->cut_through);
1118 status.opmodal({}, op->type, KNF_MODAL_X_AXIS, kcd->constrain_axis == 1);
1119 status.opmodal({}, op->type, KNF_MODAL_Y_AXIS, kcd->constrain_axis == 2);
1120 status.opmodal({}, op->type, KNF_MODAL_Z_AXIS, kcd->constrain_axis == 3);
1121 status.item(IFACE_("Axis"), ICON_NONE);
1122 status.opmodal(
1124 status.opmodal(IFACE_("X-Ray"), op->type, KNF_MODAL_DEPTH_TEST_TOGGLE, !kcd->depth_test);
1125
1126 const std::string angle = fmt::format(
1127 "{}: {:.2f}({:.2f}) ({}{}{}{})",
1128 IFACE_("Angle Constraint"),
1129 (kcd->angle >= 0.0f) ? RAD2DEGF(kcd->angle) : 360.0f + RAD2DEGF(kcd->angle),
1134 kcd->angle_snapping ?
1135 ((kcd->angle_snapping_mode == KNF_CONSTRAIN_ANGLE_MODE_SCREEN) ? "Screen" : "Relative") :
1136 "OFF", /* TODO: Can this be simplified? */
1139 get_modal_key_str(KNF_MODAL_CYCLE_ANGLE_SNAP_EDGE) :
1140 "",
1141 (kcd->angle_snapping_mode == KNF_CONSTRAIN_ANGLE_MODE_RELATIVE) ? ": cycle edge" : "");
1142
1143 status.opmodal(angle, op->type, KNF_MODAL_ANGLE_SNAP_TOGGLE);
1144}
1145
1148/* -------------------------------------------------------------------- */
1152static const int *knife_bm_tri_index_get(const KnifeTool_OpData *kcd,
1153 int ob_index,
1154 int tri_index,
1155 int tri_index_buf[3])
1156{
1157 const KnifeObjectInfo *obinfo = &kcd->objects_info[ob_index];
1158 if (!obinfo->tri_indices.is_empty()) {
1159 return obinfo->tri_indices[tri_index];
1160 }
1161 const std::array<BMLoop *, 3> &ltri = obinfo->em->looptris[tri_index];
1162 for (int i = 0; i < 3; i++) {
1163 tri_index_buf[i] = BM_elem_index_get(ltri[i]->v);
1164 }
1165 return tri_index_buf;
1166}
1167
1169 int ob_index,
1170 int tri_index,
1171 float cos[3][3])
1172{
1173 const KnifeObjectInfo *obinfo = &kcd->objects_info[ob_index];
1174 int tri_ind_buf[3];
1175 const int *tri_ind = knife_bm_tri_index_get(kcd, ob_index, tri_index, tri_ind_buf);
1176 for (int i = 0; i < 3; i++) {
1177 copy_v3_v3(cos[i], obinfo->positions_cage[tri_ind[i]]);
1178 }
1179}
1180
1182 int ob_index,
1183 int tri_index,
1184 float cos[3][3])
1185{
1186 knife_bm_tri_cagecos_get(kcd, ob_index, tri_index, cos);
1187 const Object *ob = kcd->objects[ob_index];
1188 for (int i = 0; i < 3; i++) {
1189 mul_m4_v3(ob->object_to_world().ptr(), cos[i]);
1190 }
1191}
1192
1195/* -------------------------------------------------------------------- */
1200{
1201 return (BM_elem_flag_test(f, BM_ELEM_SELECT) != 0);
1202}
1203
1205{
1206 return (BM_elem_flag_test(f, BM_ELEM_HIDDEN) == 0);
1207}
1208
1210{
1211 Object *ob;
1212 BMEditMesh *em;
1213
1214 /* Test Function. */
1215 bool (*test_fn)(BMFace *);
1216 if (kcd->only_select && kcd->cut_through) {
1217 test_fn = knife_bm_face_is_select;
1218 }
1219 else {
1221 }
1222
1223 /* Construct BVH Tree. */
1224 const float epsilon = FLT_EPSILON * 2.0f;
1225 int tottri = 0;
1226 int ob_tottri = 0;
1228 BMFace *f_test = nullptr, *f_test_prev = nullptr;
1229 bool test_fn_ret = false;
1230
1231 /* Calculate tottri. */
1232 for (Object *ob : kcd->objects) {
1233 ob_tottri = 0;
1234 em = BKE_editmesh_from_object(ob);
1235
1236 for (int i = 0; i < em->looptris.size(); i++) {
1237 f_test = em->looptris[i][0]->f;
1238 if (f_test != f_test_prev) {
1239 test_fn_ret = test_fn(f_test);
1240 f_test_prev = f_test;
1241 }
1242
1243 if (test_fn_ret) {
1244 ob_tottri++;
1245 }
1246 }
1247
1248 tottri += ob_tottri;
1249 }
1250
1251 kcd->bvh.tree = BLI_bvhtree_new(tottri, epsilon, 8, 8);
1252
1253 f_test_prev = nullptr;
1254 test_fn_ret = false;
1255
1256 /* Add triangles for each object.
1257 * TODO:
1258 * test_fn can leave large gaps between bvh tree indices.
1259 * Compacting bvh tree indices may be possible.
1260 * Don't forget to update #knife_bvh_intersect_plane! */
1261 tottri = 0;
1262 for (const int ob_index : kcd->objects.index_range()) {
1263 ob = kcd->objects[ob_index];
1264 em = BKE_editmesh_from_object(ob);
1265 looptris = em->looptris;
1266
1267 for (int i = 0; i < em->looptris.size(); i++) {
1268
1269 f_test = looptris[i][0]->f;
1270 if (f_test != f_test_prev) {
1271 test_fn_ret = test_fn(f_test);
1272 f_test_prev = f_test;
1273 }
1274
1275 if (!test_fn_ret) {
1276 continue;
1277 }
1278
1279 float tri_cos[3][3];
1280 knife_bm_tri_cagecos_get_worldspace(kcd, ob_index, i, tri_cos);
1281 BLI_bvhtree_insert(kcd->bvh.tree, i + tottri, &tri_cos[0][0], 3);
1282 }
1283
1284 tottri += em->looptris.size();
1285 }
1286
1288}
1289
1290/* Wrapper for #BLI_bvhtree_free. */
1292{
1293 if (kcd->bvh.tree) {
1295 kcd->bvh.tree = nullptr;
1296 }
1297}
1298
1299static void knife_bvh_raycast_cb(void *userdata,
1300 int index,
1301 const BVHTreeRay *ray,
1302 BVHTreeRayHit *hit)
1303{
1304 if (index == -1) {
1305 return;
1306 }
1307
1308 KnifeTool_OpData *kcd = static_cast<KnifeTool_OpData *>(userdata);
1309 BMLoop *const *ltri = nullptr;
1310 Object *ob;
1311 BMEditMesh *em;
1312
1313 float dist;
1314 bool isect;
1315 int tottri;
1316
1317 tottri = 0;
1318 int ob_index = 0;
1319 for (; ob_index < kcd->objects.size(); ob_index++) {
1320 index -= tottri;
1321 ob = kcd->objects[ob_index];
1322 em = BKE_editmesh_from_object(ob);
1323 tottri = em->looptris.size();
1324 if (index < tottri) {
1325 ltri = em->looptris[index].data();
1326 break;
1327 }
1328 }
1329 BLI_assert(ltri != nullptr);
1330
1331 if (kcd->bvh.filter_cb) {
1332 if (!kcd->bvh.filter_cb(ltri[0]->f, kcd->bvh.filter_data)) {
1333 return;
1334 }
1335 }
1336
1337 float tri_cos[3][3];
1338 knife_bm_tri_cagecos_get_worldspace(kcd, ob_index, index, tri_cos);
1339 isect = (ray->radius > 0.0f ?
1341 ray->origin, ray->direction, UNPACK3(tri_cos), &dist, nullptr, ray->radius) :
1342#ifdef USE_KDOPBVH_WATERTIGHT
1344 ray->origin, ray->isect_precalc, UNPACK3(tri_cos), &dist, nullptr));
1345#else
1346isect_ray_tri_v3(ray->origin, ray->direction, UNPACK3(tri_cos), &dist, nullptr);
1347#endif
1348
1349 if (isect && dist < hit->dist) {
1350 madd_v3_v3v3fl(hit->co, ray->origin, ray->direction, dist);
1351
1352 /* Discard clipped points. */
1353 if (RV3D_CLIPPING_ENABLED(kcd->vc.v3d, kcd->vc.rv3d) &&
1354 ED_view3d_clipping_test(kcd->vc.rv3d, hit->co, false))
1355 {
1356 return;
1357 }
1358
1359 hit->dist = dist;
1360 hit->index = index;
1361
1362 copy_v3_v3(hit->no, ltri[0]->f->no);
1363
1364 kcd->bvh.looptris = em->looptris;
1365 kcd->bvh.ob_index = ob_index;
1366 }
1367}
1368
1369/* `co` is expected to be in world space. */
1371 const float co[3],
1372 const float dir[3],
1373 const float radius,
1374 float *r_dist,
1375 float r_cagehit[3],
1376 int *r_ob_index)
1377{
1378 BMFace *face;
1379 BVHTreeRayHit hit;
1380 const float dist = r_dist ? *r_dist : FLT_MAX;
1381 hit.dist = dist;
1382 hit.index = -1;
1383
1384 BLI_bvhtree_ray_cast(kcd->bvh.tree, co, dir, radius, &hit, knife_bvh_raycast_cb, kcd);
1385
1386 /* Handle Hit */
1387 if (hit.index != -1 && hit.dist != dist) {
1388 face = kcd->bvh.looptris[hit.index][0]->f;
1389
1390 if (r_cagehit) {
1391 copy_v3_v3(r_cagehit, hit.co);
1392 }
1393
1394 if (r_dist) {
1395 *r_dist = hit.dist;
1396 }
1397
1398 if (r_ob_index) {
1399 *r_ob_index = kcd->bvh.ob_index;
1400 }
1401
1402 return face;
1403 }
1404 return nullptr;
1405}
1406
1407/* `co` is expected to be in world space. */
1409 const float co[3],
1410 const float dir[3],
1411 const float radius,
1412 float *r_dist,
1413 float r_cagehit[3],
1414 int *r_ob_index,
1415 bool (*filter_cb)(BMFace *f, void *userdata),
1416 void *filter_userdata)
1417{
1418 kcd->bvh.filter_cb = filter_cb;
1419 kcd->bvh.filter_data = filter_userdata;
1420
1421 BMFace *face = knife_bvh_raycast(kcd, co, dir, radius, r_dist, r_cagehit, r_ob_index);
1422
1423 kcd->bvh.filter_cb = nullptr;
1424 kcd->bvh.filter_data = nullptr;
1425
1426 return face;
1427}
1428
1431/* -------------------------------------------------------------------- */
1435static void knife_project_v2(const KnifeTool_OpData *kcd, const float co[3], float sco[2])
1436{
1438}
1439
1440/* Ray is returned in world space. */
1442 const float mval[2],
1443 float r_origin[3],
1444 float r_end[3])
1445{
1446 /* Unproject to find view ray. */
1448 kcd->vc.depsgraph, kcd->region, kcd->vc.v3d, mval, r_origin, r_end, false);
1449}
1450
1451/* No longer used, but may be useful in the future. */
1453 float mval[3],
1454 float r_cage[3])
1455{
1456 float origin[3];
1457 float origin_ofs[3];
1458 float ray[3], ray_normal[3];
1459
1460 knife_input_ray_segment(kcd, mval, origin, origin_ofs);
1461
1462 sub_v3_v3v3(ray, origin_ofs, origin);
1463 normalize_v3_v3(ray_normal, ray);
1464
1465 knife_bvh_raycast(kcd, origin, ray_normal, 0.0f, nullptr, r_cage, nullptr);
1466}
1467
1469{
1470 bool v1_inside, v2_inside;
1471 bool v1_inface, v2_inface;
1472 BMLoop *l1, *l2;
1473
1474 if (!f || !v1 || !v2) {
1475 return false;
1476 }
1477
1478 l1 = v1->v ? BM_face_vert_share_loop(f, v1->v) : nullptr;
1479 l2 = v2->v ? BM_face_vert_share_loop(f, v2->v) : nullptr;
1480
1481 if ((l1 && l2) && BM_loop_is_adjacent(l1, l2)) {
1482 /* Boundary-case, always false to avoid edge-in-face checks below. */
1483 return false;
1484 }
1485
1486 /* Find out if v1 and v2, if set, are part of the face. */
1487 v1_inface = (l1 != nullptr);
1488 v2_inface = (l2 != nullptr);
1489
1490 /* BM_face_point_inside_test uses best-axis projection so this isn't most accurate test... */
1491 v1_inside = v1_inface ? false : BM_face_point_inside_test(f, v1->co);
1492 v2_inside = v2_inface ? false : BM_face_point_inside_test(f, v2->co);
1493 if ((v1_inface && v2_inside) || (v2_inface && v1_inside) || (v1_inside && v2_inside)) {
1494 return true;
1495 }
1496
1497 if (v1_inface && v2_inface) {
1498 float mid[3];
1499 /* Can have case where v1 and v2 are on shared chain between two faces.
1500 * BM_face_splits_check_legal does visibility and self-intersection tests,
1501 * but it is expensive and maybe a bit buggy, so use a simple
1502 * "is the midpoint in the face" test. */
1503 mid_v3_v3v3(mid, v1->co, v2->co);
1504 return BM_face_point_inside_test(f, mid);
1505 }
1506 return false;
1507}
1508
1510{
1512 kcd->vc.depsgraph, kcd->vc.v3d, kcd->vc.rv3d, true, &kcd->clipsta, &kcd->clipend);
1513}
1514
1517/* -------------------------------------------------------------------- */
1524{
1525 BMElem *ele_test;
1526 KnifeEdge *kfe = nullptr;
1527
1528 /* vert? */
1529 ele_test = (BMElem *)kfv->v;
1530
1531 if (r_kfe || ele_test == nullptr) {
1532 if (kfv->v == nullptr) {
1533 LISTBASE_FOREACH (LinkData *, ref, &kfv->edges) {
1534 kfe = static_cast<KnifeEdge *>(ref->data);
1535 if (kfe->e) {
1536 if (r_kfe) {
1537 *r_kfe = kfe;
1538 }
1539 break;
1540 }
1541 }
1542 }
1543 }
1544
1545 /* edge? */
1546 if (ele_test == nullptr) {
1547 if (kfe) {
1548 ele_test = (BMElem *)kfe->e;
1549 }
1550 }
1551
1552 /* face? */
1553 if (ele_test == nullptr) {
1554 if (BLI_listbase_is_single(&kfe->faces)) {
1555 ele_test = static_cast<BMElem *>(((LinkData *)kfe->faces.first)->data);
1556 }
1557 }
1558
1559 return ele_test;
1560}
1561
1563{
1564 BMElem *ele_test;
1565
1566 ele_test = (BMElem *)kfe->e;
1567
1568 if (ele_test == nullptr) {
1569 ele_test = (BMElem *)kfe->basef;
1570 }
1571
1572 return ele_test;
1573}
1574
1577/* -------------------------------------------------------------------- */
1582{
1583 ListBase *list;
1584
1585 list = static_cast<ListBase *>(BLI_memarena_alloc(kcd->arena, sizeof(ListBase)));
1586 BLI_listbase_clear(list);
1587 return list;
1588}
1589
1590static void knife_append_list(KnifeTool_OpData *kcd, ListBase *lst, void *elem)
1591{
1592 LinkData *ref;
1593
1594 ref = static_cast<LinkData *>(BLI_mempool_calloc(kcd->refs));
1595 ref->data = elem;
1596 BLI_addtail(lst, ref);
1597}
1598
1599static LinkData *find_ref(ListBase *lb, void *ref)
1600{
1601 LISTBASE_FOREACH (LinkData *, ref1, lb) {
1602 if (ref1->data == ref) {
1603 return ref1;
1604 }
1605 }
1606
1607 return nullptr;
1608}
1609
1610static void knife_append_list_no_dup(KnifeTool_OpData *kcd, ListBase *lst, void *elem)
1611{
1612 if (!find_ref(lst, elem)) {
1613 knife_append_list(kcd, lst, elem);
1614 }
1615}
1616
1618{
1619 knife_append_list(kcd, &kfe->v1->edges, kfe);
1620 knife_append_list(kcd, &kfe->v2->edges, kfe);
1621}
1622
1623/* Add faces of an edge to a KnifeVert's faces list. No checks for duplicates. */
1625{
1626 BMIter bmiter;
1627 BMFace *f;
1628
1629 BM_ITER_ELEM (f, &bmiter, e, BM_FACES_OF_EDGE) {
1630 knife_append_list(kcd, &kfv->faces, f);
1631 }
1632}
1633
1634/* Find a face in common in the two faces lists.
1635 * If more than one, return the first; if none, return nullptr. */
1637{
1638 LISTBASE_FOREACH (LinkData *, ref1, faces1) {
1639 LISTBASE_FOREACH (LinkData *, ref2, faces2) {
1640 if (ref1->data == ref2->data) {
1641 return (BMFace *)(ref1->data);
1642 }
1643 }
1644 }
1645 return nullptr;
1646}
1647
1650/* -------------------------------------------------------------------- */
1654static KnifeVert *new_knife_vert(KnifeTool_OpData *kcd, const float co[3], const float cageco[3])
1655{
1656 KnifeVert *kfv = static_cast<KnifeVert *>(BLI_mempool_calloc(kcd->kverts));
1657
1658 kcd->totkvert++;
1659
1660 copy_v3_v3(kfv->co, co);
1661 copy_v3_v3(kfv->cageco, cageco);
1662
1663 return kfv;
1664}
1665
1667{
1668 KnifeEdge *kfe = static_cast<KnifeEdge *>(BLI_mempool_calloc(kcd->kedges));
1669 kcd->totkedge++;
1670 return kfe;
1671}
1672
1673/* Get a KnifeVert wrapper for an existing BMVert. */
1675{
1676 KnifeVert *kfv = static_cast<KnifeVert *>(BLI_ghash_lookup(kcd->origvertmap, v));
1677 const float *cageco;
1678
1679 if (!kfv) {
1680 BMIter bmiter;
1681 BMFace *f;
1682
1683 if (BM_elem_index_get(v) >= 0) {
1684 cageco = kcd->objects_info[ob_index].positions_cage[BM_elem_index_get(v)];
1685 }
1686 else {
1687 cageco = v->co;
1688 }
1689
1690 float cageco_ws[3];
1691 Object *ob = kcd->objects[ob_index];
1692 mul_v3_m4v3(cageco_ws, ob->object_to_world().ptr(), cageco);
1693
1694 kfv = new_knife_vert(kcd, v->co, cageco_ws);
1695 kfv->v = v;
1696 kfv->ob_index = ob_index;
1697
1698 BLI_ghash_insert(kcd->origvertmap, v, kfv);
1699 BM_ITER_ELEM (f, &bmiter, v, BM_FACES_OF_VERT) {
1700 knife_append_list(kcd, &kfv->faces, f);
1701 }
1702 }
1703
1704 return kfv;
1705}
1706
1707/* Get a KnifeEdge wrapper for an existing BMEdge. */
1709{
1710 KnifeEdge *kfe = static_cast<KnifeEdge *>(BLI_ghash_lookup(kcd->origedgemap, e));
1711 if (!kfe) {
1712 BMIter bmiter;
1713 BMFace *f;
1714
1715 kfe = new_knife_edge(kcd);
1716 kfe->e = e;
1717 kfe->v1 = get_bm_knife_vert(kcd, e->v1, ob_index);
1718 kfe->v2 = get_bm_knife_vert(kcd, e->v2, ob_index);
1719
1720 knife_add_to_vert_edges(kcd, kfe);
1721
1722 BLI_ghash_insert(kcd->origedgemap, e, kfe);
1723
1724 BM_ITER_ELEM (f, &bmiter, e, BM_FACES_OF_EDGE) {
1725 knife_append_list(kcd, &kfe->faces, f);
1726 }
1727 }
1728
1729 return kfe;
1730}
1731
1733{
1734 ListBase *list = static_cast<ListBase *>(BLI_ghash_lookup(kcd->kedgefacemap, f));
1735
1736 if (!list) {
1737 BMIter bmiter;
1738 BMEdge *e;
1739
1740 list = knife_empty_list(kcd);
1741
1742 BM_ITER_ELEM (e, &bmiter, f, BM_EDGES_OF_FACE) {
1743 knife_append_list(kcd, list, get_bm_knife_edge(kcd, e, ob_index));
1744 }
1745
1746 BLI_ghash_insert(kcd->kedgefacemap, f, list);
1747 }
1748
1749 return list;
1750}
1751
1753{
1754 knife_append_list(kcd, knife_get_face_kedges(kcd, kfe->v1->ob_index, f), kfe);
1755 knife_append_list(kcd, &kfe->faces, f);
1756}
1757
1759 KnifeEdge *kfe,
1760 const float co[3],
1761 const float cageco[3],
1762 KnifeEdge **r_kfe)
1763{
1764 KnifeEdge *newkfe = new_knife_edge(kcd);
1765 LinkData *ref;
1766 BMFace *f;
1767
1768 newkfe->v1 = kfe->v1;
1769 newkfe->v2 = new_knife_vert(kcd, co, cageco);
1770 newkfe->v2->ob_index = kfe->v1->ob_index;
1771 newkfe->v2->is_cut = true;
1772 if (kfe->e) {
1773 knife_add_edge_faces_to_vert(kcd, newkfe->v2, kfe->e);
1774 }
1775 else {
1776 /* kfe cuts across an existing face.
1777 * If v1 and v2 are in multiple faces together (e.g., if they
1778 * are in doubled polys) then this arbitrarily chooses one of them. */
1779 f = knife_find_common_face(&kfe->v1->faces, &kfe->v2->faces);
1780 if (f) {
1781 knife_append_list(kcd, &newkfe->v2->faces, f);
1782 }
1783 }
1784 newkfe->basef = kfe->basef;
1785
1786 ref = find_ref(&kfe->v1->edges, kfe);
1787 BLI_remlink(&kfe->v1->edges, ref);
1788
1789 kfe->v1 = newkfe->v2;
1790 kfe->v1->is_splitting = true;
1791 BLI_addtail(&kfe->v1->edges, ref);
1792
1793 LISTBASE_FOREACH (LinkData *, ref, &kfe->faces) {
1794 knife_edge_append_face(kcd, newkfe, static_cast<BMFace *>(ref->data));
1795 }
1796
1797 knife_add_to_vert_edges(kcd, newkfe);
1798
1799 newkfe->is_cut = kfe->is_cut;
1800 newkfe->e = kfe->e;
1801
1802 newkfe->splits++;
1803 kfe->splits++;
1804
1805 kcd->undo->splits++;
1806
1807 BLI_stack_push(kcd->splitstack, (void *)&kfe);
1808 BLI_stack_push(kcd->splitstack, (void *)&newkfe);
1809
1810 *r_kfe = newkfe;
1811
1812 return newkfe->v2;
1813}
1814
1815/* Rejoin two edges split by #knife_split_edge. */
1816static void knife_join_edge(KnifeEdge *newkfe, KnifeEdge *kfe)
1817{
1818 newkfe->is_invalid = true;
1819 newkfe->v2->is_invalid = true;
1820
1821 kfe->v1 = newkfe->v1;
1822
1823 kfe->splits--;
1824 kfe->v1->is_splitting = false;
1825 kfe->v2->is_splitting = false;
1826}
1827
1830/* -------------------------------------------------------------------- */
1834static void knife_snap_curr(KnifeTool_OpData *kcd, const float2 &mval);
1835
1836/* User has just clicked for first time or first time after a restart (E key).
1837 * Copy the current position data into prev. */
1838static void knife_start_cut(KnifeTool_OpData *kcd, const float2 &mval)
1839{
1840 knife_snap_curr(kcd, mval);
1841 kcd->prev = kcd->curr;
1842 kcd->mdata.is_stored = false;
1843}
1844
1846{
1847 kpos->bmface = lh->f;
1848 kpos->vert = lh->v;
1849 kpos->edge = lh->kfe;
1850 copy_v3_v3(kpos->cage, lh->cagehit);
1851 copy_v2_v2(kpos->mval, lh->schit);
1852}
1853
1854/* Primary key: lambda along cut
1855 * Secondary key: lambda along depth
1856 * Tertiary key: pointer comparisons of verts if both snapped to verts
1857 */
1858static int linehit_compare(const KnifeLineHit &lh1, const KnifeLineHit &lh2)
1859{
1860 if (lh1.l < lh2.l) {
1861 return true;
1862 }
1863 if (lh1.l > lh2.l) {
1864 return false;
1865 }
1866 if (lh1.m < lh2.m) {
1867 return true;
1868 }
1869 if (lh1.m > lh2.m) {
1870 return false;
1871 }
1872 if (lh1.v < lh2.v) {
1873 return true;
1874 }
1875 if (lh1.v > lh2.v) {
1876 return false;
1877 }
1878 return false;
1879}
1880
1881/*
1882 * Sort linehits by distance along cut line, and secondarily from
1883 * front to back (from eye), and tertiarily by snap vertex,
1884 * and remove any duplicates.
1885 */
1887{
1888 bool is_double = false;
1889
1890 if (kcd->linehits.is_empty()) {
1891 return;
1892 }
1893
1894 std::sort(kcd->linehits.begin(), kcd->linehits.end(), linehit_compare);
1895
1896 /* Remove any edge hits that are preceded or followed
1897 * by a vertex hit that is very near. Mark such edge hits using
1898 * l == -1 and then do another pass to actually remove.
1899 * Also remove all but one of a series of vertex hits for the same vertex. */
1900 const int64_t total_hits = kcd->linehits.size();
1901 for (int i = 0; i < total_hits; i++) {
1902 KnifeLineHit *lhi = &kcd->linehits[i];
1903 if (lhi->v == nullptr) {
1904 continue;
1905 }
1906
1907 for (int j = i - 1; j >= 0; j--) {
1908 KnifeLineHit *lhj = &kcd->linehits[j];
1909 if (!lhj->kfe || fabsf(lhi->l - lhj->l) > KNIFE_FLT_EPSBIG ||
1910 fabsf(lhi->m - lhj->m) > KNIFE_FLT_EPSBIG)
1911 {
1912 break;
1913 }
1914
1915 if (lhi->kfe == lhj->kfe) {
1916 lhj->l = -1.0f;
1917 is_double = true;
1918 }
1919 }
1920 for (int j = i + 1; j < total_hits; j++) {
1921 KnifeLineHit *lhj = &kcd->linehits[j];
1922 if (fabsf(lhi->l - lhj->l) > KNIFE_FLT_EPSBIG || fabsf(lhi->m - lhj->m) > KNIFE_FLT_EPSBIG) {
1923 break;
1924 }
1925 if ((lhj->kfe && (lhi->kfe == lhj->kfe)) || (lhi->v == lhj->v)) {
1926 lhj->l = -1.0f;
1927 is_double = true;
1928 }
1929 }
1930 }
1931
1932 if (is_double) {
1933 /* Delete-in-place loop: copying from pos j to pos i+1. */
1934 int i = 0;
1935 int j = 1;
1936 while (j < total_hits) {
1937 KnifeLineHit *lhi = &kcd->linehits[i];
1938 KnifeLineHit *lhj = &kcd->linehits[j];
1939 if (lhj->l == -1.0f) {
1940 j++; /* Skip copying this one. */
1941 }
1942 else {
1943 /* Copy unless a no-op. */
1944 if (lhi->l == -1.0f) {
1945 /* Could happen if linehits[0] is being deleted. */
1946 memcpy(&kcd->linehits[i], &kcd->linehits[j], sizeof(KnifeLineHit));
1947 }
1948 else {
1949 if (i + 1 != j) {
1950 memcpy(&kcd->linehits[i + 1], &kcd->linehits[j], sizeof(KnifeLineHit));
1951 }
1952 i++;
1953 }
1954 j++;
1955 }
1956 }
1957 kcd->linehits.resize(i + 1);
1958 }
1959}
1960
1961/* Add hit to list of hits in facehits[f], where facehits is a map, if not already there. */
1963 GHash *facehits,
1964 BMFace *f,
1965 KnifeLineHit *hit)
1966{
1967 ListBase *list = static_cast<ListBase *>(BLI_ghash_lookup(facehits, f));
1968
1969 if (!list) {
1970 list = knife_empty_list(kcd);
1971 BLI_ghash_insert(facehits, f, list);
1972 }
1973 knife_append_list_no_dup(kcd, list, hit);
1974}
1975
1981 const KnifeLineHit *lh,
1982 const float co[3])
1983{
1984
1985 if (lh->v && lh->v->v) {
1986 BMLoop *l; /* side-of-loop */
1987 if ((l = BM_face_vert_share_loop(f, lh->v->v)) &&
1988 (BM_loop_point_side_of_loop_test(l, co) < 0.0f))
1989 {
1990 return true;
1991 }
1992 }
1993 else if (lh->kfe && lh->kfe->e) {
1994 BMLoop *l; /* side-of-edge */
1995 if ((l = BM_face_edge_share_loop(f, lh->kfe->e)) &&
1996 (BM_loop_point_side_of_edge_test(l, co) < 0.0f))
1997 {
1998 return true;
1999 }
2000 }
2001
2002 return false;
2003}
2004
2006 KnifeLineHit *lh1,
2007 KnifeLineHit *lh2,
2008 BMFace *f)
2009{
2010 KnifeEdge *kfe, *kfe2;
2011 BMEdge *e_base;
2012
2013 if ((lh1->v && lh1->v == lh2->v) || (lh1->kfe && lh1->kfe == lh2->kfe)) {
2014 return;
2015 }
2016
2017 /* If the cut is on an edge. */
2018 if ((lh1->v && lh2->v) && (lh1->v->v && lh2->v && lh2->v->v) &&
2019 (e_base = BM_edge_exists(lh1->v->v, lh2->v->v)))
2020 {
2021 return;
2022 }
2025 {
2026 return;
2027 }
2028
2029 /* Check if edge actually lies within face (might not, if this face is concave). */
2030 if ((lh1->v && !lh1->kfe) && (lh2->v && !lh2->kfe)) {
2031 if (!knife_verts_edge_in_face(lh1->v, lh2->v, f)) {
2032 return;
2033 }
2034 }
2035
2036 kfe = new_knife_edge(kcd);
2037 kfe->is_cut = true;
2038 kfe->basef = f;
2039
2040 if (lh1->v) {
2041 kfe->v1 = lh1->v;
2042 }
2043 else if (lh1->kfe) {
2044 kfe->v1 = knife_split_edge(kcd, lh1->kfe, lh1->hit, lh1->cagehit, &kfe2);
2045 lh1->v = kfe->v1; /* Record the #KnifeVert for this hit. */
2046 }
2047 else {
2048 BLI_assert(lh1->f);
2049 kfe->v1 = new_knife_vert(kcd, lh1->hit, lh1->cagehit);
2050 kfe->v1->ob_index = lh1->ob_index;
2051 kfe->v1->is_cut = true;
2052 knife_append_list(kcd, &kfe->v1->faces, lh1->f);
2053 lh1->v = kfe->v1; /* Record the #KnifeVert for this hit. */
2054 }
2055
2056 if (lh2->v) {
2057 kfe->v2 = lh2->v;
2058 }
2059 else if (lh2->kfe) {
2060 kfe->v2 = knife_split_edge(kcd, lh2->kfe, lh2->hit, lh2->cagehit, &kfe2);
2061 lh2->v = kfe->v2; /* Future uses of lh2 won't split again. */
2062 }
2063 else {
2064 BLI_assert(lh2->f);
2065 kfe->v2 = new_knife_vert(kcd, lh2->hit, lh2->cagehit);
2066 kfe->v2->ob_index = lh2->ob_index;
2067 kfe->v2->is_cut = true;
2068 knife_append_list(kcd, &kfe->v2->faces, lh2->f);
2069 lh2->v = kfe->v2; /* Record the KnifeVert for this hit. */
2070 }
2071
2072 knife_add_to_vert_edges(kcd, kfe);
2073
2074 if (kfe->basef && !find_ref(&kfe->faces, kfe->basef)) {
2075 knife_edge_append_face(kcd, kfe, kfe->basef);
2076 }
2077
2078 /* Update current undo frame cut count. */
2079 kcd->undo->cuts++;
2080}
2081
2082/* Given a list of KnifeLineHits for one face, sorted by l
2083 * and then by m, make the required KnifeVerts and
2084 * KnifeEdges.
2085 */
2087{
2088 LinkData *r;
2089
2090 if (BLI_listbase_count_at_most(hits, 2) != 2) {
2091 return;
2092 }
2093
2094 for (r = static_cast<LinkData *>(hits->first); r->next; r = r->next) {
2096 kcd, static_cast<KnifeLineHit *>(r->data), static_cast<KnifeLineHit *>(r->next->data), f);
2097 }
2098}
2099
2101{
2102 KnifeEdge *kfe;
2103 int edge_array_len = BLI_listbase_count(kfedges);
2104 int i;
2105
2106 BMEdge **edge_array = static_cast<BMEdge **>(BLI_array_alloca(edge_array, edge_array_len));
2107
2108 /* Point to knife edges we've created edges in, edge_array aligned. */
2109 KnifeEdge **kfe_array = static_cast<KnifeEdge **>(BLI_array_alloca(kfe_array, edge_array_len));
2110
2112
2113 i = 0;
2114 LISTBASE_FOREACH (LinkData *, ref, kfedges) {
2115 bool is_new_edge = false;
2116 kfe = static_cast<KnifeEdge *>(ref->data);
2117
2118 if (kfe->is_invalid) {
2119 continue;
2120 }
2121
2122 if (kfe->e == nullptr) {
2123 if (kfe->v1->v && kfe->v2->v) {
2124 kfe->e = BM_edge_exists(kfe->v1->v, kfe->v2->v);
2125 }
2126 }
2127
2128 if (kfe->e) {
2129 if (BM_edge_in_face(kfe->e, f)) {
2130 /* Shouldn't happen, but in this case just ignore. */
2131 continue;
2132 }
2133 }
2134 else {
2135 if (kfe->v1->v == nullptr) {
2136 kfe->v1->v = BM_vert_create(bm, kfe->v1->co, nullptr, eBMCreateFlag(0));
2137 }
2138 if (kfe->v2->v == nullptr) {
2139 kfe->v2->v = BM_vert_create(bm, kfe->v2->co, nullptr, eBMCreateFlag(0));
2140 }
2141 BLI_assert(kfe->e == nullptr);
2142 kfe->e = BM_edge_create(bm, kfe->v1->v, kfe->v2->v, nullptr, eBMCreateFlag(0));
2143 if (kfe->e) {
2145 BM_edge_select_set(bm, kfe->e, true);
2146 }
2147 is_new_edge = true;
2148 }
2149 }
2150
2151 BLI_assert(kfe->e);
2152
2153 if (BLI_gset_add(kcd->edgenet.edge_visit, kfe->e)) {
2154 kfe_array[i] = is_new_edge ? kfe : nullptr;
2155 edge_array[i] = kfe->e;
2156 i += 1;
2157 }
2158 }
2159
2160 if (i) {
2161 const int edge_array_len_orig = i;
2162 edge_array_len = i;
2163
2164#ifdef USE_NET_ISLAND_CONNECT
2165 uint edge_array_holes_len;
2166 BMEdge **edge_array_holes;
2168 f,
2169 edge_array,
2170 edge_array_len,
2171 true,
2172 kcd->edgenet.arena,
2173 &edge_array_holes,
2174 &edge_array_holes_len))
2175 {
2177 for (i = edge_array_len; i < edge_array_holes_len; i++) {
2178 BM_edge_select_set(bm, edge_array_holes[i], true);
2179 }
2180 }
2181
2182 edge_array_len = edge_array_holes_len;
2183 edge_array = edge_array_holes; /* Owned by the arena. */
2184 }
2185#endif
2186
2187 {
2188 BM_face_split_edgenet(bm, f, edge_array, edge_array_len, nullptr);
2189 }
2190
2191 /* Remove dangling edges, not essential - but nice for users. */
2192 for (i = 0; i < edge_array_len_orig; i++) {
2193 if (kfe_array[i] == nullptr) {
2194 continue;
2195 }
2196 if (BM_edge_is_wire(kfe_array[i]->e)) {
2197 BM_edge_kill(bm, kfe_array[i]->e);
2198 kfe_array[i]->e = nullptr;
2199 }
2200 }
2201
2202#ifdef USE_NET_ISLAND_CONNECT
2204#endif
2205 }
2206
2207 BLI_gset_clear(kcd->edgenet.edge_visit, nullptr);
2208}
2209
2210static int sort_verts_by_dist_cb(void *co_p, const void *cur_a_p, const void *cur_b_p)
2211{
2212 const KnifeVert *cur_a = static_cast<const KnifeVert *>(((const LinkData *)cur_a_p)->data);
2213 const KnifeVert *cur_b = static_cast<const KnifeVert *>(((const LinkData *)cur_b_p)->data);
2214 const float *co = static_cast<const float *>(co_p);
2215 const float a_sq = len_squared_v3v3(co, cur_a->co);
2216 const float b_sq = len_squared_v3v3(co, cur_b->co);
2217
2218 if (a_sq < b_sq) {
2219 return -1;
2220 }
2221 if (a_sq > b_sq) {
2222 return 1;
2223 }
2224 return 0;
2225}
2226
2227/* Use the network of KnifeEdges and KnifeVerts accumulated to make real BMVerts and BMEdedges. */
2228static void knife_make_cuts(KnifeTool_OpData *kcd, int ob_index)
2229{
2230 Object *ob = kcd->objects[ob_index];
2232 BMesh *bm = em->bm;
2233 KnifeEdge *kfe;
2234 KnifeVert *kfv;
2235 BMEdge *enew;
2236 ListBase *list;
2237 float pct;
2238 BLI_mempool_iter iter;
2241
2242 /* Put list of cutting edges for a face into fhash, keyed by face. */
2243 BLI_mempool_iternew(kcd->kedges, &iter);
2244 for (kfe = static_cast<KnifeEdge *>(BLI_mempool_iterstep(&iter)); kfe;
2245 kfe = static_cast<KnifeEdge *>(BLI_mempool_iterstep(&iter)))
2246 {
2247 if (kfe->is_invalid || kfe->v1->ob_index != ob_index) {
2248 continue;
2249 }
2250
2251 /* Select edges that lie directly on the cut. */
2252 if (kcd->select_result) {
2253 if (kfe->e && kfe->is_cut) {
2254 BM_edge_select_set(bm, kfe->e, true);
2255 }
2256 }
2257
2258 BMFace *f = kfe->basef;
2259 if (!f || kfe->e) {
2260 continue;
2261 }
2262 list = fhash.lookup_default(f, nullptr);
2263 if (!list) {
2264 list = knife_empty_list(kcd);
2265 fhash.add(f, list);
2266 }
2267 knife_append_list(kcd, list, kfe);
2268 }
2269
2270 /* Put list of splitting vertices for an edge into ehash, keyed by edge. */
2271 BLI_mempool_iternew(kcd->kverts, &iter);
2272 for (kfv = static_cast<KnifeVert *>(BLI_mempool_iterstep(&iter)); kfv;
2273 kfv = static_cast<KnifeVert *>(BLI_mempool_iterstep(&iter)))
2274 {
2275 if (kfv->v || kfv->is_invalid || kfv->ob_index != ob_index) {
2276 continue; /* Already have a BMVert. */
2277 }
2278 LISTBASE_FOREACH (LinkData *, ref, &kfv->edges) {
2279 kfe = static_cast<KnifeEdge *>(ref->data);
2280 BMEdge *e = kfe->e;
2281 if (!e) {
2282 continue;
2283 }
2284 list = ehash.lookup_default(e, nullptr);
2285 if (!list) {
2286 list = knife_empty_list(kcd);
2287 ehash.add(e, list);
2288 }
2289 /* There can be more than one kfe in kfv's list with same e. */
2290 if (!find_ref(list, kfv)) {
2291 knife_append_list(kcd, list, kfv);
2292 }
2293 }
2294 }
2295
2296 /* Split bmesh edges where needed. */
2297 for (auto [e, list] : ehash.items()) {
2299
2300 LISTBASE_FOREACH (LinkData *, ref, list) {
2301 kfv = static_cast<KnifeVert *>(ref->data);
2302 pct = line_point_factor_v3(kfv->co, e->v1->co, e->v2->co);
2303 kfv->v = BM_edge_split(bm, e, e->v1, &enew, pct);
2304 }
2305 }
2306
2307 if (kcd->only_select) {
2309 }
2310
2311 /* Do cuts for each face. */
2312 for (auto [f, list] : fhash.items()) {
2313 knife_make_face_cuts(kcd, bm, f, list);
2314 }
2315}
2316
2317/* User has just left-clicked after the first time.
2318 * Add all knife cuts implied by line from prev to curr.
2319 * If that line crossed edges then kcd->linehits will be non-null.
2320 * Make all of the KnifeVerts and KnifeEdges implied by this cut.
2321 */
2323{
2324 GHash *facehits;
2325 BMFace *f;
2326 GHashIterator giter;
2327 ListBase *list;
2328
2329 /* Allocate new undo frame on stack, unless cut is being dragged. */
2330 if (!kcd->is_drag_undo) {
2331 kcd->undo = static_cast<KnifeUndoFrame *>(BLI_stack_push_r(kcd->undostack));
2332 kcd->undo->pos = kcd->prev;
2333 kcd->undo->cuts = 0;
2334 kcd->undo->splits = 0;
2335 kcd->undo->mdata = kcd->mdata;
2336 kcd->is_drag_undo = true;
2337 }
2338
2339 /* Save values for angle drawing calculations. */
2340 copy_v3_v3(kcd->mdata.cage, kcd->prev.cage);
2341 copy_v2_v2(kcd->mdata.mval, kcd->prev.mval);
2342 kcd->mdata.is_stored = true;
2343
2345 if (kcd->linehits.is_empty()) {
2346 if (kcd->is_drag_hold == false) {
2347 kcd->prev = kcd->curr;
2348 }
2349 return;
2350 }
2351
2352 /* Consider most recent linehit in angle drawing calculations. */
2353 if (kcd->linehits.size() >= 2) {
2354 copy_v3_v3(kcd->mdata.cage, kcd->linehits[kcd->linehits.size() - 2].cagehit);
2355 }
2356
2357 /* Make facehits: map face -> list of linehits touching it. */
2358 facehits = BLI_ghash_ptr_new("knife facehits");
2359 for (KnifeLineHit &hit : kcd->linehits) {
2360 KnifeLineHit *lh = &hit;
2361 if (lh->f) {
2362 add_hit_to_facehits(kcd, facehits, lh->f, lh);
2363 }
2364 if (lh->v) {
2365 LISTBASE_FOREACH (LinkData *, r, &lh->v->faces) {
2366 add_hit_to_facehits(kcd, facehits, static_cast<BMFace *>(r->data), lh);
2367 }
2368 }
2369 if (lh->kfe) {
2370 LISTBASE_FOREACH (LinkData *, r, &lh->kfe->faces) {
2371 add_hit_to_facehits(kcd, facehits, static_cast<BMFace *>(r->data), lh);
2372 }
2373 }
2374 }
2375
2376 /* NOTE: as following loop progresses, the 'v' fields of
2377 * the linehits will be filled in (as edges are split or
2378 * in-face verts are made), so it may be true that both
2379 * the v and the kfe or f fields will be non-null. */
2380 GHASH_ITER (giter, facehits) {
2381 f = (BMFace *)BLI_ghashIterator_getKey(&giter);
2382 list = (ListBase *)BLI_ghashIterator_getValue(&giter);
2383 knife_cut_face(kcd, f, list);
2384 }
2385
2386 /* Set up for next cut. */
2387 kcd->prev = kcd->curr;
2388
2389 if (kcd->prev.bmface) {
2390 /* Was "in face" but now we have a KnifeVert it is snapped to. */
2391 KnifeLineHit *lh = &kcd->linehits.last();
2392 kcd->prev.vert = lh->v;
2393 kcd->prev.bmface = nullptr;
2394 }
2395
2396 if (kcd->is_drag_hold) {
2397 KnifeLineHit *lh = &kcd->linehits.last();
2398 linehit_to_knifepos(&kcd->prev, lh);
2399 }
2400
2401 BLI_ghash_free(facehits, nullptr, nullptr);
2403}
2404
2406{
2408}
2409
2412/* -------------------------------------------------------------------- */
2416/* Record the index in kcd->em->looptris of first looptri triple for a given face,
2417 * given an index for some triple in that array.
2418 * This assumes that all of the triangles for a given face are contiguous
2419 * in that array (as they are by the current tessellation routines).
2420 * Actually store index + 1 in the hash, because 0 looks like "no entry"
2421 * to hash lookup routine; will reverse this in the get routine.
2422 * Doing this lazily rather than all at once for all faces.
2423 */
2424static void set_lowest_face_tri(KnifeTool_OpData *kcd, BMEditMesh *em, BMFace *f, int index)
2425{
2426 int i;
2427
2428 if (BLI_ghash_lookup(kcd->facetrimap, f)) {
2429 return;
2430 }
2431
2432 BLI_assert(index >= 0 && index < em->looptris.size());
2433 BLI_assert(em->looptris[index][0]->f == f);
2434 for (i = index - 1; i >= 0; i--) {
2435 if (em->looptris[i][0]->f != f) {
2436 i++;
2437 break;
2438 }
2439 }
2440 if (i == -1) {
2441 i++;
2442 }
2443
2445}
2446
2447/* This should only be called for faces that have had a lowest face tri set by previous function.
2448 */
2450{
2451 int ans;
2452
2454 BLI_assert(ans != 0);
2455 return ans - 1;
2456}
2457
2467 const float s[2],
2468 const float v1[3],
2469 const float v2[3],
2470 int ob_index,
2471 BMFace *f,
2472 const float face_tol_sq,
2473 float hit_co[3],
2474 float hit_cageco[3])
2475{
2476 Object *ob = kcd->objects[ob_index];
2478
2479 int tottri, tri_i;
2480 float raydir[3];
2481 float tri_norm[3], tri_plane[4];
2482 float se1[2], se2[2];
2483 float d, lambda;
2484 ListBase *list;
2485 KnifeEdge *kfe;
2486
2487 sub_v3_v3v3(raydir, v2, v1);
2488 normalize_v3(raydir);
2489 tri_i = get_lowest_face_tri(kcd, f);
2490 tottri = em->looptris.size();
2491 BLI_assert(tri_i >= 0 && tri_i < tottri);
2492
2493 for (; tri_i < tottri; tri_i++) {
2494 float tri_cos[3][3];
2495 float ray_tri_uv[2];
2496
2497 const std::array<BMLoop *, 3> &ltri = em->looptris[tri_i];
2498 if (ltri[0]->f != f) {
2499 break;
2500 }
2501
2502 knife_bm_tri_cagecos_get_worldspace(kcd, ob_index, tri_i, tri_cos);
2503
2504 /* Using epsilon test in case ray is directly through an internal
2505 * tessellation edge and might not hit either tessellation tri with
2506 * an exact test;
2507 * We will exclude hits near real edges by a later test. */
2508 if (isect_ray_tri_epsilon_v3(v1, raydir, UNPACK3(tri_cos), &lambda, ray_tri_uv, KNIFE_FLT_EPS))
2509 {
2510 /* Check if line coplanar with tri. */
2511 normal_tri_v3(tri_norm, UNPACK3(tri_cos));
2512 plane_from_point_normal_v3(tri_plane, tri_cos[0], tri_norm);
2513 if ((dist_squared_to_plane_v3(v1, tri_plane) < KNIFE_FLT_EPS) &&
2515 {
2516 return false;
2517 }
2518 interp_v3_v3v3v3_uv(hit_cageco, UNPACK3(tri_cos), ray_tri_uv);
2519 /* Now check that far enough away from verts and edges. */
2520 list = knife_get_face_kedges(kcd, ob_index, f);
2521 LISTBASE_FOREACH (LinkData *, ref, list) {
2522 kfe = static_cast<KnifeEdge *>(ref->data);
2523 if (kfe->is_invalid) {
2524 continue;
2525 }
2526 knife_project_v2(kcd, kfe->v1->cageco, se1);
2527 knife_project_v2(kcd, kfe->v2->cageco, se2);
2528 d = dist_squared_to_line_segment_v2(s, se1, se2);
2529 if (d < face_tol_sq) {
2530 return false;
2531 }
2532 }
2533 interp_v3_v3v3v3_uv(hit_co, ltri[0]->v->co, ltri[1]->v->co, ltri[2]->v->co, ray_tri_uv);
2534 return true;
2535 }
2536 }
2537 return false;
2538}
2539
2545{
2546 Object *ob;
2547 BMEditMesh *em;
2548 BMIter iter;
2549 BMVert *v;
2550 float min[3], max[3];
2551 float ws[3];
2552 INIT_MINMAX(min, max);
2553
2554 for (int ob_index = 0; ob_index < kcd->objects.size(); ob_index++) {
2555 ob = kcd->objects[ob_index];
2556 em = BKE_editmesh_from_object(ob);
2557
2558 const Span<float3> positions_cage = kcd->objects_info[ob_index].positions_cage;
2559 if (!positions_cage.is_empty()) {
2560 for (int i = 0; i < em->bm->totvert; i++) {
2561 copy_v3_v3(ws, positions_cage[i]);
2562 mul_m4_v3(ob->object_to_world().ptr(), ws);
2563 minmax_v3v3_v3(min, max, ws);
2564 }
2565 }
2566 else {
2567 BM_ITER_MESH (v, &iter, em->bm, BM_VERTS_OF_MESH) {
2568 copy_v3_v3(ws, v->co);
2569 mul_m4_v3(ob->object_to_world().ptr(), ws);
2570 minmax_v3v3_v3(min, max, ws);
2571 }
2572 }
2573 }
2574
2575 kcd->ortho_extent = len_v3v3(min, max) / 2;
2577}
2578
2579/* Do edges e1 and e2 go between exactly the same coordinates? */
2580static bool coinciding_edges(BMEdge *e1, BMEdge *e2)
2581{
2582 const float *co11, *co12, *co21, *co22;
2583
2584 co11 = e1->v1->co;
2585 co12 = e1->v2->co;
2586 co21 = e2->v1->co;
2587 co22 = e2->v2->co;
2588 if ((equals_v3v3(co11, co21) && equals_v3v3(co12, co22)) ||
2589 (equals_v3v3(co11, co22) && equals_v3v3(co12, co21)))
2590 {
2591 return true;
2592 }
2593 return false;
2594}
2595
2596/* Callback used in point_is_visible to exclude hits on the faces that are the same
2597 * as or contain the hitting element (which is in user_data).
2598 * Also (see #44492) want to exclude hits on faces that butt up to the hitting element
2599 * (e.g., when you double an edge by an edge split).
2600 */
2601static bool bm_ray_cast_cb_elem_not_in_face_check(BMFace *f, void *user_data)
2602{
2603 bool ans;
2604 BMEdge *e, *e2;
2605 BMIter iter;
2606
2607 switch (((BMElem *)user_data)->head.htype) {
2608 case BM_FACE:
2609 ans = (BMFace *)user_data != f;
2610 break;
2611 case BM_EDGE:
2612 e = (BMEdge *)user_data;
2613 ans = !BM_edge_in_face(e, f);
2614 if (ans) {
2615 /* Is it a boundary edge, coincident with a split edge? */
2616 if (BM_edge_is_boundary(e)) {
2617 BM_ITER_ELEM (e2, &iter, f, BM_EDGES_OF_FACE) {
2618 if (coinciding_edges(e, e2)) {
2619 ans = false;
2620 break;
2621 }
2622 }
2623 }
2624 }
2625 break;
2626 case BM_VERT:
2627 ans = !BM_vert_in_face((BMVert *)user_data, f);
2628 break;
2629 default:
2630 ans = true;
2631 break;
2632 }
2633 return ans;
2634}
2635
2644 const float p[3],
2645 const float s[2],
2646 BMElem *ele_test)
2647{
2648 BMFace *f_hit;
2649
2650 /* If box clipping on, make sure p is not clipped. */
2651 if (RV3D_CLIPPING_ENABLED(kcd->vc.v3d, kcd->vc.rv3d) &&
2652 ED_view3d_clipping_test(kcd->vc.rv3d, p, false))
2653 {
2654 return false;
2655 }
2656
2657 /* If not cutting through, make sure no face is in front of p. */
2658 if (!kcd->cut_through) {
2659 float dist;
2660 float view[3], p_ofs[3];
2661
2662 /* TODO: I think there's a simpler way to get the required raycast ray. */
2663 ED_view3d_unproject_v3(kcd->vc.region, s[0], s[1], 0.0f, view);
2664
2665 /* Make p_ofs a little towards view, so ray doesn't hit p's face. */
2666 sub_v3_v3(view, p);
2667 dist = normalize_v3(view);
2668 copy_v3_v3(p_ofs, p);
2669
2670 /* Avoid projecting behind the viewpoint. */
2671 if (kcd->is_ortho && (kcd->vc.rv3d->persp != RV3D_CAMOB)) {
2672 dist = kcd->vc.v3d->clip_end * 2.0f;
2673 }
2674
2675 if (RV3D_CLIPPING_ENABLED(kcd->vc.v3d, kcd->vc.rv3d)) {
2676 float view_clip[2][3];
2677 /* NOTE: view_clip[0] should never get clipped. */
2678 copy_v3_v3(view_clip[0], p_ofs);
2679 madd_v3_v3v3fl(view_clip[1], p_ofs, view, dist);
2680
2682 view_clip[0], view_clip[1], kcd->vc.rv3d->clip_local, 6, view_clip[0], view_clip[1]))
2683 {
2684 dist = len_v3v3(p_ofs, view_clip[1]);
2685 }
2686 }
2687
2688 /* See if there's a face hit between p1 and the view. */
2689 if (ele_test) {
2690 f_hit = knife_bvh_raycast_filter(kcd,
2691 p_ofs,
2692 view,
2694 &dist,
2695 nullptr,
2696 nullptr,
2698 ele_test);
2699 }
2700 else {
2701 f_hit = knife_bvh_raycast(kcd, p_ofs, view, KNIFE_FLT_EPS, &dist, nullptr, nullptr);
2702 }
2703
2704 if (f_hit) {
2705 return false;
2706 }
2707 }
2708
2709 return true;
2710}
2711
2712/* Clip the line (v1, v2) to planes perpendicular to it and distances d from
2713 * the closest point on the line to the origin. */
2714static void clip_to_ortho_planes(float v1[3], float v2[3], const float center[3], const float d)
2715{
2716 float closest[3], dir[3];
2717
2718 sub_v3_v3v3(dir, v1, v2);
2719 normalize_v3(dir);
2720
2721 /* could be v1 or v2 */
2722 sub_v3_v3(v1, center);
2723 project_plane_normalized_v3_v3v3(closest, v1, dir);
2724 add_v3_v3(closest, center);
2725
2726 madd_v3_v3v3fl(v1, closest, dir, d);
2727 madd_v3_v3v3fl(v2, closest, dir, -d);
2728}
2729
2731 float s1[2],
2732 float s2[2],
2733 float sco[2],
2734 float cage[3],
2735 int ob_index,
2736 KnifeVert *v,
2737 KnifeEdge *kfe,
2738 KnifeLineHit *r_hit)
2739{
2740 memset(r_hit, 0, sizeof(*r_hit));
2741 copy_v3_v3(r_hit->cagehit, cage);
2742 copy_v2_v2(r_hit->schit, sco);
2743 r_hit->ob_index = ob_index;
2744
2745 /* Find position along screen line, used for sorting. */
2746 r_hit->l = len_v2v2(sco, s1) / len_v2v2(s2, s1);
2747
2748 r_hit->m = dot_m4_v3_row_z(kcd->vc.rv3d->persmatob, cage);
2749
2750 r_hit->v = v;
2751
2752 /* If this isn't from an existing BMVert, it may have been added to a BMEdge originally.
2753 * Knowing if the hit comes from an edge is important for edge-in-face checks later on.
2754 * See: #knife_add_single_cut -> #knife_verts_edge_in_face, #42611. */
2755 r_hit->kfe = kfe;
2756
2757 if (v) {
2758 copy_v3_v3(r_hit->hit, v->co);
2759 }
2760 else if (kfe) {
2762 r_hit->hit, cage, kfe->v1->co, kfe->v2->co, kfe->v1->cageco, kfe->v2->cageco);
2763 }
2764}
2765
2767 float s1[2],
2768 float s2[2],
2769 float sco[2],
2770 float ray_start[3],
2771 float ray_end[3],
2772 int ob_index,
2773 BMFace *f,
2774 float face_tol_sq,
2775 KnifeLineHit *r_hit)
2776{
2777 float3 p, cage;
2778 if (!knife_ray_intersect_face(kcd, sco, ray_start, ray_end, ob_index, f, face_tol_sq, p, cage)) {
2779 return false;
2780 }
2781 if (!point_is_visible(kcd, cage, sco, (BMElem *)f)) {
2782 return false;
2783 }
2784 knife_linehit_set(kcd, s1, s2, sco, cage, ob_index, nullptr, nullptr, r_hit);
2785 copy_v3_v3(r_hit->hit, p);
2786 r_hit->f = f;
2787 return true;
2788}
2789
2790/* Finds visible (or all, if cutting through) edges that intersects the current screen drag line.
2791 */
2793{
2794 float v1[3], v2[3], v3[3], v4[3], s1[2], s2[2];
2795 int *results, *result;
2796 ListBase *list;
2797 KnifeLineHit hit;
2798 float s[2], se1[2], se2[2];
2799 float d1, d2;
2800 float vert_tol, vert_tol_sq;
2801 float line_tol, line_tol_sq;
2802 float face_tol, face_tol_sq;
2803 uint tot;
2804 int i;
2805
2807
2808 copy_v3_v3(v1, kcd->prev.cage);
2809 copy_v3_v3(v2, kcd->curr.cage);
2810
2811 /* Project screen line's 3d coordinates back into 2d. */
2812 knife_project_v2(kcd, v1, s1);
2813 knife_project_v2(kcd, v2, s2);
2814
2815 if (kcd->is_interactive) {
2816 if (len_squared_v2v2(s1, s2) < 1.0f) {
2817 return;
2818 }
2819 }
2820 else {
2822 return;
2823 }
2824 }
2825
2826 /* Unproject screen line. */
2827 ED_view3d_win_to_segment_clipped(kcd->vc.depsgraph, kcd->region, kcd->vc.v3d, s1, v1, v3, true);
2828 ED_view3d_win_to_segment_clipped(kcd->vc.depsgraph, kcd->region, kcd->vc.v3d, s2, v2, v4, true);
2829
2830 /* Numeric error, 'v1' -> 'v2', 'v2' -> 'v4'
2831 * can end up being ~2000 units apart with an orthogonal perspective.
2832 *
2833 * (from ED_view3d_win_to_segment_clipped() above)
2834 * This gives precision error; rather than solving properly
2835 * (which may involve using doubles everywhere!),
2836 * limit the distance between these points. */
2837 if (kcd->is_ortho && (kcd->vc.rv3d->persp != RV3D_CAMOB)) {
2838 if (kcd->ortho_extent == 0.0f) {
2839 calc_ortho_extent(kcd);
2840 }
2841 clip_to_ortho_planes(v1, v3, kcd->ortho_extent_center, kcd->ortho_extent + 10.0f);
2843 }
2844
2845 float plane[4];
2846 {
2847 float v1_v2[3], v1_v3[3];
2848 sub_v3_v3v3(v1_v2, v2, v1);
2849 sub_v3_v3v3(v1_v3, v3, v1);
2850 cross_v3_v3v3(plane, v1_v2, v1_v3);
2851 plane_from_point_normal_v3(plane, v1, plane);
2852 }
2853
2854 /* First use BVH tree to find faces, knife edges, and knife verts that might
2855 * intersect the cut plane with rays v1-v3 and v2-v4.
2856 * This de-duplicates the candidates before doing more expensive intersection tests. */
2857
2858 results = BLI_bvhtree_intersect_plane(kcd->bvh.tree, plane, &tot);
2859 if (!results) {
2860 return;
2861 }
2862
2865 Set<KnifeEdge *> kfes;
2866 Set<KnifeVert *> kfvs;
2867
2868 Object *ob;
2869 BMEditMesh *em;
2870
2871 for (i = 0, result = results; i < tot; i++, result++) {
2872 uint ob_index = 0;
2873 BMLoop *const *ltri = nullptr;
2874 for (ob_index = 0; ob_index < kcd->objects.size(); ob_index++) {
2875 ob = kcd->objects[ob_index];
2876 em = BKE_editmesh_from_object(ob);
2877 if (*result >= 0 && *result < em->looptris.size()) {
2878 ltri = em->looptris[*result].data();
2879 break;
2880 }
2881 *result -= em->looptris.size();
2882 }
2883 BLI_assert(ltri != nullptr);
2884 BMFace *f = ltri[0]->f;
2885 set_lowest_face_tri(kcd, em, f, *result);
2886
2887 /* Occlude but never cut unselected faces (when only_select is used). */
2888 if (kcd->only_select && !BM_elem_flag_test(f, BM_ELEM_SELECT)) {
2889 continue;
2890 }
2891 /* For faces, store index of lowest hit looptri in hash. */
2892 if (faces.contains(f)) {
2893 continue;
2894 }
2895 /* Don't care what the value is except that it is non-null, for iterator. */
2896 faces.add(f);
2897 fobs.add(f, ob_index);
2898
2899 list = knife_get_face_kedges(kcd, ob_index, f);
2900 LISTBASE_FOREACH (LinkData *, ref, list) {
2901 KnifeEdge *kfe = static_cast<KnifeEdge *>(ref->data);
2902 if (kfe->is_invalid) {
2903 continue;
2904 }
2905 if (kfes.contains(kfe)) {
2906 continue;
2907 }
2908 kfes.add(kfe);
2909 kfvs.add(kfe->v1);
2910 kfvs.add(kfe->v2);
2911 }
2912 }
2913
2914 /* Now go through the candidates and find intersections. */
2915 /* These tolerances, in screen space, are for intermediate hits,
2916 * as ends are already snapped to screen. */
2917
2918 if (kcd->is_interactive) {
2919 vert_tol = KNIFE_FLT_EPS_PX_VERT;
2920 line_tol = KNIFE_FLT_EPS_PX_EDGE;
2921 face_tol = KNIFE_FLT_EPS_PX_FACE;
2922 }
2923 else {
2924 /* Use 1/100th of a pixel, see #43896 (too big), #47910 (too small).
2925 *
2926 * Update, leave this as is until we investigate not using pixel coords
2927 * for geometry calculations: #48023. */
2928 vert_tol = line_tol = face_tol = 0.5f;
2929 }
2930
2931 vert_tol_sq = vert_tol * vert_tol;
2932 line_tol_sq = line_tol * line_tol;
2933 face_tol_sq = face_tol * face_tol;
2934
2935 /* Assume these tolerances swamp floating point rounding errors in calculations below. */
2936
2937 /* First look for vertex hits. */
2938 Vector<KnifeLineHit> linehits;
2939 for (KnifeVert *v : kfvs) {
2940 KnifeEdge *kfe_hit = nullptr;
2941
2942 bool kfv_is_in_cut = false;
2943 if (ELEM(v, kcd->prev.vert, kcd->curr.vert)) {
2944 /* This KnifeVert was captured by the snap system.
2945 * Since the tolerance distance can be different, add this vertex directly.
2946 * Otherwise, the cut may fail or a close cut on a connected edge can be performed. */
2947 bm_elem_from_knife_vert(v, &kfe_hit);
2948 copy_v2_v2(s, (v == kcd->prev.vert) ? kcd->prev.mval : kcd->curr.mval);
2949 kfv_is_in_cut = true;
2950 }
2951 else {
2952 knife_project_v2(kcd, v->cageco, s);
2953 float d = dist_squared_to_line_segment_v2(s, s1, s2);
2954 if ((d <= vert_tol_sq) &&
2955 point_is_visible(kcd, v->cageco, s, bm_elem_from_knife_vert(v, &kfe_hit)))
2956 {
2957 kfv_is_in_cut = true;
2958 }
2959 }
2960
2961 if (kfv_is_in_cut) {
2962 knife_linehit_set(kcd, s1, s2, s, v->cageco, v->ob_index, v, kfe_hit, &hit);
2963 linehits.append(hit);
2964 }
2965 else {
2966 /* This vertex isn't used so remove from `kfvs`.
2967 * This is useful to detect KnifeEdges that can be skipped.
2968 * And it optimizes iteration a little bit. */
2969 kfvs.remove(v);
2970 }
2971 }
2972
2973 /* Now edge hits; don't add if a vertex at end of edge should have hit. */
2974 for (KnifeEdge *kfe : kfes) {
2975 /* If we intersect any of the vertices, don't attempt to intersect the edge. */
2976 if (kfvs.contains(kfe->v1) || kfvs.contains(kfe->v2)) {
2977 continue;
2978 }
2979
2980 knife_project_v2(kcd, kfe->v1->cageco, se1);
2981 knife_project_v2(kcd, kfe->v2->cageco, se2);
2982 float3 p_cage;
2983 float2 p_cage_ss;
2984 bool kfe_is_in_cut = false;
2985 if (kfe == kcd->prev.edge) {
2986 /* This KnifeEdge was captured by the snap system. */
2987 p_cage = kcd->prev.cage;
2988 p_cage_ss = kcd->prev.mval;
2989 kfe_is_in_cut = true;
2990 }
2991 else if (kfe == kcd->curr.edge) {
2992 /* This KnifeEdge was captured by the snap system. */
2993 p_cage = kcd->curr.cage;
2994 p_cage_ss = kcd->curr.mval;
2995 kfe_is_in_cut = true;
2996 }
2997 else {
2998 int isect_kind = isect_seg_seg_v2_point_ex(s1, s2, se1, se2, 0.0f, p_cage_ss);
2999 if (isect_kind == -1) {
3000 /* isect_seg_seg_v2_point doesn't do tolerance test around ends of s1-s2. */
3001 closest_to_line_segment_v2(p_cage_ss, s1, se1, se2);
3002 if (len_squared_v2v2(p_cage_ss, s1) <= line_tol_sq) {
3003 isect_kind = 1;
3004 }
3005 else {
3006 closest_to_line_segment_v2(p_cage_ss, s2, se1, se2);
3007 if (len_squared_v2v2(p_cage_ss, s2) <= line_tol_sq) {
3008 isect_kind = 1;
3009 }
3010 }
3011 }
3012 if (isect_kind == 1) {
3013 d1 = len_v2v2(p_cage_ss, se1);
3014 d2 = len_v2v2(se2, se1);
3015 if (!(d1 <= line_tol || d2 <= line_tol || fabsf(d1 - d2) <= line_tol)) {
3016 float3 r1, r2;
3017 float3 p_cage_dummy;
3018 /* Can't just interpolate between ends of `kfe` because
3019 * that doesn't work with perspective transformation.
3020 * Need to find 3d intersection of ray through `p_cage_ss`. */
3021 knife_input_ray_segment(kcd, p_cage_ss, r1, r2);
3022
3023 isect_kind = isect_line_line_v3(
3024 kfe->v1->cageco, kfe->v2->cageco, r1, r2, p_cage, p_cage_dummy);
3025 if (isect_kind >= 1 &&
3026 point_is_visible(kcd, p_cage, p_cage_ss, bm_elem_from_knife_edge(kfe)))
3027 {
3028 if (kcd->snap_midpoints) {
3029 /* Choose intermediate point snap too. */
3030 mid_v3_v3v3(p_cage, kfe->v1->cageco, kfe->v2->cageco);
3031 mid_v2_v2v2(p_cage_ss, se1, se2);
3032 }
3033 kfe_is_in_cut = true;
3034 }
3035 }
3036 }
3037 }
3038 if (kfe_is_in_cut) {
3039 knife_linehit_set(kcd, s1, s2, p_cage_ss, p_cage, kfe->v1->ob_index, nullptr, kfe, &hit);
3040 linehits.append(hit);
3041 }
3042 }
3043
3044 /* Now face hits; don't add if a vertex or edge in face should have hit. */
3045 const bool use_hit_prev = (kcd->prev.vert == nullptr) && (kcd->prev.edge == nullptr);
3046 const bool use_hit_curr = (kcd->curr.vert == nullptr) && (kcd->curr.edge == nullptr) &&
3047 !kcd->is_drag_hold;
3048 if (use_hit_prev || use_hit_curr) {
3049 for (BMFace *f : faces) {
3050 int ob_index = fobs.lookup(f);
3051 if (use_hit_prev &&
3052 knife_linehit_face_test(kcd, s1, s2, s1, v1, v3, ob_index, f, face_tol_sq, &hit))
3053 {
3054 linehits.append(hit);
3055 }
3056
3057 if (use_hit_curr &&
3058 knife_linehit_face_test(kcd, s1, s2, s2, v2, v4, ob_index, f, face_tol_sq, &hit))
3059 {
3060 linehits.append(hit);
3061 }
3062 }
3063 }
3064
3065 kcd->linehits = std::move(linehits);
3066
3067 MEM_freeN(results);
3068}
3069
3072/* -------------------------------------------------------------------- */
3077{
3078 zero_v3(kpd->cage);
3079 kpd->vert = nullptr;
3080 kpd->edge = nullptr;
3081 kpd->bmface = nullptr;
3082 kpd->ob_index = -1;
3083 zero_v2(kpd->mval);
3084}
3085
3088/* -------------------------------------------------------------------- */
3093 const float3 &ray_origin,
3094 const float3 &ray_normal,
3095 const float2 &mval,
3096 KnifePosData *r_kpd)
3097{
3098 float3 cage;
3099 int ob_index;
3100 BMFace *f;
3101 float dist = KMAXDIST;
3102
3103 f = knife_bvh_raycast(kcd, ray_origin, ray_normal, 0.0f, nullptr, cage, &ob_index);
3104
3105 if (f && kcd->only_select && BM_elem_flag_test(f, BM_ELEM_SELECT) == 0) {
3106 f = nullptr;
3107 ob_index = -1;
3108 }
3109
3110 if (f == nullptr) {
3111 if (kcd->is_interactive) {
3112 /* Try to use back-buffer selection method if ray casting failed.
3113 *
3114 * Apply the mouse coordinates to a copy of the view-context
3115 * since we don't want to rely on this being set elsewhere. */
3116 ViewContext vc = kcd->vc;
3117 vc.mval[0] = int(mval[0]);
3118 vc.mval[1] = int(mval[1]);
3119
3121 f = EDBM_face_find_nearest(&vc, &dist);
3122 }
3123
3124 if (f) {
3125 /* Cheat for now; just put in the origin instead
3126 * of a true coordinate on the face.
3127 * This just puts a point 1.0f in front of the view. */
3128 cage = ray_origin + ray_normal;
3129
3130 ob_index = 0;
3131 BLI_assert(ob_index == kcd->objects.first_index_of_try(vc.obact));
3132 }
3133 }
3134 }
3135
3136 if (f) {
3137 r_kpd->cage = cage;
3138 r_kpd->bmface = f;
3139 r_kpd->ob_index = ob_index;
3140 r_kpd->mval = mval;
3141
3142 return true;
3143 }
3144 return false;
3145}
3146
3154 KnifeTool_OpData *kcd, const float radius, int ob_index, BMFace *f, const float cageco[3])
3155{
3156 const float radius_sq = radius * radius;
3157 ListBase *list;
3158 float sco[2];
3159 float dis_sq;
3160 int c = 0;
3161
3162 knife_project_v2(kcd, cageco, sco);
3163
3164 list = knife_get_face_kedges(kcd, ob_index, f);
3165 LISTBASE_FOREACH (LinkData *, ref, list) {
3166 KnifeEdge *kfe = static_cast<KnifeEdge *>(ref->data);
3167 int i;
3168
3169 if (kfe->is_invalid) {
3170 continue;
3171 }
3172
3173 for (i = 0; i < 2; i++) {
3174 KnifeVert *kfv = i ? kfe->v2 : kfe->v1;
3175 float kfv_sco[2];
3176
3177 if (kfv->is_invalid) {
3178 continue;
3179 }
3180
3181 knife_project_v2(kcd, kfv->cageco, kfv_sco);
3182
3183 dis_sq = len_squared_v2v2(kfv_sco, sco);
3184 if (dis_sq < radius_sq) {
3185 if (RV3D_CLIPPING_ENABLED(kcd->vc.v3d, kcd->vc.rv3d)) {
3186 if (ED_view3d_clipping_test(kcd->vc.rv3d, kfv->cageco, false) == 0) {
3187 c++;
3188 }
3189 }
3190 else {
3191 c++;
3192 }
3193 }
3194 }
3195 }
3196
3197 return c;
3198}
3199
3206static float knife_snap_size(KnifeTool_OpData *kcd, float maxsize)
3207{
3208 BLI_assert(kcd->is_interactive == true);
3209 int density = 0;
3210
3211 if (!kcd->curr.is_space()) {
3213 kcd, maxsize * 2.0f, kcd->curr.ob_index, kcd->curr.bmface, kcd->curr.cage));
3214 }
3215
3216 return density ? min_ff(maxsize / (float(density) * 0.5f), maxsize) : maxsize;
3217}
3218
3225 const float3 &kfv1_cageco,
3226 const float3 &kfv2_cageco,
3227 float r_close[3])
3228{
3229 /* If snapping, check we're in bounds. */
3230 float lambda;
3231 float3 dir = kcd->curr.cage - kcd->prev.cage;
3232 if (!isect_ray_line_v3(kcd->prev.cage, dir, kfv1_cageco, kfv2_cageco, &lambda)) {
3233 return false;
3234 }
3235
3236 /* Be strict when constrained within edge. */
3237 if ((lambda < 0.0f - KNIFE_FLT_EPSBIG) || (lambda > 1.0f + KNIFE_FLT_EPSBIG)) {
3238 return false;
3239 }
3240
3241 interp_v3_v3v3(r_close, kfv1_cageco, kfv2_cageco, lambda);
3242 return true;
3243}
3244
3245/* `r_kpd->cage` is closest point on edge to the knife point. */
3247 int ob_index,
3248 BMFace *f,
3249 const float3 &ray_origin,
3250 const float3 &ray_normal,
3251 const float2 &curr_cage_ss,
3252 KnifePosData *r_kpd)
3253{
3254 float maxdist;
3255
3256 if (kcd->is_interactive) {
3257 maxdist = knife_snap_size(kcd, kcd->ethresh);
3258
3259 if (kcd->ignore_vert_snapping) {
3260 maxdist *= 0.5f;
3261 }
3262 }
3263 else {
3264 maxdist = KNIFE_FLT_EPS;
3265 }
3266
3267 const float maxdist_sq = maxdist * maxdist;
3268 float cur_dist_sq = maxdist_sq;
3269 bool has_hit = false;
3270
3271 /* Look through all edges associated with this face. */
3272 ListBase *list = knife_get_face_kedges(kcd, ob_index, f);
3273 LISTBASE_FOREACH (LinkData *, ref, list) {
3274 KnifeEdge *kfe = static_cast<KnifeEdge *>(ref->data);
3275 float test_cagep[3];
3276
3277 if (kfe->is_invalid) {
3278 continue;
3279 }
3280
3281 /* Get the closest point on the edge. */
3282 if ((kcd->is_angle_snapping || kcd->axis_constrained) && (kfe != kcd->prev.edge) &&
3283 (kcd->mode == MODE_DRAGGING))
3284 {
3285 /* Check if it is within the edges' bounds. */
3286 if (!knife_closest_constrain_to_edge(kcd, kfe->v1->cageco, kfe->v2->cageco, test_cagep)) {
3287 continue;
3288 }
3289 }
3290 else {
3292 ray_origin, ray_normal, kfe->v1->cageco, kfe->v2->cageco, test_cagep);
3293 }
3294
3295 /* Check if we're close enough. */
3296 float2 closest_ss;
3297 knife_project_v2(kcd, test_cagep, closest_ss);
3298 float dis_sq = len_squared_v2v2(closest_ss, curr_cage_ss);
3299 if (dis_sq >= cur_dist_sq) {
3300 continue;
3301 }
3302
3303 if (RV3D_CLIPPING_ENABLED(kcd->vc.v3d, kcd->vc.rv3d)) {
3304 /* Check we're in the view */
3305 if (ED_view3d_clipping_test(kcd->vc.rv3d, test_cagep, false)) {
3306 continue;
3307 }
3308 }
3309
3310 cur_dist_sq = dis_sq;
3311
3312 r_kpd->edge = kfe;
3313 if (kcd->snap_midpoints) {
3314 mid_v3_v3v3(r_kpd->cage, kfe->v1->cageco, kfe->v2->cageco);
3315 knife_project_v2(kcd, r_kpd->cage, r_kpd->mval);
3316 }
3317 else {
3318 copy_v3_v3(r_kpd->cage, test_cagep);
3319 r_kpd->mval = closest_ss;
3320 }
3321
3322 has_hit = true;
3323 }
3324
3325 return has_hit;
3326}
3327
3328/* Find a vertex near the mouse cursor, if it exists. */
3330 const KnifeEdge *kfe,
3331 const float2 &cage_ss,
3332 KnifePosData *r_kpd)
3333{
3334 float maxdist;
3335
3336 if (kcd->is_interactive) {
3337 maxdist = knife_snap_size(kcd, kcd->vthresh);
3338 if (kcd->ignore_vert_snapping) {
3339 maxdist *= 0.5f;
3340 }
3341 }
3342 else {
3343 maxdist = KNIFE_FLT_EPS;
3344 }
3345
3346 const float maxdist_sq = maxdist * maxdist;
3347 KnifeVert *curv = nullptr;
3348 float cur_kfv_sco[2];
3349 float dis_sq, curdis_sq = FLT_MAX;
3350
3351 for (int i = 0; i < 2; i++) {
3352 KnifeVert *kfv = i ? kfe->v2 : kfe->v1;
3353 float kfv_sco[2];
3354
3355 knife_project_v2(kcd, kfv->cageco, kfv_sco);
3356
3357 /* Be strict when in a constrained mode, the vertex needs to be very close to the cut line,
3358 * or we ignore. */
3359 if ((kcd->is_angle_snapping || kcd->axis_constrained) && (kcd->mode == MODE_DRAGGING)) {
3360 if (dist_squared_to_line_segment_v2(kfv_sco, kcd->prev.mval, kcd->curr.mval) >
3362 {
3363 continue;
3364 }
3365 }
3366
3367 dis_sq = len_squared_v2v2(kfv_sco, cage_ss);
3368 if (dis_sq < curdis_sq && dis_sq < maxdist_sq) {
3369 if (!RV3D_CLIPPING_ENABLED(kcd->vc.v3d, kcd->vc.rv3d) ||
3370 !ED_view3d_clipping_test(kcd->vc.rv3d, kfv->cageco, false))
3371 {
3372 curv = kfv;
3373 curdis_sq = dis_sq;
3374 copy_v2_v2(cur_kfv_sco, kfv_sco);
3375 }
3376 }
3377 }
3378
3379 if (curv) {
3380 r_kpd->cage = curv->cageco;
3381
3382 /* Update mouse coordinates to the snapped-to vertex's screen coordinates
3383 * this is important for angle snap, which uses the previous mouse position. */
3384 r_kpd->mval = cur_kfv_sco;
3385
3386 return true;
3387 }
3388
3389 return false;
3390}
3391
3395static float snap_v2_angle(float r[2], const float v[2], const float v_ref[2], float angle_snap)
3396{
3397 float m2[2][2];
3398 float v_unit[2];
3399 float angle, angle_delta;
3400
3401 BLI_ASSERT_UNIT_V2(v_ref);
3402
3403 normalize_v2_v2(v_unit, v);
3404 angle = angle_signed_v2v2(v_unit, v_ref);
3405 angle_delta = (roundf(angle / angle_snap) * angle_snap) - angle;
3406 angle_to_mat2(m2, angle_delta);
3407
3408 mul_v2_m2v2(r, m2, v);
3409 return angle + angle_delta;
3410}
3411
3412/* Update both kcd->curr.mval and kcd->mval to snap to required angle. */
3414{
3415 const float dvec_ref[2] = {1.0f, 0.0f};
3416 float dvec[2], dvec_snap[2];
3417
3418 float snap_step;
3419 /* Currently user can input any float between 0 and 180. */
3422 {
3423 snap_step = DEG2RADF(kcd->angle_snapping_increment);
3424 }
3425 else {
3427 }
3428
3429 sub_v2_v2v2(dvec, kcd->curr.mval, kcd->prev.mval);
3430 if (is_zero_v2(dvec)) {
3431 return false;
3432 }
3433
3434 kcd->angle = snap_v2_angle(dvec_snap, dvec, dvec_ref, snap_step);
3435
3436 add_v2_v2v2(kcd->curr.mval, kcd->prev.mval, dvec_snap);
3437
3438 copy_v2_v2(kcd->mval, kcd->curr.mval);
3439
3440 return true;
3441}
3442
3447 float r[3], const float v[3], const float v_ref[3], const float plane_no[3], float snap_step)
3448{
3449 /* Calculate angle between current cut vector and reference vector. */
3450 float angle, angle_delta;
3451 angle = angle_signed_on_axis_v3v3_v3(v, v_ref, plane_no);
3452 /* Use this to calculate the angle to rotate by based on snap_step. */
3453 angle_delta = (roundf(angle / snap_step) * snap_step) - angle;
3454
3455 /* Snap to angle. */
3456 rotate_v3_v3v3fl(r, v, plane_no, angle_delta);
3457 return angle + angle_delta;
3458}
3459
3460/* Snap to required angle along the plane of the face nearest to kcd->prev. */
3462{
3463 KnifeEdge *kfe;
3464 KnifeVert *kfv;
3465 BMFace *f;
3466 float refv[3];
3467
3468 /* Ray for kcd->curr. */
3469 float curr_origin[3];
3470 float curr_origin_ofs[3];
3471 float curr_ray[3], curr_ray_normal[3];
3472 float curr_cage[3]; /* Unused. */
3473
3474 float ray_hit[3];
3475 float lambda;
3476
3477 knife_input_ray_segment(kcd, kcd->curr.mval, curr_origin, curr_origin_ofs);
3478 sub_v3_v3v3(curr_ray, curr_origin_ofs, curr_origin);
3479 normalize_v3_v3(curr_ray_normal, curr_ray);
3480
3481 BMFace *fcurr = knife_bvh_raycast(
3482 kcd, curr_origin, curr_ray_normal, 0.0f, nullptr, curr_cage, nullptr);
3483
3484 if (!fcurr) {
3485 return false;
3486 }
3487
3488 /* Calculate a reference vector using previous cut segment, edge or vertex.
3489 * If none exists then exit. */
3490 if (kcd->prev.vert) {
3491 int count = 0;
3492 LISTBASE_FOREACH (LinkData *, ref, &kcd->prev.vert->edges) {
3493 kfe = ((KnifeEdge *)(ref->data));
3494 if (kfe->is_invalid) {
3495 continue;
3496 }
3497 if (kfe->e) {
3498 if (!BM_edge_in_face(kfe->e, fcurr)) {
3499 continue;
3500 }
3501 }
3502 if (count == kcd->snap_edge) {
3503 kfv = compare_v3v3(kfe->v1->cageco, kcd->prev.cage, KNIFE_FLT_EPSBIG) ? kfe->v2 : kfe->v1;
3504 sub_v3_v3v3(refv, kfv->cageco, kcd->prev.cage);
3505 kcd->snap_ref_edge = kfe;
3506 break;
3507 }
3508 count++;
3509 }
3510 }
3511 else if (kcd->prev.edge) {
3512 kfv = compare_v3v3(kcd->prev.edge->v1->cageco, kcd->prev.cage, KNIFE_FLT_EPSBIG) ?
3513 kcd->prev.edge->v2 :
3514 kcd->prev.edge->v1;
3515 sub_v3_v3v3(refv, kfv->cageco, kcd->prev.cage);
3516 kcd->snap_ref_edge = kcd->prev.edge;
3517 }
3518 else {
3519 return false;
3520 }
3521
3522 /* Choose best face for plane. */
3523 BMFace *fprev = nullptr;
3524 int fprev_ob_index = kcd->prev.ob_index;
3525 if (kcd->prev.vert && kcd->prev.vert->v) {
3526 LISTBASE_FOREACH (LinkData *, ref, &kcd->prev.vert->faces) {
3527 f = ((BMFace *)(ref->data));
3528 if (f == fcurr) {
3529 fprev = f;
3530 }
3531 }
3532 }
3533 else if (kcd->prev.edge) {
3534 LISTBASE_FOREACH (LinkData *, ref, &kcd->prev.edge->faces) {
3535 f = ((BMFace *)(ref->data));
3536 if (f == fcurr) {
3537 fprev = f;
3538 }
3539 }
3540 }
3541 else {
3542 /* Cut segment was started in a face. */
3543 float prev_origin[3];
3544 float prev_origin_ofs[3];
3545 float prev_ray[3], prev_ray_normal[3];
3546 float prev_cage[3]; /* Unused. */
3547
3548 knife_input_ray_segment(kcd, kcd->prev.mval, prev_origin, prev_origin_ofs);
3549
3550 sub_v3_v3v3(prev_ray, prev_origin_ofs, prev_origin);
3551 normalize_v3_v3(prev_ray_normal, prev_ray);
3552
3553 /* kcd->prev.face is usually not set. */
3554 fprev = knife_bvh_raycast(
3555 kcd, prev_origin, prev_ray_normal, 0.0f, nullptr, prev_cage, &fprev_ob_index);
3556 }
3557
3558 if (!fprev || fprev != fcurr) {
3559 return false;
3560 }
3561
3562 /* Use normal global direction. */
3563 Object *ob = kcd->objects[fprev_ob_index];
3564 float no_global[3];
3565 copy_v3_v3(no_global, fprev->no);
3566 mul_transposed_mat3_m4_v3(ob->world_to_object().ptr(), no_global);
3567 normalize_v3(no_global);
3568
3569 if (isect_ray_plane_v3_factor(curr_origin, curr_ray_normal, kcd->prev.cage, no_global, &lambda))
3570 {
3571 madd_v3_v3v3fl(ray_hit, curr_origin, curr_ray_normal, lambda);
3572
3573 /* Calculate snap step. */
3574 float snap_step;
3577 {
3578 snap_step = DEG2RADF(kcd->angle_snapping_increment);
3579 }
3580 else {
3582 }
3583
3584 float v1[3];
3585 float v2[3];
3586 float rotated_vec[3];
3587 /* Maybe check for vectors being zero here? */
3588 sub_v3_v3v3(v1, ray_hit, kcd->prev.cage);
3589 copy_v3_v3(v2, refv);
3590 kcd->angle = snap_v3_angle_plane(rotated_vec, v1, v2, no_global, snap_step);
3591 add_v3_v3(rotated_vec, kcd->prev.cage);
3592
3593 knife_project_v2(kcd, rotated_vec, kcd->curr.mval);
3594 copy_v2_v2(kcd->mval, kcd->curr.mval);
3595 return true;
3596 }
3597 return false;
3598}
3599
3601{
3602 KnifeEdge *kfe;
3603
3604 /* Ray for kcd->curr. */
3605 float curr_origin[3];
3606 float curr_origin_ofs[3];
3607 float curr_ray[3], curr_ray_normal[3];
3608 float curr_cage[3]; /* Unused. */
3609
3610 knife_input_ray_segment(kcd, kcd->curr.mval, curr_origin, curr_origin_ofs);
3611 sub_v3_v3v3(curr_ray, curr_origin_ofs, curr_origin);
3612 normalize_v3_v3(curr_ray_normal, curr_ray);
3613
3614 BMFace *fcurr = knife_bvh_raycast(
3615 kcd, curr_origin, curr_ray_normal, 0.0f, nullptr, curr_cage, nullptr);
3616
3617 int count = 0;
3618
3619 if (!fcurr) {
3620 return count;
3621 }
3622
3623 if (kcd->prev.vert) {
3624 LISTBASE_FOREACH (LinkData *, ref, &kcd->prev.vert->edges) {
3625 kfe = ((KnifeEdge *)(ref->data));
3626 if (kfe->is_invalid) {
3627 continue;
3628 }
3629 if (kfe->e) {
3630 if (!BM_edge_in_face(kfe->e, fcurr)) {
3631 continue;
3632 }
3633 }
3634 count++;
3635 }
3636 }
3637 else if (kcd->prev.edge) {
3638 return 1;
3639 }
3640 return count;
3641}
3642
3643/* Reset the snapping angle num input. */
3645{
3646 kcd->num.val[0] = 0;
3647 while (kcd->num.str_cur > 0) {
3648 kcd->num.str[kcd->num.str_cur - 1] = '\0';
3649 kcd->num.str_cur--;
3650 }
3651}
3658{
3659 /* Obtain current mouse position in world space. */
3660 float curr_cage_adjusted[3];
3662 kcd->vc.v3d, kcd->region, kcd->prev.cage, kcd->curr.mval, curr_cage_adjusted);
3663
3664 /* Constrain axes. */
3665 Scene *scene = kcd->scene;
3666 ViewLayer *view_layer = kcd->vc.view_layer;
3667 Object *obedit = (kcd->prev.ob_index != -1) ? kcd->objects[kcd->prev.ob_index] : kcd->vc.obedit;
3668 RegionView3D *rv3d = static_cast<RegionView3D *>(kcd->region->regiondata);
3669 const short scene_orientation = BKE_scene_orientation_get_index(scene, SCE_ORIENT_DEFAULT);
3670 /* Scene orientation takes priority. */
3671 const short orientation_type = scene_orientation ? scene_orientation :
3672 kcd->constrain_axis_mode - 1;
3673 const int pivot_point = scene->toolsettings->transform_pivot_point;
3674 float mat[3][3];
3676 scene, view_layer, kcd->vc.v3d, rv3d, obedit, obedit, orientation_type, pivot_point, mat);
3677
3678 /* Apply orientation matrix (can be simplified?). */
3679 float co[3][3];
3680 copy_v3_v3(co[0], kcd->prev.cage);
3681 copy_v3_v3(co[2], curr_cage_adjusted);
3682 invert_m3(mat);
3683 mul_m3_m3_pre(co, mat);
3684 for (int i = 0; i <= 2; i++) {
3685 if ((kcd->constrain_axis - 1) != i) {
3686 // kcd->curr_cage_adjusted[i] = prev_cage_adjusted[i];
3687 co[2][i] = co[0][i];
3688 }
3689 }
3690 invert_m3(mat);
3691 mul_m3_m3_pre(co, mat);
3692 copy_v3_v3(kcd->prev.cage, co[0]);
3693 copy_v3_v3(curr_cage_adjusted, co[2]);
3694
3695 /* Set mval to closest point on constrained line in screen space. */
3696 float curr_screenspace[2];
3697 float prev_screenspace[2];
3698 knife_project_v2(kcd, curr_cage_adjusted, curr_screenspace);
3699 knife_project_v2(kcd, kcd->prev.cage, prev_screenspace);
3700 float intersection[2];
3701 if (closest_to_line_v2(intersection, kcd->curr.mval, prev_screenspace, curr_screenspace)) {
3702 copy_v2_v2(kcd->curr.mval, intersection);
3703 }
3704 else {
3705 copy_v2_v2(kcd->curr.mval, curr_screenspace);
3706 }
3707 copy_v2_v2(kcd->mval, kcd->curr.mval);
3708}
3709
3710static void knife_snap_curr(KnifeTool_OpData *kcd, const float2 &mval)
3711{
3713
3714 float3 ray_origin;
3715 float3 ray_normal;
3717 kcd->vc.depsgraph, kcd->region, kcd->vc.v3d, mval, ray_origin, ray_normal, false);
3718
3719 if (knife_find_closest_face(kcd, ray_origin, ray_normal, mval, &kcd->curr)) {
3720 if (!kcd->ignore_edge_snapping || !kcd->ignore_vert_snapping) {
3721 KnifePosData kpos_tmp = kcd->curr;
3723 kcd, kpos_tmp.ob_index, kpos_tmp.bmface, ray_origin, ray_normal, mval, &kpos_tmp))
3724 {
3725 if (!kcd->ignore_edge_snapping) {
3726 kcd->curr = kpos_tmp;
3727 }
3728 if (!kcd->ignore_vert_snapping) {
3729 knife_find_closest_vert_of_edge(kcd, kpos_tmp.edge, kpos_tmp.mval, &kcd->curr);
3730 }
3731 }
3732 }
3733 }
3734
3735 if (kcd->curr.vert || kcd->curr.edge || kcd->curr.bmface) {
3736 return;
3737 }
3738
3739 /* If no hits are found this would normally default to (0, 0, 0) so instead
3740 * get a point at the mouse ray closest to the previous point.
3741 * Note that drawing lines in `free-space` isn't properly supported
3742 * but there's no guarantee (0, 0, 0) has any geometry either - campbell */
3743
3744 float origin[3];
3745 float origin_ofs[3];
3746
3747 copy_v2_v2(kcd->curr.mval, mval);
3748 knife_input_ray_segment(kcd, kcd->curr.mval, origin, origin_ofs);
3749
3751 kcd->curr.cage, origin, origin_ofs, kcd->prev.cage, kcd->vc.rv3d->viewinv[2]))
3752 {
3753 copy_v3_v3(kcd->curr.cage, kcd->prev.cage);
3754
3755 /* Should never fail! */
3756 BLI_assert(0);
3757 }
3758}
3759
3768static void knife_snap_update_from_mval(KnifeTool_OpData *kcd, const float mval[2])
3769{
3771 copy_v2_v2(kcd->curr.mval, mval);
3772
3773 /* view matrix may have changed, reproject */
3774 knife_project_v2(kcd, kcd->prev.cage, kcd->prev.mval);
3775
3776 kcd->is_angle_snapping = false;
3777 if (kcd->mode == MODE_DRAGGING) {
3778 if (kcd->angle_snapping) {
3781 }
3784 if (kcd->is_angle_snapping) {
3786 }
3787 }
3788 }
3789
3790 if (kcd->axis_constrained) {
3792 }
3793 }
3794
3795 /* Use `kcd->mval` as this is the value of the snap point in screen space that can change in
3796 * angle snapping or axis constrained modes. */
3797 knife_snap_curr(kcd, kcd->mval);
3798}
3799
3807{
3808 KnifeEdge *kfe, *newkfe;
3809 KnifeEdge *lastkfe = nullptr;
3810 KnifeVert *v1, *v2;
3811 KnifeUndoFrame *undo;
3812 BLI_mempool_iter iterkfe;
3813
3814 undo = static_cast<KnifeUndoFrame *>(BLI_stack_peek(kcd->undostack));
3815
3816 /* Undo edge splitting. */
3817 for (int i = 0; i < undo->splits; i++) {
3818 BLI_stack_pop(kcd->splitstack, &newkfe);
3819 BLI_stack_pop(kcd->splitstack, &kfe);
3820 knife_join_edge(newkfe, kfe);
3821 }
3822
3823 for (int i = 0; i < undo->cuts; i++) {
3824
3825 BLI_mempool_iternew(kcd->kedges, &iterkfe);
3826 for (kfe = static_cast<KnifeEdge *>(BLI_mempool_iterstep(&iterkfe)); kfe;
3827 kfe = static_cast<KnifeEdge *>(BLI_mempool_iterstep(&iterkfe)))
3828 {
3829 if (!kfe->is_cut || kfe->is_invalid || kfe->splits) {
3830 continue;
3831 }
3832 lastkfe = kfe;
3833 }
3834
3835 if (lastkfe) {
3836 lastkfe->is_invalid = true;
3837
3838 /* TODO: Are they always guaranteed to be in this order? */
3839 v1 = lastkfe->v1;
3840 v2 = lastkfe->v2;
3841
3842 /* Only remove first vertex if it is the start segment of the cut. */
3843 if (!v1->is_invalid && !v1->is_splitting) {
3844 v1->is_invalid = true;
3845 /* If the first vertex is touching any other cut edges don't remove it. */
3846 LISTBASE_FOREACH (LinkData *, ref, &v1->edges) {
3847 kfe = static_cast<KnifeEdge *>(ref->data);
3848 if (kfe->is_cut && !kfe->is_invalid) {
3849 v1->is_invalid = false;
3850 break;
3851 }
3852 }
3853 }
3854
3855 /* Only remove second vertex if it is the end segment of the cut. */
3856 if (!v2->is_invalid && !v2->is_splitting) {
3857 v2->is_invalid = true;
3858 /* If the second vertex is touching any other cut edges don't remove it. */
3859 LISTBASE_FOREACH (LinkData *, ref, &v2->edges) {
3860 kfe = static_cast<KnifeEdge *>(ref->data);
3861 if (kfe->is_cut && !kfe->is_invalid) {
3862 v2->is_invalid = false;
3863 break;
3864 }
3865 }
3866 }
3867 }
3868 }
3869
3870 if (kcd->mode == MODE_DRAGGING) {
3871 /* Restore kcd->prev. */
3872 kcd->prev = undo->pos;
3873 }
3874
3875 /* Restore data for distance and angle measurements. */
3876 kcd->mdata = undo->mdata;
3877
3879}
3880
3883/* -------------------------------------------------------------------- */
3888 Object *ob,
3889 int ob_index,
3890 bool use_tri_indices)
3891{
3892
3893 Scene *scene_eval = (Scene *)DEG_get_evaluated_id(kcd->vc.depsgraph, &kcd->scene->id);
3894 Object *obedit_eval = (Object *)DEG_get_evaluated_id(kcd->vc.depsgraph, &ob->id);
3895 BMEditMesh *em_eval = BKE_editmesh_from_object(obedit_eval);
3896
3898
3899 KnifeObjectInfo *obinfo = &kcd->objects_info[ob_index];
3900 obinfo->em = em_eval;
3902 kcd->vc.depsgraph, em_eval, scene_eval, obedit_eval);
3903
3904 if (use_tri_indices) {
3905 obinfo->tri_indices.reinitialize(em_eval->looptris.size());
3906 for (int i = 0; i < em_eval->looptris.size(); i++) {
3907 const std::array<BMLoop *, 3> &ltri = em_eval->looptris[i];
3908 obinfo->tri_indices[i][0] = BM_elem_index_get(ltri[0]->v);
3909 obinfo->tri_indices[i][1] = BM_elem_index_get(ltri[1]->v);
3910 obinfo->tri_indices[i][2] = BM_elem_index_get(ltri[2]->v);
3911 }
3912 }
3913}
3914
3917/* -------------------------------------------------------------------- */
3921static void knife_init_colors(KnifeColors *colors)
3922{
3923 /* Possible BMESH_TODO: add explicit themes or calculate these by
3924 * figuring out contrasting colors with grid / edges / verts
3925 * a la UI_make_axis_color. */
3931 colors->curpoint_a[3] = 102;
3934 colors->point_a[3] = 102;
3935
3940}
3941
3942/* called when modal loop selection gets set up... */
3944 KnifeTool_OpData *kcd,
3945 Vector<Object *> objects,
3946 const bool only_select,
3947 const bool cut_through,
3948 const bool xray,
3949 const int visible_measurements,
3950 const int angle_snapping,
3951 const float angle_snapping_increment,
3952 const bool is_interactive)
3953{
3954 /* Needed so multiple non-interactive cuts (also called knife-project)
3955 * doesn't access indices of loops that were created by cutting, see: #97153. */
3956 bool use_tri_indices = !is_interactive;
3957
3958 kcd->vc = *vc;
3959
3960 Scene *scene = vc->scene;
3961
3962 /* Assign the drawing handle for drawing preview line... */
3963 kcd->scene = scene;
3964 kcd->region = vc->region;
3965
3966 kcd->objects = std::move(objects);
3967
3968 Object *ob;
3969 BMEditMesh *em;
3971 for (int ob_index = 0; ob_index < kcd->objects.size(); ob_index++) {
3972 ob = kcd->objects[ob_index];
3973 em = BKE_editmesh_from_object(ob);
3974 knifetool_init_obinfo(kcd, ob, ob_index, use_tri_indices);
3975
3976 /* Can't usefully select resulting edges in face mode. */
3978 }
3979 knife_bvh_init(kcd);
3980
3981 /* Cut all the way through the mesh if use_occlude_geometry button not pushed. */
3982 kcd->is_interactive = is_interactive;
3983 kcd->cut_through = cut_through;
3984 kcd->only_select = only_select;
3985 kcd->depth_test = xray;
3986 kcd->dist_angle_mode = visible_measurements;
3988 kcd->angle_snapping_mode = angle_snapping;
3990 kcd->angle_snapping_increment = angle_snapping_increment;
3991
3992 kcd->arena = BLI_memarena_new(MEM_SIZE_OPTIMAL(1 << 15), "knife");
3993#ifdef USE_NET_ISLAND_CONNECT
3994 kcd->edgenet.arena = BLI_memarena_new(MEM_SIZE_OPTIMAL(1 << 15), __func__);
3995#endif
3996 kcd->edgenet.edge_visit = BLI_gset_ptr_new(__func__);
3997
3998 kcd->vthresh = KMAXDIST - 1;
3999 kcd->ethresh = KMAXDIST;
4000
4001 knife_recalc_ortho(kcd);
4002
4004
4005 kcd->refs = BLI_mempool_create(sizeof(LinkData), 0, 2048, 0);
4008
4009 kcd->undostack = BLI_stack_new(sizeof(KnifeUndoFrame), "knife undostack");
4010 kcd->splitstack = BLI_stack_new(sizeof(KnifeEdge *), "knife splitstack");
4011
4012 kcd->origedgemap = BLI_ghash_ptr_new("knife origedgemap");
4013 kcd->origvertmap = BLI_ghash_ptr_new("knife origvertmap");
4014 kcd->kedgefacemap = BLI_ghash_ptr_new("knife kedgefacemap");
4015 kcd->facetrimap = BLI_ghash_ptr_new("knife facetrimap");
4016
4019
4020 if (is_interactive) {
4023
4025 }
4026
4027 kcd->no_cuts = true;
4028
4029 kcd->axis_string[0] = ' ';
4030 kcd->axis_string[1] = '\0';
4031
4032 /* Initialize number input handling for angle snapping. */
4033 initNumInput(&kcd->num);
4034 kcd->num.idx_max = 0;
4035 kcd->num.val_flag[0] |= NUM_NO_NEGATIVE;
4036 kcd->num.unit_sys = scene->unit.system;
4037 kcd->num.unit_type[0] = B_UNIT_NONE;
4038}
4039
4040/* called when modal loop selection is done... */
4042{
4043 if (!kcd) {
4044 return;
4045 }
4046
4047 if (kcd->is_interactive) {
4049
4050 /* Deactivate the extra drawing stuff in 3D-View. */
4052 }
4053
4054 /* Free the custom data. */
4058
4061
4062 BLI_ghash_free(kcd->origedgemap, nullptr, nullptr);
4063 BLI_ghash_free(kcd->origvertmap, nullptr, nullptr);
4064 BLI_ghash_free(kcd->kedgefacemap, nullptr, nullptr);
4065 BLI_ghash_free(kcd->facetrimap, nullptr, nullptr);
4066
4068#ifdef USE_NET_ISLAND_CONNECT
4070#endif
4071 BLI_gset_free(kcd->edgenet.edge_visit, nullptr);
4072
4073 /* Tag for redraw. */
4075
4076 /* Knife BVH cleanup. */
4077 knife_bvh_free(kcd);
4078
4079 /* Destroy kcd itself. */
4080 MEM_delete(kcd);
4081}
4082
4084{
4085 KnifeTool_OpData *kcd = static_cast<KnifeTool_OpData *>(op->customdata);
4086 knifetool_exit_ex(kcd);
4087 op->customdata = nullptr;
4088}
4089
4092/* -------------------------------------------------------------------- */
4098{
4100
4101 if (kcd->mode == MODE_DRAGGING) {
4103 }
4104 return 1;
4105}
4106
4107static void knifetool_update_mval(KnifeTool_OpData *kcd, const float mval[2])
4108{
4109 knife_recalc_ortho(kcd);
4110 copy_v2_v2(kcd->mval, mval);
4111
4112 if (knife_update_active(kcd)) {
4114 }
4115}
4116
4117static void knifetool_update_mval_i(KnifeTool_OpData *kcd, const int mval_i[2])
4118{
4119 const float mval[2] = {float(mval_i[0]), float(mval_i[1])};
4120 knifetool_update_mval(kcd, mval);
4121}
4122
4125/* -------------------------------------------------------------------- */
4129static void knifetool_finish_single_pre(KnifeTool_OpData *kcd, int ob_index)
4130{
4131 knife_make_cuts(kcd, ob_index);
4132}
4133
4139{
4143 params.calc_looptris = true;
4144 params.calc_normals = true;
4145 params.is_destructive = true;
4146 EDBM_update(static_cast<Mesh *>(ob->data), &params);
4147}
4148
4149/* Called on tool confirmation. */
4151{
4152 /* Separate pre/post passes are needed because `em->looptris` recalculation from the 'post' pass
4153 * causes triangle indices in #KnifeTool_OpData.bvh to get out of sync.
4154 * So perform all the cuts before doing any mesh recalculation, see: #101721. */
4155 for (int ob_index : kcd->objects.index_range()) {
4156 knifetool_finish_single_pre(kcd, ob_index);
4157 }
4158 for (Object *ob : kcd->objects) {
4160 }
4161}
4162
4164{
4165 KnifeTool_OpData *kcd = static_cast<KnifeTool_OpData *>(op->customdata);
4167}
4168
4171/* -------------------------------------------------------------------- */
4175static void knifetool_cancel(bContext * /*C*/, wmOperator *op)
4176{
4177 /* this is just a wrapper around exit() */
4178 knifetool_exit(op);
4179}
4180
4182{
4183 static const EnumPropertyItem modal_items[] = {
4184 {KNF_MODAL_CANCEL, "CANCEL", 0, "Cancel", ""},
4185 {KNF_MODAL_CONFIRM, "CONFIRM", 0, "Confirm", ""},
4186 {KNF_MODAL_UNDO, "UNDO", 0, "Undo", ""},
4187 {KNF_MODAL_MIDPOINT_ON, "SNAP_MIDPOINTS_ON", 0, "Snap to Midpoints On", ""},
4188 {KNF_MODAL_MIDPOINT_OFF, "SNAP_MIDPOINTS_OFF", 0, "Snap to Midpoints Off", ""},
4189 {KNF_MODAL_IGNORE_SNAP_ON, "IGNORE_SNAP_ON", 0, "Ignore Snapping On", ""},
4190 {KNF_MODAL_IGNORE_SNAP_OFF, "IGNORE_SNAP_OFF", 0, "Ignore Snapping Off", ""},
4191 {KNF_MODAL_ANGLE_SNAP_TOGGLE, "ANGLE_SNAP_TOGGLE", 0, "Toggle Angle Snapping", ""},
4193 "CYCLE_ANGLE_SNAP_EDGE",
4194 0,
4195 "Cycle Angle Snapping Relative Edge",
4196 ""},
4197 {KNF_MODAL_CUT_THROUGH_TOGGLE, "CUT_THROUGH_TOGGLE", 0, "Toggle Cut Through", ""},
4199 "SHOW_DISTANCE_ANGLE_TOGGLE",
4200 0,
4201 "Toggle Distance and Angle Measurements",
4202 ""},
4203 {KNF_MODAL_DEPTH_TEST_TOGGLE, "DEPTH_TEST_TOGGLE", 0, "Toggle Depth Testing", ""},
4204 {KNF_MODAL_NEW_CUT, "NEW_CUT", 0, "End Current Cut", ""},
4205 {KNF_MODAL_ADD_CUT, "ADD_CUT", 0, "Add Cut", ""},
4206 {KNF_MODAL_ADD_CUT_CLOSED, "ADD_CUT_CLOSED", 0, "Add Cut Closed", ""},
4207 {KNF_MODAL_PANNING, "PANNING", 0, "Panning", ""},
4208 {KNF_MODAL_X_AXIS, "X_AXIS", 0, "X Axis Locking", ""},
4209 {KNF_MODAL_Y_AXIS, "Y_AXIS", 0, "Y Axis Locking", ""},
4210 {KNF_MODAL_Z_AXIS, "Z_AXIS", 0, "Z Axis Locking", ""},
4211 {0, nullptr, 0, nullptr, nullptr},
4212 };
4213
4214 wmKeyMap *keymap = WM_modalkeymap_find(keyconf, "Knife Tool Modal Map");
4215
4216 /* This function is called for each space-type, only needs to add map once. */
4217 if (keymap && keymap->modal_items) {
4218 return nullptr;
4219 }
4220
4221 keymap = WM_modalkeymap_ensure(keyconf, "Knife Tool Modal Map", modal_items);
4222
4223 WM_modalkeymap_assign(keymap, "MESH_OT_knife_tool");
4224
4225 return keymap;
4226}
4227
4228/* Turn off angle snapping. */
4235
4236/* Turn off orientation locking. */
4243
4244static int knifetool_modal(bContext *C, wmOperator *op, const wmEvent *event)
4245{
4246 KnifeTool_OpData *kcd = static_cast<KnifeTool_OpData *>(op->customdata);
4247 bool do_refresh = false;
4248
4249 Object *ob = (kcd->curr.ob_index != -1) ? kcd->objects[kcd->curr.ob_index] : kcd->vc.obedit;
4250 if (!ob || ob->type != OB_MESH) {
4251 knifetool_exit(op);
4252 ED_workspace_status_text(C, nullptr);
4253 return OPERATOR_FINISHED;
4254 }
4255
4256 kcd->region = kcd->vc.region;
4257
4258 ED_view3d_init_mats_rv3d(ob, kcd->vc.rv3d); /* Needed to initialize clipping. */
4259
4260 if (kcd->mode == MODE_PANNING) {
4261 kcd->mode = KnifeMode(kcd->prevmode);
4262 }
4263
4264 bool handled = false;
4265 float snapping_increment_temp;
4266
4267 if (kcd->angle_snapping) {
4268 if (kcd->num.str_cur >= 3 ||
4270 {
4272 }
4273 knife_update_header(C, op, kcd); /* Update the angle multiple. */
4274 /* Modal numinput active, try to handle numeric inputs first... */
4275 if (event->val == KM_PRESS && hasNumInput(&kcd->num) && handleNumInput(C, &kcd->num, event)) {
4276 handled = true;
4277 applyNumInput(&kcd->num, &snapping_increment_temp);
4278 /* Restrict number key input to 0 - 180 degree range. */
4279 if (snapping_increment_temp > KNIFE_MIN_ANGLE_SNAPPING_INCREMENT &&
4280 snapping_increment_temp <= KNIFE_MAX_ANGLE_SNAPPING_INCREMENT)
4281 {
4282 kcd->angle_snapping_increment = snapping_increment_temp;
4283 }
4285 knife_update_header(C, op, kcd);
4288 }
4289 }
4290
4291 /* Handle modal keymap. */
4292 if (event->type == EVT_MODAL_MAP) {
4293 switch (event->val) {
4294 case KNF_MODAL_CANCEL:
4295 /* finish */
4297
4298 knifetool_exit(op);
4299 ED_workspace_status_text(C, nullptr);
4300
4301 return OPERATOR_CANCELLED;
4302 case KNF_MODAL_CONFIRM: {
4303 const bool changed = (kcd->totkvert != 0);
4304 /* finish */
4306
4307 knifetool_finish(op);
4308 knifetool_exit(op);
4309 ED_workspace_status_text(C, nullptr);
4310
4311 /* Cancel to prevent undo push for empty cuts. */
4312 if (!changed) {
4313 return OPERATOR_CANCELLED;
4314 }
4315 return OPERATOR_FINISHED;
4316 }
4317 case KNF_MODAL_UNDO:
4318 if (BLI_stack_is_empty(kcd->undostack)) {
4320 knifetool_exit(op);
4321 ED_workspace_status_text(C, nullptr);
4322 return OPERATOR_CANCELLED;
4323 }
4324 knifetool_undo(kcd);
4327 handled = true;
4328 break;
4330 kcd->snap_midpoints = true;
4331
4332 knife_recalc_ortho(kcd);
4334 knife_update_header(C, op, kcd);
4336 do_refresh = true;
4337 handled = true;
4338 break;
4340 kcd->snap_midpoints = false;
4341
4342 knife_recalc_ortho(kcd);
4344 knife_update_header(C, op, kcd);
4346 do_refresh = true;
4347 handled = true;
4348 break;
4351 kcd->ignore_vert_snapping = kcd->ignore_edge_snapping = true;
4352 knife_update_header(C, op, kcd);
4353 do_refresh = true;
4354 handled = true;
4355 break;
4358 kcd->ignore_vert_snapping = kcd->ignore_edge_snapping = false;
4359 knife_update_header(C, op, kcd);
4360 do_refresh = true;
4361 handled = true;
4362 break;
4365 kcd->angle_snapping_mode++;
4366 kcd->snap_ref_edges_count = 0;
4367 kcd->snap_edge = 0;
4368 }
4369 else {
4371 }
4374 RNA_float_get(op->ptr, "angle_snapping_increment"));
4378 knife_update_header(C, op, kcd);
4380 do_refresh = true;
4381 handled = true;
4382 break;
4385 if (kcd->snap_ref_edges_count) {
4386 kcd->snap_edge++;
4387 kcd->snap_edge %= kcd->snap_ref_edges_count;
4388 }
4389 }
4390 do_refresh = true;
4391 handled = true;
4392 break;
4394 kcd->cut_through = !kcd->cut_through;
4395 knife_update_header(C, op, kcd);
4396 do_refresh = true;
4397 handled = true;
4398 break;
4401 kcd->dist_angle_mode++;
4402 }
4403 else {
4405 }
4407 knife_update_header(C, op, kcd);
4408 do_refresh = true;
4409 handled = true;
4410 break;
4412 kcd->depth_test = !kcd->depth_test;
4414 knife_update_header(C, op, kcd);
4415 do_refresh = true;
4416 handled = true;
4417 break;
4418 case KNF_MODAL_NEW_CUT:
4419 /* If no cuts have been made, exit.
4420 * Preserves right click cancel workflow which most tools use,
4421 * but stops accidentally deleting entire cuts with right click.
4422 */
4423 if (kcd->no_cuts) {
4425 knifetool_exit(op);
4426 ED_workspace_status_text(C, nullptr);
4427 return OPERATOR_CANCELLED;
4428 }
4430 knife_finish_cut(kcd);
4431 kcd->mode = MODE_IDLE;
4432 handled = true;
4433 break;
4434 case KNF_MODAL_ADD_CUT:
4435 kcd->no_cuts = false;
4436 knife_recalc_ortho(kcd);
4437
4438 /* Get the value of the event which triggered this one. */
4439 if (event->prev_val != KM_RELEASE) {
4440 if (kcd->mode == MODE_DRAGGING) {
4441 knife_add_cut(kcd);
4442 }
4443 else if (kcd->mode != MODE_PANNING) {
4444 knife_start_cut(kcd, float2(event->mval));
4445 kcd->mode = MODE_DRAGGING;
4446 kcd->init = kcd->curr;
4447 }
4448
4449 /* Freehand drawing is incompatible with cut-through. */
4450 if (kcd->cut_through == false) {
4451 kcd->is_drag_hold = true;
4452 /* No edge snapping while dragging (edges are too sticky when cuts are immediate). */
4453 kcd->ignore_edge_snapping = true;
4454 }
4455 }
4456 else {
4457 kcd->is_drag_hold = false;
4458 kcd->ignore_edge_snapping = false;
4459 kcd->is_drag_undo = false;
4460
4461 /* Needed because the last face 'hit' is ignored when dragging. */
4462 knifetool_update_mval(kcd, kcd->curr.mval);
4463 }
4464
4466 handled = true;
4467 break;
4469 if (kcd->mode == MODE_DRAGGING) {
4470
4471 /* Shouldn't be possible with default key-layout, just in case. */
4472 if (kcd->is_drag_hold) {
4473 kcd->is_drag_hold = false;
4474 kcd->is_drag_undo = false;
4475 knifetool_update_mval(kcd, kcd->curr.mval);
4476 }
4477
4478 kcd->prev = kcd->curr;
4479 kcd->curr = kcd->init;
4480
4481 knife_project_v2(kcd, kcd->curr.cage, kcd->curr.mval);
4482 knifetool_update_mval(kcd, kcd->curr.mval);
4483
4484 knife_add_cut(kcd);
4485
4486 /* KNF_MODAL_NEW_CUT */
4487 knife_finish_cut(kcd);
4488 kcd->mode = MODE_IDLE;
4489 }
4490 handled = true;
4491 break;
4492 case KNF_MODAL_PANNING:
4493 if (event->val != KM_RELEASE) {
4494 if (kcd->mode != MODE_PANNING) {
4495 kcd->prevmode = kcd->mode;
4496 kcd->mode = MODE_PANNING;
4497 }
4498 }
4499 else {
4500 kcd->mode = KnifeMode(kcd->prevmode);
4501 }
4502
4504 return OPERATOR_PASS_THROUGH;
4505 }
4506 }
4507 else { /* non-modal-mapped events */
4508 switch (event->type) {
4509 case MOUSEPAN:
4510 case MOUSEZOOM:
4511 case MOUSEROTATE:
4512 case WHEELUPMOUSE:
4513 case WHEELDOWNMOUSE:
4514 case NDOF_MOTION:
4515 return OPERATOR_PASS_THROUGH;
4516 case MOUSEMOVE: /* Mouse moved somewhere to select another loop. */
4517 if (kcd->mode != MODE_PANNING) {
4518 knifetool_update_mval_i(kcd, event->mval);
4519 knife_update_header(C, op, kcd);
4520
4521 if (kcd->is_drag_hold) {
4522 if (kcd->linehits.size() >= 2) {
4523 knife_add_cut(kcd);
4524 }
4525 }
4526 }
4527
4528 break;
4529 }
4530 }
4531
4532 if (kcd->angle_snapping) {
4533 if (kcd->num.str_cur >= 3 ||
4535 {
4537 }
4538 if (event->type != EVT_MODAL_MAP) {
4539 /* Modal number-input inactive, try to handle numeric inputs last. */
4540 if (!handled && event->val == KM_PRESS && handleNumInput(C, &kcd->num, event)) {
4541 applyNumInput(&kcd->num, &snapping_increment_temp);
4542 /* Restrict number key input to 0 - 180 degree range. */
4543 if (snapping_increment_temp > KNIFE_MIN_ANGLE_SNAPPING_INCREMENT &&
4544 snapping_increment_temp <= KNIFE_MAX_ANGLE_SNAPPING_INCREMENT)
4545 {
4546 kcd->angle_snapping_increment = snapping_increment_temp;
4547 }
4549 knife_update_header(C, op, kcd);
4552 }
4553 }
4554 }
4555
4556 /* Constrain axes with X,Y,Z keys. */
4557 if (event->type == EVT_MODAL_MAP) {
4559 if (event->val == KNF_MODAL_X_AXIS && kcd->constrain_axis != KNF_CONSTRAIN_AXIS_X) {
4562 kcd->axis_string[0] = 'X';
4563 }
4564 else if (event->val == KNF_MODAL_Y_AXIS && kcd->constrain_axis != KNF_CONSTRAIN_AXIS_Y) {
4567 kcd->axis_string[0] = 'Y';
4568 }
4569 else if (event->val == KNF_MODAL_Z_AXIS && kcd->constrain_axis != KNF_CONSTRAIN_AXIS_Z) {
4572 kcd->axis_string[0] = 'Z';
4573 }
4574 else {
4575 /* Cycle through modes with repeated key presses. */
4577 kcd->constrain_axis_mode++;
4578 kcd->axis_string[0] += 32; /* Lower case. */
4579 }
4580 else {
4583 }
4584 }
4587 knife_update_header(C, op, kcd);
4589 do_refresh = true;
4590 }
4591 }
4592
4593 if (kcd->mode == MODE_DRAGGING) {
4594 op->flag &= ~OP_IS_MODAL_CURSOR_REGION;
4595 }
4596 else {
4598 }
4599
4600 if (do_refresh) {
4601 /* We don't really need to update mval,
4602 * but this happens to be the best way to refresh at the moment. */
4603 knifetool_update_mval_i(kcd, event->mval);
4604 knife_update_header(C, op, kcd);
4605 }
4606
4607 /* Keep going until the user confirms. */
4609}
4610
4611static int knifetool_invoke(bContext *C, wmOperator *op, const wmEvent *event)
4612{
4613 const bool only_select = RNA_boolean_get(op->ptr, "only_selected");
4614 const bool cut_through = !RNA_boolean_get(op->ptr, "use_occlude_geometry");
4615 const bool xray = !RNA_boolean_get(op->ptr, "xray");
4616 const int visible_measurements = RNA_enum_get(op->ptr, "visible_measurements");
4617 const int angle_snapping = RNA_enum_get(op->ptr, "angle_snapping");
4618 const bool wait_for_input = RNA_boolean_get(op->ptr, "wait_for_input");
4619 const float angle_snapping_increment = RAD2DEGF(
4620 RNA_float_get(op->ptr, "angle_snapping_increment"));
4621
4623
4624 /* alloc new customdata */
4625 KnifeTool_OpData *kcd = MEM_new<KnifeTool_OpData>(__func__);
4626 op->customdata = kcd;
4628 &vc,
4629 kcd,
4631 only_select,
4632 cut_through,
4633 xray,
4634 visible_measurements,
4635 angle_snapping,
4636 angle_snapping_increment,
4637 true);
4638
4639 if (only_select) {
4640 bool faces_selected = false;
4641 for (Object *obedit : kcd->objects) {
4643 if (em->bm->totfacesel != 0) {
4644 faces_selected = true;
4645 }
4646 }
4647
4648 if (!faces_selected) {
4649 BKE_report(op->reports, RPT_ERROR, "Selected faces required");
4650 knifetool_cancel(C, op);
4651 return OPERATOR_CANCELLED;
4652 }
4653 }
4654
4656
4657 /* Add a modal handler for this operator - handles loop selection. */
4660
4661 if (wait_for_input == false) {
4662 /* Avoid copy-paste logic. */
4663 wmEvent event_modal{};
4664 event_modal.prev_val = KM_NOTHING;
4665 event_modal.type = EVT_MODAL_MAP;
4666 event_modal.val = KNF_MODAL_ADD_CUT;
4667
4668 copy_v2_v2_int(event_modal.mval, event->mval);
4669
4670 int ret = knifetool_modal(C, op, &event_modal);
4673 }
4674
4675 knife_update_header(C, op, kcd);
4676
4678}
4679
4681{
4682 /* Description. */
4683 ot->name = "Knife Topology Tool";
4684 ot->idname = "MESH_OT_knife_tool";
4685 ot->description = "Cut new topology";
4686
4687 /* Callbacks. */
4692
4693 /* Flags. */
4695
4696 /* Properties. */
4697 PropertyRNA *prop;
4698 static const EnumPropertyItem visible_measurements_items[] = {
4699 {KNF_MEASUREMENT_NONE, "NONE", 0, "None", "Show no measurements"},
4700 {KNF_MEASUREMENT_BOTH, "BOTH", 0, "Both", "Show both distances and angles"},
4701 {KNF_MEASUREMENT_DISTANCE, "DISTANCE", 0, "Distance", "Show just distance measurements"},
4702 {KNF_MEASUREMENT_ANGLE, "ANGLE", 0, "Angle", "Show just angle measurements"},
4703 {0, nullptr, 0, nullptr, nullptr},
4704 };
4705
4706 static const EnumPropertyItem angle_snapping_items[] = {
4707 {KNF_CONSTRAIN_ANGLE_MODE_NONE, "NONE", 0, "None", "No angle snapping"},
4708 {KNF_CONSTRAIN_ANGLE_MODE_SCREEN, "SCREEN", 0, "Screen", "Screen space angle snapping"},
4710 "RELATIVE",
4711 0,
4712 "Relative",
4713 "Angle snapping relative to the previous cut edge"},
4714 {0, nullptr, 0, nullptr, nullptr},
4715 };
4716
4718 "use_occlude_geometry",
4719 true,
4720 "Occlude Geometry",
4721 "Only cut the front most geometry");
4722 RNA_def_boolean(ot->srna, "only_selected", false, "Only Selected", "Only cut selected geometry");
4723 RNA_def_boolean(ot->srna, "xray", true, "X-Ray", "Show cuts hidden by geometry");
4724
4726 "visible_measurements",
4727 visible_measurements_items,
4729 "Measurements",
4730 "Visible distance and angle measurements");
4731 prop = RNA_def_enum(ot->srna,
4732 "angle_snapping",
4733 angle_snapping_items,
4735 "Angle Snapping",
4736 "Angle snapping mode");
4738
4739 prop = RNA_def_float(ot->srna,
4740 "angle_snapping_increment",
4744 "Angle Snap Increment",
4745 "The angle snap increment used when in constrained angle mode",
4749
4750 prop = RNA_def_boolean(ot->srna, "wait_for_input", true, "Wait for Input", "");
4752}
4753
4756/* -------------------------------------------------------------------- */
4762static bool edbm_mesh_knife_point_isect(LinkNode *polys, const float cent_ss[2])
4763{
4764 LinkNode *p = polys;
4765 int isect = 0;
4766
4767 while (p) {
4768 const float(*mval_fl)[2] = static_cast<const float(*)[2]>(p->link);
4769 const int mval_tot = MEM_allocN_len(mval_fl) / sizeof(*mval_fl);
4770 isect += int(isect_point_poly_v2(cent_ss, mval_fl, mval_tot - 1));
4771 p = p->next;
4772 }
4773
4774 if (isect % 2) {
4775 return true;
4776 }
4777 return false;
4778}
4779
4781 ViewContext *vc, const Span<Object *> objects, LinkNode *polys, bool use_tag, bool cut_through)
4782{
4783 KnifeTool_OpData *kcd;
4784
4785 /* Init. */
4786 {
4787 const bool only_select = false;
4788 const bool is_interactive = false; /* Can enable for testing. */
4789 const bool xray = false;
4790 const int visible_measurements = KNF_MEASUREMENT_NONE;
4791 const int angle_snapping = KNF_CONSTRAIN_ANGLE_MODE_NONE;
4792 const float angle_snapping_increment = KNIFE_DEFAULT_ANGLE_SNAPPING_INCREMENT;
4793
4794 kcd = MEM_new<KnifeTool_OpData>(__func__);
4795
4796 knifetool_init(vc,
4797 kcd,
4798 {objects},
4799 only_select,
4800 cut_through,
4801 xray,
4802 visible_measurements,
4803 angle_snapping,
4804 angle_snapping_increment,
4805 is_interactive);
4806
4807 kcd->ignore_edge_snapping = true;
4808 kcd->ignore_vert_snapping = true;
4809 }
4810
4811 /* Execute. */
4812 {
4813 LinkNode *p = polys;
4814
4815 knife_recalc_ortho(kcd);
4816
4817 while (p) {
4818 const float(*mval_fl)[2] = static_cast<const float(*)[2]>(p->link);
4819 const int mval_tot = MEM_allocN_len(mval_fl) / sizeof(*mval_fl);
4820 int i;
4821
4822 knife_start_cut(kcd, mval_fl[0]);
4823 kcd->mode = MODE_DRAGGING;
4824
4825 for (i = 1; i < mval_tot; i++) {
4826 knifetool_update_mval(kcd, mval_fl[i]);
4827 knife_add_cut(kcd);
4828 }
4829
4830 knife_finish_cut(kcd);
4831 kcd->mode = MODE_IDLE;
4832 p = p->next;
4833 }
4834 }
4835
4836 /* Finish. */
4837 {
4838 /* See #knifetool_finish_ex for why multiple passes are needed. */
4839 for (int ob_index : kcd->objects.index_range()) {
4840 Object *ob = kcd->objects[ob_index];
4842
4843 if (use_tag) {
4845 }
4846
4847 knifetool_finish_single_pre(kcd, ob_index);
4848 }
4849
4850 for (Object *ob : kcd->objects) {
4852
4853 /* Tag faces inside! */
4854 if (use_tag) {
4855 BMesh *bm = em->bm;
4856 BMEdge *e;
4857 BMIter iter;
4858 bool keep_search;
4859
4860/* Use face-loop tag to store if we have intersected. */
4861#define F_ISECT_IS_UNKNOWN(f) BM_elem_flag_test(BM_FACE_FIRST_LOOP(f), BM_ELEM_TAG)
4862#define F_ISECT_SET_UNKNOWN(f) BM_elem_flag_enable(BM_FACE_FIRST_LOOP(f), BM_ELEM_TAG)
4863#define F_ISECT_SET_OUTSIDE(f) BM_elem_flag_disable(BM_FACE_FIRST_LOOP(f), BM_ELEM_TAG)
4864 {
4865 BMFace *f;
4866 BM_ITER_MESH (f, &iter, bm, BM_FACES_OF_MESH) {
4869 }
4870 }
4871
4872 /* Tag all faces linked to cut edges. */
4873 BM_ITER_MESH (e, &iter, bm, BM_EDGES_OF_MESH) {
4874 /* Check are we tagged?, then we are an original face. */
4876 continue;
4877 }
4878
4879 BMFace *f;
4880 BMIter fiter;
4881 BM_ITER_ELEM (f, &fiter, e, BM_FACES_OF_EDGE) {
4882 float cent[3], cent_ss[2];
4884 mul_m4_v3(ob->object_to_world().ptr(), cent);
4885 knife_project_v2(kcd, cent, cent_ss);
4886 if (edbm_mesh_knife_point_isect(polys, cent_ss)) {
4888 }
4889 }
4890 }
4891
4892 /* Expand tags for faces which are not cut, but are inside the polys. */
4893 do {
4894 BMFace *f;
4895 keep_search = false;
4896 BM_ITER_MESH (f, &iter, bm, BM_FACES_OF_MESH) {
4898 continue;
4899 }
4900
4901 /* Am I connected to a tagged face via an un-tagged edge
4902 * (ie, not across a cut)? */
4903 BMLoop *l_first = BM_FACE_FIRST_LOOP(f);
4904 BMLoop *l_iter = l_first;
4905 bool found = false;
4906
4907 do {
4908 if (BM_elem_flag_test(l_iter->e, BM_ELEM_TAG) != false) {
4909 /* Now check if the adjacent faces is tagged. */
4910 BMLoop *l_radial_iter = l_iter->radial_next;
4911 if (l_radial_iter != l_iter) {
4912 do {
4913 if (BM_elem_flag_test(l_radial_iter->f, BM_ELEM_TAG)) {
4914 found = true;
4915 }
4916 } while ((l_radial_iter = l_radial_iter->radial_next) != l_iter &&
4917 (found == false));
4918 }
4919 }
4920 } while ((l_iter = l_iter->next) != l_first && (found == false));
4921
4922 if (found) {
4923 float cent[3], cent_ss[2];
4925 mul_m4_v3(ob->object_to_world().ptr(), cent);
4926 knife_project_v2(kcd, cent, cent_ss);
4927 if ((kcd->cut_through || point_is_visible(kcd, cent, cent_ss, (BMElem *)f)) &&
4928 edbm_mesh_knife_point_isect(polys, cent_ss))
4929 {
4931 keep_search = true;
4932 }
4933 else {
4934 /* Don't lose time on this face again, set it as outside. */
4936 }
4937 }
4938 }
4939 } while (keep_search);
4940
4941#undef F_ISECT_IS_UNKNOWN
4942#undef F_ISECT_SET_UNKNOWN
4943#undef F_ISECT_SET_OUTSIDE
4944 }
4945 }
4946
4947 for (Object *ob : kcd->objects) {
4948 /* Defer freeing data until the BVH tree is finished with, see: #point_is_visible and
4949 * the doc-string for #knifetool_finish_single_post. */
4951 }
4952
4953 knifetool_exit_ex(kcd);
4954 kcd = nullptr;
4955 }
4956}
4957
wmWindow * CTX_wm_window(const bContext *C)
BMEditMesh * BKE_editmesh_from_object(Object *ob)
Return the BMEditMesh for a given object.
Definition editmesh.cc:63
blender::Array< blender::float3 > BKE_editmesh_vert_coords_alloc(Depsgraph *depsgraph, BMEditMesh *em, Scene *scene, Object *ob)
Definition editmesh.cc:148
bool BKE_object_is_visible_in_viewport(const View3D *v3d, const Object *ob)
blender::Vector< Object * > BKE_view_layer_array_from_objects_in_edit_mode_unique_data(const Scene *scene, ViewLayer *view_layer, const View3D *v3d)
void BKE_report(ReportList *reports, eReportType type, const char *message)
Definition report.cc:125
int BKE_scene_orientation_get_index(Scene *scene, int slot_index)
Definition scene.cc:2386
@ B_UNIT_LENGTH
Definition BKE_unit.hh:107
@ B_UNIT_ROTATION
Definition BKE_unit.hh:111
@ B_UNIT_NONE
Definition BKE_unit.hh:106
size_t BKE_unit_value_as_string(char *str, int str_maxncpy, double value, int prec, int type, const UnitSettings *settings, bool pad)
Definition unit.cc:1876
void BLF_size(int fontid, float size)
Definition blf.cc:426
void BLF_color3ubv(int fontid, const unsigned char rgb[3])
Definition blf.cc:459
void BLF_width_and_height(int fontid, const char *str, size_t str_len, float *r_width, float *r_height) ATTR_NONNULL()
Definition blf.cc:778
void BLF_disable(int fontid, int option)
Definition blf.cc:321
void BLF_rotation(int fontid, float angle)
Definition blf.cc:872
void BLF_draw(int fontid, const char *str, size_t str_len, ResultBLF *r_info=nullptr) ATTR_NONNULL(2)
Definition blf.cc:568
int blf_mono_font
Definition blf.cc:51
void BLF_enable(int fontid, int option)
Definition blf.cc:312
@ BLF_ROTATION
Definition BLF_api.hh:361
void BLF_position(int fontid, float x, float y, float z)
Definition blf.cc:371
#define BLI_array_alloca(arr, realsize)
Definition BLI_alloca.h:25
#define BLI_assert(a)
Definition BLI_assert.h:50
struct GSet GSet
Definition BLI_ghash.h:341
BLI_INLINE void * BLI_ghashIterator_getKey(GHashIterator *ghi) ATTR_WARN_UNUSED_RESULT
Definition BLI_ghash.h:299
GSet * BLI_gset_ptr_new(const char *info)
BLI_INLINE void * BLI_ghashIterator_getValue(GHashIterator *ghi) ATTR_WARN_UNUSED_RESULT
Definition BLI_ghash.h:303
#define GHASH_ITER(gh_iter_, ghash_)
Definition BLI_ghash.h:322
GHash * BLI_ghash_ptr_new(const char *info) ATTR_MALLOC ATTR_WARN_UNUSED_RESULT
void BLI_gset_clear(GSet *gs, GSetKeyFreeFP keyfreefp)
Definition BLI_ghash.c:1029
unsigned int BLI_gset_len(const GSet *gs) ATTR_WARN_UNUSED_RESULT
Definition BLI_ghash.c:954
void * BLI_ghash_lookup(const GHash *gh, const void *key) ATTR_WARN_UNUSED_RESULT
Definition BLI_ghash.c:731
void BLI_ghash_insert(GHash *gh, void *key, void *val)
Definition BLI_ghash.c:707
void BLI_ghash_free(GHash *gh, GHashKeyFreeFP keyfreefp, GHashValFreeFP valfreefp)
Definition BLI_ghash.c:860
void BLI_gset_free(GSet *gs, GSetKeyFreeFP keyfreefp)
Definition BLI_ghash.c:1034
bool BLI_gset_add(GSet *gs, void *key)
Definition BLI_ghash.c:966
BVHTree * BLI_bvhtree_new(int maxsize, float epsilon, char tree_type, char axis)
int * BLI_bvhtree_intersect_plane(const BVHTree *tree, float plane[4], uint *r_intersect_num)
void BLI_bvhtree_balance(BVHTree *tree)
void BLI_bvhtree_free(BVHTree *tree)
void BLI_bvhtree_insert(BVHTree *tree, int index, const float co[3], int numpoints)
int BLI_bvhtree_ray_cast(const BVHTree *tree, const float co[3], const float dir[3], float radius, BVHTreeRayHit *hit, BVHTree_RayCastCallback callback, void *userdata)
#define LISTBASE_FOREACH(type, var, list)
BLI_INLINE void BLI_listbase_clear(struct ListBase *lb)
int BLI_listbase_count_at_most(const struct ListBase *listbase, int count_max) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL(1)
void void BLI_INLINE bool BLI_listbase_is_single(const struct ListBase *lb)
void BLI_addtail(struct ListBase *listbase, void *vlink) ATTR_NONNULL(1)
Definition listbase.cc:110
void BLI_remlink(struct ListBase *listbase, void *vlink) ATTR_NONNULL(1)
Definition listbase.cc:130
void void void BLI_listbase_sort_r(ListBase *listbase, int(*cmp)(void *, const void *, const void *), void *thunk) ATTR_NONNULL(1
int BLI_listbase_count(const struct ListBase *listbase) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL(1)
#define BLI_ASSERT_UNIT_V2(v)
MINLINE float min_ff(float a, float b)
MINLINE float min_fff(float a, float b, float c)
void rgba_uchar_to_float(float r_col[4], const unsigned char col_ub[4])
int isect_seg_seg_v2_point_ex(const float v0[2], const float v1[2], const float v2[2], const float v3[2], float endpoint_bias, float r_vi[2])
void plane_from_point_normal_v3(float r_plane[4], const float plane_co[3], const float plane_no[3])
Definition math_geom.cc:215
bool clip_segment_v3_plane_n(const float p1[3], const float p2[3], const float plane_array[][4], int plane_num, float r_p1[3], float r_p2[3])
bool isect_ray_plane_v3(const float ray_origin[3], const float ray_direction[3], const float plane[4], float *r_lambda, bool clip)
int isect_line_line_v3(const float v1[3], const float v2[3], const float v3[3], const float v4[3], float r_i1[3], float r_i2[3])
float dist_squared_to_plane_v3(const float p[3], const float plane[4])
Definition math_geom.cc:468
bool isect_ray_line_v3(const float ray_origin[3], const float ray_direction[3], const float v0[3], const float v1[3], float *r_lambda)
void transform_point_by_seg_v3(float p_dst[3], const float p_src[3], const float l_dst_p1[3], const float l_dst_p2[3], const float l_src_p1[3], const float l_src_p2[3])
bool isect_ray_tri_v3(const float ray_origin[3], const float ray_direction[3], const float v0[3], const float v1[3], const float v2[3], float *r_lambda, float r_uv[2])
bool isect_point_poly_v2(const float pt[2], const float verts[][2], unsigned int nr)
bool isect_ray_plane_v3_factor(const float ray_origin[3], const float ray_direction[3], const float plane_co[3], const float plane_no[3], float *r_lambda)
float closest_ray_to_segment_v3(const float ray_origin[3], const float ray_direction[3], const float v0[3], const float v1[3], float r_close[3])
Definition math_geom.cc:407
bool isect_ray_tri_watertight_v3(const float ray_origin[3], const struct IsectRayPrecalc *isect_precalc, const float v0[3], const float v1[3], const float v2[3], float *r_lambda, float r_uv[2])
float closest_to_line_segment_v2(float r_close[2], const float p[2], const float l1[2], const float l2[2])
Definition math_geom.cc:363
float line_point_factor_v3(const float p[3], const float l1[3], const float l2[3])
float closest_to_line_v2(float r_close[2], const float p[2], const float l1[2], const float l2[2])
bool isect_line_plane_v3(float r_isect_co[3], const float l1[3], const float l2[3], const float plane_co[3], const float plane_no[3]) ATTR_WARN_UNUSED_RESULT
float normal_tri_v3(float n[3], const float v1[3], const float v2[3], const float v3[3])
Definition math_geom.cc:39
bool isect_ray_tri_epsilon_v3(const float ray_origin[3], const float ray_direction[3], const float v0[3], const float v1[3], const float v2[3], float *r_lambda, float r_uv[2], float epsilon)
void planes_from_projmat(const float mat[4][4], float left[4], float right[4], float bottom[4], float top[4], float near[4], float far[4])
float dist_squared_to_line_segment_v2(const float p[2], const float l1[2], const float l2[2])
Definition math_geom.cc:289
void mul_m3_m3_pre(float R[3][3], const float A[3][3])
void mul_m4_v3(const float M[4][4], float r[3])
void mul_v2_m2v2(float r[2], const float mat[2][2], const float vec[2])
void mul_transposed_mat3_m4_v3(const float M[4][4], float r[3])
void mul_v3_m4v3(float r[3], const float mat[4][4], const float vec[3])
bool invert_m3(float mat[3][3])
void axis_angle_to_quat(float r[4], const float axis[3], float angle)
#define DEG2RADF(_deg)
void mul_qt_v3(const float q[4], float r[3])
void angle_to_mat2(float R[2][2], float angle)
#define RAD2DEGF(_rad)
void rotate_v3_v3v3fl(float r[3], const float p[3], const float axis[3], float angle)
MINLINE float len_v3v3(const float a[3], const float b[3]) ATTR_WARN_UNUSED_RESULT
void minmax_v3v3_v3(float min[3], float max[3], const float vec[3])
MINLINE float len_squared_v2v2(const float a[2], const float b[2]) ATTR_WARN_UNUSED_RESULT
MINLINE void sub_v3_v3(float r[3], const float a[3])
MINLINE bool equals_v3v3(const float v1[3], const float v2[3]) ATTR_WARN_UNUSED_RESULT
MINLINE float len_squared_v3v3(const float a[3], const float b[3]) ATTR_WARN_UNUSED_RESULT
MINLINE void sub_v3_v3v3(float r[3], const float a[3], const float b[3])
MINLINE float len_v2v2(const float v1[2], const float v2[2]) ATTR_WARN_UNUSED_RESULT
MINLINE void copy_v2_v2(float r[2], const float a[2])
MINLINE void copy_v2_v2_int(int r[2], const int a[2])
MINLINE bool is_zero_v2(const float v[2]) ATTR_WARN_UNUSED_RESULT
MINLINE void copy_v3_v3(float r[3], const float a[3])
void project_plane_normalized_v3_v3v3(float out[3], const float p[3], const float v_plane[3])
void interp_v3_v3v3(float r[3], const float a[3], const float b[3], float t)
Definition math_vector.c:36
MINLINE void cross_v3_v3v3(float r[3], const float a[3], const float b[3])
float angle_signed_on_axis_v3v3_v3(const float v1[3], const float v2[3], const float axis[3]) ATTR_WARN_UNUSED_RESULT
MINLINE float dot_m4_v3_row_z(const float M[4][4], const float a[3]) ATTR_WARN_UNUSED_RESULT
float angle_signed_v2v2(const float v1[2], const float v2[2]) ATTR_WARN_UNUSED_RESULT
void mid_v2_v2v2(float r[2], const float a[2], const float b[2])
MINLINE float normalize_v3_v3(float r[3], const float a[3])
MINLINE bool compare_v3v3(const float v1[3], const float v2[3], float limit) ATTR_WARN_UNUSED_RESULT
MINLINE void add_v2_v2v2(float r[2], const float a[2], const float b[2])
MINLINE void sub_v2_v2v2(float r[2], const float a[2], const float b[2])
MINLINE void zero_v2(float r[2])
void mid_v3_v3v3(float r[3], const float a[3], const float b[3])
MINLINE void madd_v3_v3v3fl(float r[3], const float a[3], const float b[3], float f)
MINLINE void zero_v3(float r[3])
float angle_v3v3v3(const float a[3], const float b[3], const float c[3]) ATTR_WARN_UNUSED_RESULT
float angle_normalized_v3v3(const float v1[3], const float v2[3]) ATTR_WARN_UNUSED_RESULT
MINLINE void add_v3_v3(float r[3], const float a[3])
MINLINE float normalize_v2_v2(float r[2], const float a[2])
void interp_v3_v3v3v3_uv(float p[3], const float v1[3], const float v2[3], const float v3[3], const float uv[2])
MINLINE float normalize_v3(float n[3])
void * BLI_memarena_alloc(struct MemArena *ma, size_t size) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL(1) ATTR_MALLOC ATTR_ALLOC_SIZE(2)
void BLI_memarena_free(struct MemArena *ma) ATTR_NONNULL(1)
struct MemArena * BLI_memarena_new(size_t bufsize, const char *name) ATTR_WARN_UNUSED_RESULT ATTR_RETURNS_NONNULL ATTR_NONNULL(2) ATTR_MALLOC
void void BLI_memarena_clear(MemArena *ma) ATTR_NONNULL(1)
void BLI_mempool_iternew(BLI_mempool *pool, BLI_mempool_iter *iter) ATTR_NONNULL()
void * BLI_mempool_iterstep(BLI_mempool_iter *iter) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL()
BLI_mempool * BLI_mempool_create(unsigned int esize, unsigned int elem_num, unsigned int pchunk, unsigned int flag) ATTR_MALLOC ATTR_WARN_UNUSED_RESULT ATTR_RETURNS_NONNULL
int BLI_mempool_len(const BLI_mempool *pool) ATTR_NONNULL(1)
@ BLI_MEMPOOL_ALLOW_ITER
Definition BLI_mempool.h:95
void * BLI_mempool_calloc(BLI_mempool *pool) ATTR_MALLOC ATTR_WARN_UNUSED_RESULT ATTR_RETURNS_NONNULL ATTR_NONNULL(1)
void BLI_mempool_destroy(BLI_mempool *pool) ATTR_NONNULL(1)
void BLI_stack_pop(BLI_Stack *stack, void *dst) ATTR_NONNULL()
Definition stack.c:137
void BLI_stack_push(BLI_Stack *stack, const void *src) ATTR_NONNULL()
Definition stack.c:131
bool BLI_stack_is_empty(const BLI_Stack *stack) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL()
Definition stack.c:249
void BLI_stack_free(BLI_Stack *stack) ATTR_NONNULL()
Definition stack.c:96
void * BLI_stack_peek(BLI_Stack *stack) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL()
Definition stack.c:168
void * BLI_stack_push_r(BLI_Stack *stack) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL()
Definition stack.c:103
void BLI_stack_discard(BLI_Stack *stack) ATTR_NONNULL()
Definition stack.c:175
#define BLI_stack_new(esize, descr)
#define SNPRINTF(dst, format,...)
Definition BLI_string.h:597
#define BLI_STR_UTF8_DEGREE_SIGN
unsigned char uchar
unsigned int uint
#define UNUSED_FUNCTION(x)
#define INIT_MINMAX(min, max)
#define POINTER_FROM_INT(i)
#define UNUSED_VARS_NDEBUG(...)
#define POINTER_AS_INT(i)
#define UNPACK3(a)
#define ELEM(...)
#define IFACE_(msgid)
#define BLT_I18NCONTEXT_ID_MESH
ID * DEG_get_evaluated_id(const Depsgraph *depsgraph, ID *id)
Object is a sort of wrapper for general info.
@ OB_MESH
@ USER_UNIT_NONE
@ SCE_ORIENT_DEFAULT
@ SCE_SELECT_FACE
@ SPACE_VIEW3D
#define UI_SCALE_FAC
#define RV3D_CLIPPING_ENABLED(v3d, rv3d)
@ RV3D_CAMOB
@ OP_IS_MODAL_CURSOR_REGION
@ OPERATOR_RUNNING_MODAL
@ OPERATOR_PASS_THROUGH
void EDBM_flag_disable_all(BMEditMesh *em, char hflag)
void EDBM_update(Mesh *mesh, const EDBMUpdate_Params *params)
BMFace * EDBM_face_find_nearest(ViewContext *vc, float *dist_px_manhattan_p)
ViewContext em_setup_viewcontext(bContext *C)
void EDBM_selectmode_flush(BMEditMesh *em)
void initNumInput(NumInput *n)
Definition numinput.cc:70
bool handleNumInput(bContext *C, NumInput *n, const wmEvent *event)
Definition numinput.cc:312
bool applyNumInput(NumInput *n, float *vec)
Definition numinput.cc:190
@ NUM_NO_NEGATIVE
bool hasNumInput(const NumInput *n)
Definition numinput.cc:171
bool ED_operator_editmesh_view3d(bContext *C)
void ED_workspace_status_text(bContext *C, const char *str)
Definition area.cc:966
void ED_region_tag_redraw(ARegion *region)
Definition area.cc:634
void * ED_region_draw_cb_activate(ARegionType *art, void(*draw)(const bContext *, ARegion *, void *), void *customdata, int type)
#define REGION_DRAW_POST_VIEW
bool ED_region_draw_cb_exit(ARegionType *art, void *handle)
short ED_transform_calc_orientation_from_type_ex(const Scene *scene, ViewLayer *view_layer, const View3D *v3d, const RegionView3D *rv3d, Object *ob, Object *obedit, short orientation_index, int pivot_point, float r_mat[3][3])
bool ED_view3d_win_to_ray_clipped(Depsgraph *depsgraph, const ARegion *region, const View3D *v3d, const float mval[2], float r_ray_start[3], float r_ray_normal[3], bool do_clip_planes)
@ V3D_PROJ_TEST_NOP
Definition ED_view3d.hh:266
float ED_view3d_pixel_size_no_ui_scale(const RegionView3D *rv3d, const float co[3])
void ED_view3d_win_to_3d(const View3D *v3d, const ARegion *region, const float depth_pt[3], const float mval[2], float r_out[3])
void ED_view3d_init_mats_rv3d(const Object *ob, RegionView3D *rv3d)
bool ED_view3d_unproject_v3(const ARegion *region, float regionx, float regiony, float regionz, float world[3])
bool ED_view3d_win_to_segment_clipped(const Depsgraph *depsgraph, const ARegion *region, const View3D *v3d, const float mval[2], float r_ray_start[3], float r_ray_end[3], bool do_clip_planes)
bool ED_view3d_clip_range_get(const Depsgraph *depsgraph, const View3D *v3d, const RegionView3D *rv3d, bool use_ortho_factor, float *r_clip_start, float *r_clip_end)
bool ED_view3d_clipping_test(const RegionView3D *rv3d, const float co[3], bool is_local)
eV3DProjStatus ED_view3d_project_float_global(const ARegion *region, const float co[3], float r_co[2], eV3DProjTest flag)
blender::gpu::Batch * GPU_batch_create_ex(GPUPrimType primitive_type, blender::gpu::VertBuf *vertex_buf, blender::gpu::IndexBuf *index_buf, eGPUBatchFlag owns_flag)
Definition gpu_batch.cc:56
void GPU_batch_discard(blender::gpu::Batch *batch)
void GPU_batch_program_set_builtin(blender::gpu::Batch *batch, eGPUBuiltinShader shader_id)
void GPU_batch_draw(blender::gpu::Batch *batch)
#define GPU_batch_uniform_4fv(batch, name, val)
Definition GPU_batch.hh:307
void GPU_batch_draw_range(blender::gpu::Batch *batch, int vertex_first, int vertex_count)
@ GPU_BATCH_OWNS_VBO
Definition GPU_batch.hh:42
void immEnd()
void immUnbindProgram()
void immBindBuiltinProgram(eGPUBuiltinShader shader_id)
blender::gpu::Batch * immBeginBatchAtMost(GPUPrimType, uint vertex_len)
void immUniformColor3ubv(const unsigned char rgb[3])
void immUniformThemeColor3(int color_id)
GPUVertFormat * immVertexFormat()
void immUniformColor4fv(const float rgba[4])
void immVertex3fv(uint attr_id, const float data[3])
void immBegin(GPUPrimType, uint vertex_len)
void immRectf(uint pos, float x1, float y1, float x2, float y2)
void GPU_matrix_identity_set()
void GPU_matrix_push()
void GPU_matrix_push_projection()
void GPU_matrix_pop_projection()
void GPU_polygon_offset(float viewdist, float dist)
void GPU_matrix_pop()
@ GPU_PRIM_LINES
@ GPU_PRIM_POINTS
@ GPU_PRIM_LINE_STRIP
@ GPU_SHADER_3D_UNIFORM_COLOR
@ GPU_BLEND_NONE
Definition GPU_state.hh:85
@ GPU_BLEND_ALPHA
Definition GPU_state.hh:87
void GPU_blend(eGPUBlend blend)
Definition gpu_state.cc:42
void GPU_line_width(float width)
Definition gpu_state.cc:161
void GPU_point_size(float size)
Definition gpu_state.cc:167
@ GPU_DEPTH_LESS_EQUAL
Definition GPU_state.hh:111
@ GPU_DEPTH_NONE
Definition GPU_state.hh:108
void GPU_depth_test(eGPUDepthTest test)
Definition gpu_state.cc:68
#define GPU_vertbuf_create_with_format(format)
void GPU_vertbuf_attr_set(blender::gpu::VertBuf *, uint a_idx, uint v_idx, const void *data)
void GPU_vertbuf_data_alloc(blender::gpu::VertBuf &verts, uint v_len)
@ GPU_FETCH_FLOAT
uint GPU_vertformat_attr_add(GPUVertFormat *, const char *name, GPUVertCompType, uint comp_len, GPUVertFetchMode)
@ GPU_COMP_F32
static double angle(const Eigen::Vector3d &v1, const Eigen::Vector3d &v2)
Definition IK_Math.h:125
Read Guarded memory(de)allocation.
#define MEM_SIZE_OPTIMAL(size)
@ PROP_SKIP_SAVE
Definition RNA_types.hh:245
@ PROP_HIDDEN
Definition RNA_types.hh:239
@ PROP_ANGLE
Definition RNA_types.hh:155
void UI_GetThemeColorType3ubv(int colorid, int spacetype, unsigned char col[3])
@ TH_WIRE
@ TH_ACTIVE_SPLINE
@ TH_TRANSFORM
@ TH_NURB_VLINE
@ TH_NURB_ULINE
@ TH_AXIS_Y
@ TH_AXIS_X
@ TH_HANDLE_SEL_VECT
@ TH_AXIS_Z
@ TH_TEXT
@ TH_NURB_SEL_ULINE
void UI_GetThemeColor3ubv(int colorid, unsigned char col[3])
@ OPTYPE_BLOCKING
Definition WM_types.hh:164
@ OPTYPE_UNDO
Definition WM_types.hh:162
@ OPTYPE_REGISTER
Definition WM_types.hh:160
@ KM_NOTHING
Definition WM_types.hh:283
@ KM_PRESS
Definition WM_types.hh:284
@ KM_RELEASE
Definition WM_types.hh:285
@ BM_ELEM_HIDDEN
@ BM_ELEM_SELECT
@ BM_ELEM_TAG
#define BM_FACE_FIRST_LOOP(p)
void BM_edge_kill(BMesh *bm, BMEdge *e)
BMVert * BM_vert_create(BMesh *bm, const float co[3], const BMVert *v_example, const eBMCreateFlag create_flag)
Main function for creating a new vertex.
Definition bmesh_core.cc:43
BMEdge * BM_edge_create(BMesh *bm, BMVert *v1, BMVert *v2, const BMEdge *e_example, const eBMCreateFlag create_flag)
Main function for creating a new edge.
eBMCreateFlag
Definition bmesh_core.hh:23
#define BM_elem_index_get(ele)
#define BM_elem_flag_disable(ele, hflag)
#define BM_elem_flag_test(ele, hflag)
#define BM_elem_flag_enable(ele, hflag)
#define BM_ITER_ELEM(ele, iter, data, itype)
#define BM_ITER_MESH(ele, iter, bm, itype)
@ BM_FACES_OF_EDGE
@ BM_FACES_OF_VERT
@ BM_EDGES_OF_MESH
@ BM_VERTS_OF_MESH
@ BM_FACES_OF_MESH
@ BM_EDGES_OF_FACE
ATTR_WARN_UNUSED_RESULT BMesh * bm
void BM_mesh_elem_hflag_enable_all(BMesh *bm, const char htype, const char hflag, const bool respecthide)
void BM_edge_select_set(BMesh *bm, BMEdge *e, const bool select)
Select Edge.
void BM_mesh_elem_index_ensure(BMesh *bm, const char htype)
BMVert * BM_edge_split(BMesh *bm, BMEdge *e, BMVert *v, BMEdge **r_e, float fac)
Edge Split.
#define BM_FACE
#define BM_EDGE
#define BM_VERT
void BM_face_calc_point_in_face(const BMFace *f, float r_co[3])
bool BM_face_point_inside_test(const BMFace *f, const float co[3])
bool BM_face_split_edgenet_connect_islands(BMesh *bm, BMFace *f, BMEdge **edge_net_init, const uint edge_net_init_len, bool use_partial_connect, MemArena *mem_arena, BMEdge ***r_edge_net_new, uint *r_edge_net_new_len)
bool BM_face_split_edgenet(BMesh *bm, BMFace *f, BMEdge **edge_net, const int edge_net_len, blender::Vector< BMFace * > *r_face_arr)
float BM_loop_point_side_of_loop_test(const BMLoop *l, const float co[3])
bool BM_edge_in_face(const BMEdge *e, const BMFace *f)
BMEdge * BM_edge_exists(BMVert *v_a, BMVert *v_b)
bool BM_vert_in_face(BMVert *v, BMFace *f)
BMLoop * BM_face_edge_share_loop(BMFace *f, BMEdge *e)
Return the Loop Shared by Face and Edge.
BMLoop * BM_face_vert_share_loop(BMFace *f, BMVert *v)
Return the Loop Shared by Face and Vertex.
float BM_loop_point_side_of_edge_test(const BMLoop *l, const float co[3])
BLI_INLINE bool BM_edge_is_boundary(const BMEdge *e) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL()
BLI_INLINE bool BM_loop_is_adjacent(const BMLoop *l_a, const BMLoop *l_b) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL()
BLI_INLINE bool BM_edge_is_wire(const BMEdge *e) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL()
ATTR_WARN_UNUSED_RESULT const BMVert * v2
ATTR_WARN_UNUSED_RESULT const BMLoop * l
ATTR_WARN_UNUSED_RESULT const BMVert const BMEdge * e
ATTR_WARN_UNUSED_RESULT const BMVert * v
void item(std::string text, int icon1, int icon2=0)
Definition area.cc:908
void opmodal(std::string text, const wmOperatorType *ot, int propvalue, bool inverted=false)
Definition area.cc:934
int64_t size() const
Definition BLI_array.hh:245
const T * data() const
Definition BLI_array.hh:301
void reinitialize(const int64_t new_size)
Definition BLI_array.hh:388
bool add(const Key &key, const Value &value)
Definition BLI_map.hh:271
const Value & lookup(const Key &key) const
Definition BLI_map.hh:506
Value lookup_default(const Key &key, const Value &default_value) const
Definition BLI_map.hh:531
ItemIterator items() const
Definition BLI_map.hh:864
bool contains(const Key &key) const
Definition BLI_set.hh:291
bool add(const Key &key)
Definition BLI_set.hh:248
bool remove(const Key &key)
Definition BLI_set.hh:366
constexpr bool is_empty() const
Definition BLI_span.hh:261
int64_t size() const
void append(const T &value)
const T & last(const int64_t n=0) const
bool is_empty() const
IndexRange index_range() const
void resize(const int64_t new_size)
void clear_and_shrink()
int64_t first_index_of_try(const T &value) const
#define fabsf(x)
static void knife_bvh_init(KnifeTool_OpData *kcd)
static void knifetool_disable_angle_snapping(KnifeTool_OpData *kcd)
static void knife_edge_append_face(KnifeTool_OpData *kcd, KnifeEdge *kfe, BMFace *f)
static void knife_find_line_hits(KnifeTool_OpData *kcd)
static KnifeVert * get_bm_knife_vert(KnifeTool_OpData *kcd, BMVert *v, int ob_index)
static bool knife_find_closest_vert_of_edge(KnifeTool_OpData *kcd, const KnifeEdge *kfe, const float2 &cage_ss, KnifePosData *r_kpd)
static void knifetool_draw_angle(const KnifeTool_OpData *kcd, const float start[3], const float mid[3], const float end[3], const float start_ss[2], const float mid_ss[2], const float end_ss[2], const float angle)
static int knife_calculate_snap_ref_edges(KnifeTool_OpData *kcd)
static BMFace * knife_bvh_raycast(KnifeTool_OpData *kcd, const float co[3], const float dir[3], const float radius, float *r_dist, float r_cagehit[3], int *r_ob_index)
static void knife_snap_curr(KnifeTool_OpData *kcd, const float2 &mval)
static bool knife_snap_angle_screen(KnifeTool_OpData *kcd)
static ListBase * knife_get_face_kedges(KnifeTool_OpData *kcd, int ob_index, BMFace *f)
static bool knife_ray_intersect_face(KnifeTool_OpData *kcd, const float s[2], const float v1[3], const float v2[3], int ob_index, BMFace *f, const float face_tol_sq, float hit_co[3], float hit_cageco[3])
#define KNIFE_FLT_EPS_PX_VERT
static void knife_constrain_axis(KnifeTool_OpData *kcd)
static void knife_bvh_free(KnifeTool_OpData *kcd)
static void calc_ortho_extent(KnifeTool_OpData *kcd)
static BMFace * knife_find_common_face(ListBase *faces1, ListBase *faces2)
static bool knife_snap_angle_relative(KnifeTool_OpData *kcd)
static float snap_v3_angle_plane(float r[3], const float v[3], const float v_ref[3], const float plane_no[3], float snap_step)
static void knifetool_draw_visible_distances(const KnifeTool_OpData *kcd)
static ListBase * knife_empty_list(KnifeTool_OpData *kcd)
static void knifetool_draw_orientation_locking(const KnifeTool_OpData *kcd)
static void knife_project_v2(const KnifeTool_OpData *kcd, const float co[3], float sco[2])
static void knife_make_cuts(KnifeTool_OpData *kcd, int ob_index)
static void UNUSED_FUNCTION knifetool_recast_cageco(KnifeTool_OpData *kcd, float mval[3], float r_cage[3])
static void knifetool_init(ViewContext *vc, KnifeTool_OpData *kcd, Vector< Object * > objects, const bool only_select, const bool cut_through, const bool xray, const int visible_measurements, const int angle_snapping, const float angle_snapping_increment, const bool is_interactive)
static void knifetool_draw_dist_angle(const KnifeTool_OpData *kcd)
KnifeMode
@ MODE_PANNING
@ MODE_CONNECT
@ MODE_DRAGGING
@ MODE_IDLE
static void linehit_to_knifepos(KnifePosData *kpos, KnifeLineHit *lh)
static bool knife_verts_edge_in_face(KnifeVert *v1, KnifeVert *v2, BMFace *f)
static void knifetool_undo(KnifeTool_OpData *kcd)
static void knifetool_update_mval_i(KnifeTool_OpData *kcd, const int mval_i[2])
@ KNF_MODAL_CANCEL
@ KNF_MODAL_DEPTH_TEST_TOGGLE
@ KNF_MODAL_CYCLE_ANGLE_SNAP_EDGE
@ KNF_MODAL_SHOW_DISTANCE_ANGLE_TOGGLE
@ KNF_MODAL_X_AXIS
@ KNF_MODAL_MIDPOINT_OFF
@ KNF_MODAL_PANNING
@ KNF_MODAL_Y_AXIS
@ KNF_MODAL_NEW_CUT
@ KNF_MODAL_ANGLE_SNAP_TOGGLE
@ KNF_MODAL_IGNORE_SNAP_OFF
@ KNF_MODAL_UNDO
@ KNF_MODAL_ADD_CUT_CLOSED
@ KNF_MODAL_ADD_CUT
@ KNF_MODAL_IGNORE_SNAP_ON
@ KNF_MODAL_CONFIRM
@ KNF_MODAL_MIDPOINT_ON
@ KNF_MODAL_CUT_THROUGH_TOGGLE
@ KNF_MODAL_Z_AXIS
static bool knife_find_closest_edge_of_face(KnifeTool_OpData *kcd, int ob_index, BMFace *f, const float3 &ray_origin, const float3 &ray_normal, const float2 &curr_cage_ss, KnifePosData *r_kpd)
#define KNIFE_FLT_EPS_PX_EDGE
static bool knife_closest_constrain_to_edge(KnifeTool_OpData *kcd, const float3 &kfv1_cageco, const float3 &kfv2_cageco, float r_close[3])
static void knifetool_finish_ex(KnifeTool_OpData *kcd)
static void knife_append_list(KnifeTool_OpData *kcd, ListBase *lst, void *elem)
static void knifetool_cancel(bContext *, wmOperator *op)
static void prepare_linehits_for_cut(KnifeTool_OpData *kcd)
static BMElem * bm_elem_from_knife_edge(KnifeEdge *kfe)
#define F_ISECT_SET_UNKNOWN(f)
static LinkData * find_ref(ListBase *lb, void *ref)
static void knifetool_finish_single_post(KnifeTool_OpData *, Object *ob)
static void knife_make_face_cuts(KnifeTool_OpData *kcd, BMesh *bm, BMFace *f, ListBase *kfedges)
static int knifetool_invoke(bContext *C, wmOperator *op, const wmEvent *event)
static void knifetool_update_mval(KnifeTool_OpData *kcd, const float mval[2])
static bool knife_bm_face_is_not_hidden(BMFace *f)
static void knifetool_exit(wmOperator *op)
wmKeyMap * knifetool_modal_keymap(wmKeyConfig *keyconf)
static void knife_finish_cut(KnifeTool_OpData *kcd)
static void knife_bm_tri_cagecos_get_worldspace(const KnifeTool_OpData *kcd, int ob_index, int tri_index, float cos[3][3])
#define F_ISECT_SET_OUTSIDE(f)
static void knife_add_to_vert_edges(KnifeTool_OpData *kcd, KnifeEdge *kfe)
void MESH_OT_knife_tool(wmOperatorType *ot)
static void clip_to_ortho_planes(float v1[3], float v2[3], const float center[3], const float d)
#define KNIFE_FLT_EPSBIG
static void knife_bvh_raycast_cb(void *userdata, int index, const BVHTreeRay *ray, BVHTreeRayHit *hit)
static void knife_recalc_ortho(KnifeTool_OpData *kcd)
static bool knife_linehit_face_test(KnifeTool_OpData *kcd, float s1[2], float s2[2], float sco[2], float ray_start[3], float ray_end[3], int ob_index, BMFace *f, float face_tol_sq, KnifeLineHit *r_hit)
#define KNIFE_MAX_ANGLE_SNAPPING_INCREMENT
static void knife_join_edge(KnifeEdge *newkfe, KnifeEdge *kfe)
@ KNF_MEASUREMENT_NONE
@ KNF_MEASUREMENT_BOTH
@ KNF_MEASUREMENT_DISTANCE
@ KNF_MEASUREMENT_ANGLE
static void add_hit_to_facehits(KnifeTool_OpData *kcd, GHash *facehits, BMFace *f, KnifeLineHit *hit)
static int get_lowest_face_tri(KnifeTool_OpData *kcd, BMFace *f)
#define KNIFE_DEFAULT_ANGLE_SNAPPING_INCREMENT
static void knifetool_raycast_planes(const KnifeTool_OpData *kcd, float r_v1[3], float r_v2[3])
static void knife_pos_data_clear(KnifePosData *kpd)
static void knifetool_draw_visible_angles(const KnifeTool_OpData *kcd)
static void knife_snap_update_from_mval(KnifeTool_OpData *kcd, const float mval[2])
static bool knife_add_single_cut__is_linehit_outside_face(BMFace *f, const KnifeLineHit *lh, const float co[3])
static void knifetool_draw_angle_snapping(const KnifeTool_OpData *kcd)
void EDBM_mesh_knife(ViewContext *vc, const Span< Object * > objects, LinkNode *polys, bool use_tag, bool cut_through)
static float snap_v2_angle(float r[2], const float v[2], const float v_ref[2], float angle_snap)
static BMFace * knife_bvh_raycast_filter(KnifeTool_OpData *kcd, const float co[3], const float dir[3], const float radius, float *r_dist, float r_cagehit[3], int *r_ob_index, bool(*filter_cb)(BMFace *f, void *userdata), void *filter_userdata)
static void knife_reset_snap_angle_input(KnifeTool_OpData *kcd)
static KnifeVert * new_knife_vert(KnifeTool_OpData *kcd, const float co[3], const float cageco[3])
static void knifetool_finish_single_pre(KnifeTool_OpData *kcd, int ob_index)
static void knife_cut_face(KnifeTool_OpData *kcd, BMFace *f, ListBase *hits)
static void knifetool_draw(const bContext *, ARegion *, void *arg)
static const int * knife_bm_tri_index_get(const KnifeTool_OpData *kcd, int ob_index, int tri_index, int tri_index_buf[3])
static void knife_input_ray_segment(KnifeTool_OpData *kcd, const float mval[2], float r_origin[3], float r_end[3])
static bool bm_ray_cast_cb_elem_not_in_face_check(BMFace *f, void *user_data)
static KnifeEdge * new_knife_edge(KnifeTool_OpData *kcd)
static void knife_linehit_set(KnifeTool_OpData *kcd, float s1[2], float s2[2], float sco[2], float cage[3], int ob_index, KnifeVert *v, KnifeEdge *kfe, KnifeLineHit *r_hit)
@ KNF_CONSTRAIN_ANGLE_MODE_NONE
@ KNF_CONSTRAIN_ANGLE_MODE_SCREEN
@ KNF_CONSTRAIN_ANGLE_MODE_RELATIVE
static void knife_add_single_cut(KnifeTool_OpData *kcd, KnifeLineHit *lh1, KnifeLineHit *lh2, BMFace *f)
static int knife_update_active(KnifeTool_OpData *kcd)
#define KNIFE_FLT_EPS_PX_FACE
static void knife_bm_tri_cagecos_get(const KnifeTool_OpData *kcd, int ob_index, int tri_index, float cos[3][3])
static float knife_snap_size(KnifeTool_OpData *kcd, float maxsize)
#define KNIFE_MIN_ANGLE_SNAPPING_INCREMENT
#define KMAXDIST
static bool point_is_visible(KnifeTool_OpData *kcd, const float p[3], const float s[2], BMElem *ele_test)
static void knife_add_cut(KnifeTool_OpData *kcd)
static void knifetool_init_obinfo(KnifeTool_OpData *kcd, Object *ob, int ob_index, bool use_tri_indices)
static void knifetool_finish(wmOperator *op)
static void knife_update_header(bContext *C, wmOperator *op, KnifeTool_OpData *kcd)
static void knife_append_list_no_dup(KnifeTool_OpData *kcd, ListBase *lst, void *elem)
#define KNIFE_FLT_EPS_SQUARED
static bool knife_find_closest_face(KnifeTool_OpData *kcd, const float3 &ray_origin, const float3 &ray_normal, const float2 &mval, KnifePosData *r_kpd)
static int knifetool_modal(bContext *C, wmOperator *op, const wmEvent *event)
static BMElem * bm_elem_from_knife_vert(KnifeVert *kfv, KnifeEdge **r_kfe)
#define KNIFE_FLT_EPS
@ KNF_CONSTRAIN_AXIS_MODE_LOCAL
@ KNF_CONSTRAIN_AXIS_MODE_GLOBAL
@ KNF_CONSTRAIN_AXIS_MODE_NONE
static int sort_verts_by_dist_cb(void *co_p, const void *cur_a_p, const void *cur_b_p)
static KnifeEdge * get_bm_knife_edge(KnifeTool_OpData *kcd, BMEdge *e, int ob_index)
static void knifetool_disable_orientation_locking(KnifeTool_OpData *kcd)
static int knife_sample_screen_density_from_closest_face(KnifeTool_OpData *kcd, const float radius, int ob_index, BMFace *f, const float cageco[3])
static int linehit_compare(const KnifeLineHit &lh1, const KnifeLineHit &lh2)
static void knife_add_edge_faces_to_vert(KnifeTool_OpData *kcd, KnifeVert *kfv, BMEdge *e)
static void knife_start_cut(KnifeTool_OpData *kcd, const float2 &mval)
static bool coinciding_edges(BMEdge *e1, BMEdge *e2)
static bool knife_bm_face_is_select(BMFace *f)
static bool edbm_mesh_knife_point_isect(LinkNode *polys, const float cent_ss[2])
@ KNF_CONSTRAIN_AXIS_Y
@ KNF_CONSTRAIN_AXIS_Z
@ KNF_CONSTRAIN_AXIS_X
@ KNF_CONSTRAIN_AXIS_NONE
static void knife_init_colors(KnifeColors *colors)
static void knifetool_exit_ex(KnifeTool_OpData *kcd)
static void set_lowest_face_tri(KnifeTool_OpData *kcd, BMEditMesh *em, BMFace *f, int index)
static KnifeVert * knife_split_edge(KnifeTool_OpData *kcd, KnifeEdge *kfe, const float co[3], const float cageco[3], KnifeEdge **r_kfe)
#define F_ISECT_IS_UNKNOWN(f)
draw_view in_light_buf[] float
draw_view push_constant(Type::INT, "radiance_src") .push_constant(Type capture_info_buf storage_buf(1, Qualifier::READ, "ObjectBounds", "bounds_buf[]") .push_constant(Type draw_view int
struct @620::@622 batch
uiWidgetBaseParameters params[MAX_WIDGET_BASE_BATCH]
int count
format
size_t(* MEM_allocN_len)(const void *vmemh)
Definition mallocn.cc:36
void MEM_freeN(void *vmemh)
Definition mallocn.cc:105
ccl_device_inline float3 cos(float3 v)
static char faces[256]
VecBase< float, 2 > float2
return ret
float RNA_float_get(PointerRNA *ptr, const char *name)
bool RNA_boolean_get(PointerRNA *ptr, const char *name)
int RNA_enum_get(PointerRNA *ptr, const char *name)
PropertyRNA * RNA_def_float(StructOrFunctionRNA *cont_, const char *identifier, const float default_value, const float hardmin, const float hardmax, const char *ui_name, const char *ui_description, const float softmin, const float softmax)
PropertyRNA * RNA_def_enum(StructOrFunctionRNA *cont_, const char *identifier, const EnumPropertyItem *items, const int default_value, const char *ui_name, const char *ui_description)
PropertyRNA * RNA_def_boolean(StructOrFunctionRNA *cont_, const char *identifier, const bool default_value, const char *ui_name, const char *ui_description)
void RNA_def_property_translation_context(PropertyRNA *prop, const char *context)
void RNA_def_property_flag(PropertyRNA *prop, PropertyFlag flag)
void RNA_def_property_subtype(PropertyRNA *prop, PropertySubType subtype)
#define min(a, b)
Definition sort.c:32
#define FLT_MAX
Definition stdcycles.h:14
__int64 int64_t
Definition stdint.h:89
void * regiondata
struct ARegionType * type
BMVert * v1
BMVert * v2
short selectmode
blender::Array< std::array< BMLoop *, 3 > > looptris
float no[3]
struct BMEdge * e
struct BMLoop * radial_next
struct BMFace * f
struct BMLoop * next
float co[3]
int totvert
int totfacesel
void * filter_data
bool(* filter_cb)(BMFace *f, void *userdata)
blender::Span< std::array< BMLoop *, 3 > > looptris
BVHTree * tree
uchar edge_extra[3]
uchar axis_extra[3]
uchar curpoint[3]
uchar curpoint_a[4]
BMFace * basef
KnifeVert * v1
ListBase faces
KnifeVert * v2
KnifeEdge * kfe
KnifeVert * v
Array< float3 > positions_cage
Array< int3 > tri_indices
KnifeVert * vert
KnifeEdge * edge
bool is_space() const
KnifeEdge * snap_ref_edge
enum KnifeMode mode
KnifeMeasureData mdata
BLI_mempool * refs
KnifeUndoFrame * undo
Array< KnifeObjectInfo > objects_info
BLI_mempool * kverts
BLI_Stack * splitstack
struct KnifeTool_OpData::@427 edgenet
BLI_Stack * undostack
BLI_mempool * kedges
Vector< KnifeLineHit > linehits
Vector< Object * > objects
float ortho_extent_center[3]
KnifePosData pos
KnifeMeasureData mdata
float cageco[3]
ListBase edges
ListBase faces
void * data
struct LinkData * next
void * link
struct LinkNode * next
void * first
short idx_max
float val[NUM_MAX_ELEMENTS]
short val_flag[NUM_MAX_ELEMENTS]
char str[NUM_STR_REP_LEN]
int unit_type[NUM_MAX_ELEMENTS]
float persmat[4][4]
float persmatob[4][4]
float clip_local[6][4]
float viewinv[4][4]
struct UnitSettings unit
RegionView3D * rv3d
Definition ED_view3d.hh:76
ARegion * region
Definition ED_view3d.hh:73
int mval[2]
Definition ED_view3d.hh:78
Scene * scene
Definition ED_view3d.hh:69
wmWindow * win
Definition ED_view3d.hh:75
ViewLayer * view_layer
Definition ED_view3d.hh:70
View3D * v3d
Definition ED_view3d.hh:74
Object * obact
Definition ED_view3d.hh:71
Object * obedit
Definition ED_view3d.hh:72
Depsgraph * depsgraph
Definition ED_view3d.hh:68
short val
Definition WM_types.hh:724
int mval[2]
Definition WM_types.hh:728
short prev_val
Definition WM_types.hh:779
short type
Definition WM_types.hh:722
const void * modal_items
const char * name
Definition WM_types.hh:990
bool(* poll)(bContext *C) ATTR_WARN_UNUSED_RESULT
Definition WM_types.hh:1042
const char * idname
Definition WM_types.hh:992
int(* modal)(bContext *C, wmOperator *op, const wmEvent *event) ATTR_WARN_UNUSED_RESULT
Definition WM_types.hh:1036
int(* invoke)(bContext *C, wmOperator *op, const wmEvent *event) ATTR_WARN_UNUSED_RESULT
Definition WM_types.hh:1022
const char * description
Definition WM_types.hh:996
StructRNA * srna
Definition WM_types.hh:1080
void(* cancel)(bContext *C, wmOperator *op)
Definition WM_types.hh:1028
struct ReportList * reports
struct wmOperatorType * type
struct PointerRNA * ptr
void WM_cursor_modal_set(wmWindow *win, int val)
void WM_cursor_modal_restore(wmWindow *win)
@ WM_CURSOR_KNIFE
Definition wm_cursors.hh:31
wmEventHandler_Op * WM_event_add_modal_handler(bContext *C, wmOperator *op)
@ MOUSEPAN
@ EVT_MODAL_MAP
@ MOUSEZOOM
@ MOUSEROTATE
@ WHEELUPMOUSE
@ WHEELDOWNMOUSE
@ MOUSEMOVE
@ NDOF_MOTION
wmOperatorType * ot
Definition wm_files.cc:4125
std::optional< std::string > WM_modalkeymap_operator_items_to_string(wmOperatorType *ot, const int propvalue, const bool compact)
wmKeyMap * WM_modalkeymap_ensure(wmKeyConfig *keyconf, const char *idname, const EnumPropertyItem *items)
Definition wm_keymap.cc:933
void WM_modalkeymap_assign(wmKeyMap *km, const char *opname)
wmKeyMap * WM_modalkeymap_find(wmKeyConfig *keyconf, const char *idname)
Definition wm_keymap.cc:960
void wmOrtho2_region_pixelspace(const ARegion *region)