Blender V4.5
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
10
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_geom.h"
27#include "BLI_math_matrix.h"
28#include "BLI_math_rotation.h"
29#include "BLI_math_vector.hh"
31
32#include "BLI_memarena.h"
33#include "BLI_set.hh"
34#include "BLI_stack.h"
35#include "BLI_string.h"
36#include "BLI_vector.hh"
37
38#include "BLT_translation.hh"
39
40#include "BKE_bvhutils.hh"
41#include "BKE_context.hh"
42#include "BKE_editmesh.hh"
43#include "BKE_layer.hh"
44#include "BKE_mesh_types.hh"
45#include "BKE_report.hh"
46#include "BKE_scene.hh"
47#include "BKE_screen.hh"
48#include "BKE_unit.hh"
49
50#include "GPU_immediate.hh"
51#include "GPU_matrix.hh"
52#include "GPU_state.hh"
53
54#include "ED_mesh.hh"
55#include "ED_numinput.hh"
56#include "ED_screen.hh"
57#include "ED_space_api.hh"
58#include "ED_transform.hh"
59#include "ED_view3d.hh"
60
61#include "WM_api.hh"
62#include "WM_types.hh"
63
64#include "DNA_mesh_types.h"
65#include "DNA_object_types.h"
66
67#include "UI_interface.hh"
68#include "UI_resources.hh"
69
70#include "RNA_access.hh"
71#include "RNA_define.hh"
72
74
75#include "mesh_intern.hh" /* Own include. */
76
77using namespace blender;
78
79/* Detect isolated holes and fill them. */
80#define USE_NET_ISLAND_CONNECT
81
82#define KMAXDIST (10 * UI_SCALE_FAC) /* Max mouse distance from edge before not detecting it. */
83
84/* WARNING: Knife float precision is fragile:
85 * Be careful before making changes here see: (#43229, #42864, #42459, #41164).
86 */
87#define KNIFE_FLT_EPS 0.00001f
88#define KNIFE_FLT_EPS_SQUARED (KNIFE_FLT_EPS * KNIFE_FLT_EPS)
89#define KNIFE_FLT_EPSBIG 0.0005f
90
91#define KNIFE_FLT_EPS_PX_VERT 0.5f
92#define KNIFE_FLT_EPS_PX_EDGE 0.05f
93#define KNIFE_FLT_EPS_PX_FACE 0.05f
94
95#define KNIFE_DEFAULT_ANGLE_SNAPPING_INCREMENT 30.0f
96#define KNIFE_MIN_ANGLE_SNAPPING_INCREMENT 0.0f
97#define KNIFE_MAX_ANGLE_SNAPPING_INCREMENT 180.0f
98
112
113/* Knife-tool Operator. */
114struct KnifeVert {
115 BMVert *v; /* Non-null if this is an original vert. */
118
119 /* Index of the associated object.
120 * -1 represents the absence of an object. */
122
123 float co[3]; /* Vertex position in the original mesh. Equivalent to #BMVert::co[3]. */
124 float3 cageco; /* Vertex position in the Cage mesh and in World Space. */
125 bool is_cut; /* Along a cut created by user input (will draw too). */
127 bool is_splitting; /* Created when an edge was split. */
128};
129
130struct KnifeEdge {
132 BMFace *basef; /* Face to restrict face fill to. */
134
135 BMEdge *e; /* Non-null if this is an original edge. */
136 bool is_cut; /* Along a cut created by user input (will draw too). */
138 int splits; /* Number of times this edge has been split. */
139};
140
142 float hit[3], cagehit[3];
143 float schit[2]; /* Screen coordinates for cagehit. */
144 float l; /* Lambda along cut line. */
145 float m; /* Depth front-to-back. */
146
147 /* Exactly one of kfe, v, or f should be non-null,
148 * saying whether cut line crosses and edge,
149 * is snapped to a vert, or is in the middle of some face. */
153
154 /* Index of the associated object.
155 * -1 represents the absence of an object. */
157};
158
161
162 /* At most one of vert, edge, or bmface should be non-null,
163 * saying whether the point is snapped to a vertex, edge, or in a face.
164 * If none are set, this point is in space and is_space should be true. */
168
169 /* Index of the associated object.
170 * -1 represents the absence of an object. */
172
173 float2 mval; /* Mouse screen position (may be non-integral if snapped to something). */
174
175 bool is_space() const
176 {
177 return this->ob_index == -1;
178 }
179};
180
182 float cage[3];
183 float mval[2];
185};
186
188 int cuts; /* Line hits cause multiple edges/cuts to be created at once. */
189 int splits; /* Number of edges split. */
190 KnifePosData pos; /* Store previous KnifePosData. */
192};
193
194struct KnifeBVH {
195 BVHTree *tree; /* Knife Custom BVH Tree. */
196 /* Used by #knife_bvh_raycast_cb to store the intersecting triangles. */
199
200 /* Use #bm_ray_cast_cb_elem_not_in_face_check. */
201 bool (*filter_cb)(BMFace *f, void *userdata);
203};
204
220
222
223/* struct for properties used while drawing */
225 ARegion *region; /* Region that knifetool was activated in. */
226 void *draw_handle; /* For drawing preview loop. */
227 ViewContext vc; /* NOTE: _don't_ use 'mval', instead use the one we define below. */
228
230
231 /* Used for swapping current object when in multi-object edit mode. */
233
236
238
239 /* Reused for edge-net filling. */
240 struct {
241 /* Cleared each use. */
243#ifdef USE_NET_ISLAND_CONNECT
245#endif
247
252
254
257 bool no_cuts; /* A cut has not been made yet. */
258
260 BLI_Stack *splitstack; /* Store edge splits by #knife_split_edge. */
261
262 float vthresh;
263 float ethresh;
264
265 /* Used for drag-cutting. */
267
268 /* Data for mouse-position-derived data. */
269 KnifePosData curr; /* Current point under the cursor. */
270 KnifePosData prev; /* Last added cut (a line draws from the cursor to this). */
271 KnifePosData init; /* The first point in the cut-list, used for closing the loop. */
272
273 /* Number of knife edges `kedges`. */
275 /* Number of knife vertices, `kverts`. */
277
279
281
282 /* Run by the UI or not. */
284
285 /* Operator options. */
286 bool cut_through; /* Preference, can be modified at runtime (that feature may go). */
287 bool only_select; /* Set on initialization. */
288 bool select_result; /* Set on initialization. */
289
293
295
298
303
305 float angle_snapping_increment; /* Degrees */
306
307 /* Use to check if we're currently dragging an angle snapped line. */
311 float angle;
312 /* Relative angle snapping reference edge. */
315 int snap_edge; /* Used by #KNF_MODAL_CYCLE_ANGLE_SNAP_EDGE to choose an edge for snapping. */
316
320 char axis_string[2];
321
324 KnifeMeasureData mdata; /* Data for distance and angle drawing calculations. */
325
326 KnifeUndoFrame *undo; /* Current undo frame. */
328
330};
331
332enum {
352};
353
354enum {
358};
359
360enum {
365};
366
367enum {
371};
372
373enum {
378};
379
380/* -------------------------------------------------------------------- */
383
384static void knife_draw_line(const KnifeTool_OpData *kcd, const uchar color[3])
385{
386 if (compare_v3v3(kcd->prev.cage, kcd->curr.cage, KNIFE_FLT_EPSBIG)) {
387 return;
388 }
389 const float3 dir = math::normalize(kcd->curr.cage - kcd->prev.cage) * kcd->vc.v3d->clip_end;
390 const float3 v1 = kcd->prev.cage + dir;
391 const float3 v2 = kcd->prev.cage - dir;
392
396 GPU_line_width(2.0);
398 immVertex3fv(pos, v1);
400 immEnd();
402}
403
410
412{
413 const uchar *color;
414 switch (kcd->constrain_axis) {
416 color = kcd->colors.xaxis;
417 break;
418 }
420 color = kcd->colors.yaxis;
421 break;
422 }
424 color = kcd->colors.zaxis;
425 break;
426 }
427 default: {
428 color = kcd->colors.axis_extra;
429 break;
430 }
431 }
433}
434
436{
441
444
445 char numstr[256];
446 float numstr_size[2];
447 float posit[2];
448 const float bg_margin = 4.0f * UI_SCALE_FAC;
449 const float font_size = 14.0f;
450 const int distance_precision = 4;
451
452 /* Calculate distance and convert to string. */
453 const float cut_len = len_v3v3(kcd->prev.cage, kcd->curr.cage);
454
455 const UnitSettings &unit = kcd->scene->unit;
456 if (unit.system == USER_UNIT_NONE) {
457 SNPRINTF(numstr, "%.*f", distance_precision, cut_len);
458 }
459 else {
461 numstr, sizeof(numstr), cut_len, distance_precision, B_UNIT_LENGTH, unit, false);
462 }
463
465 BLF_size(blf_mono_font, font_size * UI_SCALE_FAC);
467 BLF_width_and_height(blf_mono_font, numstr, sizeof(numstr), &numstr_size[0], &numstr_size[1]);
468
469 /* Center text. */
470 mid_v2_v2v2(posit, kcd->prev.mval, kcd->curr.mval);
471 posit[0] -= numstr_size[0] / 2.0f;
472 posit[1] -= numstr_size[1] / 2.0f;
473
474 /* Draw text background. */
475 float color_back[4] = {0.0f, 0.0f, 0.0f, 0.5f}; /* TODO: Replace with theme color. */
476 immUniformColor4fv(color_back);
477
480 posit[0] - bg_margin,
481 posit[1] - bg_margin,
482 posit[0] + bg_margin + numstr_size[0],
483 posit[1] + bg_margin + numstr_size[1]);
486
487 /* Draw text. */
488 uchar color_text[3];
489 UI_GetThemeColor3ubv(TH_TEXT, color_text);
490
491 BLF_color3ubv(blf_mono_font, color_text);
492 BLF_position(blf_mono_font, posit[0], posit[1], 0.0f);
493 BLF_draw(blf_mono_font, numstr, sizeof(numstr));
495
498}
499
501 const float start[3],
502 const float mid[3],
503 const float end[3],
504 const float start_ss[2],
505 const float mid_ss[2],
506 const float end_ss[2],
507 const float angle)
508{
509 const RegionView3D *rv3d = static_cast<const RegionView3D *>(kcd->region->regiondata);
510 const int arc_steps = 24;
511 const float arc_size = 64.0f * UI_SCALE_FAC;
512 const float bg_margin = 4.0f * UI_SCALE_FAC;
513 const float cap_size = 4.0f * UI_SCALE_FAC;
514 const float font_size = 14.0f;
515 const int angle_precision = 3;
516
517 /* Angle arc in 3d space. */
519
520 const uint pos_3d = GPU_vertformat_attr_add(
523
524 {
525 float dir_tmp[3];
526 float ar_coord[3];
527
528 float dir_a[3];
529 float dir_b[3];
530 float quat[4];
531 float axis[3];
532 float arc_angle;
533
534 Object *ob = kcd->objects[kcd->bvh.ob_index];
535 const float inverse_average_scale = 1 / (ob->object_to_world().ptr()[0][0] +
536 ob->object_to_world().ptr()[1][1] +
537 ob->object_to_world().ptr()[2][2]);
538
539 const float px_scale =
540 3.0f * inverse_average_scale *
542 min_fff(arc_size, len_v2v2(start_ss, mid_ss) / 2.0f, len_v2v2(end_ss, mid_ss) / 2.0f));
543
544 sub_v3_v3v3(dir_a, start, mid);
545 sub_v3_v3v3(dir_b, end, mid);
546 normalize_v3(dir_a);
547 normalize_v3(dir_b);
548
549 cross_v3_v3v3(axis, dir_a, dir_b);
550 arc_angle = angle_normalized_v3v3(dir_a, dir_b);
551
552 axis_angle_to_quat(quat, axis, arc_angle / arc_steps);
553
554 copy_v3_v3(dir_tmp, dir_a);
555
557 GPU_line_width(1.0);
558
559 immBegin(GPU_PRIM_LINE_STRIP, arc_steps + 1);
560 for (int j = 0; j <= arc_steps; j++) {
561 madd_v3_v3v3fl(ar_coord, mid, dir_tmp, px_scale);
562 mul_qt_v3(quat, dir_tmp);
563
564 immVertex3fv(pos_3d, ar_coord);
565 }
566 immEnd();
567 }
568
570
571 /* Angle text and background in 2d space. */
576
580
581 /* Angle as string. */
582 char numstr[256];
583 float numstr_size[2];
584 float posit[2];
585
586 const UnitSettings &unit = kcd->scene->unit;
587 if (unit.system == USER_UNIT_NONE) {
588 SNPRINTF(numstr, "%.*f" BLI_STR_UTF8_DEGREE_SIGN, angle_precision, RAD2DEGF(angle));
589 }
590 else {
592 numstr, sizeof(numstr), double(angle), angle_precision, B_UNIT_ROTATION, unit, false);
593 }
594
596 BLF_size(blf_mono_font, font_size * UI_SCALE_FAC);
598 BLF_width_and_height(blf_mono_font, numstr, sizeof(numstr), &numstr_size[0], &numstr_size[1]);
599
600 posit[0] = mid_ss[0] + (cap_size * 2.0f);
601 posit[1] = mid_ss[1] - (numstr_size[1] / 2.0f);
602
603 /* Draw text background. */
604 float color_back[4] = {0.0f, 0.0f, 0.0f, 0.5f}; /* TODO: Replace with theme color. */
605 immUniformColor4fv(color_back);
606
608 immRectf(pos_2d,
609 posit[0] - bg_margin,
610 posit[1] - bg_margin,
611 posit[0] + bg_margin + numstr_size[0],
612 posit[1] + bg_margin + numstr_size[1]);
615
616 /* Draw text. */
617 uchar color_text[3];
618 UI_GetThemeColor3ubv(TH_TEXT, color_text);
619
620 BLF_color3ubv(blf_mono_font, color_text);
621 BLF_position(blf_mono_font, posit[0], posit[1], 0.0f);
623 BLF_draw(blf_mono_font, numstr, sizeof(numstr));
625
628
630}
631
633{
634 KnifeVert *kfv;
635 KnifeVert *tempkfv;
636 KnifeEdge *kfe;
637 KnifeEdge *tempkfe;
638
639 if (kcd->curr.vert) {
640 kfv = kcd->curr.vert;
641
642 float min_angle = FLT_MAX;
643 float angle = 0.0f;
644 float *end;
645
646 kfe = static_cast<KnifeEdge *>(((LinkData *)kfv->edges.first)->data);
647 LISTBASE_FOREACH (LinkData *, ref, &kfv->edges) {
648 tempkfe = static_cast<KnifeEdge *>(ref->data);
649 if (tempkfe->v1 != kfv) {
650 tempkfv = tempkfe->v1;
651 }
652 else {
653 tempkfv = tempkfe->v2;
654 }
655 angle = angle_v3v3v3(kcd->prev.cage, kcd->curr.cage, tempkfv->cageco);
656 if (angle < min_angle) {
657 min_angle = angle;
658 kfe = tempkfe;
659 end = tempkfv->cageco;
660 }
661 }
662
663 if (min_angle > KNIFE_FLT_EPSBIG) {
664 /* Last vertex in screen space. */
665 float end_ss[2];
667
669 kcd->prev.cage,
670 kcd->curr.cage,
671 end,
672 kcd->prev.mval,
673 kcd->curr.mval,
674 end_ss,
675 min_angle);
676 }
677 }
678 else if (kcd->curr.edge) {
679 kfe = kcd->curr.edge;
680
681 /* Check for most recent cut (if cage is part of previous cut). */
682 if (!compare_v3v3(kfe->v1->cageco, kcd->prev.cage, KNIFE_FLT_EPSBIG) &&
684 {
685 /* Determine acute angle. */
686 float angle1 = angle_v3v3v3(kcd->prev.cage, kcd->curr.cage, kfe->v1->cageco);
687 float angle2 = angle_v3v3v3(kcd->prev.cage, kcd->curr.cage, kfe->v2->cageco);
688
689 float angle;
690 float *end;
691 if (angle1 < angle2) {
692 angle = angle1;
693 end = kfe->v1->cageco;
694 }
695 else {
696 angle = angle2;
697 end = kfe->v2->cageco;
698 }
699
700 /* Last vertex in screen space. */
701 float end_ss[2];
703
705 kcd, kcd->prev.cage, kcd->curr.cage, end, kcd->prev.mval, kcd->curr.mval, end_ss, angle);
706 }
707 }
708
709 if (kcd->prev.vert) {
710 kfv = kcd->prev.vert;
711 float min_angle = FLT_MAX;
712 float angle = 0.0f;
713 float *end;
714
715 /* If using relative angle snapping, always draw angle to reference edge. */
717 kfe = kcd->snap_ref_edge;
718 if (kfe->v1 != kfv) {
719 tempkfv = kfe->v1;
720 }
721 else {
722 tempkfv = kfe->v2;
723 }
724 min_angle = angle_v3v3v3(kcd->curr.cage, kcd->prev.cage, tempkfv->cageco);
725 end = tempkfv->cageco;
726 }
727 else {
728 /* Choose minimum angle edge. */
729 kfe = static_cast<KnifeEdge *>(((LinkData *)kfv->edges.first)->data);
730 LISTBASE_FOREACH (LinkData *, ref, &kfv->edges) {
731 tempkfe = static_cast<KnifeEdge *>(ref->data);
732 if (tempkfe->v1 != kfv) {
733 tempkfv = tempkfe->v1;
734 }
735 else {
736 tempkfv = tempkfe->v2;
737 }
738 angle = angle_v3v3v3(kcd->curr.cage, kcd->prev.cage, tempkfv->cageco);
739 if (angle < min_angle) {
740 min_angle = angle;
741 kfe = tempkfe;
742 end = tempkfv->cageco;
743 }
744 }
745 }
746
747 if (min_angle > KNIFE_FLT_EPSBIG) {
748 /* Last vertex in screen space. */
749 float end_ss[2];
751
753 kcd->curr.cage,
754 kcd->prev.cage,
755 end,
756 kcd->curr.mval,
757 kcd->prev.mval,
758 end_ss,
759 min_angle);
760 }
761 }
762 else if (kcd->prev.edge) {
763 /* Determine acute angle. */
764 kfe = kcd->prev.edge;
765 float angle1 = angle_v3v3v3(kcd->curr.cage, kcd->prev.cage, kfe->v1->cageco);
766 float angle2 = angle_v3v3v3(kcd->curr.cage, kcd->prev.cage, kfe->v2->cageco);
767
768 float angle;
769 float *end;
770 /* kcd->prev.edge can have one vertex part of cut and one part of mesh? */
771 /* This never seems to happen for kcd->curr.edge. */
772 if ((!kcd->prev.vert || kcd->prev.vert->v == kfe->v1->v) || kfe->v1->is_cut) {
773 angle = angle2;
774 end = kfe->v2->cageco;
775 }
776 else if ((!kcd->prev.vert || kcd->prev.vert->v == kfe->v2->v) || kfe->v2->is_cut) {
777 angle = angle1;
778 end = kfe->v1->cageco;
779 }
780 else {
781 if (angle1 < angle2) {
782 angle = angle1;
783 end = kfe->v1->cageco;
784 }
785 else {
786 angle = angle2;
787 end = kfe->v2->cageco;
788 }
789 }
790
791 /* Last vertex in screen space. */
792 float end_ss[2];
794
796 kcd, kcd->curr.cage, kcd->prev.cage, end, kcd->curr.mval, kcd->prev.mval, end_ss, angle);
797 }
798 else if (kcd->mdata.is_stored && !kcd->prev.is_space()) {
799 float angle = angle_v3v3v3(kcd->curr.cage, kcd->prev.cage, kcd->mdata.cage);
801 kcd->curr.cage,
802 kcd->prev.cage,
803 kcd->mdata.cage,
804 kcd->curr.mval,
805 kcd->prev.mval,
806 kcd->mdata.mval,
807 angle);
808 }
809}
810
812{
813 switch (kcd->dist_angle_mode) {
817 break;
818 }
821 break;
822 }
825 break;
826 }
827 }
828}
829
830/* Modal loop selection drawing callback. */
831static void knifetool_draw(const bContext * /*C*/, ARegion * /*region*/, void *arg)
832{
833 const KnifeTool_OpData *kcd = static_cast<const KnifeTool_OpData *>(arg);
835
837 GPU_polygon_offset(1.0f, 1.0f);
838
841
842 /* Draw points. */
845
846 /* Needed for AA points. */
848
849 if (kcd->prev.vert) {
851 immUniform1f("size", 11 * UI_SCALE_FAC);
852
854 immVertex3fv(pos, kcd->prev.cage);
855 immEnd();
856 }
857
858 if (kcd->prev.bmface || kcd->prev.edge) {
860 immUniform1f("size", 9 * UI_SCALE_FAC);
861
863 immVertex3fv(pos, kcd->prev.cage);
864 immEnd();
865 }
866
867 if (kcd->curr.vert) {
869 immUniform1f("size", 11 * UI_SCALE_FAC);
870
872 immVertex3fv(pos, kcd->curr.cage);
873 immEnd();
874 }
875 else if (kcd->curr.edge) {
876 /* Lines (handled below.) */
877 }
878
879 if (kcd->curr.bmface || kcd->curr.edge) {
881 immUniform1f("size", 9 * UI_SCALE_FAC);
882
884 immVertex3fv(pos, kcd->curr.cage);
885 immEnd();
886 }
887
888 if (kcd->depth_test) {
890 }
891
892 if (kcd->totkvert > 0) {
893 BLI_mempool_iter iter;
894 KnifeVert *kfv;
895
897 immUniform1f("size", 5.0 * UI_SCALE_FAC);
898
900
901 BLI_mempool_iternew(kcd->kverts, &iter);
902 for (kfv = static_cast<KnifeVert *>(BLI_mempool_iterstep(&iter)); kfv;
903 kfv = static_cast<KnifeVert *>(BLI_mempool_iterstep(&iter)))
904 {
905 if (!kfv->is_cut || kfv->is_invalid) {
906 continue;
907 }
908
909 immVertex3fv(pos, kfv->cageco);
910 }
911
912 immEnd();
913
916 }
917
919
921
922 /* Draw lines. */
924
925 if (kcd->mode == MODE_DRAGGING) {
927 GPU_line_width(2.0);
928
930 immVertex3fv(pos, kcd->prev.cage);
931 immVertex3fv(pos, kcd->curr.cage);
932 immEnd();
933 }
934
935 if (kcd->curr.vert) {
936 /* Points (handled above). */
937 }
938 else if (kcd->curr.edge) {
940 GPU_line_width(2.0);
941
945 immEnd();
946 }
947
948 if (kcd->totkedge > 0) {
949 BLI_mempool_iter iter;
950 KnifeEdge *kfe;
951
953 GPU_line_width(1.0);
954
956
957 BLI_mempool_iternew(kcd->kedges, &iter);
958 for (kfe = static_cast<KnifeEdge *>(BLI_mempool_iterstep(&iter)); kfe;
959 kfe = static_cast<KnifeEdge *>(BLI_mempool_iterstep(&iter)))
960 {
961 if (!kfe->is_cut || kfe->is_invalid) {
962 continue;
963 }
964
965 immVertex3fv(pos, kfe->v1->cageco);
966 immVertex3fv(pos, kfe->v2->cageco);
967 }
968
969 immEnd();
970
973 }
974
975 /* Draw relative angle snapping reference edge. */
978 GPU_line_width(2.0);
979
983 immEnd();
984 }
985
986 const int64_t total_hits = kcd->linehits.size();
987 if (total_hits > 0) {
989
991 GPU_vertbuf_data_alloc(*vert, total_hits);
992
993 int other_verts_count = 0;
994 int snapped_verts_count = 0;
995 for (const KnifeLineHit &hit : kcd->linehits) {
996 if (hit.v) {
997 GPU_vertbuf_attr_set(vert, pos, snapped_verts_count++, hit.cagehit);
998 }
999 else {
1000 GPU_vertbuf_attr_set(vert, pos, total_hits - 1 - other_verts_count++, hit.cagehit);
1001 }
1002 }
1003
1004 gpu::Batch *batch = GPU_batch_create_ex(GPU_PRIM_POINTS, vert, nullptr, GPU_BATCH_OWNS_VBO);
1006
1007 /* Draw any snapped verts first. */
1008 float fcol[4];
1010 GPU_batch_uniform_4fv(batch, "color", fcol);
1012
1013 if (snapped_verts_count > 0) {
1014 GPU_batch_draw_range(batch, 0, snapped_verts_count);
1015 }
1016
1017 /* Now draw the rest. */
1019 GPU_batch_uniform_4fv(batch, "color", fcol);
1021
1022 if (other_verts_count > 0) {
1023 GPU_batch_draw_range(batch, snapped_verts_count, other_verts_count);
1024 }
1025
1027
1029 }
1030
1032
1034
1035 if (kcd->mode == MODE_DRAGGING) {
1036 if (kcd->is_angle_snapping) {
1038 }
1039 else if (kcd->axis_constrained) {
1041 }
1042
1043 if (kcd->show_dist_angle) {
1045 }
1046 }
1047
1049
1050 /* Reset default. */
1052}
1053
1055
1056/* -------------------------------------------------------------------- */
1059
1061{
1062 auto get_modal_key_str = [&](int id) {
1063 return WM_modalkeymap_operator_items_to_string(op->type, id, true).value_or("");
1064 };
1065
1066 WorkspaceStatus status(C);
1067 status.opmodal(IFACE_("Cut"), op->type, KNF_MODAL_ADD_CUT);
1068 status.opmodal(IFACE_("Close"), op->type, KNF_MODAL_ADD_CUT_CLOSED);
1069 status.opmodal(IFACE_("Stop"), op->type, KNF_MODAL_NEW_CUT);
1070 status.opmodal(IFACE_("Confirm"), op->type, KNF_MODAL_CONFIRM);
1071 status.opmodal(IFACE_("Cancel"), op->type, KNF_MODAL_CANCEL);
1072 status.opmodal(IFACE_("Undo"), op->type, KNF_MODAL_UNDO);
1073 status.opmodal(IFACE_("Pan View"), op->type, KNF_MODAL_PANNING);
1074 status.opmodal(IFACE_("Midpoint Snap"), op->type, KNF_MODAL_MIDPOINT_ON, kcd->snap_midpoints);
1075 status.opmodal(
1076 IFACE_("Ignore Snap"), op->type, KNF_MODAL_IGNORE_SNAP_ON, kcd->ignore_edge_snapping);
1077 status.opmodal(IFACE_("Cut Through"), op->type, KNF_MODAL_CUT_THROUGH_TOGGLE, kcd->cut_through);
1078 status.opmodal({}, op->type, KNF_MODAL_X_AXIS, kcd->constrain_axis == 1);
1079 status.opmodal({}, op->type, KNF_MODAL_Y_AXIS, kcd->constrain_axis == 2);
1080 status.opmodal({}, op->type, KNF_MODAL_Z_AXIS, kcd->constrain_axis == 3);
1081 status.item(IFACE_("Axis"), ICON_NONE);
1082 status.opmodal(
1084 status.opmodal(IFACE_("X-Ray"), op->type, KNF_MODAL_DEPTH_TEST_TOGGLE, !kcd->depth_test);
1085
1086 const std::string angle = fmt::format(
1087 "{}: {:.2f}({:.2f}) ({}{}{}{})",
1088 IFACE_("Angle Constraint"),
1089 (kcd->angle >= 0.0f) ? RAD2DEGF(kcd->angle) : 360.0f + RAD2DEGF(kcd->angle),
1094 kcd->angle_snapping ?
1095 ((kcd->angle_snapping_mode == KNF_CONSTRAIN_ANGLE_MODE_SCREEN) ? "Screen" : "Relative") :
1096 "OFF", /* TODO: Can this be simplified? */
1099 get_modal_key_str(KNF_MODAL_CYCLE_ANGLE_SNAP_EDGE) :
1100 "",
1101 (kcd->angle_snapping_mode == KNF_CONSTRAIN_ANGLE_MODE_RELATIVE) ? ": Cycle Edge" : "");
1102
1104}
1105
1107
1108/* -------------------------------------------------------------------- */
1111
1112static const int *knife_bm_tri_index_get(const KnifeTool_OpData *kcd,
1113 int ob_index,
1114 int tri_index,
1115 int tri_index_buf[3])
1116{
1117 const KnifeObjectInfo *obinfo = &kcd->objects_info[ob_index];
1118 if (!obinfo->tri_indices.is_empty()) {
1119 return obinfo->tri_indices[tri_index];
1120 }
1121 const std::array<BMLoop *, 3> &ltri = obinfo->em->looptris[tri_index];
1122 for (int i = 0; i < 3; i++) {
1123 tri_index_buf[i] = BM_elem_index_get(ltri[i]->v);
1124 }
1125 return tri_index_buf;
1126}
1127
1129 int ob_index,
1130 int tri_index,
1131 float cos[3][3])
1132{
1133 const KnifeObjectInfo *obinfo = &kcd->objects_info[ob_index];
1134 int tri_ind_buf[3];
1135 const int *tri_ind = knife_bm_tri_index_get(kcd, ob_index, tri_index, tri_ind_buf);
1136 for (int i = 0; i < 3; i++) {
1137 copy_v3_v3(cos[i], obinfo->positions_cage[tri_ind[i]]);
1138 }
1139}
1140
1142 int ob_index,
1143 int tri_index,
1144 float cos[3][3])
1145{
1146 knife_bm_tri_cagecos_get(kcd, ob_index, tri_index, cos);
1147 const Object *ob = kcd->objects[ob_index];
1148 for (int i = 0; i < 3; i++) {
1149 mul_m4_v3(ob->object_to_world().ptr(), cos[i]);
1150 }
1151}
1152
1154
1155/* -------------------------------------------------------------------- */
1158
1160{
1161 return (BM_elem_flag_test(f, BM_ELEM_SELECT) != 0);
1162}
1163
1165{
1166 return (BM_elem_flag_test(f, BM_ELEM_HIDDEN) == 0);
1167}
1168
1170{
1171 Object *ob;
1172 BMEditMesh *em;
1173
1174 /* Test Function. */
1175 bool (*test_fn)(BMFace *);
1176 if (kcd->only_select && kcd->cut_through) {
1177 test_fn = knife_bm_face_is_select;
1178 }
1179 else {
1181 }
1182
1183 /* Construct BVH Tree. */
1184 const float epsilon = FLT_EPSILON * 2.0f;
1185 int tottri = 0;
1186 int ob_tottri = 0;
1188 BMFace *f_test = nullptr, *f_test_prev = nullptr;
1189 bool test_fn_ret = false;
1190
1191 /* Calculate tottri. */
1192 for (Object *ob : kcd->objects) {
1193 ob_tottri = 0;
1194 em = BKE_editmesh_from_object(ob);
1195
1196 for (int i = 0; i < em->looptris.size(); i++) {
1197 f_test = em->looptris[i][0]->f;
1198 if (f_test != f_test_prev) {
1199 test_fn_ret = test_fn(f_test);
1200 f_test_prev = f_test;
1201 }
1202
1203 if (test_fn_ret) {
1204 ob_tottri++;
1205 }
1206 }
1207
1208 tottri += ob_tottri;
1209 }
1210
1211 kcd->bvh.tree = BLI_bvhtree_new(tottri, epsilon, 8, 8);
1212
1213 f_test_prev = nullptr;
1214 test_fn_ret = false;
1215
1216 /* Add triangles for each object.
1217 * TODO:
1218 * test_fn can leave large gaps between bvh tree indices.
1219 * Compacting bvh tree indices may be possible.
1220 * Don't forget to update #knife_bvh_intersect_plane! */
1221 tottri = 0;
1222 for (const int ob_index : kcd->objects.index_range()) {
1223 ob = kcd->objects[ob_index];
1224 em = BKE_editmesh_from_object(ob);
1225 looptris = em->looptris;
1226
1227 for (int i = 0; i < em->looptris.size(); i++) {
1228
1229 f_test = looptris[i][0]->f;
1230 if (f_test != f_test_prev) {
1231 test_fn_ret = test_fn(f_test);
1232 f_test_prev = f_test;
1233 }
1234
1235 if (!test_fn_ret) {
1236 continue;
1237 }
1238
1239 float tri_cos[3][3];
1240 knife_bm_tri_cagecos_get_worldspace(kcd, ob_index, i, tri_cos);
1241 BLI_bvhtree_insert(kcd->bvh.tree, i + tottri, &tri_cos[0][0], 3);
1242 }
1243
1244 tottri += em->looptris.size();
1245 }
1246
1248}
1249
1250/* Wrapper for #BLI_bvhtree_free. */
1252{
1253 if (kcd->bvh.tree) {
1255 kcd->bvh.tree = nullptr;
1256 }
1257}
1258
1259static void knife_bvh_raycast_cb(void *userdata,
1260 int index,
1261 const BVHTreeRay *ray,
1262 BVHTreeRayHit *hit)
1263{
1264 if (index == -1) {
1265 return;
1266 }
1267
1268 KnifeTool_OpData *kcd = static_cast<KnifeTool_OpData *>(userdata);
1269 BMLoop *const *ltri = nullptr;
1270 Object *ob;
1271 BMEditMesh *em;
1272
1273 float dist;
1274 bool isect;
1275 int tottri;
1276
1277 tottri = 0;
1278 int ob_index = 0;
1279 for (; ob_index < kcd->objects.size(); ob_index++) {
1280 index -= tottri;
1281 ob = kcd->objects[ob_index];
1282 em = BKE_editmesh_from_object(ob);
1283 tottri = em->looptris.size();
1284 if (index < tottri) {
1285 ltri = em->looptris[index].data();
1286 break;
1287 }
1288 }
1289 BLI_assert(ltri != nullptr);
1290
1291 if (kcd->bvh.filter_cb) {
1292 if (!kcd->bvh.filter_cb(ltri[0]->f, kcd->bvh.filter_data)) {
1293 return;
1294 }
1295 }
1296
1297 float tri_cos[3][3];
1298 knife_bm_tri_cagecos_get_worldspace(kcd, ob_index, index, tri_cos);
1299 isect = (ray->radius > 0.0f ?
1301 ray->origin, ray->direction, UNPACK3(tri_cos), &dist, nullptr, ray->radius) :
1302#ifdef USE_KDOPBVH_WATERTIGHT
1304 ray->origin, ray->isect_precalc, UNPACK3(tri_cos), &dist, nullptr));
1305#else
1306isect_ray_tri_v3(ray->origin, ray->direction, UNPACK3(tri_cos), &dist, nullptr);
1307#endif
1308
1309 if (isect && dist < hit->dist) {
1310 madd_v3_v3v3fl(hit->co, ray->origin, ray->direction, dist);
1311
1312 /* Discard clipped points. */
1313 if (RV3D_CLIPPING_ENABLED(kcd->vc.v3d, kcd->vc.rv3d) &&
1314 ED_view3d_clipping_test(kcd->vc.rv3d, hit->co, false))
1315 {
1316 return;
1317 }
1318
1319 hit->dist = dist;
1320 hit->index = index;
1321
1322 copy_v3_v3(hit->no, ltri[0]->f->no);
1323
1324 kcd->bvh.looptris = em->looptris;
1325 kcd->bvh.ob_index = ob_index;
1326 }
1327}
1328
1329/* `co` is expected to be in world space. */
1331 const float co[3],
1332 const float dir[3],
1333 const float radius,
1334 float *r_dist,
1335 float r_cagehit[3],
1336 int *r_ob_index)
1337{
1338 BMFace *face;
1339 BVHTreeRayHit hit;
1340 const float dist = r_dist ? *r_dist : FLT_MAX;
1341 hit.dist = dist;
1342 hit.index = -1;
1343
1344 BLI_bvhtree_ray_cast(kcd->bvh.tree, co, dir, radius, &hit, knife_bvh_raycast_cb, kcd);
1345
1346 /* Handle Hit */
1347 if (hit.index != -1 && hit.dist != dist) {
1348 face = kcd->bvh.looptris[hit.index][0]->f;
1349
1350 if (r_cagehit) {
1351 copy_v3_v3(r_cagehit, hit.co);
1352 }
1353
1354 if (r_dist) {
1355 *r_dist = hit.dist;
1356 }
1357
1358 if (r_ob_index) {
1359 *r_ob_index = kcd->bvh.ob_index;
1360 }
1361
1362 return face;
1363 }
1364 return nullptr;
1365}
1366
1367/* `co` is expected to be in world space. */
1369 const float co[3],
1370 const float dir[3],
1371 const float radius,
1372 float *r_dist,
1373 float r_cagehit[3],
1374 int *r_ob_index,
1375 bool (*filter_cb)(BMFace *f, void *userdata),
1376 void *filter_userdata)
1377{
1378 kcd->bvh.filter_cb = filter_cb;
1379 kcd->bvh.filter_data = filter_userdata;
1380
1381 BMFace *face = knife_bvh_raycast(kcd, co, dir, radius, r_dist, r_cagehit, r_ob_index);
1382
1383 kcd->bvh.filter_cb = nullptr;
1384 kcd->bvh.filter_data = nullptr;
1385
1386 return face;
1387}
1388
1390
1391/* -------------------------------------------------------------------- */
1394
1395static void knife_project_v2(const KnifeTool_OpData *kcd, const float co[3], float sco[2])
1396{
1398}
1399
1400/* Ray is returned in world space. */
1402 const float mval[2],
1403 float r_origin[3],
1404 float r_end[3])
1405{
1406 /* Unproject to find view ray. */
1408 kcd->vc.depsgraph, kcd->region, kcd->vc.v3d, mval, r_origin, r_end, false);
1409}
1410
1411/* No longer used, but may be useful in the future. */
1413 float mval[3],
1414 float r_cage[3])
1415{
1416 float origin[3];
1417 float origin_ofs[3];
1418 float ray[3], ray_normal[3];
1419
1420 knife_input_ray_segment(kcd, mval, origin, origin_ofs);
1421
1422 sub_v3_v3v3(ray, origin_ofs, origin);
1423 normalize_v3_v3(ray_normal, ray);
1424
1425 knife_bvh_raycast(kcd, origin, ray_normal, 0.0f, nullptr, r_cage, nullptr);
1426}
1427
1429{
1430 bool v1_inside, v2_inside;
1431 bool v1_inface, v2_inface;
1432 BMLoop *l1, *l2;
1433
1434 if (!f || !v1 || !v2) {
1435 return false;
1436 }
1437
1438 l1 = v1->v ? BM_face_vert_share_loop(f, v1->v) : nullptr;
1439 l2 = v2->v ? BM_face_vert_share_loop(f, v2->v) : nullptr;
1440
1441 if ((l1 && l2) && BM_loop_is_adjacent(l1, l2)) {
1442 /* Boundary-case, always false to avoid edge-in-face checks below. */
1443 return false;
1444 }
1445
1446 /* Find out if v1 and v2, if set, are part of the face. */
1447 v1_inface = (l1 != nullptr);
1448 v2_inface = (l2 != nullptr);
1449
1450 /* BM_face_point_inside_test uses best-axis projection so this isn't most accurate test... */
1451 v1_inside = v1_inface ? false : BM_face_point_inside_test(f, v1->co);
1452 v2_inside = v2_inface ? false : BM_face_point_inside_test(f, v2->co);
1453 if ((v1_inface && v2_inside) || (v2_inface && v1_inside) || (v1_inside && v2_inside)) {
1454 return true;
1455 }
1456
1457 if (v1_inface && v2_inface) {
1458 float mid[3];
1459 /* Can have case where v1 and v2 are on shared chain between two faces.
1460 * BM_face_splits_check_legal does visibility and self-intersection tests,
1461 * but it is expensive and maybe a bit buggy, so use a simple
1462 * "is the midpoint in the face" test. */
1463 mid_v3_v3v3(mid, v1->co, v2->co);
1464 return BM_face_point_inside_test(f, mid);
1465 }
1466 return false;
1467}
1468
1470{
1472 kcd->vc.depsgraph, kcd->vc.v3d, kcd->vc.rv3d, true, &kcd->clipsta, &kcd->clipend);
1473}
1474
1476
1477/* -------------------------------------------------------------------- */
1482
1484{
1485 BMElem *ele_test;
1486 KnifeEdge *kfe = nullptr;
1487
1488 /* vert? */
1489 ele_test = (BMElem *)kfv->v;
1490
1491 if (r_kfe || ele_test == nullptr) {
1492 if (kfv->v == nullptr) {
1493 LISTBASE_FOREACH (LinkData *, ref, &kfv->edges) {
1494 kfe = static_cast<KnifeEdge *>(ref->data);
1495 if (kfe->e) {
1496 if (r_kfe) {
1497 *r_kfe = kfe;
1498 }
1499 break;
1500 }
1501 }
1502 }
1503 }
1504
1505 /* edge? */
1506 if (ele_test == nullptr) {
1507 if (kfe) {
1508 ele_test = (BMElem *)kfe->e;
1509 }
1510 }
1511
1512 /* face? */
1513 if (ele_test == nullptr) {
1514 if (BLI_listbase_is_single(&kfe->faces)) {
1515 ele_test = static_cast<BMElem *>(((LinkData *)kfe->faces.first)->data);
1516 }
1517 }
1518
1519 return ele_test;
1520}
1521
1523{
1524 BMElem *ele_test;
1525
1526 ele_test = (BMElem *)kfe->e;
1527
1528 if (ele_test == nullptr) {
1529 ele_test = (BMElem *)kfe->basef;
1530 }
1531
1532 return ele_test;
1533}
1534
1536
1537/* -------------------------------------------------------------------- */
1540
1542{
1543 ListBase *list;
1544
1545 list = static_cast<ListBase *>(BLI_memarena_alloc(kcd->arena, sizeof(ListBase)));
1546 BLI_listbase_clear(list);
1547 return list;
1548}
1549
1550static void knife_append_list(KnifeTool_OpData *kcd, ListBase *lst, void *elem)
1551{
1552 LinkData *ref;
1553
1554 ref = static_cast<LinkData *>(BLI_mempool_calloc(kcd->refs));
1555 ref->data = elem;
1556 BLI_addtail(lst, ref);
1557}
1558
1559static LinkData *find_ref(ListBase *lb, void *ref)
1560{
1561 LISTBASE_FOREACH (LinkData *, ref1, lb) {
1562 if (ref1->data == ref) {
1563 return ref1;
1564 }
1565 }
1566
1567 return nullptr;
1568}
1569
1570static void knife_append_list_no_dup(KnifeTool_OpData *kcd, ListBase *lst, void *elem)
1571{
1572 if (!find_ref(lst, elem)) {
1573 knife_append_list(kcd, lst, elem);
1574 }
1575}
1576
1578{
1579 knife_append_list(kcd, &kfe->v1->edges, kfe);
1580 knife_append_list(kcd, &kfe->v2->edges, kfe);
1581}
1582
1583/* Add faces of an edge to a KnifeVert's faces list. No checks for duplicates. */
1585{
1586 BMIter bmiter;
1587 BMFace *f;
1588
1589 BM_ITER_ELEM (f, &bmiter, e, BM_FACES_OF_EDGE) {
1590 knife_append_list(kcd, &kfv->faces, f);
1591 }
1592}
1593
1594/* Find a face in common in the two faces lists.
1595 * If more than one, return the first; if none, return nullptr. */
1597{
1598 LISTBASE_FOREACH (LinkData *, ref1, faces1) {
1599 LISTBASE_FOREACH (LinkData *, ref2, faces2) {
1600 if (ref1->data == ref2->data) {
1601 return (BMFace *)(ref1->data);
1602 }
1603 }
1604 }
1605 return nullptr;
1606}
1607
1609
1610/* -------------------------------------------------------------------- */
1613
1614static KnifeVert *new_knife_vert(KnifeTool_OpData *kcd, const float co[3], const float cageco[3])
1615{
1616 KnifeVert *kfv = static_cast<KnifeVert *>(BLI_mempool_calloc(kcd->kverts));
1617
1618 kcd->totkvert++;
1619
1620 copy_v3_v3(kfv->co, co);
1621 copy_v3_v3(kfv->cageco, cageco);
1622
1623 return kfv;
1624}
1625
1627{
1628 KnifeEdge *kfe = static_cast<KnifeEdge *>(BLI_mempool_calloc(kcd->kedges));
1629 kcd->totkedge++;
1630 return kfe;
1631}
1632
1633/* Get a KnifeVert wrapper for an existing BMVert. */
1635{
1636 KnifeVert *kfv = static_cast<KnifeVert *>(BLI_ghash_lookup(kcd->origvertmap, v));
1637 const float *cageco;
1638
1639 if (!kfv) {
1640 BMIter bmiter;
1641 BMFace *f;
1642
1643 if (BM_elem_index_get(v) >= 0) {
1644 cageco = kcd->objects_info[ob_index].positions_cage[BM_elem_index_get(v)];
1645 }
1646 else {
1647 cageco = v->co;
1648 }
1649
1650 float cageco_ws[3];
1651 Object *ob = kcd->objects[ob_index];
1652 mul_v3_m4v3(cageco_ws, ob->object_to_world().ptr(), cageco);
1653
1654 kfv = new_knife_vert(kcd, v->co, cageco_ws);
1655 kfv->v = v;
1656 kfv->ob_index = ob_index;
1657
1658 BLI_ghash_insert(kcd->origvertmap, v, kfv);
1659 BM_ITER_ELEM (f, &bmiter, v, BM_FACES_OF_VERT) {
1660 knife_append_list(kcd, &kfv->faces, f);
1661 }
1662 }
1663
1664 return kfv;
1665}
1666
1667/* Get a KnifeEdge wrapper for an existing BMEdge. */
1669{
1670 KnifeEdge *kfe = static_cast<KnifeEdge *>(BLI_ghash_lookup(kcd->origedgemap, e));
1671 if (!kfe) {
1672 BMIter bmiter;
1673 BMFace *f;
1674
1675 kfe = new_knife_edge(kcd);
1676 kfe->e = e;
1677 kfe->v1 = get_bm_knife_vert(kcd, e->v1, ob_index);
1678 kfe->v2 = get_bm_knife_vert(kcd, e->v2, ob_index);
1679
1680 knife_add_to_vert_edges(kcd, kfe);
1681
1682 BLI_ghash_insert(kcd->origedgemap, e, kfe);
1683
1684 BM_ITER_ELEM (f, &bmiter, e, BM_FACES_OF_EDGE) {
1685 knife_append_list(kcd, &kfe->faces, f);
1686 }
1687 }
1688
1689 return kfe;
1690}
1691
1693{
1694 ListBase *list = static_cast<ListBase *>(BLI_ghash_lookup(kcd->kedgefacemap, f));
1695
1696 if (!list) {
1697 BMIter bmiter;
1698 BMEdge *e;
1699
1700 list = knife_empty_list(kcd);
1701
1702 BM_ITER_ELEM (e, &bmiter, f, BM_EDGES_OF_FACE) {
1703 knife_append_list(kcd, list, get_bm_knife_edge(kcd, e, ob_index));
1704 }
1705
1706 BLI_ghash_insert(kcd->kedgefacemap, f, list);
1707 }
1708
1709 return list;
1710}
1711
1713{
1714 knife_append_list(kcd, knife_get_face_kedges(kcd, kfe->v1->ob_index, f), kfe);
1715 knife_append_list(kcd, &kfe->faces, f);
1716}
1717
1719 KnifeEdge *kfe,
1720 const float co[3],
1721 const float cageco[3],
1722 KnifeEdge **r_kfe)
1723{
1724 KnifeEdge *newkfe = new_knife_edge(kcd);
1725 LinkData *ref;
1726 BMFace *f;
1727
1728 newkfe->v1 = kfe->v1;
1729 newkfe->v2 = new_knife_vert(kcd, co, cageco);
1730 newkfe->v2->ob_index = kfe->v1->ob_index;
1731 newkfe->v2->is_cut = true;
1732 if (kfe->e) {
1733 knife_add_edge_faces_to_vert(kcd, newkfe->v2, kfe->e);
1734 }
1735 else {
1736 /* kfe cuts across an existing face.
1737 * If v1 and v2 are in multiple faces together (e.g., if they
1738 * are in doubled polys) then this arbitrarily chooses one of them. */
1739 f = knife_find_common_face(&kfe->v1->faces, &kfe->v2->faces);
1740 if (f) {
1741 knife_append_list(kcd, &newkfe->v2->faces, f);
1742 }
1743 }
1744 newkfe->basef = kfe->basef;
1745
1746 ref = find_ref(&kfe->v1->edges, kfe);
1747 BLI_remlink(&kfe->v1->edges, ref);
1748
1749 kfe->v1 = newkfe->v2;
1750 kfe->v1->is_splitting = true;
1751 BLI_addtail(&kfe->v1->edges, ref);
1752
1753 LISTBASE_FOREACH (LinkData *, ref, &kfe->faces) {
1754 knife_edge_append_face(kcd, newkfe, static_cast<BMFace *>(ref->data));
1755 }
1756
1757 knife_add_to_vert_edges(kcd, newkfe);
1758
1759 newkfe->is_cut = kfe->is_cut;
1760 newkfe->e = kfe->e;
1761
1762 newkfe->splits++;
1763 kfe->splits++;
1764
1765 kcd->undo->splits++;
1766
1767 BLI_stack_push(kcd->splitstack, (void *)&kfe);
1768 BLI_stack_push(kcd->splitstack, (void *)&newkfe);
1769
1770 *r_kfe = newkfe;
1771
1772 return newkfe->v2;
1773}
1774
1775/* Rejoin two edges split by #knife_split_edge. */
1776static void knife_join_edge(KnifeEdge *newkfe, KnifeEdge *kfe)
1777{
1778 newkfe->is_invalid = true;
1779 newkfe->v2->is_invalid = true;
1780
1781 kfe->v1 = newkfe->v1;
1782
1783 kfe->splits--;
1784 kfe->v1->is_splitting = false;
1785 kfe->v2->is_splitting = false;
1786}
1787
1789
1790/* -------------------------------------------------------------------- */
1793
1794static void knife_snap_curr(KnifeTool_OpData *kcd,
1795 const float2 &mval,
1796 const float3 &ray_orig,
1797 const float3 &ray_dir,
1798 const float3 *curr_cage_constrain,
1799 const float3 *fallback);
1800
1801/* User has just clicked for first time or first time after a restart (E key).
1802 * Copy the current position data into prev. */
1803static void knife_start_cut(KnifeTool_OpData *kcd, const float2 &mval)
1804{
1805 float3 ray_orig;
1806 float3 ray_dir;
1808 kcd->vc.depsgraph, kcd->region, kcd->vc.v3d, mval, ray_orig, ray_dir, false);
1809
1810 knife_snap_curr(kcd, mval, ray_orig, ray_dir, nullptr, nullptr);
1811 kcd->prev = kcd->curr;
1812 kcd->mdata.is_stored = false;
1813}
1814
1816{
1817 kpos->bmface = lh->f;
1818 kpos->vert = lh->v;
1819 kpos->edge = lh->kfe;
1820 copy_v3_v3(kpos->cage, lh->cagehit);
1821 copy_v2_v2(kpos->mval, lh->schit);
1822}
1823
1824/* Primary key: lambda along cut
1825 * Secondary key: lambda along depth
1826 * Tertiary key: pointer comparisons of verts if both snapped to verts
1827 */
1828static int linehit_compare(const KnifeLineHit &lh1, const KnifeLineHit &lh2)
1829{
1830 if (lh1.l < lh2.l) {
1831 return true;
1832 }
1833 if (lh1.l > lh2.l) {
1834 return false;
1835 }
1836 if (lh1.m < lh2.m) {
1837 return true;
1838 }
1839 if (lh1.m > lh2.m) {
1840 return false;
1841 }
1842 if (lh1.v < lh2.v) {
1843 return true;
1844 }
1845 if (lh1.v > lh2.v) {
1846 return false;
1847 }
1848 return false;
1849}
1850
1851/*
1852 * Sort linehits by distance along cut line, and secondarily from
1853 * front to back (from eye), and tertiarily by snap vertex,
1854 * and remove any duplicates.
1855 */
1857{
1858 bool is_double = false;
1859
1860 if (kcd->linehits.is_empty()) {
1861 return;
1862 }
1863
1864 std::sort(kcd->linehits.begin(), kcd->linehits.end(), linehit_compare);
1865
1866 /* Remove any edge hits that are preceded or followed
1867 * by a vertex hit that is very near. Mark such edge hits using
1868 * l == -1 and then do another pass to actually remove.
1869 * Also remove all but one of a series of vertex hits for the same vertex. */
1870 const int64_t total_hits = kcd->linehits.size();
1871 for (int i = 0; i < total_hits; i++) {
1872 KnifeLineHit *lhi = &kcd->linehits[i];
1873 if (lhi->v == nullptr) {
1874 continue;
1875 }
1876
1877 for (int j = i - 1; j >= 0; j--) {
1878 KnifeLineHit *lhj = &kcd->linehits[j];
1879 if (!lhj->kfe || fabsf(lhi->l - lhj->l) > KNIFE_FLT_EPSBIG ||
1880 fabsf(lhi->m - lhj->m) > KNIFE_FLT_EPSBIG)
1881 {
1882 break;
1883 }
1884
1885 if (lhi->kfe == lhj->kfe) {
1886 lhj->l = -1.0f;
1887 is_double = true;
1888 }
1889 }
1890 for (int j = i + 1; j < total_hits; j++) {
1891 KnifeLineHit *lhj = &kcd->linehits[j];
1892 if (fabsf(lhi->l - lhj->l) > KNIFE_FLT_EPSBIG || fabsf(lhi->m - lhj->m) > KNIFE_FLT_EPSBIG) {
1893 break;
1894 }
1895 if ((lhj->kfe && (lhi->kfe == lhj->kfe)) || (lhi->v == lhj->v)) {
1896 lhj->l = -1.0f;
1897 is_double = true;
1898 }
1899 }
1900 }
1901
1902 if (is_double) {
1903 /* Delete-in-place loop: copying from pos j to pos i+1. */
1904 int i = 0;
1905 int j = 1;
1906 while (j < total_hits) {
1907 KnifeLineHit *lhi = &kcd->linehits[i];
1908 KnifeLineHit *lhj = &kcd->linehits[j];
1909 if (lhj->l == -1.0f) {
1910 j++; /* Skip copying this one. */
1911 }
1912 else {
1913 /* Copy unless a no-op. */
1914 if (lhi->l == -1.0f) {
1915 /* Could happen if linehits[0] is being deleted. */
1916 memcpy(&kcd->linehits[i], &kcd->linehits[j], sizeof(KnifeLineHit));
1917 }
1918 else {
1919 if (i + 1 != j) {
1920 memcpy(&kcd->linehits[i + 1], &kcd->linehits[j], sizeof(KnifeLineHit));
1921 }
1922 i++;
1923 }
1924 j++;
1925 }
1926 }
1927 kcd->linehits.resize(i + 1);
1928 }
1929}
1930
1931/* Add hit to list of hits in facehits[f], where facehits is a map, if not already there. */
1933 GHash *facehits,
1934 BMFace *f,
1935 KnifeLineHit *hit)
1936{
1937 ListBase *list = static_cast<ListBase *>(BLI_ghash_lookup(facehits, f));
1938
1939 if (!list) {
1940 list = knife_empty_list(kcd);
1941 BLI_ghash_insert(facehits, f, list);
1942 }
1943 knife_append_list_no_dup(kcd, list, hit);
1944}
1945
1951 const KnifeLineHit *lh,
1952 const float co[3])
1953{
1954
1955 if (lh->v && lh->v->v) {
1956 BMLoop *l; /* side-of-loop */
1957 if ((l = BM_face_vert_share_loop(f, lh->v->v)) &&
1958 (BM_loop_point_side_of_loop_test(l, co) < 0.0f))
1959 {
1960 return true;
1961 }
1962 }
1963 else if (lh->kfe && lh->kfe->e) {
1964 BMLoop *l; /* side-of-edge */
1965 if ((l = BM_face_edge_share_loop(f, lh->kfe->e)) &&
1966 (BM_loop_point_side_of_edge_test(l, co) < 0.0f))
1967 {
1968 return true;
1969 }
1970 }
1971
1972 return false;
1973}
1974
1976 KnifeLineHit *lh1,
1977 KnifeLineHit *lh2,
1978 BMFace *f)
1979{
1980 KnifeEdge *kfe, *kfe2;
1981 BMEdge *e_base;
1982
1983 if ((lh1->v && lh1->v == lh2->v) || (lh1->kfe && lh1->kfe == lh2->kfe)) {
1984 return;
1985 }
1986
1987 /* If the cut is on an edge. */
1988 if ((lh1->v && lh2->v) && (lh1->v->v && lh2->v && lh2->v->v) &&
1989 (e_base = BM_edge_exists(lh1->v->v, lh2->v->v)))
1990 {
1991 return;
1992 }
1995 {
1996 return;
1997 }
1998
1999 /* Check if edge actually lies within face (might not, if this face is concave). */
2000 if ((lh1->v && !lh1->kfe) && (lh2->v && !lh2->kfe)) {
2001 if (!knife_verts_edge_in_face(lh1->v, lh2->v, f)) {
2002 return;
2003 }
2004 }
2005
2006 kfe = new_knife_edge(kcd);
2007 kfe->is_cut = true;
2008 kfe->basef = f;
2009
2010 if (lh1->v) {
2011 kfe->v1 = lh1->v;
2012 }
2013 else if (lh1->kfe) {
2014 kfe->v1 = knife_split_edge(kcd, lh1->kfe, lh1->hit, lh1->cagehit, &kfe2);
2015 lh1->v = kfe->v1; /* Record the #KnifeVert for this hit. */
2016 }
2017 else {
2018 BLI_assert(lh1->f);
2019 kfe->v1 = new_knife_vert(kcd, lh1->hit, lh1->cagehit);
2020 kfe->v1->ob_index = lh1->ob_index;
2021 kfe->v1->is_cut = true;
2022 knife_append_list(kcd, &kfe->v1->faces, lh1->f);
2023 lh1->v = kfe->v1; /* Record the #KnifeVert for this hit. */
2024 }
2025
2026 if (lh2->v) {
2027 kfe->v2 = lh2->v;
2028 }
2029 else if (lh2->kfe) {
2030 kfe->v2 = knife_split_edge(kcd, lh2->kfe, lh2->hit, lh2->cagehit, &kfe2);
2031 lh2->v = kfe->v2; /* Future uses of lh2 won't split again. */
2032 }
2033 else {
2034 BLI_assert(lh2->f);
2035 kfe->v2 = new_knife_vert(kcd, lh2->hit, lh2->cagehit);
2036 kfe->v2->ob_index = lh2->ob_index;
2037 kfe->v2->is_cut = true;
2038 knife_append_list(kcd, &kfe->v2->faces, lh2->f);
2039 lh2->v = kfe->v2; /* Record the KnifeVert for this hit. */
2040 }
2041
2042 knife_add_to_vert_edges(kcd, kfe);
2043
2044 if (kfe->basef && !find_ref(&kfe->faces, kfe->basef)) {
2045 knife_edge_append_face(kcd, kfe, kfe->basef);
2046 }
2047
2048 /* Update current undo frame cut count. */
2049 kcd->undo->cuts++;
2050}
2051
2052/* Given a list of KnifeLineHits for one face, sorted by l
2053 * and then by m, make the required KnifeVerts and
2054 * KnifeEdges.
2055 */
2057{
2058 LinkData *r;
2059
2060 if (BLI_listbase_count_at_most(hits, 2) != 2) {
2061 return;
2062 }
2063
2064 for (r = static_cast<LinkData *>(hits->first); r->next; r = r->next) {
2066 kcd, static_cast<KnifeLineHit *>(r->data), static_cast<KnifeLineHit *>(r->next->data), f);
2067 }
2068}
2069
2071{
2072 KnifeEdge *kfe;
2073 int edge_array_len = BLI_listbase_count(kfedges);
2074 int i;
2075
2076 BMEdge **edge_array = static_cast<BMEdge **>(BLI_array_alloca(edge_array, edge_array_len));
2077
2078 /* Point to knife edges we've created edges in, edge_array aligned. */
2079 KnifeEdge **kfe_array = static_cast<KnifeEdge **>(BLI_array_alloca(kfe_array, edge_array_len));
2080
2082
2083 i = 0;
2084 LISTBASE_FOREACH (LinkData *, ref, kfedges) {
2085 bool is_new_edge = false;
2086 kfe = static_cast<KnifeEdge *>(ref->data);
2087
2088 if (kfe->is_invalid) {
2089 continue;
2090 }
2091
2092 if (kfe->e == nullptr) {
2093 if (kfe->v1->v && kfe->v2->v) {
2094 kfe->e = BM_edge_exists(kfe->v1->v, kfe->v2->v);
2095 }
2096 }
2097
2098 if (kfe->e) {
2099 if (BM_edge_in_face(kfe->e, f)) {
2100 /* Shouldn't happen, but in this case just ignore. */
2101 continue;
2102 }
2103 }
2104 else {
2105 if (kfe->v1->v == nullptr) {
2106 kfe->v1->v = BM_vert_create(bm, kfe->v1->co, nullptr, eBMCreateFlag(0));
2107 }
2108 if (kfe->v2->v == nullptr) {
2109 kfe->v2->v = BM_vert_create(bm, kfe->v2->co, nullptr, eBMCreateFlag(0));
2110 }
2111 BLI_assert(kfe->e == nullptr);
2112 kfe->e = BM_edge_create(bm, kfe->v1->v, kfe->v2->v, nullptr, eBMCreateFlag(0));
2113 if (kfe->e) {
2115 BM_edge_select_set(bm, kfe->e, true);
2116 }
2117 is_new_edge = true;
2118 }
2119 }
2120
2121 BLI_assert(kfe->e);
2122
2123 if (BLI_gset_add(kcd->edgenet.edge_visit, kfe->e)) {
2124 kfe_array[i] = is_new_edge ? kfe : nullptr;
2125 edge_array[i] = kfe->e;
2126 i += 1;
2127 }
2128 }
2129
2130 if (i) {
2131 const int edge_array_len_orig = i;
2132 edge_array_len = i;
2133
2134#ifdef USE_NET_ISLAND_CONNECT
2135 uint edge_array_holes_len;
2136 BMEdge **edge_array_holes;
2138 f,
2139 edge_array,
2140 edge_array_len,
2141 true,
2142 kcd->edgenet.arena,
2143 &edge_array_holes,
2144 &edge_array_holes_len))
2145 {
2147 for (i = edge_array_len; i < edge_array_holes_len; i++) {
2148 BM_edge_select_set(bm, edge_array_holes[i], true);
2149 }
2150 }
2151
2152 edge_array_len = edge_array_holes_len;
2153 edge_array = edge_array_holes; /* Owned by the arena. */
2154 }
2155#endif
2156
2157 {
2158 BM_face_split_edgenet(bm, f, edge_array, edge_array_len, nullptr);
2159 }
2160
2161 /* Remove dangling edges, not essential - but nice for users. */
2162 for (i = 0; i < edge_array_len_orig; i++) {
2163 if (kfe_array[i] == nullptr) {
2164 continue;
2165 }
2166 if (BM_edge_is_wire(kfe_array[i]->e)) {
2167 BM_edge_kill(bm, kfe_array[i]->e);
2168 kfe_array[i]->e = nullptr;
2169 }
2170 }
2171
2172#ifdef USE_NET_ISLAND_CONNECT
2174#endif
2175 }
2176
2177 BLI_gset_clear(kcd->edgenet.edge_visit, nullptr);
2178}
2179
2180static int sort_verts_by_dist_cb(void *co_p, const void *cur_a_p, const void *cur_b_p)
2181{
2182 const KnifeVert *cur_a = static_cast<const KnifeVert *>(((const LinkData *)cur_a_p)->data);
2183 const KnifeVert *cur_b = static_cast<const KnifeVert *>(((const LinkData *)cur_b_p)->data);
2184 const float *co = static_cast<const float *>(co_p);
2185 const float a_sq = len_squared_v3v3(co, cur_a->co);
2186 const float b_sq = len_squared_v3v3(co, cur_b->co);
2187
2188 if (a_sq < b_sq) {
2189 return -1;
2190 }
2191 if (a_sq > b_sq) {
2192 return 1;
2193 }
2194 return 0;
2195}
2196
2197/* Use the network of KnifeEdges and KnifeVerts accumulated to make real BMVerts and BMEdedges. */
2198static void knife_make_cuts(KnifeTool_OpData *kcd, int ob_index)
2199{
2200 Object *ob = kcd->objects[ob_index];
2202 BMesh *bm = em->bm;
2203 KnifeEdge *kfe;
2204 KnifeVert *kfv;
2205 BMEdge *enew;
2206 ListBase *list;
2207 float pct;
2208 BLI_mempool_iter iter;
2211
2212 /* Put list of cutting edges for a face into fhash, keyed by face. */
2213 BLI_mempool_iternew(kcd->kedges, &iter);
2214 for (kfe = static_cast<KnifeEdge *>(BLI_mempool_iterstep(&iter)); kfe;
2215 kfe = static_cast<KnifeEdge *>(BLI_mempool_iterstep(&iter)))
2216 {
2217 if (kfe->is_invalid || kfe->v1->ob_index != ob_index) {
2218 continue;
2219 }
2220
2221 /* Select edges that lie directly on the cut. */
2222 if (kcd->select_result) {
2223 if (kfe->e && kfe->is_cut) {
2224 BM_edge_select_set(bm, kfe->e, true);
2225 }
2226 }
2227
2228 BMFace *f = kfe->basef;
2229 if (!f || kfe->e) {
2230 continue;
2231 }
2232 list = fhash.lookup_default(f, nullptr);
2233 if (!list) {
2234 list = knife_empty_list(kcd);
2235 fhash.add(f, list);
2236 }
2237 knife_append_list(kcd, list, kfe);
2238 }
2239
2240 /* Put list of splitting vertices for an edge into ehash, keyed by edge. */
2241 BLI_mempool_iternew(kcd->kverts, &iter);
2242 for (kfv = static_cast<KnifeVert *>(BLI_mempool_iterstep(&iter)); kfv;
2243 kfv = static_cast<KnifeVert *>(BLI_mempool_iterstep(&iter)))
2244 {
2245 if (kfv->v || kfv->is_invalid || kfv->ob_index != ob_index) {
2246 continue; /* Already have a BMVert. */
2247 }
2248 LISTBASE_FOREACH (LinkData *, ref, &kfv->edges) {
2249 kfe = static_cast<KnifeEdge *>(ref->data);
2250 BMEdge *e = kfe->e;
2251 if (!e) {
2252 continue;
2253 }
2254 list = ehash.lookup_default(e, nullptr);
2255 if (!list) {
2256 list = knife_empty_list(kcd);
2257 ehash.add(e, list);
2258 }
2259 /* There can be more than one kfe in kfv's list with same e. */
2260 if (!find_ref(list, kfv)) {
2261 knife_append_list(kcd, list, kfv);
2262 }
2263 }
2264 }
2265
2266 /* Split bmesh edges where needed. */
2267 for (auto [e, list] : ehash.items()) {
2269
2270 LISTBASE_FOREACH (LinkData *, ref, list) {
2271 kfv = static_cast<KnifeVert *>(ref->data);
2272 pct = line_point_factor_v3(kfv->co, e->v1->co, e->v2->co);
2273 kfv->v = BM_edge_split(bm, e, e->v1, &enew, pct);
2274 }
2275 }
2276
2277 if (kcd->only_select) {
2279 }
2280
2281 /* Do cuts for each face. */
2282 for (auto [f, list] : fhash.items()) {
2283 knife_make_face_cuts(kcd, bm, f, list);
2284 }
2285}
2286
2287/* User has just left-clicked after the first time.
2288 * Add all knife cuts implied by line from prev to curr.
2289 * If that line crossed edges then kcd->linehits will be non-null.
2290 * Make all of the KnifeVerts and KnifeEdges implied by this cut.
2291 */
2293{
2294 GHash *facehits;
2295 BMFace *f;
2296 GHashIterator giter;
2297 ListBase *list;
2298
2299 /* Allocate new undo frame on stack, unless cut is being dragged. */
2300 if (!kcd->is_drag_undo) {
2301 kcd->undo = static_cast<KnifeUndoFrame *>(BLI_stack_push_r(kcd->undostack));
2302 kcd->undo->pos = kcd->prev;
2303 kcd->undo->cuts = 0;
2304 kcd->undo->splits = 0;
2305 kcd->undo->mdata = kcd->mdata;
2306 kcd->is_drag_undo = true;
2307 }
2308
2309 /* Save values for angle drawing calculations. */
2310 copy_v3_v3(kcd->mdata.cage, kcd->prev.cage);
2311 copy_v2_v2(kcd->mdata.mval, kcd->prev.mval);
2312 kcd->mdata.is_stored = true;
2313
2315 if (kcd->linehits.is_empty()) {
2316 if (kcd->is_drag_hold == false) {
2317 kcd->prev = kcd->curr;
2318 }
2319 return;
2320 }
2321
2322 /* Consider most recent linehit in angle drawing calculations. */
2323 if (kcd->linehits.size() >= 2) {
2324 copy_v3_v3(kcd->mdata.cage, kcd->linehits[kcd->linehits.size() - 2].cagehit);
2325 }
2326
2327 /* Make facehits: map face -> list of linehits touching it. */
2328 facehits = BLI_ghash_ptr_new("knife facehits");
2329 for (KnifeLineHit &hit : kcd->linehits) {
2330 KnifeLineHit *lh = &hit;
2331 if (lh->f) {
2332 add_hit_to_facehits(kcd, facehits, lh->f, lh);
2333 }
2334 if (lh->v) {
2335 LISTBASE_FOREACH (LinkData *, r, &lh->v->faces) {
2336 add_hit_to_facehits(kcd, facehits, static_cast<BMFace *>(r->data), lh);
2337 }
2338 }
2339 if (lh->kfe) {
2340 LISTBASE_FOREACH (LinkData *, r, &lh->kfe->faces) {
2341 add_hit_to_facehits(kcd, facehits, static_cast<BMFace *>(r->data), lh);
2342 }
2343 }
2344 }
2345
2346 /* NOTE: as following loop progresses, the 'v' fields of
2347 * the linehits will be filled in (as edges are split or
2348 * in-face verts are made), so it may be true that both
2349 * the v and the kfe or f fields will be non-null. */
2350 GHASH_ITER (giter, facehits) {
2351 f = (BMFace *)BLI_ghashIterator_getKey(&giter);
2352 list = (ListBase *)BLI_ghashIterator_getValue(&giter);
2353 knife_cut_face(kcd, f, list);
2354 }
2355
2356 /* Set up for next cut. */
2357 kcd->prev = kcd->curr;
2358
2359 if (kcd->prev.bmface) {
2360 /* Was "in face" but now we have a KnifeVert it is snapped to. */
2361 KnifeLineHit *lh = &kcd->linehits.last();
2362 kcd->prev.vert = lh->v;
2363 kcd->prev.bmface = nullptr;
2364 }
2365
2366 if (kcd->is_drag_hold) {
2367 KnifeLineHit *lh = &kcd->linehits.last();
2368 linehit_to_knifepos(&kcd->prev, lh);
2369 }
2370
2371 BLI_ghash_free(facehits, nullptr, nullptr);
2373}
2374
2376{
2378}
2379
2381
2382/* -------------------------------------------------------------------- */
2385
2386/* Record the index in kcd->em->looptris of first looptri triple for a given face,
2387 * given an index for some triple in that array.
2388 * This assumes that all of the triangles for a given face are contiguous
2389 * in that array (as they are by the current tessellation routines).
2390 * Actually store index + 1 in the hash, because 0 looks like "no entry"
2391 * to hash lookup routine; will reverse this in the get routine.
2392 * Doing this lazily rather than all at once for all faces.
2393 */
2394static void set_lowest_face_tri(KnifeTool_OpData *kcd, BMEditMesh *em, BMFace *f, int index)
2395{
2396 int i;
2397
2398 if (BLI_ghash_lookup(kcd->facetrimap, f)) {
2399 return;
2400 }
2401
2402 BLI_assert(index >= 0 && index < em->looptris.size());
2403 BLI_assert(em->looptris[index][0]->f == f);
2404 for (i = index - 1; i >= 0; i--) {
2405 if (em->looptris[i][0]->f != f) {
2406 i++;
2407 break;
2408 }
2409 }
2410 if (i == -1) {
2411 i++;
2412 }
2413
2415}
2416
2417/* This should only be called for faces that have had a lowest face tri set by previous function.
2418 */
2420{
2421 int ans;
2422
2424 BLI_assert(ans != 0);
2425 return ans - 1;
2426}
2427
2437 const float s[2],
2438 const float v1[3],
2439 const float v2[3],
2440 int ob_index,
2441 BMFace *f,
2442 const float face_tol_sq,
2443 float hit_co[3],
2444 float hit_cageco[3])
2445{
2446 Object *ob = kcd->objects[ob_index];
2448
2449 int tottri, tri_i;
2450 float raydir[3];
2451 float tri_norm[3], tri_plane[4];
2452 float se1[2], se2[2];
2453 float d, lambda;
2454 ListBase *list;
2455 KnifeEdge *kfe;
2456
2457 sub_v3_v3v3(raydir, v2, v1);
2458 normalize_v3(raydir);
2459 tri_i = get_lowest_face_tri(kcd, f);
2460 tottri = em->looptris.size();
2461 BLI_assert(tri_i >= 0 && tri_i < tottri);
2462
2463 for (; tri_i < tottri; tri_i++) {
2464 float tri_cos[3][3];
2465 float ray_tri_uv[2];
2466
2467 const std::array<BMLoop *, 3> &ltri = em->looptris[tri_i];
2468 if (ltri[0]->f != f) {
2469 break;
2470 }
2471
2472 knife_bm_tri_cagecos_get_worldspace(kcd, ob_index, tri_i, tri_cos);
2473
2474 /* Using epsilon test in case ray is directly through an internal
2475 * tessellation edge and might not hit either tessellation tri with
2476 * an exact test;
2477 * We will exclude hits near real edges by a later test. */
2478 if (isect_ray_tri_epsilon_v3(v1, raydir, UNPACK3(tri_cos), &lambda, ray_tri_uv, KNIFE_FLT_EPS))
2479 {
2480 /* Check if line coplanar with tri. */
2481 normal_tri_v3(tri_norm, UNPACK3(tri_cos));
2482 plane_from_point_normal_v3(tri_plane, tri_cos[0], tri_norm);
2483 if ((dist_squared_to_plane_v3(v1, tri_plane) < KNIFE_FLT_EPS) &&
2485 {
2486 return false;
2487 }
2488 interp_v3_v3v3v3_uv(hit_cageco, UNPACK3(tri_cos), ray_tri_uv);
2489 /* Now check that far enough away from verts and edges. */
2490 list = knife_get_face_kedges(kcd, ob_index, f);
2491 LISTBASE_FOREACH (LinkData *, ref, list) {
2492 kfe = static_cast<KnifeEdge *>(ref->data);
2493 if (kfe->is_invalid) {
2494 continue;
2495 }
2496 knife_project_v2(kcd, kfe->v1->cageco, se1);
2497 knife_project_v2(kcd, kfe->v2->cageco, se2);
2498 d = dist_squared_to_line_segment_v2(s, se1, se2);
2499 if (d < face_tol_sq) {
2500 return false;
2501 }
2502 }
2503 interp_v3_v3v3v3_uv(hit_co, ltri[0]->v->co, ltri[1]->v->co, ltri[2]->v->co, ray_tri_uv);
2504 return true;
2505 }
2506 }
2507 return false;
2508}
2509
2515{
2516 Object *ob;
2517 BMEditMesh *em;
2518 BMIter iter;
2519 BMVert *v;
2520 float min[3], max[3];
2521 float ws[3];
2523
2524 for (int ob_index = 0; ob_index < kcd->objects.size(); ob_index++) {
2525 ob = kcd->objects[ob_index];
2526 em = BKE_editmesh_from_object(ob);
2527
2528 const Span<float3> positions_cage = kcd->objects_info[ob_index].positions_cage;
2529 if (!positions_cage.is_empty()) {
2530 for (int i = 0; i < em->bm->totvert; i++) {
2531 copy_v3_v3(ws, positions_cage[i]);
2532 mul_m4_v3(ob->object_to_world().ptr(), ws);
2533 minmax_v3v3_v3(min, max, ws);
2534 }
2535 }
2536 else {
2537 BM_ITER_MESH (v, &iter, em->bm, BM_VERTS_OF_MESH) {
2538 copy_v3_v3(ws, v->co);
2539 mul_m4_v3(ob->object_to_world().ptr(), ws);
2540 minmax_v3v3_v3(min, max, ws);
2541 }
2542 }
2543 }
2544
2545 kcd->ortho_extent = len_v3v3(min, max) / 2;
2547}
2548
2549/* Do edges e1 and e2 go between exactly the same coordinates? */
2550static bool coinciding_edges(BMEdge *e1, BMEdge *e2)
2551{
2552 const float *co11, *co12, *co21, *co22;
2553
2554 co11 = e1->v1->co;
2555 co12 = e1->v2->co;
2556 co21 = e2->v1->co;
2557 co22 = e2->v2->co;
2558 if ((equals_v3v3(co11, co21) && equals_v3v3(co12, co22)) ||
2559 (equals_v3v3(co11, co22) && equals_v3v3(co12, co21)))
2560 {
2561 return true;
2562 }
2563 return false;
2564}
2565
2566/* Callback used in point_is_visible to exclude hits on the faces that are the same
2567 * as or contain the hitting element (which is in user_data).
2568 * Also (see #44492) want to exclude hits on faces that butt up to the hitting element
2569 * (e.g., when you double an edge by an edge split).
2570 */
2571static bool bm_ray_cast_cb_elem_not_in_face_check(BMFace *f, void *user_data)
2572{
2573 bool ans;
2574 BMEdge *e, *e2;
2575 BMIter iter;
2576
2577 switch (((BMElem *)user_data)->head.htype) {
2578 case BM_FACE:
2579 ans = (BMFace *)user_data != f;
2580 break;
2581 case BM_EDGE:
2582 e = (BMEdge *)user_data;
2583 ans = !BM_edge_in_face(e, f);
2584 if (ans) {
2585 /* Is it a boundary edge, coincident with a split edge? */
2586 if (BM_edge_is_boundary(e)) {
2587 BM_ITER_ELEM (e2, &iter, f, BM_EDGES_OF_FACE) {
2588 if (coinciding_edges(e, e2)) {
2589 ans = false;
2590 break;
2591 }
2592 }
2593 }
2594 }
2595 break;
2596 case BM_VERT:
2597 ans = !BM_vert_in_face((BMVert *)user_data, f);
2598 break;
2599 default:
2600 ans = true;
2601 break;
2602 }
2603 return ans;
2604}
2605
2614 const float p[3],
2615 const float s[2],
2616 BMElem *ele_test)
2617{
2618 BMFace *f_hit;
2619
2620 /* If box clipping on, make sure p is not clipped. */
2621 if (RV3D_CLIPPING_ENABLED(kcd->vc.v3d, kcd->vc.rv3d) &&
2622 ED_view3d_clipping_test(kcd->vc.rv3d, p, false))
2623 {
2624 return false;
2625 }
2626
2627 /* If not cutting through, make sure no face is in front of p. */
2628 if (!kcd->cut_through) {
2629 float dist;
2630 float view[3], p_ofs[3];
2631
2632 /* TODO: I think there's a simpler way to get the required raycast ray. */
2633 ED_view3d_unproject_v3(kcd->vc.region, s[0], s[1], 0.0f, view);
2634
2635 /* Make p_ofs a little towards view, so ray doesn't hit p's face. */
2636 sub_v3_v3(view, p);
2637 dist = normalize_v3(view);
2638 copy_v3_v3(p_ofs, p);
2639
2640 /* Avoid projecting behind the viewpoint. */
2641 if (kcd->is_ortho && (kcd->vc.rv3d->persp != RV3D_CAMOB)) {
2642 dist = kcd->vc.v3d->clip_end * 2.0f;
2643 }
2644
2645 if (RV3D_CLIPPING_ENABLED(kcd->vc.v3d, kcd->vc.rv3d)) {
2646 float view_clip[2][3];
2647 /* NOTE: view_clip[0] should never get clipped. */
2648 copy_v3_v3(view_clip[0], p_ofs);
2649 madd_v3_v3v3fl(view_clip[1], p_ofs, view, dist);
2650
2652 view_clip[0], view_clip[1], kcd->vc.rv3d->clip_local, 6, view_clip[0], view_clip[1]))
2653 {
2654 dist = len_v3v3(p_ofs, view_clip[1]);
2655 }
2656 }
2657
2658 /* See if there's a face hit between p1 and the view. */
2659 if (ele_test) {
2660 f_hit = knife_bvh_raycast_filter(kcd,
2661 p_ofs,
2662 view,
2664 &dist,
2665 nullptr,
2666 nullptr,
2668 ele_test);
2669 }
2670 else {
2671 f_hit = knife_bvh_raycast(kcd, p_ofs, view, KNIFE_FLT_EPS, &dist, nullptr, nullptr);
2672 }
2673
2674 if (f_hit) {
2675 return false;
2676 }
2677 }
2678
2679 return true;
2680}
2681
2682/* Clip the line (v1, v2) to planes perpendicular to it and distances d from
2683 * the closest point on the line to the origin. */
2684static void clip_to_ortho_planes(float v1[3], float v2[3], const float center[3], const float d)
2685{
2686 float closest[3], dir[3];
2687
2688 sub_v3_v3v3(dir, v1, v2);
2689 normalize_v3(dir);
2690
2691 /* could be v1 or v2 */
2692 sub_v3_v3(v1, center);
2694 add_v3_v3(closest, center);
2695
2696 madd_v3_v3v3fl(v1, closest, dir, d);
2697 madd_v3_v3v3fl(v2, closest, dir, -d);
2698}
2699
2701 float s1[2],
2702 float s2[2],
2703 float sco[2],
2704 float cage[3],
2705 int ob_index,
2706 KnifeVert *v,
2707 KnifeEdge *kfe,
2708 KnifeLineHit *r_hit)
2709{
2710 memset(r_hit, 0, sizeof(*r_hit));
2711 copy_v3_v3(r_hit->cagehit, cage);
2712 copy_v2_v2(r_hit->schit, sco);
2713 r_hit->ob_index = ob_index;
2714
2715 /* Find position along screen line, used for sorting. */
2716 r_hit->l = len_v2v2(sco, s1) / len_v2v2(s2, s1);
2717
2718 r_hit->m = dot_m4_v3_row_z(kcd->vc.rv3d->persmatob, cage);
2719
2720 r_hit->v = v;
2721
2722 /* If this isn't from an existing BMVert, it may have been added to a BMEdge originally.
2723 * Knowing if the hit comes from an edge is important for edge-in-face checks later on.
2724 * See: #knife_add_single_cut -> #knife_verts_edge_in_face, #42611. */
2725 r_hit->kfe = kfe;
2726
2727 if (v) {
2728 copy_v3_v3(r_hit->hit, v->co);
2729 }
2730 else if (kfe) {
2732 r_hit->hit, cage, kfe->v1->co, kfe->v2->co, kfe->v1->cageco, kfe->v2->cageco);
2733 }
2734}
2735
2737 float s1[2],
2738 float s2[2],
2739 float sco[2],
2740 float ray_start[3],
2741 float ray_end[3],
2742 int ob_index,
2743 BMFace *f,
2744 float face_tol_sq,
2745 KnifeLineHit *r_hit)
2746{
2747 float3 p, cage;
2748 if (!knife_ray_intersect_face(kcd, sco, ray_start, ray_end, ob_index, f, face_tol_sq, p, cage)) {
2749 return false;
2750 }
2751 if (!point_is_visible(kcd, cage, sco, (BMElem *)f)) {
2752 return false;
2753 }
2754 knife_linehit_set(kcd, s1, s2, sco, cage, ob_index, nullptr, nullptr, r_hit);
2755 copy_v3_v3(r_hit->hit, p);
2756 r_hit->f = f;
2757 return true;
2758}
2759
2760/* Finds visible (or all, if cutting through) edges that intersects the current screen drag line.
2761 */
2763{
2764 float3 v1, v2;
2765 float2 s1, s2;
2766 int *results, *result;
2767 ListBase *list;
2768 KnifeLineHit hit;
2769 float s[2], se1[2], se2[2];
2770 float d1, d2;
2771 float vert_tol, vert_tol_sq;
2772 float line_tol, line_tol_sq;
2773 float face_tol, face_tol_sq;
2774 uint tot;
2775 int i;
2776
2778
2779 copy_v3_v3(v1, kcd->prev.cage);
2780 copy_v3_v3(v2, kcd->curr.cage);
2781
2782 /* Project screen line's 3d coordinates back into 2d. */
2783 knife_project_v2(kcd, v1, s1);
2784 knife_project_v2(kcd, v2, s2);
2785
2786 if (kcd->is_interactive) {
2787 if (len_squared_v2v2(s1, s2) < 1.0f) {
2788 return;
2789 }
2790 }
2791 else {
2793 return;
2794 }
2795 }
2796
2797 float4 plane;
2798 {
2799 if (kcd->is_ortho) {
2800 cross_v3_v3v3(plane, v2 - v1, kcd->vc.rv3d->viewinv[2]);
2801 }
2802 else {
2803 float3 orig = kcd->vc.rv3d->viewinv[3];
2804 float3 o_v1 = v1 - orig;
2805 float3 o_v2 = v2 - orig;
2806 cross_v3_v3v3(plane, o_v1, o_v2);
2807 }
2808 plane_from_point_normal_v3(plane, v1, plane);
2809 }
2810
2811 /* First use BVH tree to find faces, knife edges, and knife verts that might
2812 * intersect the cut plane with rays v1-v3 and v2-v4.
2813 * This de-duplicates the candidates before doing more expensive intersection tests. */
2814
2815 results = BLI_bvhtree_intersect_plane(kcd->bvh.tree, plane, &tot);
2816 if (!results) {
2817 return;
2818 }
2819
2822 Set<KnifeEdge *> kfes;
2823 Set<KnifeVert *> kfvs;
2824
2825 Object *ob;
2826 BMEditMesh *em;
2827
2828 for (i = 0, result = results; i < tot; i++, result++) {
2829 uint ob_index = 0;
2830 BMLoop *const *ltri = nullptr;
2831 for (ob_index = 0; ob_index < kcd->objects.size(); ob_index++) {
2832 ob = kcd->objects[ob_index];
2833 em = BKE_editmesh_from_object(ob);
2834 if (*result >= 0 && *result < em->looptris.size()) {
2835 ltri = em->looptris[*result].data();
2836 break;
2837 }
2838 *result -= em->looptris.size();
2839 }
2840 BLI_assert(ltri != nullptr);
2841 BMFace *f = ltri[0]->f;
2842 set_lowest_face_tri(kcd, em, f, *result);
2843
2844 /* Occlude but never cut unselected faces (when only_select is used). */
2845 if (kcd->only_select && !BM_elem_flag_test(f, BM_ELEM_SELECT)) {
2846 continue;
2847 }
2848 /* For faces, store index of lowest hit looptri in hash. */
2849 if (faces.contains(f)) {
2850 continue;
2851 }
2852 /* Don't care what the value is except that it is non-null, for iterator. */
2853 faces.add(f);
2854 fobs.add(f, ob_index);
2855
2856 list = knife_get_face_kedges(kcd, ob_index, f);
2857 LISTBASE_FOREACH (LinkData *, ref, list) {
2858 KnifeEdge *kfe = static_cast<KnifeEdge *>(ref->data);
2859 if (kfe->is_invalid) {
2860 continue;
2861 }
2862 if (kfes.contains(kfe)) {
2863 continue;
2864 }
2865 kfes.add(kfe);
2866 kfvs.add(kfe->v1);
2867 kfvs.add(kfe->v2);
2868 }
2869 }
2870
2871 /* Now go through the candidates and find intersections. */
2872 /* These tolerances, in screen space, are for intermediate hits,
2873 * as ends are already snapped to screen. */
2874
2875 if (kcd->is_interactive) {
2876 vert_tol = KNIFE_FLT_EPS_PX_VERT;
2877 line_tol = KNIFE_FLT_EPS_PX_EDGE;
2878 face_tol = KNIFE_FLT_EPS_PX_FACE;
2879 }
2880 else {
2881 /* Use 1/100th of a pixel, see #43896 (too big), #47910 (too small).
2882 *
2883 * Update, leave this as is until we investigate not using pixel coords
2884 * for geometry calculations: #48023. */
2885 vert_tol = line_tol = face_tol = 0.5f;
2886 }
2887
2888 vert_tol_sq = vert_tol * vert_tol;
2889 line_tol_sq = line_tol * line_tol;
2890 face_tol_sq = face_tol * face_tol;
2891
2892 /* Assume these tolerances swamp floating point rounding errors in calculations below. */
2893
2894 /* First look for vertex hits. */
2895 Vector<KnifeLineHit> linehits;
2896 for (KnifeVert *v : kfvs) {
2897 KnifeEdge *kfe_hit = nullptr;
2898
2899 bool kfv_is_in_cut = false;
2900 if (ELEM(v, kcd->prev.vert, kcd->curr.vert)) {
2901 /* This KnifeVert was captured by the snap system.
2902 * Since the tolerance distance can be different, add this vertex directly.
2903 * Otherwise, the cut may fail or a close cut on a connected edge can be performed. */
2904 bm_elem_from_knife_vert(v, &kfe_hit);
2905 copy_v2_v2(s, (v == kcd->prev.vert) ? kcd->prev.mval : kcd->curr.mval);
2906 kfv_is_in_cut = true;
2907 }
2908 else {
2909 knife_project_v2(kcd, v->cageco, s);
2910 float d = dist_squared_to_line_segment_v2(s, s1, s2);
2911 if ((d <= vert_tol_sq) &&
2912 point_is_visible(kcd, v->cageco, s, bm_elem_from_knife_vert(v, &kfe_hit)))
2913 {
2914 kfv_is_in_cut = true;
2915 }
2916 }
2917
2918 if (kfv_is_in_cut) {
2919 knife_linehit_set(kcd, s1, s2, s, v->cageco, v->ob_index, v, kfe_hit, &hit);
2920 linehits.append(hit);
2921 }
2922 else {
2923 /* This vertex isn't used so remove from `kfvs`.
2924 * This is useful to detect KnifeEdges that can be skipped.
2925 * And it optimizes iteration a little bit. */
2926 kfvs.remove(v);
2927 }
2928 }
2929
2930 /* Now edge hits; don't add if a vertex at end of edge should have hit. */
2931 for (KnifeEdge *kfe : kfes) {
2932 /* If we intersect any of the vertices, don't attempt to intersect the edge. */
2933 if (kfvs.contains(kfe->v1) || kfvs.contains(kfe->v2)) {
2934 continue;
2935 }
2936
2937 knife_project_v2(kcd, kfe->v1->cageco, se1);
2938 knife_project_v2(kcd, kfe->v2->cageco, se2);
2939 float3 p_cage;
2940 float2 p_cage_ss;
2941 bool kfe_is_in_cut = false;
2942 if (kfe == kcd->prev.edge) {
2943 /* This KnifeEdge was captured by the snap system. */
2944 p_cage = kcd->prev.cage;
2945 p_cage_ss = kcd->prev.mval;
2946 kfe_is_in_cut = true;
2947 }
2948 else if (kfe == kcd->curr.edge) {
2949 /* This KnifeEdge was captured by the snap system. */
2950 p_cage = kcd->curr.cage;
2951 p_cage_ss = kcd->curr.mval;
2952 kfe_is_in_cut = true;
2953 }
2954 else {
2955 int isect_kind = isect_seg_seg_v2_point_ex(s1, s2, se1, se2, 0.0f, p_cage_ss);
2956 if (isect_kind == -1) {
2957 /* isect_seg_seg_v2_point doesn't do tolerance test around ends of s1-s2. */
2958 closest_to_line_segment_v2(p_cage_ss, s1, se1, se2);
2959 if (len_squared_v2v2(p_cage_ss, s1) <= line_tol_sq) {
2960 isect_kind = 1;
2961 }
2962 else {
2963 closest_to_line_segment_v2(p_cage_ss, s2, se1, se2);
2964 if (len_squared_v2v2(p_cage_ss, s2) <= line_tol_sq) {
2965 isect_kind = 1;
2966 }
2967 }
2968 }
2969 if (isect_kind == 1) {
2970 d1 = len_v2v2(p_cage_ss, se1);
2971 d2 = len_v2v2(se2, se1);
2972 if (!(d1 <= line_tol || d2 <= line_tol || fabsf(d1 - d2) <= line_tol)) {
2973 /* Can't just interpolate between ends of `kfe` because
2974 * that doesn't work with perspective transformation. */
2975 float lambda;
2976 float3 kfe_dir = kfe->v2->cageco - kfe->v1->cageco;
2977 if (isect_ray_plane_v3(kfe->v1->cageco, kfe_dir, plane, &lambda, false)) {
2978 p_cage = kfe->v1->cageco + kfe_dir * lambda;
2979 if (point_is_visible(kcd, p_cage, p_cage_ss, bm_elem_from_knife_edge(kfe))) {
2980 if (kcd->snap_midpoints) {
2981 /* Choose intermediate point snap too. */
2982 mid_v3_v3v3(p_cage, kfe->v1->cageco, kfe->v2->cageco);
2983 mid_v2_v2v2(p_cage_ss, se1, se2);
2984 }
2985 kfe_is_in_cut = true;
2986 }
2987 }
2988 }
2989 }
2990 }
2991 if (kfe_is_in_cut) {
2992 knife_linehit_set(kcd, s1, s2, p_cage_ss, p_cage, kfe->v1->ob_index, nullptr, kfe, &hit);
2993 linehits.append(hit);
2994 }
2995 }
2996
2997 /* Now face hits; don't add if a vertex or edge in face should have hit. */
2998 const bool use_hit_prev = (kcd->prev.vert == nullptr) && (kcd->prev.edge == nullptr);
2999 const bool use_hit_curr = (kcd->curr.vert == nullptr) && (kcd->curr.edge == nullptr) &&
3000 !kcd->is_drag_hold;
3001 if (use_hit_prev || use_hit_curr) {
3002 float3 v3, v4;
3003
3004 /* Unproject screen line. */
3006 kcd->vc.depsgraph, kcd->region, kcd->vc.v3d, s1, v1, v3, true);
3008 kcd->vc.depsgraph, kcd->region, kcd->vc.v3d, s2, v2, v4, true);
3009
3010 /* Numeric error, 'v1' -> 'v2', 'v2' -> 'v4'
3011 * can end up being ~2000 units apart with an orthogonal perspective.
3012 *
3013 * (from ED_view3d_win_to_segment_clipped() above)
3014 * This gives precision error; rather than solving properly
3015 * (which may involve using doubles everywhere!),
3016 * limit the distance between these points. */
3017 if (kcd->is_ortho && (kcd->vc.rv3d->persp != RV3D_CAMOB)) {
3018 if (kcd->ortho_extent == 0.0f) {
3019 calc_ortho_extent(kcd);
3020 }
3021 clip_to_ortho_planes(v1, v3, kcd->ortho_extent_center, kcd->ortho_extent + 10.0f);
3023 }
3024
3025 for (BMFace *f : faces) {
3026 int ob_index = fobs.lookup(f);
3027 if (use_hit_prev &&
3028 knife_linehit_face_test(kcd, s1, s2, s1, v1, v3, ob_index, f, face_tol_sq, &hit))
3029 {
3030 linehits.append(hit);
3031 }
3032
3033 if (use_hit_curr &&
3034 knife_linehit_face_test(kcd, s1, s2, s2, v2, v4, ob_index, f, face_tol_sq, &hit))
3035 {
3036 linehits.append(hit);
3037 }
3038 }
3039 }
3040
3041 kcd->linehits = std::move(linehits);
3042
3043 MEM_freeN(results);
3044}
3045
3047
3048/* -------------------------------------------------------------------- */
3051
3053{
3054 zero_v3(kpd->cage);
3055 kpd->vert = nullptr;
3056 kpd->edge = nullptr;
3057 kpd->bmface = nullptr;
3058 kpd->ob_index = -1;
3059 zero_v2(kpd->mval);
3060}
3061
3063
3064/* -------------------------------------------------------------------- */
3067
3069 const float2 &mval,
3070 const float3 &ray_orig,
3071 const float3 &ray_dir,
3072 KnifePosData *r_kpd)
3073{
3074 float3 cage;
3075 int ob_index;
3076 BMFace *f;
3077 float dist = KMAXDIST;
3078
3079 f = knife_bvh_raycast(kcd, ray_orig, ray_dir, 0.0f, nullptr, cage, &ob_index);
3080
3081 if (f && kcd->only_select && BM_elem_flag_test(f, BM_ELEM_SELECT) == 0) {
3082 f = nullptr;
3083 ob_index = -1;
3084 }
3085
3086 if (f == nullptr) {
3087 if (kcd->is_interactive) {
3088 /* Try to use back-buffer selection method if ray casting failed.
3089 *
3090 * Apply the mouse coordinates to a copy of the view-context
3091 * since we don't want to rely on this being set elsewhere. */
3092 ViewContext vc = kcd->vc;
3093 vc.mval[0] = int(mval[0]);
3094 vc.mval[1] = int(mval[1]);
3095
3097 f = EDBM_face_find_nearest(&vc, &dist);
3098 }
3099
3100 if (f) {
3101 /* Cheat for now; just put in the origin instead
3102 * of a true coordinate on the face.
3103 * This just puts a point 1.0f in front of the view. */
3104 cage = ray_orig + ray_dir;
3105
3106 ob_index = 0;
3107 BLI_assert(ob_index == kcd->objects.first_index_of_try(vc.obact));
3108 }
3109 }
3110 }
3111
3112 if (f) {
3113 r_kpd->cage = cage;
3114 r_kpd->bmface = f;
3115 r_kpd->ob_index = ob_index;
3116 r_kpd->mval = mval;
3117
3118 return true;
3119 }
3120 return false;
3121}
3122
3130 KnifeTool_OpData *kcd, const float radius, int ob_index, BMFace *f, const float cageco[3])
3131{
3132 const float radius_sq = radius * radius;
3133 ListBase *list;
3134 float sco[2];
3135 float dis_sq;
3136 int c = 0;
3137
3138 knife_project_v2(kcd, cageco, sco);
3139
3140 list = knife_get_face_kedges(kcd, ob_index, f);
3141 LISTBASE_FOREACH (LinkData *, ref, list) {
3142 KnifeEdge *kfe = static_cast<KnifeEdge *>(ref->data);
3143 int i;
3144
3145 if (kfe->is_invalid) {
3146 continue;
3147 }
3148
3149 for (i = 0; i < 2; i++) {
3150 KnifeVert *kfv = i ? kfe->v2 : kfe->v1;
3151 float kfv_sco[2];
3152
3153 if (kfv->is_invalid) {
3154 continue;
3155 }
3156
3157 knife_project_v2(kcd, kfv->cageco, kfv_sco);
3158
3159 dis_sq = len_squared_v2v2(kfv_sco, sco);
3160 if (dis_sq < radius_sq) {
3161 if (RV3D_CLIPPING_ENABLED(kcd->vc.v3d, kcd->vc.rv3d)) {
3162 if (ED_view3d_clipping_test(kcd->vc.rv3d, kfv->cageco, false) == 0) {
3163 c++;
3164 }
3165 }
3166 else {
3167 c++;
3168 }
3169 }
3170 }
3171 }
3172
3173 return c;
3174}
3175
3182static float knife_snap_size(KnifeTool_OpData *kcd, float maxsize)
3183{
3184 BLI_assert(kcd->is_interactive == true);
3185 int density = 0;
3186
3187 if (!kcd->curr.is_space()) {
3189 kcd, maxsize * 2.0f, kcd->curr.ob_index, kcd->curr.bmface, kcd->curr.cage));
3190 }
3191
3192 return density ? min_ff(maxsize / (float(density) * 0.5f), maxsize) : maxsize;
3193}
3194
3200static bool knife_closest_constrain_to_edge(const float3 &cut_origin,
3201 const float3 &cut_dir,
3202 const float3 &kfv1_cageco,
3203 const float3 &kfv2_cageco,
3204 float r_close[3])
3205{
3206 /* If snapping, check we're in bounds. */
3207 float lambda;
3208 if (!isect_ray_line_v3(cut_origin, cut_dir, kfv1_cageco, kfv2_cageco, &lambda)) {
3209 return false;
3210 }
3211
3212 /* Be strict when constrained within edge. */
3213 if ((lambda < 0.0f - KNIFE_FLT_EPSBIG) || (lambda > 1.0f + KNIFE_FLT_EPSBIG)) {
3214 return false;
3215 }
3216
3217 interp_v3_v3v3(r_close, kfv1_cageco, kfv2_cageco, lambda);
3218 return true;
3219}
3220
3221/* `r_kpd->cage` is closest point on edge to the knife point. */
3223 int ob_index,
3224 BMFace *f,
3225 const float2 &curr_cage_ss,
3226 const float3 *curr_cage_constrain,
3227 const float3 &ray_orig,
3228 const float3 &ray_dir,
3229 KnifePosData *r_kpd)
3230{
3231 float maxdist;
3232
3233 if (kcd->is_interactive) {
3234 maxdist = knife_snap_size(kcd, kcd->ethresh);
3235
3236 if (kcd->ignore_vert_snapping) {
3237 maxdist *= 0.5f;
3238 }
3239 }
3240 else {
3241 maxdist = KNIFE_FLT_EPS;
3242 }
3243
3244 const float maxdist_sq = maxdist * maxdist;
3245 float cur_dist_sq = maxdist_sq;
3246 bool has_hit = false;
3247
3248 const float3 &cut_origin = kcd->prev.cage;
3249 const float3 cut_dir = math::normalize(
3250 (curr_cage_constrain ? *curr_cage_constrain : kcd->curr.cage) - kcd->prev.cage);
3251
3252 /* Look through all edges associated with this face. */
3253 ListBase *list = knife_get_face_kedges(kcd, ob_index, f);
3254 LISTBASE_FOREACH (LinkData *, ref, list) {
3255 KnifeEdge *kfe = static_cast<KnifeEdge *>(ref->data);
3256 float test_cagep[3];
3257
3258 if (kfe->is_invalid) {
3259 continue;
3260 }
3261
3262 /* Get the closest point on the edge. */
3263 if ((kcd->is_angle_snapping || kcd->axis_constrained) && (kfe != kcd->prev.edge) &&
3264 (kcd->mode == MODE_DRAGGING))
3265 {
3266 /* Check if it is within the edges' bounds. */
3268 cut_origin, cut_dir, kfe->v1->cageco, kfe->v2->cageco, test_cagep))
3269 {
3270 continue;
3271 }
3272 }
3273 else {
3274 closest_ray_to_segment_v3(ray_orig, ray_dir, kfe->v1->cageco, kfe->v2->cageco, test_cagep);
3275 }
3276
3277 /* Check if we're close enough. */
3278 float2 closest_ss;
3279 knife_project_v2(kcd, test_cagep, closest_ss);
3280 float dis_sq = len_squared_v2v2(closest_ss, curr_cage_ss);
3281 if (dis_sq >= cur_dist_sq) {
3282 continue;
3283 }
3284
3285 if (RV3D_CLIPPING_ENABLED(kcd->vc.v3d, kcd->vc.rv3d)) {
3286 /* Check we're in the view */
3287 if (ED_view3d_clipping_test(kcd->vc.rv3d, test_cagep, false)) {
3288 continue;
3289 }
3290 }
3291
3292 cur_dist_sq = dis_sq;
3293
3294 r_kpd->edge = kfe;
3295 if (kcd->snap_midpoints) {
3296 mid_v3_v3v3(r_kpd->cage, kfe->v1->cageco, kfe->v2->cageco);
3297 knife_project_v2(kcd, r_kpd->cage, r_kpd->mval);
3298 }
3299 else {
3300 copy_v3_v3(r_kpd->cage, test_cagep);
3301 r_kpd->mval = closest_ss;
3302 }
3303
3304 has_hit = true;
3305 }
3306
3307 return has_hit;
3308}
3309
3310/* Find a vertex near the mouse cursor, if it exists. */
3312 const KnifeEdge *kfe,
3313 const float2 &cage_ss,
3314 KnifePosData *r_kpd)
3315{
3316 float maxdist;
3317
3318 if (kcd->is_interactive) {
3319 maxdist = knife_snap_size(kcd, kcd->vthresh);
3320 if (kcd->ignore_vert_snapping) {
3321 maxdist *= 0.5f;
3322 }
3323 }
3324 else {
3325 maxdist = KNIFE_FLT_EPS;
3326 }
3327
3328 const float maxdist_sq = maxdist * maxdist;
3329 KnifeVert *curv = nullptr;
3330 float cur_kfv_sco[2];
3331 float dis_sq, curdis_sq = FLT_MAX;
3332
3333 for (int i = 0; i < 2; i++) {
3334 KnifeVert *kfv = i ? kfe->v2 : kfe->v1;
3335 float kfv_sco[2];
3336
3337 knife_project_v2(kcd, kfv->cageco, kfv_sco);
3338
3339 /* Be strict when in a constrained mode, the vertex needs to be very close to the cut line,
3340 * or we ignore. */
3341 if ((kcd->is_angle_snapping || kcd->axis_constrained) && (kcd->mode == MODE_DRAGGING)) {
3342 if (dist_squared_to_line_segment_v2(kfv_sco, kcd->prev.mval, kcd->curr.mval) >
3344 {
3345 continue;
3346 }
3347 }
3348
3349 dis_sq = len_squared_v2v2(kfv_sco, cage_ss);
3350 if (dis_sq < curdis_sq && dis_sq < maxdist_sq) {
3351 if (!RV3D_CLIPPING_ENABLED(kcd->vc.v3d, kcd->vc.rv3d) ||
3352 !ED_view3d_clipping_test(kcd->vc.rv3d, kfv->cageco, false))
3353 {
3354 curv = kfv;
3355 curdis_sq = dis_sq;
3356 copy_v2_v2(cur_kfv_sco, kfv_sco);
3357 }
3358 }
3359 }
3360
3361 if (curv) {
3362 r_kpd->cage = curv->cageco;
3363 r_kpd->vert = curv;
3364
3365 /* Update mouse coordinates to the snapped-to vertex's screen coordinates
3366 * this is important for angle snap, which uses the previous mouse position. */
3367 r_kpd->mval = cur_kfv_sco;
3368
3369 return true;
3370 }
3371
3372 return false;
3373}
3374
3379 float3 &r, const float3 &dvec, const float3 &vecx, const float3 &axis, float angle_snap)
3380{
3381 const float angle = angle_signed_on_axis_v3v3_v3(dvec, vecx, axis);
3382 const float angle_delta = (roundf(angle / angle_snap) * angle_snap) - angle;
3383 rotate_normalized_v3_v3v3fl(r, dvec, axis, angle_delta);
3384 return angle + angle_delta;
3385}
3386
3388 const float3 &vec_x,
3389 const float3 &axis,
3390 const float3 &ray_orig,
3391 const float3 &ray_dir,
3392 float3 &r_cage,
3393 float &r_angle)
3394{
3395 float3 curr_cage_projected;
3397 curr_cage_projected, ray_orig, ray_orig + ray_dir, kcd->prev.cage, axis))
3398 {
3399 return false;
3400 }
3401 const float3 dvec = curr_cage_projected - kcd->prev.cage;
3402 float snap_step;
3403 /* Currently user can input any float between 0 and 180. */
3406 {
3407 snap_step = DEG2RADF(kcd->angle_snapping_increment);
3408 }
3409 else {
3411 }
3412
3413 if (is_zero_v2(dvec)) {
3414 return false;
3415 }
3416
3417 float3 dvec_snap;
3418 r_angle = knife_snap_v3_angle(dvec_snap, dvec, vec_x, axis, snap_step);
3419 r_cage = kcd->prev.cage + dvec_snap;
3420 return true;
3421}
3422
3423/* Update both kcd->curr.mval and kcd->mval to snap to required angle. */
3425 const float3 &ray_orig,
3426 const float3 &ray_dir,
3427 float3 &r_cage,
3428 float &r_angle)
3429{
3430 const float3 &vec_x = kcd->vc.rv3d->viewinv[0];
3431 const float3 &vec_z = kcd->vc.rv3d->viewinv[2];
3432 return knife_snap_angle_impl(kcd, vec_x, vec_z, ray_orig, ray_dir, r_cage, r_angle);
3433}
3434
3435/* Snap to required angle along the plane of the face nearest to kcd->prev. */
3437 const float3 &ray_orig,
3438 const float3 &ray_dir,
3439 float3 &r_cage,
3440 float &r_angle)
3441{
3442 BMFace *fcurr = knife_bvh_raycast(kcd, ray_orig, ray_dir, 0.0f, nullptr, nullptr, nullptr);
3443
3444 if (!fcurr) {
3445 return false;
3446 }
3447
3448 /* Calculate a reference vector using previous cut segment, edge or vertex.
3449 * If none exists then exit. */
3450 float3 refv;
3451 if (kcd->prev.vert) {
3452 int count = 0;
3453 LISTBASE_FOREACH (LinkData *, ref, &kcd->prev.vert->edges) {
3454 KnifeEdge *kfe = ((KnifeEdge *)(ref->data));
3455 if (kfe->is_invalid) {
3456 continue;
3457 }
3458 if (kfe->e) {
3459 if (!BM_edge_in_face(kfe->e, fcurr)) {
3460 continue;
3461 }
3462 }
3463 if (count == kcd->snap_edge) {
3465 kfe->v2 :
3466 kfe->v1;
3467 refv = kfv->cageco - kcd->prev.cage;
3468 kcd->snap_ref_edge = kfe;
3469 break;
3470 }
3471 count++;
3472 }
3473 }
3474 else if (kcd->prev.edge) {
3476 kcd->prev.edge->v2 :
3477 kcd->prev.edge->v1;
3478 refv = kfv->cageco - kcd->prev.cage;
3479 kcd->snap_ref_edge = kcd->prev.edge;
3480 }
3481 else {
3482 return false;
3483 }
3484
3485 /* Choose best face for plane. */
3486 BMFace *fprev = nullptr;
3487 int fprev_ob_index = kcd->prev.ob_index;
3488 if (kcd->prev.vert && kcd->prev.vert->v) {
3489 LISTBASE_FOREACH (LinkData *, ref, &kcd->prev.vert->faces) {
3490 BMFace *f = ((BMFace *)(ref->data));
3491 if (f == fcurr) {
3492 fprev = f;
3493 }
3494 }
3495 }
3496 else if (kcd->prev.edge) {
3497 LISTBASE_FOREACH (LinkData *, ref, &kcd->prev.edge->faces) {
3498 BMFace *f = ((BMFace *)(ref->data));
3499 if (f == fcurr) {
3500 fprev = f;
3501 }
3502 }
3503 }
3504 else {
3505 /* Cut segment was started in a face. */
3506 float3 prev_ray_orig, prev_ray_dir;
3508 kcd->region,
3509 kcd->vc.v3d,
3510 kcd->prev.mval,
3511 prev_ray_orig,
3512 prev_ray_dir,
3513 false);
3514
3515 /* kcd->prev.face is usually not set. */
3516 fprev = knife_bvh_raycast(
3517 kcd, prev_ray_orig, prev_ray_dir, 0.0f, nullptr, nullptr, &fprev_ob_index);
3518 }
3519
3520 if (!fprev || fprev != fcurr) {
3521 return false;
3522 }
3523
3524 /* Use normal global direction. */
3525 Object *ob = kcd->objects[fprev_ob_index];
3526 float3 no_global = fprev->no;
3527 mul_transposed_mat3_m4_v3(ob->world_to_object().ptr(), no_global);
3528 normalize_v3(no_global);
3529
3530 return knife_snap_angle_impl(kcd, refv, no_global, ray_orig, ray_dir, r_cage, r_angle);
3531}
3532
3534 const float3 &ray_orig,
3535 const float3 &ray_dir)
3536{
3537 BMFace *fcurr = knife_bvh_raycast(kcd, ray_orig, ray_dir, 0.0f, nullptr, nullptr, nullptr);
3538
3539 int count = 0;
3540
3541 if (!fcurr) {
3542 return count;
3543 }
3544
3545 if (kcd->prev.vert) {
3546 LISTBASE_FOREACH (LinkData *, ref, &kcd->prev.vert->edges) {
3547 KnifeEdge *kfe = ((KnifeEdge *)(ref->data));
3548 if (kfe->is_invalid) {
3549 continue;
3550 }
3551 if (kfe->e) {
3552 if (!BM_edge_in_face(kfe->e, fcurr)) {
3553 continue;
3554 }
3555 }
3556 count++;
3557 }
3558 }
3559 else if (kcd->prev.edge) {
3560 return 1;
3561 }
3562 return count;
3563}
3564
3565/* Reset the snapping angle num input. */
3567{
3568 kcd->num.val[0] = 0;
3569 while (kcd->num.str_cur > 0) {
3570 kcd->num.str[kcd->num.str_cur - 1] = '\0';
3571 kcd->num.str_cur--;
3572 }
3573}
3574
3580 const float3 &ray_orig,
3581 const float3 &ray_dir,
3582 float3 &r_cage)
3583{
3584 float3 constrain_dir;
3585 {
3586 /* Constrain axes. */
3587 Scene *scene = kcd->scene;
3588 ViewLayer *view_layer = kcd->vc.view_layer;
3589 Object *obedit = (kcd->prev.ob_index != -1) ? kcd->objects[kcd->prev.ob_index] :
3590 kcd->vc.obedit;
3591 RegionView3D *rv3d = static_cast<RegionView3D *>(kcd->region->regiondata);
3592 const short scene_orientation = BKE_scene_orientation_get_index(scene, SCE_ORIENT_DEFAULT);
3593 /* Scene orientation takes priority. */
3594 const short orientation_type = scene_orientation ? scene_orientation :
3595 kcd->constrain_axis_mode - 1;
3596 const int pivot_point = scene->toolsettings->transform_pivot_point;
3597 float mat[3][3];
3599 scene, view_layer, kcd->vc.v3d, rv3d, obedit, obedit, orientation_type, pivot_point, mat);
3600
3601 constrain_dir = mat[kcd->constrain_axis - 1];
3602 }
3603
3604 float lambda;
3605 if (!isect_ray_ray_v3(kcd->prev.cage, constrain_dir, ray_orig, ray_dir, &lambda, nullptr)) {
3606 return;
3607 }
3608
3609 float3 cage_dir = constrain_dir * lambda;
3610 if (math::is_zero(cage_dir)) {
3611 return;
3612 }
3613
3614 r_cage = kcd->prev.cage + cage_dir;
3615}
3616
3623 const float2 &mval,
3624 const float3 &ray_orig,
3625 const float3 &ray_dir,
3626 const float3 *curr_cage_constrain,
3627 const float3 *fallback)
3628{
3630
3631 if (knife_find_closest_face(kcd, mval, ray_orig, ray_dir, &kcd->curr)) {
3632 if (!kcd->ignore_edge_snapping || !kcd->ignore_vert_snapping) {
3633 KnifePosData kpos_tmp = kcd->curr;
3635 kcd->curr.ob_index,
3636 kcd->curr.bmface,
3637 kcd->curr.mval,
3638 curr_cage_constrain,
3639 ray_orig,
3640 ray_dir,
3641 &kpos_tmp))
3642 {
3643 if (!kcd->ignore_edge_snapping) {
3644 kcd->curr = kpos_tmp;
3645 }
3646 if (!kcd->ignore_vert_snapping) {
3647 knife_find_closest_vert_of_edge(kcd, kpos_tmp.edge, kpos_tmp.mval, &kcd->curr);
3648 }
3649 }
3650 }
3651 }
3652
3653 if (kcd->curr.vert || kcd->curr.edge || kcd->curr.bmface) {
3654 return;
3655 }
3656
3657 kcd->curr.mval = mval;
3658 if (fallback) {
3659 /* If no geometry was found, use the fallback point. */
3660 kcd->curr.cage = *fallback;
3661 return;
3662 }
3663
3664 /* If no hits are found this would normally default to (0, 0, 0) so instead
3665 * get a point at the mouse ray closest to the previous point.
3666 * Note that drawing lines in `free-space` isn't properly supported
3667 * but there's no guarantee (0, 0, 0) has any geometry either - campbell */
3668
3670 kcd->curr.cage, ray_orig, ray_orig + ray_dir, kcd->prev.cage, kcd->vc.rv3d->viewinv[2]))
3671 {
3672 /* Should never fail! */
3673 kcd->curr.cage = kcd->prev.cage;
3674 BLI_assert(0);
3675 }
3676}
3677
3687{
3688 /* Mouse and ray with snapping applied. */
3689 float3 ray_orig;
3690 float3 ray_dir;
3691 float2 mval_constrain = mval;
3693 kcd->vc.depsgraph, kcd->region, kcd->vc.v3d, mval, ray_orig, ray_dir, false);
3694
3696
3697 /* view matrix may have changed, reproject */
3698 knife_project_v2(kcd, kcd->prev.cage, kcd->prev.mval);
3699
3700 bool is_constrained = false;
3701 kcd->is_angle_snapping = false;
3702 if (kcd->mode == MODE_DRAGGING) {
3703 if (kcd->angle_snapping) {
3706 kcd, ray_orig, ray_dir, kcd->curr.cage, kcd->angle);
3707 }
3710 kcd, ray_orig, ray_dir, kcd->curr.cage, kcd->angle);
3711 if (kcd->is_angle_snapping) {
3712 kcd->snap_ref_edges_count = knife_calculate_snap_ref_edges(kcd, ray_orig, ray_dir);
3713 }
3714 }
3715 }
3716
3717 if (kcd->is_angle_snapping) {
3718 is_constrained = true;
3719 }
3720 else if (kcd->axis_constrained) {
3721 knife_constrain_axis(kcd, ray_orig, ray_dir, kcd->curr.cage);
3722 is_constrained = true;
3723 }
3724 }
3725
3726 float3 fallback;
3727 float3 curr_cage_constrain;
3728 if (is_constrained) {
3729 /* Update ray and `mval_constrain`. */
3730 if (kcd->is_ortho) {
3731 float3 l1 = kcd->curr.cage - ray_dir;
3732 if (!isect_line_plane_v3(ray_orig, l1, kcd->curr.cage, ray_orig, ray_dir)) {
3733 /* Should never fail! */
3734 ray_orig = l1;
3736 }
3737 }
3738 else {
3739 ray_dir = math::normalize(kcd->curr.cage - ray_orig);
3740 }
3741 knife_project_v2(kcd, kcd->curr.cage, mval_constrain);
3742 curr_cage_constrain = kcd->curr.cage;
3743 fallback = kcd->curr.cage;
3744 }
3745
3746 knife_snap_curr(kcd,
3747 mval_constrain,
3748 ray_orig,
3749 ray_dir,
3750 is_constrained ? &curr_cage_constrain : nullptr,
3751 is_constrained ? &fallback : nullptr);
3752}
3753
3761{
3762 KnifeEdge *kfe, *newkfe;
3763 KnifeEdge *lastkfe = nullptr;
3764 KnifeVert *v1, *v2;
3765 KnifeUndoFrame *undo;
3766 BLI_mempool_iter iterkfe;
3767
3768 undo = static_cast<KnifeUndoFrame *>(BLI_stack_peek(kcd->undostack));
3769
3770 /* Undo edge splitting. */
3771 for (int i = 0; i < undo->splits; i++) {
3772 BLI_stack_pop(kcd->splitstack, &newkfe);
3773 BLI_stack_pop(kcd->splitstack, &kfe);
3774 knife_join_edge(newkfe, kfe);
3775 }
3776
3777 for (int i = 0; i < undo->cuts; i++) {
3778
3779 BLI_mempool_iternew(kcd->kedges, &iterkfe);
3780 for (kfe = static_cast<KnifeEdge *>(BLI_mempool_iterstep(&iterkfe)); kfe;
3781 kfe = static_cast<KnifeEdge *>(BLI_mempool_iterstep(&iterkfe)))
3782 {
3783 if (!kfe->is_cut || kfe->is_invalid || kfe->splits) {
3784 continue;
3785 }
3786 lastkfe = kfe;
3787 }
3788
3789 if (lastkfe) {
3790 lastkfe->is_invalid = true;
3791
3792 /* TODO: Are they always guaranteed to be in this order? */
3793 v1 = lastkfe->v1;
3794 v2 = lastkfe->v2;
3795
3796 /* Only remove first vertex if it is the start segment of the cut. */
3797 if (!v1->is_invalid && !v1->is_splitting) {
3798 v1->is_invalid = true;
3799 /* If the first vertex is touching any other cut edges don't remove it. */
3800 LISTBASE_FOREACH (LinkData *, ref, &v1->edges) {
3801 kfe = static_cast<KnifeEdge *>(ref->data);
3802 if (kfe->is_cut && !kfe->is_invalid) {
3803 v1->is_invalid = false;
3804 break;
3805 }
3806 }
3807 }
3808
3809 /* Only remove second vertex if it is the end segment of the cut. */
3810 if (!v2->is_invalid && !v2->is_splitting) {
3811 v2->is_invalid = true;
3812 /* If the second vertex is touching any other cut edges don't remove it. */
3813 LISTBASE_FOREACH (LinkData *, ref, &v2->edges) {
3814 kfe = static_cast<KnifeEdge *>(ref->data);
3815 if (kfe->is_cut && !kfe->is_invalid) {
3816 v2->is_invalid = false;
3817 break;
3818 }
3819 }
3820 }
3821 }
3822 }
3823
3824 if (ELEM(kcd->mode, MODE_DRAGGING, MODE_IDLE)) {
3825 /* Restore kcd->prev. */
3826 kcd->prev = undo->pos;
3827 }
3828
3829 /* Restore data for distance and angle measurements. */
3830 kcd->mdata = undo->mdata;
3831
3833}
3834
3836
3837/* -------------------------------------------------------------------- */
3840
3842 Object *ob,
3843 int ob_index,
3844 bool use_tri_indices)
3845{
3846 Scene *scene_eval = DEG_get_evaluated(kcd->vc.depsgraph, kcd->scene);
3847 Object *obedit_eval = DEG_get_evaluated(kcd->vc.depsgraph, ob);
3848 const Mesh &mesh_orig = *static_cast<const Mesh *>(ob->data);
3849 const Mesh &mesh_eval = *static_cast<const Mesh *>(obedit_eval->data);
3850
3851 KnifeObjectInfo *obinfo = &kcd->objects_info[ob_index];
3852
3853 if (BKE_editmesh_eval_orig_map_available(mesh_eval, &mesh_orig)) {
3854 BMEditMesh &em_eval = *mesh_eval.runtime->edit_mesh;
3855 obinfo->em = &em_eval;
3857 kcd->vc.depsgraph, &em_eval, scene_eval, obedit_eval);
3858 }
3859 else {
3860 obinfo->em = mesh_orig.runtime->edit_mesh.get();
3861 obinfo->positions_cage = BM_mesh_vert_coords_alloc(obinfo->em->bm);
3862 }
3863
3865
3866 if (use_tri_indices) {
3867 obinfo->tri_indices.reinitialize(obinfo->em->looptris.size());
3868 for (int i = 0; i < obinfo->em->looptris.size(); i++) {
3869 const std::array<BMLoop *, 3> &ltri = obinfo->em->looptris[i];
3870 obinfo->tri_indices[i][0] = BM_elem_index_get(ltri[0]->v);
3871 obinfo->tri_indices[i][1] = BM_elem_index_get(ltri[1]->v);
3872 obinfo->tri_indices[i][2] = BM_elem_index_get(ltri[2]->v);
3873 }
3874 }
3875}
3876
3878
3879/* -------------------------------------------------------------------- */
3882
3883static void knife_init_colors(KnifeColors *colors)
3884{
3885 /* Possible BMESH_TODO: add explicit themes or calculate these by
3886 * figuring out contrasting colors with grid / edges / verts
3887 * a la UI_make_axis_color. */
3893 colors->curpoint_a[3] = 102;
3896 colors->point_a[3] = 102;
3897
3902}
3903
3904/* called when modal loop selection gets set up... */
3906 KnifeTool_OpData *kcd,
3907 Vector<Object *> objects,
3908 const bool only_select,
3909 const bool cut_through,
3910 const bool xray,
3911 const int visible_measurements,
3912 const int angle_snapping,
3913 const float angle_snapping_increment,
3914 const bool is_interactive)
3915{
3916 /* Needed so multiple non-interactive cuts (also called knife-project)
3917 * doesn't access indices of loops that were created by cutting, see: #97153. */
3918 bool use_tri_indices = !is_interactive;
3919
3920 kcd->vc = *vc;
3921
3922 Scene *scene = vc->scene;
3923
3924 /* Assign the drawing handle for drawing preview line... */
3925 kcd->scene = scene;
3926 kcd->region = vc->region;
3927
3928 kcd->objects = std::move(objects);
3929
3930 Object *ob;
3931 BMEditMesh *em;
3933 for (int ob_index = 0; ob_index < kcd->objects.size(); ob_index++) {
3934 ob = kcd->objects[ob_index];
3935 em = BKE_editmesh_from_object(ob);
3936 knifetool_init_obinfo(kcd, ob, ob_index, use_tri_indices);
3937
3938 /* Can't usefully select resulting edges in face mode. */
3940 }
3941 knife_bvh_init(kcd);
3942
3943 /* Cut all the way through the mesh if use_occlude_geometry button not pushed. */
3944 kcd->is_interactive = is_interactive;
3945 kcd->cut_through = cut_through;
3946 kcd->only_select = only_select;
3947 kcd->depth_test = xray;
3948 kcd->dist_angle_mode = visible_measurements;
3950 kcd->angle_snapping_mode = angle_snapping;
3952 kcd->angle_snapping_increment = angle_snapping_increment;
3953
3954 kcd->arena = BLI_memarena_new(MEM_SIZE_OPTIMAL(1 << 15), "knife");
3955#ifdef USE_NET_ISLAND_CONNECT
3956 kcd->edgenet.arena = BLI_memarena_new(MEM_SIZE_OPTIMAL(1 << 15), __func__);
3957#endif
3958 kcd->edgenet.edge_visit = BLI_gset_ptr_new(__func__);
3959
3960 kcd->vthresh = KMAXDIST - 1;
3961 kcd->ethresh = KMAXDIST;
3962
3963 knife_recalc_ortho(kcd);
3964
3966
3967 kcd->refs = BLI_mempool_create(sizeof(LinkData), 0, 2048, 0);
3970
3971 kcd->undostack = BLI_stack_new(sizeof(KnifeUndoFrame), "knife undostack");
3972 kcd->splitstack = BLI_stack_new(sizeof(KnifeEdge *), "knife splitstack");
3973
3974 kcd->origedgemap = BLI_ghash_ptr_new("knife origedgemap");
3975 kcd->origvertmap = BLI_ghash_ptr_new("knife origvertmap");
3976 kcd->kedgefacemap = BLI_ghash_ptr_new("knife kedgefacemap");
3977 kcd->facetrimap = BLI_ghash_ptr_new("knife facetrimap");
3978
3981
3982 if (is_interactive) {
3985
3987 }
3988
3989 kcd->no_cuts = true;
3990
3991 kcd->axis_string[0] = ' ';
3992 kcd->axis_string[1] = '\0';
3993
3994 /* Initialize number input handling for angle snapping. */
3995 initNumInput(&kcd->num);
3996 kcd->num.idx_max = 0;
3997 kcd->num.val_flag[0] |= NUM_NO_NEGATIVE;
3998 kcd->num.unit_sys = scene->unit.system;
3999 kcd->num.unit_type[0] = B_UNIT_NONE;
4000}
4001
4002/* called when modal loop selection is done... */
4004{
4005 if (!kcd) {
4006 return;
4007 }
4008
4009 if (kcd->is_interactive) {
4011
4012 /* Deactivate the extra drawing stuff in 3D-View. */
4014 }
4015
4016 /* Free the custom data. */
4020
4023
4024 BLI_ghash_free(kcd->origedgemap, nullptr, nullptr);
4025 BLI_ghash_free(kcd->origvertmap, nullptr, nullptr);
4026 BLI_ghash_free(kcd->kedgefacemap, nullptr, nullptr);
4027 BLI_ghash_free(kcd->facetrimap, nullptr, nullptr);
4028
4030#ifdef USE_NET_ISLAND_CONNECT
4032#endif
4033 BLI_gset_free(kcd->edgenet.edge_visit, nullptr);
4034
4035 /* Tag for redraw. */
4037
4038 /* Knife BVH cleanup. */
4039 knife_bvh_free(kcd);
4040
4041 /* Destroy kcd itself. */
4042 MEM_delete(kcd);
4043}
4044
4046{
4047 KnifeTool_OpData *kcd = static_cast<KnifeTool_OpData *>(op->customdata);
4048 knifetool_exit_ex(kcd);
4049 op->customdata = nullptr;
4050}
4051
4053
4054/* -------------------------------------------------------------------- */
4057
4059static int knife_update_active(KnifeTool_OpData *kcd, const float2 &mval)
4060{
4061 knife_snap_update_from_mval(kcd, mval);
4062
4063 if (kcd->mode == MODE_DRAGGING) {
4065 }
4066 return 1;
4067}
4068
4069static void knifetool_update_mval(KnifeTool_OpData *kcd, const float2 &mval)
4070{
4071 knife_recalc_ortho(kcd);
4072
4073 if (knife_update_active(kcd, mval)) {
4075 }
4076}
4077
4079
4080/* -------------------------------------------------------------------- */
4083
4084static void knifetool_finish_single_pre(KnifeTool_OpData *kcd, int ob_index)
4085{
4086 knife_make_cuts(kcd, ob_index);
4087}
4088
4094{
4098 params.calc_looptris = true;
4099 params.calc_normals = true;
4100 params.is_destructive = true;
4101 EDBM_update(static_cast<Mesh *>(ob->data), &params);
4102}
4103
4104/* Called on tool confirmation. */
4106{
4107 /* Separate pre/post passes are needed because `em->looptris` recalculation from the 'post' pass
4108 * causes triangle indices in #KnifeTool_OpData.bvh to get out of sync.
4109 * So perform all the cuts before doing any mesh recalculation, see: #101721. */
4110 for (int ob_index : kcd->objects.index_range()) {
4111 knifetool_finish_single_pre(kcd, ob_index);
4112 }
4113 for (Object *ob : kcd->objects) {
4115 }
4116}
4117
4119{
4120 KnifeTool_OpData *kcd = static_cast<KnifeTool_OpData *>(op->customdata);
4122}
4123
4125
4126/* -------------------------------------------------------------------- */
4129
4130static void knifetool_cancel(bContext * /*C*/, wmOperator *op)
4131{
4132 /* this is just a wrapper around exit() */
4133 knifetool_exit(op);
4134}
4135
4137{
4138 static const EnumPropertyItem modal_items[] = {
4139 {KNF_MODAL_CANCEL, "CANCEL", 0, "Cancel", ""},
4140 {KNF_MODAL_CONFIRM, "CONFIRM", 0, "Confirm", ""},
4141 {KNF_MODAL_UNDO, "UNDO", 0, "Undo", ""},
4142 {KNF_MODAL_MIDPOINT_ON, "SNAP_MIDPOINTS_ON", 0, "Snap to Midpoints On", ""},
4143 {KNF_MODAL_MIDPOINT_OFF, "SNAP_MIDPOINTS_OFF", 0, "Snap to Midpoints Off", ""},
4144 {KNF_MODAL_IGNORE_SNAP_ON, "IGNORE_SNAP_ON", 0, "Ignore Snapping On", ""},
4145 {KNF_MODAL_IGNORE_SNAP_OFF, "IGNORE_SNAP_OFF", 0, "Ignore Snapping Off", ""},
4146 {KNF_MODAL_ANGLE_SNAP_TOGGLE, "ANGLE_SNAP_TOGGLE", 0, "Toggle Angle Snapping", ""},
4148 "CYCLE_ANGLE_SNAP_EDGE",
4149 0,
4150 "Cycle Angle Snapping Relative Edge",
4151 ""},
4152 {KNF_MODAL_CUT_THROUGH_TOGGLE, "CUT_THROUGH_TOGGLE", 0, "Toggle Cut Through", ""},
4154 "SHOW_DISTANCE_ANGLE_TOGGLE",
4155 0,
4156 "Toggle Distance and Angle Measurements",
4157 ""},
4158 {KNF_MODAL_DEPTH_TEST_TOGGLE, "DEPTH_TEST_TOGGLE", 0, "Toggle Depth Testing", ""},
4159 {KNF_MODAL_NEW_CUT, "NEW_CUT", 0, "End Current Cut", ""},
4160 {KNF_MODAL_ADD_CUT, "ADD_CUT", 0, "Add Cut", ""},
4161 {KNF_MODAL_ADD_CUT_CLOSED, "ADD_CUT_CLOSED", 0, "Add Cut Closed", ""},
4162 {KNF_MODAL_PANNING, "PANNING", 0, "Panning", ""},
4163 {KNF_MODAL_X_AXIS, "X_AXIS", 0, "X Axis Locking", ""},
4164 {KNF_MODAL_Y_AXIS, "Y_AXIS", 0, "Y Axis Locking", ""},
4165 {KNF_MODAL_Z_AXIS, "Z_AXIS", 0, "Z Axis Locking", ""},
4166 {0, nullptr, 0, nullptr, nullptr},
4167 };
4168
4169 wmKeyMap *keymap = WM_modalkeymap_find(keyconf, "Knife Tool Modal Map");
4170
4171 /* This function is called for each space-type, only needs to add map once. */
4172 if (keymap && keymap->modal_items) {
4173 return nullptr;
4174 }
4175
4176 keymap = WM_modalkeymap_ensure(keyconf, "Knife Tool Modal Map", modal_items);
4177
4178 WM_modalkeymap_assign(keymap, "MESH_OT_knife_tool");
4179
4180 return keymap;
4181}
4182
4183/* Turn off angle snapping. */
4190
4191/* Turn off orientation locking. */
4198
4200{
4201 KnifeTool_OpData *kcd = static_cast<KnifeTool_OpData *>(op->customdata);
4202 bool do_refresh = false;
4203
4204 Object *ob = (kcd->curr.ob_index != -1) ? kcd->objects[kcd->curr.ob_index] : kcd->vc.obedit;
4205 if (!ob || ob->type != OB_MESH) {
4206 knifetool_exit(op);
4207 ED_workspace_status_text(C, nullptr);
4208 return OPERATOR_FINISHED;
4209 }
4210
4211 kcd->region = kcd->vc.region;
4212
4213 ED_view3d_init_mats_rv3d(ob, kcd->vc.rv3d); /* Needed to initialize clipping. */
4214
4215 if (kcd->mode == MODE_PANNING) {
4216 kcd->mode = KnifeMode(kcd->prevmode);
4217 }
4218
4219 bool handled = false;
4220 float snapping_increment_temp;
4221 const float2 mval = {float(event->mval[0]), float(event->mval[1])};
4222
4223 if (kcd->angle_snapping) {
4224 if (kcd->num.str_cur >= 3 ||
4226 {
4228 }
4229 knife_update_header(C, op, kcd); /* Update the angle multiple. */
4230 /* Modal numinput active, try to handle numeric inputs first... */
4231 if (event->val == KM_PRESS && hasNumInput(&kcd->num) && handleNumInput(C, &kcd->num, event)) {
4232 handled = true;
4233 applyNumInput(&kcd->num, &snapping_increment_temp);
4234 /* Restrict number key input to 0 - 180 degree range. */
4235 if (snapping_increment_temp > KNIFE_MIN_ANGLE_SNAPPING_INCREMENT &&
4236 snapping_increment_temp <= KNIFE_MAX_ANGLE_SNAPPING_INCREMENT)
4237 {
4238 kcd->angle_snapping_increment = snapping_increment_temp;
4239 }
4240 knife_update_active(kcd, mval);
4241 knife_update_header(C, op, kcd);
4244 }
4245 }
4246
4247 /* Handle modal keymap. */
4248 if (event->type == EVT_MODAL_MAP) {
4249 switch (event->val) {
4250 case KNF_MODAL_CANCEL:
4251 /* finish */
4253
4254 knifetool_exit(op);
4255 ED_workspace_status_text(C, nullptr);
4256
4257 return OPERATOR_CANCELLED;
4258 case KNF_MODAL_CONFIRM: {
4259 const bool changed = (kcd->totkvert != 0);
4260 /* finish */
4262
4263 knifetool_finish(op);
4264 knifetool_exit(op);
4265 ED_workspace_status_text(C, nullptr);
4266
4267 /* Cancel to prevent undo push for empty cuts. */
4268 if (!changed) {
4269 return OPERATOR_CANCELLED;
4270 }
4271 return OPERATOR_FINISHED;
4272 }
4273 case KNF_MODAL_UNDO:
4274 if (BLI_stack_is_empty(kcd->undostack)) {
4276 knifetool_exit(op);
4277 ED_workspace_status_text(C, nullptr);
4278 return OPERATOR_CANCELLED;
4279 }
4280 knifetool_undo(kcd);
4281 knife_update_active(kcd, mval);
4283 handled = true;
4284 break;
4286 kcd->snap_midpoints = true;
4287
4288 knife_recalc_ortho(kcd);
4289 knife_update_active(kcd, mval);
4290 do_refresh = true;
4291 handled = true;
4292 break;
4294 kcd->snap_midpoints = false;
4295
4296 knife_recalc_ortho(kcd);
4297 knife_update_active(kcd, mval);
4298 do_refresh = true;
4299 handled = true;
4300 break;
4302 kcd->ignore_vert_snapping = kcd->ignore_edge_snapping = true;
4303 do_refresh = true;
4304 handled = true;
4305 break;
4307 kcd->ignore_vert_snapping = kcd->ignore_edge_snapping = false;
4308 do_refresh = true;
4309 handled = true;
4310 break;
4313 kcd->angle_snapping_mode++;
4314 kcd->snap_ref_edges_count = 0;
4315 kcd->snap_edge = 0;
4316 }
4317 else {
4319 }
4322 RNA_float_get(op->ptr, "angle_snapping_increment"));
4325 knife_update_active(kcd, mval);
4326 do_refresh = true;
4327 handled = true;
4328 break;
4331 if (kcd->snap_ref_edges_count) {
4332 kcd->snap_edge++;
4333 kcd->snap_edge %= kcd->snap_ref_edges_count;
4335 do_refresh = true;
4336 handled = true;
4337 }
4338 }
4339 break;
4341 kcd->cut_through = !kcd->cut_through;
4342 knife_update_active(kcd, mval);
4343 do_refresh = true;
4344 handled = true;
4345 break;
4348 kcd->dist_angle_mode++;
4349 }
4350 else {
4352 }
4354 do_refresh = true;
4355 handled = true;
4356 break;
4358 kcd->depth_test = !kcd->depth_test;
4359 do_refresh = true;
4360 handled = true;
4361 break;
4362 case KNF_MODAL_NEW_CUT:
4363 /* If no cuts have been made, exit.
4364 * Preserves right click cancel workflow which most tools use,
4365 * but stops accidentally deleting entire cuts with right click.
4366 */
4367 if (kcd->no_cuts) {
4369 knifetool_exit(op);
4370 ED_workspace_status_text(C, nullptr);
4371 return OPERATOR_CANCELLED;
4372 }
4374 knife_finish_cut(kcd);
4375 kcd->mode = MODE_IDLE;
4376 handled = true;
4377 break;
4378 case KNF_MODAL_ADD_CUT:
4379 kcd->no_cuts = false;
4380 knife_recalc_ortho(kcd);
4381
4382 /* Get the value of the event which triggered this one. */
4383 if (event->prev_val != KM_RELEASE) {
4384 if (kcd->mode == MODE_DRAGGING) {
4385 knife_add_cut(kcd);
4386 }
4387 else if (kcd->mode != MODE_PANNING) {
4388 knife_start_cut(kcd, mval);
4389 kcd->mode = MODE_DRAGGING;
4390 kcd->init = kcd->curr;
4391 }
4392
4393 /* Freehand drawing is incompatible with cut-through. */
4394 if (kcd->cut_through == false) {
4395 kcd->is_drag_hold = true;
4396 /* No edge snapping while dragging (edges are too sticky when cuts are immediate). */
4397 kcd->ignore_edge_snapping = true;
4398 }
4399 }
4400 else {
4401 kcd->is_drag_hold = false;
4402 kcd->ignore_edge_snapping = false;
4403 kcd->is_drag_undo = false;
4404
4405 /* Needed because the last face 'hit' is ignored when dragging. */
4406 knifetool_update_mval(kcd, kcd->curr.mval);
4407 }
4408
4410 handled = true;
4411 break;
4413 if (kcd->mode == MODE_DRAGGING) {
4414
4415 /* Shouldn't be possible with default key-layout, just in case. */
4416 if (kcd->is_drag_hold) {
4417 kcd->is_drag_hold = false;
4418 kcd->is_drag_undo = false;
4419 knifetool_update_mval(kcd, kcd->curr.mval);
4420 }
4421
4422 kcd->prev = kcd->curr;
4423 kcd->curr = kcd->init;
4424
4425 knife_project_v2(kcd, kcd->curr.cage, kcd->curr.mval);
4426 knifetool_update_mval(kcd, kcd->curr.mval);
4427
4428 knife_add_cut(kcd);
4429
4430 /* KNF_MODAL_NEW_CUT */
4431 knife_finish_cut(kcd);
4432 kcd->mode = MODE_IDLE;
4433 }
4434 handled = true;
4435 break;
4436 case KNF_MODAL_PANNING:
4437 if (event->val != KM_RELEASE) {
4438 if (kcd->mode != MODE_PANNING) {
4439 kcd->prevmode = kcd->mode;
4440 kcd->mode = MODE_PANNING;
4441 }
4442 }
4443 else {
4444 kcd->mode = KnifeMode(kcd->prevmode);
4445 }
4446
4448 return OPERATOR_PASS_THROUGH;
4449 }
4450 }
4451 else { /* non-modal-mapped events */
4452 switch (event->type) {
4453 case MOUSEPAN:
4454 case MOUSEZOOM:
4455 case MOUSEROTATE:
4456 case WHEELUPMOUSE:
4457 case WHEELDOWNMOUSE:
4458 case NDOF_MOTION:
4459 return OPERATOR_PASS_THROUGH;
4460 case MOUSEMOVE: /* Mouse moved somewhere to select another loop. */
4461 if (kcd->mode != MODE_PANNING) {
4462 knifetool_update_mval(kcd, mval);
4463 do_refresh = true;
4464
4465 if (kcd->is_drag_hold) {
4466 if (kcd->linehits.size() >= 2) {
4467 knife_add_cut(kcd);
4468 }
4469 }
4470 }
4471
4472 break;
4473 default: {
4474 break;
4475 }
4476 }
4477 }
4478
4479 if (kcd->angle_snapping) {
4480 if (kcd->num.str_cur >= 3 ||
4482 {
4484 }
4485 if (event->type != EVT_MODAL_MAP) {
4486 /* Modal number-input inactive, try to handle numeric inputs last. */
4487 if (!handled && event->val == KM_PRESS && handleNumInput(C, &kcd->num, event)) {
4488 applyNumInput(&kcd->num, &snapping_increment_temp);
4489 /* Restrict number key input to 0 - 180 degree range. */
4490 if (snapping_increment_temp > KNIFE_MIN_ANGLE_SNAPPING_INCREMENT &&
4491 snapping_increment_temp <= KNIFE_MAX_ANGLE_SNAPPING_INCREMENT)
4492 {
4493 kcd->angle_snapping_increment = snapping_increment_temp;
4494 }
4495 knife_update_active(kcd, mval);
4496 knife_update_header(C, op, kcd);
4499 }
4500 }
4501 }
4502
4503 /* Constrain axes with X,Y,Z keys. */
4504 if (event->type == EVT_MODAL_MAP) {
4506 if (event->val == KNF_MODAL_X_AXIS && kcd->constrain_axis != KNF_CONSTRAIN_AXIS_X) {
4509 kcd->axis_string[0] = 'X';
4510 }
4511 else if (event->val == KNF_MODAL_Y_AXIS && kcd->constrain_axis != KNF_CONSTRAIN_AXIS_Y) {
4514 kcd->axis_string[0] = 'Y';
4515 }
4516 else if (event->val == KNF_MODAL_Z_AXIS && kcd->constrain_axis != KNF_CONSTRAIN_AXIS_Z) {
4519 kcd->axis_string[0] = 'Z';
4520 }
4521 else {
4522 /* Cycle through modes with repeated key presses. */
4524 kcd->constrain_axis_mode++;
4525 kcd->axis_string[0] += 32; /* Lower case. */
4526 }
4527 else {
4530 }
4531 }
4534
4535 /* Needed so changes to constraints are re-evaluated without any cursor motion. */
4536 knifetool_update_mval(kcd, mval);
4537
4538 do_refresh = true;
4539 }
4540 }
4541
4542 if (kcd->mode == MODE_DRAGGING) {
4544 }
4545 else {
4547 }
4548
4549 if (do_refresh) {
4551 knife_update_header(C, op, kcd);
4552 }
4553
4554 /* Keep going until the user confirms. */
4556}
4557
4559{
4560 const bool only_select = RNA_boolean_get(op->ptr, "only_selected");
4561 const bool cut_through = !RNA_boolean_get(op->ptr, "use_occlude_geometry");
4562 const bool xray = !RNA_boolean_get(op->ptr, "xray");
4563 const int visible_measurements = RNA_enum_get(op->ptr, "visible_measurements");
4564 const int angle_snapping = RNA_enum_get(op->ptr, "angle_snapping");
4565 const bool wait_for_input = RNA_boolean_get(op->ptr, "wait_for_input");
4566 const float angle_snapping_increment = RAD2DEGF(
4567 RNA_float_get(op->ptr, "angle_snapping_increment"));
4568
4570
4571 /* alloc new customdata */
4572 KnifeTool_OpData *kcd = MEM_new<KnifeTool_OpData>(__func__);
4573 op->customdata = kcd;
4575 &vc,
4576 kcd,
4578 only_select,
4579 cut_through,
4580 xray,
4581 visible_measurements,
4582 angle_snapping,
4583 angle_snapping_increment,
4584 true);
4585
4586 if (only_select) {
4587 bool faces_selected = false;
4588 for (Object *obedit : kcd->objects) {
4590 if (em->bm->totfacesel != 0) {
4591 faces_selected = true;
4592 }
4593 }
4594
4595 if (!faces_selected) {
4596 BKE_report(op->reports, RPT_ERROR, "Selected faces required");
4597 knifetool_cancel(C, op);
4598 return OPERATOR_CANCELLED;
4599 }
4600 }
4601
4603
4604 /* Add a modal handler for this operator - handles loop selection. */
4607
4608 if (wait_for_input == false) {
4609 /* Avoid copy-paste logic. */
4610 wmEvent event_modal{};
4611 event_modal.prev_val = KM_NOTHING;
4612 event_modal.type = EVT_MODAL_MAP;
4613 event_modal.val = KNF_MODAL_ADD_CUT;
4614
4615 copy_v2_v2_int(event_modal.mval, event->mval);
4616
4617 wmOperatorStatus retval = knifetool_modal(C, op, &event_modal);
4618 OPERATOR_RETVAL_CHECK(retval);
4620 UNUSED_VARS_NDEBUG(retval);
4621 }
4622
4623 knife_update_header(C, op, kcd);
4624
4626}
4627
4629{
4630 /* Description. */
4631 ot->name = "Knife Topology Tool";
4632 ot->idname = "MESH_OT_knife_tool";
4633 ot->description = "Cut new topology";
4634
4635 /* Callbacks. */
4636 ot->invoke = knifetool_invoke;
4637 ot->modal = knifetool_modal;
4638 ot->cancel = knifetool_cancel;
4640
4641 /* Flags. */
4643
4644 /* Properties. */
4645 PropertyRNA *prop;
4646 static const EnumPropertyItem visible_measurements_items[] = {
4647 {KNF_MEASUREMENT_NONE, "NONE", 0, "None", "Show no measurements"},
4648 {KNF_MEASUREMENT_BOTH, "BOTH", 0, "Both", "Show both distances and angles"},
4649 {KNF_MEASUREMENT_DISTANCE, "DISTANCE", 0, "Distance", "Show just distance measurements"},
4650 {KNF_MEASUREMENT_ANGLE, "ANGLE", 0, "Angle", "Show just angle measurements"},
4651 {0, nullptr, 0, nullptr, nullptr},
4652 };
4653
4654 static const EnumPropertyItem angle_snapping_items[] = {
4655 {KNF_CONSTRAIN_ANGLE_MODE_NONE, "NONE", 0, "None", "No angle snapping"},
4656 {KNF_CONSTRAIN_ANGLE_MODE_SCREEN, "SCREEN", 0, "Screen", "Screen space angle snapping"},
4658 "RELATIVE",
4659 0,
4660 "Relative",
4661 "Angle snapping relative to the previous cut edge"},
4662 {0, nullptr, 0, nullptr, nullptr},
4663 };
4664
4665 RNA_def_boolean(ot->srna,
4666 "use_occlude_geometry",
4667 true,
4668 "Occlude Geometry",
4669 "Only cut the front most geometry");
4670 RNA_def_boolean(ot->srna, "only_selected", false, "Only Selected", "Only cut selected geometry");
4671 RNA_def_boolean(ot->srna, "xray", true, "X-Ray", "Show cuts hidden by geometry");
4672
4673 RNA_def_enum(ot->srna,
4674 "visible_measurements",
4675 visible_measurements_items,
4677 "Measurements",
4678 "Visible distance and angle measurements");
4679 prop = RNA_def_enum(ot->srna,
4680 "angle_snapping",
4681 angle_snapping_items,
4683 "Angle Snapping",
4684 "Angle snapping mode");
4686
4687 prop = RNA_def_float(ot->srna,
4688 "angle_snapping_increment",
4692 "Angle Snap Increment",
4693 "The angle snap increment used when in constrained angle mode",
4697
4698 prop = RNA_def_boolean(ot->srna, "wait_for_input", true, "Wait for Input", "");
4700}
4701
4703
4704/* -------------------------------------------------------------------- */
4709
4710static bool edbm_mesh_knife_point_isect(LinkNode *polys, const float cent_ss[2])
4711{
4712 LinkNode *p = polys;
4713 int isect = 0;
4714
4715 while (p) {
4716 const float(*mval_fl)[2] = static_cast<const float(*)[2]>(p->link);
4717 const int mval_tot = MEM_allocN_len(mval_fl) / sizeof(*mval_fl);
4718 isect += int(isect_point_poly_v2(cent_ss, mval_fl, mval_tot - 1));
4719 p = p->next;
4720 }
4721
4722 if (isect % 2) {
4723 return true;
4724 }
4725 return false;
4726}
4727
4729 ViewContext *vc, const Span<Object *> objects, LinkNode *polys, bool use_tag, bool cut_through)
4730{
4731 KnifeTool_OpData *kcd;
4732
4733 /* Init. */
4734 {
4735 const bool only_select = false;
4736 const bool is_interactive = false; /* Can enable for testing. */
4737 const bool xray = false;
4738 const int visible_measurements = KNF_MEASUREMENT_NONE;
4739 const int angle_snapping = KNF_CONSTRAIN_ANGLE_MODE_NONE;
4740 const float angle_snapping_increment = KNIFE_DEFAULT_ANGLE_SNAPPING_INCREMENT;
4741
4742 kcd = MEM_new<KnifeTool_OpData>(__func__);
4743
4744 knifetool_init(vc,
4745 kcd,
4746 {objects},
4747 only_select,
4748 cut_through,
4749 xray,
4750 visible_measurements,
4751 angle_snapping,
4752 angle_snapping_increment,
4753 is_interactive);
4754
4755 kcd->ignore_edge_snapping = true;
4756 kcd->ignore_vert_snapping = true;
4757 }
4758
4759 /* Execute. */
4760 {
4761 LinkNode *p = polys;
4762
4763 knife_recalc_ortho(kcd);
4764
4765 while (p) {
4766 const float(*mval_fl)[2] = static_cast<const float(*)[2]>(p->link);
4767 const int mval_tot = MEM_allocN_len(mval_fl) / sizeof(*mval_fl);
4768 int i;
4769
4770 knife_start_cut(kcd, mval_fl[0]);
4771 kcd->mode = MODE_DRAGGING;
4772
4773 for (i = 1; i < mval_tot; i++) {
4774 knifetool_update_mval(kcd, mval_fl[i]);
4775 knife_add_cut(kcd);
4776 }
4777
4778 knife_finish_cut(kcd);
4779 kcd->mode = MODE_IDLE;
4780 p = p->next;
4781 }
4782 }
4783
4784 /* Finish. */
4785 {
4786 /* See #knifetool_finish_ex for why multiple passes are needed. */
4787 for (int ob_index : kcd->objects.index_range()) {
4788 Object *ob = kcd->objects[ob_index];
4790
4791 if (use_tag) {
4793 }
4794
4795 knifetool_finish_single_pre(kcd, ob_index);
4796 }
4797
4798 for (Object *ob : kcd->objects) {
4800
4801 /* Tag faces inside! */
4802 if (use_tag) {
4803 BMesh *bm = em->bm;
4804 BMEdge *e;
4805 BMIter iter;
4806 bool keep_search;
4807
4808/* Use face-loop tag to store if we have intersected. */
4809#define F_ISECT_IS_UNKNOWN(f) BM_elem_flag_test(BM_FACE_FIRST_LOOP(f), BM_ELEM_TAG)
4810#define F_ISECT_SET_UNKNOWN(f) BM_elem_flag_enable(BM_FACE_FIRST_LOOP(f), BM_ELEM_TAG)
4811#define F_ISECT_SET_OUTSIDE(f) BM_elem_flag_disable(BM_FACE_FIRST_LOOP(f), BM_ELEM_TAG)
4812 {
4813 BMFace *f;
4814 BM_ITER_MESH (f, &iter, bm, BM_FACES_OF_MESH) {
4817 }
4818 }
4819
4820 /* Tag all faces linked to cut edges. */
4821 BM_ITER_MESH (e, &iter, bm, BM_EDGES_OF_MESH) {
4822 /* Check are we tagged?, then we are an original face. */
4824 continue;
4825 }
4826
4827 BMFace *f;
4828 BMIter fiter;
4829 BM_ITER_ELEM (f, &fiter, e, BM_FACES_OF_EDGE) {
4830 float cent[3], cent_ss[2];
4832 mul_m4_v3(ob->object_to_world().ptr(), cent);
4833 knife_project_v2(kcd, cent, cent_ss);
4834 if (edbm_mesh_knife_point_isect(polys, cent_ss)) {
4836 }
4837 }
4838 }
4839
4840 /* Expand tags for faces which are not cut, but are inside the polys. */
4841 do {
4842 BMFace *f;
4843 keep_search = false;
4844 BM_ITER_MESH (f, &iter, bm, BM_FACES_OF_MESH) {
4846 continue;
4847 }
4848
4849 /* Am I connected to a tagged face via an un-tagged edge
4850 * (ie, not across a cut)? */
4851 BMLoop *l_first = BM_FACE_FIRST_LOOP(f);
4852 BMLoop *l_iter = l_first;
4853 bool found = false;
4854
4855 do {
4856 if (BM_elem_flag_test(l_iter->e, BM_ELEM_TAG) != false) {
4857 /* Now check if the adjacent faces is tagged. */
4858 BMLoop *l_radial_iter = l_iter->radial_next;
4859 if (l_radial_iter != l_iter) {
4860 do {
4861 if (BM_elem_flag_test(l_radial_iter->f, BM_ELEM_TAG)) {
4862 found = true;
4863 }
4864 } while ((l_radial_iter = l_radial_iter->radial_next) != l_iter &&
4865 (found == false));
4866 }
4867 }
4868 } while ((l_iter = l_iter->next) != l_first && (found == false));
4869
4870 if (found) {
4871 float cent[3], cent_ss[2];
4873 mul_m4_v3(ob->object_to_world().ptr(), cent);
4874 knife_project_v2(kcd, cent, cent_ss);
4875 if ((kcd->cut_through || point_is_visible(kcd, cent, cent_ss, (BMElem *)f)) &&
4876 edbm_mesh_knife_point_isect(polys, cent_ss))
4877 {
4879 keep_search = true;
4880 }
4881 else {
4882 /* Don't lose time on this face again, set it as outside. */
4884 }
4885 }
4886 }
4887 } while (keep_search);
4888
4889#undef F_ISECT_IS_UNKNOWN
4890#undef F_ISECT_SET_UNKNOWN
4891#undef F_ISECT_SET_OUTSIDE
4892 }
4893 }
4894
4895 for (Object *ob : kcd->objects) {
4896 /* Defer freeing data until the BVH tree is finished with, see: #point_is_visible and
4897 * the doc-string for #knifetool_finish_single_post. */
4899 }
4900
4901 knifetool_exit_ex(kcd);
4902 kcd = nullptr;
4903 }
4904}
4905
wmWindow * CTX_wm_window(const bContext *C)
BMEditMesh * BKE_editmesh_from_object(Object *ob)
Return the BMEditMesh for a given object.
Definition editmesh.cc:61
blender::Array< blender::float3 > BKE_editmesh_vert_coords_alloc(Depsgraph *depsgraph, BMEditMesh *em, Scene *scene, Object *ob)
Definition editmesh.cc:160
bool BKE_editmesh_eval_orig_map_available(const Mesh &mesh_eval, const Mesh *mesh_orig)
Definition editmesh.cc:67
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:126
int BKE_scene_orientation_get_index(Scene *scene, int slot_index)
Definition scene.cc:2436
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
size_t BKE_unit_value_as_string_scaled(char *str, int str_maxncpy, double value, int prec, int type, const UnitSettings &settings, bool pad)
Definition unit.cc:1889
@ B_UNIT_LENGTH
Definition BKE_unit.hh:124
@ B_UNIT_ROTATION
Definition BKE_unit.hh:128
@ B_UNIT_NONE
Definition BKE_unit.hh:123
void BLF_size(int fontid, float size)
Definition blf.cc:440
void BLF_color3ubv(int fontid, const unsigned char rgb[3])
Definition blf.cc:473
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:792
@ BLF_ROTATION
Definition BLF_api.hh:433
void BLF_disable(int fontid, int option)
Definition blf.cc:329
void BLF_rotation(int fontid, float angle)
Definition blf.cc:897
void BLF_draw(int fontid, const char *str, size_t str_len, ResultBLF *r_info=nullptr) ATTR_NONNULL(2)
Definition blf.cc:582
int blf_mono_font
Definition blf.cc:48
void BLF_enable(int fontid, int option)
Definition blf.cc:320
void BLF_position(int fontid, float x, float y, float z)
Definition blf.cc:385
#define BLI_array_alloca(arr, realsize)
Definition BLI_alloca.h:18
#define BLI_assert_unreachable()
Definition BLI_assert.h:93
#define BLI_assert(a)
Definition BLI_assert.h:46
struct GSet GSet
Definition BLI_ghash.h:337
BLI_INLINE void * BLI_ghashIterator_getKey(GHashIterator *ghi) ATTR_WARN_UNUSED_RESULT
Definition BLI_ghash.h:295
GSet * BLI_gset_ptr_new(const char *info)
BLI_INLINE void * BLI_ghashIterator_getValue(GHashIterator *ghi) ATTR_WARN_UNUSED_RESULT
Definition BLI_ghash.h:299
#define GHASH_ITER(gh_iter_, ghash_)
Definition BLI_ghash.h:318
GHash * BLI_ghash_ptr_new(const char *info) ATTR_MALLOC ATTR_WARN_UNUSED_RESULT
void BLI_gset_clear(GSet *gs, GSetKeyFreeFP keyfreefp)
unsigned int BLI_gset_len(const GSet *gs) ATTR_WARN_UNUSED_RESULT
Definition BLI_ghash.cc:954
void * BLI_ghash_lookup(const GHash *gh, const void *key) ATTR_WARN_UNUSED_RESULT
Definition BLI_ghash.cc:731
void BLI_ghash_insert(GHash *gh, void *key, void *val)
Definition BLI_ghash.cc:707
void BLI_ghash_free(GHash *gh, GHashKeyFreeFP keyfreefp, GHashValFreeFP valfreefp)
Definition BLI_ghash.cc:860
void BLI_gset_free(GSet *gs, GSetKeyFreeFP keyfreefp)
bool BLI_gset_add(GSet *gs, void *key)
Definition BLI_ghash.cc: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(ListBase *lb)
void BLI_addtail(ListBase *listbase, void *vlink) ATTR_NONNULL(1)
Definition listbase.cc:111
int BLI_listbase_count_at_most(const ListBase *listbase, int count_max) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL(1)
Definition listbase.cc:511
void BLI_remlink(ListBase *listbase, void *vlink) ATTR_NONNULL(1)
Definition listbase.cc:131
int BLI_listbase_count(const ListBase *listbase) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL(1)
Definition listbase.cc:524
void void void BLI_listbase_sort_r(ListBase *listbase, int(*cmp)(void *, const void *, const void *), void *thunk) ATTR_NONNULL(1
void void BLI_INLINE bool BLI_listbase_is_single(const ListBase *lb)
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])
#define DEG2RADF(_deg)
#define RAD2DEGF(_rad)
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:217
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_ray_v3(const float ray_origin_a[3], const float ray_direction_a[3], const float ray_origin_b[3], const float ray_direction_b[3], float *r_lambda_a, float *r_lambda_b)
bool isect_ray_plane_v3(const float ray_origin[3], const float ray_direction[3], const float plane[4], float *r_lambda, bool clip)
float dist_squared_to_plane_v3(const float p[3], const float plane[4])
Definition math_geom.cc:470
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)
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:409
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:365
float line_point_factor_v3(const float p[3], const float l1[3], const float l2[3])
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:41
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)
float dist_squared_to_line_segment_v2(const float p[2], const float l1[2], const float l2[2])
Definition math_geom.cc:291
void mul_m4_v3(const float M[4][4], float r[3])
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])
void axis_angle_to_quat(float r[4], const float axis[3], float angle)
void mul_qt_v3(const float q[4], float r[3])
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)
void rotate_normalized_v3_v3v3fl(float out[3], const float p[3], const float axis[3], float angle)
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
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 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])
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])
MemArena * BLI_memarena_new(size_t bufsize, const char *name) ATTR_WARN_UNUSED_RESULT ATTR_RETURNS_NONNULL ATTR_NONNULL(2) ATTR_MALLOC
void BLI_memarena_free(MemArena *ma) ATTR_NONNULL(1)
void * BLI_memarena_alloc(MemArena *ma, size_t size) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL(1) ATTR_MALLOC ATTR_ALLOC_SIZE(2)
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
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.cc:138
void BLI_stack_push(BLI_Stack *stack, const void *src) ATTR_NONNULL()
Definition stack.cc:132
bool BLI_stack_is_empty(const BLI_Stack *stack) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL()
Definition stack.cc:250
void BLI_stack_free(BLI_Stack *stack) ATTR_NONNULL()
Definition stack.cc:96
void * BLI_stack_peek(BLI_Stack *stack) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL()
Definition stack.cc:169
void * BLI_stack_push_r(BLI_Stack *stack) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL()
Definition stack.cc:103
void BLI_stack_discard(BLI_Stack *stack) ATTR_NONNULL()
Definition stack.cc:176
#define BLI_stack_new(esize, descr)
#define SNPRINTF(dst, format,...)
Definition BLI_string.h:599
#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
T * DEG_get_evaluated(const Depsgraph *depsgraph, T *id)
Object is a sort of wrapper for general info.
@ OB_MESH
@ USER_UNIT_NONE
@ SCE_SELECT_FACE
@ SCE_ORIENT_DEFAULT
@ SPACE_VIEW3D
#define UI_SCALE_FAC
@ RV3D_CAMOB
#define RV3D_CLIPPING_ENABLED(v3d, rv3d)
@ OPERATOR_CANCELLED
@ OPERATOR_FINISHED
@ OPERATOR_RUNNING_MODAL
@ OPERATOR_PASS_THROUGH
@ OP_IS_MODAL_CURSOR_REGION
#define OPERATOR_RETVAL_CHECK(ret)
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:69
bool handleNumInput(bContext *C, NumInput *n, const wmEvent *event)
Definition numinput.cc:311
bool applyNumInput(NumInput *n, float *vec)
Definition numinput.cc:189
@ NUM_NO_NEGATIVE
bool hasNumInput(const NumInput *n)
Definition numinput.cc:170
bool ED_operator_editmesh_view3d(bContext *C)
void ED_workspace_status_text(bContext *C, const char *str)
Definition area.cc:1040
void ED_region_tag_redraw(ARegion *region)
Definition area.cc:639
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)
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:279
float ED_view3d_pixel_size_no_ui_scale(const RegionView3D *rv3d, const float co[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)
static AppView * view
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:51
#define GPU_batch_uniform_1f(batch, name, x)
Definition GPU_batch.hh:301
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:309
void GPU_batch_draw_range(blender::gpu::Batch *batch, int vertex_first, int vertex_count)
@ GPU_BATCH_OWNS_VBO
Definition GPU_batch.hh:41
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)
void immUniform1f(const char *name, float x)
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_POINT_UNIFORM_SIZE_UNIFORM_COLOR_AA
@ GPU_SHADER_3D_UNIFORM_COLOR
void GPU_program_point_size(bool enable)
Definition gpu_state.cc:180
@ 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:166
@ GPU_DEPTH_LESS_EQUAL
Definition GPU_state.hh:114
@ GPU_DEPTH_NONE
Definition GPU_state.hh:111
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 *, blender::StringRef name, GPUVertCompType, uint comp_len, GPUVertFetchMode)
@ GPU_COMP_F32
static double angle(const Eigen::Vector3d &v1, const Eigen::Vector3d &v2)
Definition IK_Math.h:117
Read Guarded memory(de)allocation.
@ PROP_SKIP_SAVE
Definition RNA_types.hh:330
@ PROP_HIDDEN
Definition RNA_types.hh:324
@ PROP_ANGLE
Definition RNA_types.hh:240
#define C
Definition RandGen.cpp:29
void UI_GetThemeColorType3ubv(int colorid, int spacetype, unsigned char col[3])
@ TH_GIZMO_B
@ TH_WIRE
@ TH_TRANSFORM
@ TH_VERTEX
@ TH_GIZMO_A
@ TH_AXIS_Y
@ TH_AXIS_X
@ TH_GIZMO_PRIMARY
@ TH_AXIS_Z
@ TH_GIZMO_SECONDARY
@ TH_TEXT
void UI_GetThemeColor3ubv(int colorid, unsigned char col[3])
@ OPTYPE_BLOCKING
Definition WM_types.hh:184
@ OPTYPE_UNDO
Definition WM_types.hh:182
@ OPTYPE_REGISTER
Definition WM_types.hh:180
@ KM_NOTHING
Definition WM_types.hh:307
@ KM_PRESS
Definition WM_types.hh:308
@ KM_RELEASE
Definition WM_types.hh:309
#define BM_FACE_FIRST_LOOP(p)
@ BM_ELEM_HIDDEN
@ BM_ELEM_SELECT
@ BM_ELEM_TAG
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:41
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:27
#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
BMesh const char void * data
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.
Array< float3 > BM_mesh_vert_coords_alloc(BMesh *bm)
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
long long int int64_t
bool closest(btVector3 &v)
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:398
int64_t size() const
const T & last(const int64_t n=0) const
bool is_empty() const
IndexRange index_range() const
void resize(const int64_t new_size)
T * end()
T * begin()
void clear_and_shrink()
int64_t first_index_of_try(const T &value) const
void item(std::string text, int icon1, int icon2=0)
Definition area.cc:979
void opmodal(std::string text, const wmOperatorType *ot, int propvalue, bool inverted=false)
Definition area.cc:1005
bool add(const Key &key, const Value &value)
Definition BLI_map.hh:295
const Value & lookup(const Key &key) const
Definition BLI_map.hh:545
Value lookup_default(const Key &key, const Value &default_value) const
Definition BLI_map.hh:570
ItemIterator items() const &
Definition BLI_map.hh:902
bool contains(const Key &key) const
Definition BLI_set.hh:310
bool add(const Key &key)
Definition BLI_set.hh:248
bool remove(const Key &key)
Definition BLI_set.hh:385
constexpr bool is_empty() const
Definition BLI_span.hh:260
void append(const T &value)
#define fabsf(x)
static bool knife_find_closest_face(KnifeTool_OpData *kcd, const float2 &mval, const float3 &ray_orig, const float3 &ray_dir, KnifePosData *r_kpd)
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 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 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_input_ray_segment(const KnifeTool_OpData *kcd, const float mval[2], float r_origin[3], float r_end[3])
static bool knife_closest_constrain_to_edge(const float3 &cut_origin, const float3 &cut_dir, const float3 &kfv1_cageco, const float3 &kfv2_cageco, float r_close[3])
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 void knifetool_draw_visible_distances(const KnifeTool_OpData *kcd)
static ListBase * knife_empty_list(KnifeTool_OpData *kcd)
static void knife_draw_line(const KnifeTool_OpData *kcd, const uchar color[3])
static int knife_calculate_snap_ref_edges(KnifeTool_OpData *kcd, const float3 &ray_orig, const float3 &ray_dir)
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)
#define KNIFE_FLT_EPS_PX_EDGE
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)
@ 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_bm_face_is_not_hidden(BMFace *f)
static void knifetool_update_mval(KnifeTool_OpData *kcd, const float2 &mval)
static void knifetool_exit(wmOperator *op)
static bool knife_snap_angle_impl(const KnifeTool_OpData *kcd, const float3 &vec_x, const float3 &axis, const float3 &ray_orig, const float3 &ray_dir, float3 &r_cage, float &r_angle)
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)
static wmOperatorStatus knifetool_modal(bContext *C, wmOperator *op, const wmEvent *event)
#define KNIFE_MAX_ANGLE_SNAPPING_INCREMENT
static void knife_join_edge(KnifeEdge *newkfe, KnifeEdge *kfe)
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 knife_pos_data_clear(KnifePosData *kpd)
static void knife_constrain_axis(const KnifeTool_OpData *kcd, const float3 &ray_orig, const float3 &ray_dir, float3 &r_cage)
static void knifetool_draw_visible_angles(const KnifeTool_OpData *kcd)
static bool knife_add_single_cut__is_linehit_outside_face(BMFace *f, const KnifeLineHit *lh, const float co[3])
static int knife_update_active(KnifeTool_OpData *kcd, const float2 &mval)
@ KNF_CONSTRAIN_AXIS_Y
@ KNF_CONSTRAIN_AXIS_Z
@ KNF_CONSTRAIN_AXIS_X
@ KNF_CONSTRAIN_AXIS_NONE
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 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)
@ KNF_MEASUREMENT_NONE
@ KNF_MEASUREMENT_BOTH
@ KNF_MEASUREMENT_DISTANCE
@ KNF_MEASUREMENT_ANGLE
static void knife_cut_face(KnifeTool_OpData *kcd, BMFace *f, ListBase *hits)
static void knifetool_draw(const bContext *, ARegion *, void *arg)
static void knife_snap_update_from_mval(KnifeTool_OpData *kcd, const float2 &mval)
static const int * knife_bm_tri_index_get(const KnifeTool_OpData *kcd, int ob_index, int tri_index, int tri_index_buf[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 bool knife_find_closest_edge_of_face(KnifeTool_OpData *kcd, int ob_index, BMFace *f, const float2 &curr_cage_ss, const float3 *curr_cage_constrain, const float3 &ray_orig, const float3 &ray_dir, KnifePosData *r_kpd)
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_AXIS_MODE_LOCAL
@ KNF_CONSTRAIN_AXIS_MODE_GLOBAL
@ KNF_CONSTRAIN_AXIS_MODE_NONE
static void knife_add_single_cut(KnifeTool_OpData *kcd, KnifeLineHit *lh1, KnifeLineHit *lh2, BMFace *f)
static bool knife_snap_angle_screen(const KnifeTool_OpData *kcd, const float3 &ray_orig, const float3 &ray_dir, float3 &r_cage, float &r_angle)
#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
static bool knife_snap_angle_relative(KnifeTool_OpData *kcd, const float3 &ray_orig, const float3 &ray_dir, float3 &r_cage, float &r_angle)
#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 knife_snap_curr(KnifeTool_OpData *kcd, const float2 &mval, const float3 &ray_orig, const float3 &ray_dir, const float3 *curr_cage_constrain, const float3 *fallback)
@ KNF_CONSTRAIN_ANGLE_MODE_NONE
@ KNF_CONSTRAIN_ANGLE_MODE_SCREEN
@ KNF_CONSTRAIN_ANGLE_MODE_RELATIVE
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)
static float knife_snap_v3_angle(float3 &r, const float3 &dvec, const float3 &vecx, const float3 &axis, float angle_snap)
#define KNIFE_FLT_EPS_SQUARED
static BMElem * bm_elem_from_knife_vert(KnifeVert *kfv, KnifeEdge **r_kfe)
#define KNIFE_FLT_EPS
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])
static wmOperatorStatus knifetool_invoke(bContext *C, wmOperator *op, const wmEvent *event)
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)
uint pos
struct @064345207361167251075330302113175271221317160336::@113254110077376341056327177062323111323010325277 batch
#define cos
#define MEM_SIZE_OPTIMAL(size)
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:113
static char faces[256]
short 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 is_zero(const T &a)
MatBase< T, NumCol, NumRow > normalize(const MatBase< T, NumCol, NumRow > &a)
VecBase< float, 4 > float4
VecBase< float, 2 > float2
VecBase< float, 3 > float3
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.cc:36
#define FLT_MAX
Definition stdcycles.h:14
void * regiondata
ARegionRuntimeHandle * runtime
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
float origin[3]
struct IsectRayPrecalc * isect_precalc
float direction[3]
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
struct KnifeTool_OpData::@031103252032361030157015371106106230254070215072 edgenet
BLI_mempool * refs
KnifeUndoFrame * undo
Array< KnifeObjectInfo > objects_info
BLI_mempool * kverts
BLI_Stack * splitstack
BLI_Stack * undostack
BLI_mempool * kedges
Vector< KnifeLineHit > linehits
Vector< Object * > objects
float ortho_extent_center[3]
KnifePosData pos
KnifeMeasureData mdata
ListBase edges
ListBase faces
void * data
struct LinkData * next
void * link
struct LinkNode * next
void * first
MeshRuntimeHandle * runtime
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 persmatob[4][4]
float clip_local[6][4]
float viewinv[4][4]
struct ToolSettings * toolsettings
struct UnitSettings unit
RegionView3D * rv3d
Definition ED_view3d.hh:80
ARegion * region
Definition ED_view3d.hh:77
int mval[2]
Definition ED_view3d.hh:82
Scene * scene
Definition ED_view3d.hh:73
wmWindow * win
Definition ED_view3d.hh:79
ViewLayer * view_layer
Definition ED_view3d.hh:74
View3D * v3d
Definition ED_view3d.hh:78
Object * obact
Definition ED_view3d.hh:75
Object * obedit
Definition ED_view3d.hh:76
Depsgraph * depsgraph
Definition ED_view3d.hh:72
wmEventType type
Definition WM_types.hh:754
short val
Definition WM_types.hh:756
int mval[2]
Definition WM_types.hh:760
short prev_val
Definition WM_types.hh:811
const void * modal_items
struct ReportList * reports
struct wmOperatorType * type
struct PointerRNA * ptr
i
Definition text_draw.cc:230
max
Definition text_draw.cc:251
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
PointerRNA * ptr
Definition wm_files.cc:4226
wmOperatorType * ot
Definition wm_files.cc:4225
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:929
void WM_modalkeymap_assign(wmKeyMap *km, const char *opname)
wmKeyMap * WM_modalkeymap_find(wmKeyConfig *keyconf, const char *idname)
Definition wm_keymap.cc:956
void wmOrtho2_region_pixelspace(const ARegion *region)