Blender V5.0
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_utf8.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
394 immVertexFormat(), "pos", blender::gpu::VertAttrType::SFLOAT_32_32_32);
397 GPU_line_width(2.0);
399 immVertex3fv(pos, v1);
401 immEnd();
403}
404
411
413{
414 const uchar *color;
415 switch (kcd->constrain_axis) {
417 color = kcd->colors.xaxis;
418 break;
419 }
421 color = kcd->colors.yaxis;
422 break;
423 }
425 color = kcd->colors.zaxis;
426 break;
427 }
428 default: {
429 color = kcd->colors.axis_extra;
430 break;
431 }
432 }
434}
435
437{
442
444 immVertexFormat(), "pos", blender::gpu::VertAttrType::SFLOAT_32_32);
446
447 char numstr[256];
448 float numstr_size[2];
449 float posit[2];
450 const float bg_margin = 4.0f * UI_SCALE_FAC;
451 const float font_size = 14.0f;
452 const int distance_precision = 4;
453
454 /* Calculate distance and convert to string. */
455 const float cut_len = len_v3v3(kcd->prev.cage, kcd->curr.cage);
456
457 const UnitSettings &unit = kcd->scene->unit;
458 if (unit.system == USER_UNIT_NONE) {
459 SNPRINTF_UTF8(numstr, "%.*f", distance_precision, cut_len);
460 }
461 else {
463 numstr, sizeof(numstr), cut_len, distance_precision, B_UNIT_LENGTH, unit, false);
464 }
465
467 BLF_size(blf_mono_font, font_size * UI_SCALE_FAC);
469 BLF_width_and_height(blf_mono_font, numstr, sizeof(numstr), &numstr_size[0], &numstr_size[1]);
470
471 /* Center text. */
472 mid_v2_v2v2(posit, kcd->prev.mval, kcd->curr.mval);
473 posit[0] -= numstr_size[0] / 2.0f;
474 posit[1] -= numstr_size[1] / 2.0f;
475
476 /* Draw text background. */
477 float color_back[4] = {0.0f, 0.0f, 0.0f, 0.5f}; /* TODO: Replace with theme color. */
478 immUniformColor4fv(color_back);
479
482 posit[0] - bg_margin,
483 posit[1] - bg_margin,
484 posit[0] + bg_margin + numstr_size[0],
485 posit[1] + bg_margin + numstr_size[1]);
488
489 /* Draw text. */
490 uchar color_text[3];
491 UI_GetThemeColor3ubv(TH_TEXT, color_text);
492
493 BLF_color3ubv(blf_mono_font, color_text);
494 BLF_position(blf_mono_font, posit[0], posit[1], 0.0f);
495 BLF_draw(blf_mono_font, numstr, sizeof(numstr));
497
500}
501
503 const float start[3],
504 const float mid[3],
505 const float end[3],
506 const float start_ss[2],
507 const float mid_ss[2],
508 const float end_ss[2],
509 const float angle)
510{
511 const RegionView3D *rv3d = static_cast<const RegionView3D *>(kcd->region->regiondata);
512 const int arc_steps = 24;
513 const float arc_size = 64.0f * UI_SCALE_FAC;
514 const float bg_margin = 4.0f * UI_SCALE_FAC;
515 const float cap_size = 4.0f * UI_SCALE_FAC;
516 const float font_size = 14.0f;
517 const int angle_precision = 3;
518
519 /* Angle arc in 3d space. */
521
522 const uint pos_3d = GPU_vertformat_attr_add(
523 immVertexFormat(), "pos", blender::gpu::VertAttrType::SFLOAT_32_32_32);
525
526 {
527 float dir_tmp[3];
528 float ar_coord[3];
529
530 float dir_a[3];
531 float dir_b[3];
532 float quat[4];
533 float axis[3];
534 float arc_angle;
535
536 Object *ob = kcd->objects[kcd->bvh.ob_index];
537 const float inverse_average_scale = 1 / (ob->object_to_world().ptr()[0][0] +
538 ob->object_to_world().ptr()[1][1] +
539 ob->object_to_world().ptr()[2][2]);
540
541 const float px_scale =
542 3.0f * inverse_average_scale *
544 min_fff(arc_size, len_v2v2(start_ss, mid_ss) / 2.0f, len_v2v2(end_ss, mid_ss) / 2.0f));
545
546 sub_v3_v3v3(dir_a, start, mid);
547 sub_v3_v3v3(dir_b, end, mid);
548 normalize_v3(dir_a);
549 normalize_v3(dir_b);
550
551 cross_v3_v3v3(axis, dir_a, dir_b);
552 arc_angle = angle_normalized_v3v3(dir_a, dir_b);
553
554 axis_angle_to_quat(quat, axis, arc_angle / arc_steps);
555
556 copy_v3_v3(dir_tmp, dir_a);
557
559 GPU_line_width(1.0);
560
561 immBegin(GPU_PRIM_LINE_STRIP, arc_steps + 1);
562 for (int j = 0; j <= arc_steps; j++) {
563 madd_v3_v3v3fl(ar_coord, mid, dir_tmp, px_scale);
564 mul_qt_v3(quat, dir_tmp);
565
566 immVertex3fv(pos_3d, ar_coord);
567 }
568 immEnd();
569 }
570
572
573 /* Angle text and background in 2d space. */
578
580 immVertexFormat(), "pos", blender::gpu::VertAttrType::SFLOAT_32_32);
582
583 /* Angle as string. */
584 char numstr[256];
585 float numstr_size[2];
586 float posit[2];
587
588 const UnitSettings &unit = kcd->scene->unit;
589 if (unit.system == USER_UNIT_NONE) {
590 SNPRINTF_UTF8(numstr, "%.*f" BLI_STR_UTF8_DEGREE_SIGN, angle_precision, RAD2DEGF(angle));
591 }
592 else {
594 numstr, sizeof(numstr), double(angle), angle_precision, B_UNIT_ROTATION, unit, false);
595 }
596
598 BLF_size(blf_mono_font, font_size * UI_SCALE_FAC);
600 BLF_width_and_height(blf_mono_font, numstr, sizeof(numstr), &numstr_size[0], &numstr_size[1]);
601
602 posit[0] = mid_ss[0] + (cap_size * 2.0f);
603 posit[1] = mid_ss[1] - (numstr_size[1] / 2.0f);
604
605 /* Draw text background. */
606 float color_back[4] = {0.0f, 0.0f, 0.0f, 0.5f}; /* TODO: Replace with theme color. */
607 immUniformColor4fv(color_back);
608
610 immRectf(pos_2d,
611 posit[0] - bg_margin,
612 posit[1] - bg_margin,
613 posit[0] + bg_margin + numstr_size[0],
614 posit[1] + bg_margin + numstr_size[1]);
617
618 /* Draw text. */
619 uchar color_text[3];
620 UI_GetThemeColor3ubv(TH_TEXT, color_text);
621
622 BLF_color3ubv(blf_mono_font, color_text);
623 BLF_position(blf_mono_font, posit[0], posit[1], 0.0f);
625 BLF_draw(blf_mono_font, numstr, sizeof(numstr));
627
630
632}
633
635{
636 KnifeVert *kfv;
637 KnifeVert *tempkfv;
638 KnifeEdge *kfe;
639 KnifeEdge *tempkfe;
640
641 if (kcd->curr.vert) {
642 kfv = kcd->curr.vert;
643
644 float min_angle = FLT_MAX;
645 float angle = 0.0f;
646 float *end;
647
648 kfe = static_cast<KnifeEdge *>(((LinkData *)kfv->edges.first)->data);
649 LISTBASE_FOREACH (LinkData *, ref, &kfv->edges) {
650 tempkfe = static_cast<KnifeEdge *>(ref->data);
651 if (tempkfe->v1 != kfv) {
652 tempkfv = tempkfe->v1;
653 }
654 else {
655 tempkfv = tempkfe->v2;
656 }
657 angle = angle_v3v3v3(kcd->prev.cage, kcd->curr.cage, tempkfv->cageco);
658 if (angle < min_angle) {
659 min_angle = angle;
660 kfe = tempkfe;
661 end = tempkfv->cageco;
662 }
663 }
664
665 if (min_angle > KNIFE_FLT_EPSBIG) {
666 /* Last vertex in screen space. */
667 float end_ss[2];
669
671 kcd->prev.cage,
672 kcd->curr.cage,
673 end,
674 kcd->prev.mval,
675 kcd->curr.mval,
676 end_ss,
677 min_angle);
678 }
679 }
680 else if (kcd->curr.edge) {
681 kfe = kcd->curr.edge;
682
683 /* Check for most recent cut (if cage is part of previous cut). */
684 if (!compare_v3v3(kfe->v1->cageco, kcd->prev.cage, KNIFE_FLT_EPSBIG) &&
686 {
687 /* Determine acute angle. */
688 float angle1 = angle_v3v3v3(kcd->prev.cage, kcd->curr.cage, kfe->v1->cageco);
689 float angle2 = angle_v3v3v3(kcd->prev.cage, kcd->curr.cage, kfe->v2->cageco);
690
691 float angle;
692 float *end;
693 if (angle1 < angle2) {
694 angle = angle1;
695 end = kfe->v1->cageco;
696 }
697 else {
698 angle = angle2;
699 end = kfe->v2->cageco;
700 }
701
702 /* Last vertex in screen space. */
703 float end_ss[2];
705
707 kcd, kcd->prev.cage, kcd->curr.cage, end, kcd->prev.mval, kcd->curr.mval, end_ss, angle);
708 }
709 }
710
711 if (kcd->prev.vert) {
712 kfv = kcd->prev.vert;
713 float min_angle = FLT_MAX;
714 float angle = 0.0f;
715 float *end;
716
717 /* If using relative angle snapping, always draw angle to reference edge. */
719 kfe = kcd->snap_ref_edge;
720 if (kfe->v1 != kfv) {
721 tempkfv = kfe->v1;
722 }
723 else {
724 tempkfv = kfe->v2;
725 }
726 min_angle = angle_v3v3v3(kcd->curr.cage, kcd->prev.cage, tempkfv->cageco);
727 end = tempkfv->cageco;
728 }
729 else {
730 /* Choose minimum angle edge. */
731 kfe = static_cast<KnifeEdge *>(((LinkData *)kfv->edges.first)->data);
732 LISTBASE_FOREACH (LinkData *, ref, &kfv->edges) {
733 tempkfe = static_cast<KnifeEdge *>(ref->data);
734 if (tempkfe->v1 != kfv) {
735 tempkfv = tempkfe->v1;
736 }
737 else {
738 tempkfv = tempkfe->v2;
739 }
740 angle = angle_v3v3v3(kcd->curr.cage, kcd->prev.cage, tempkfv->cageco);
741 if (angle < min_angle) {
742 min_angle = angle;
743 kfe = tempkfe;
744 end = tempkfv->cageco;
745 }
746 }
747 }
748
749 if (min_angle > KNIFE_FLT_EPSBIG) {
750 /* Last vertex in screen space. */
751 float end_ss[2];
753
755 kcd->curr.cage,
756 kcd->prev.cage,
757 end,
758 kcd->curr.mval,
759 kcd->prev.mval,
760 end_ss,
761 min_angle);
762 }
763 }
764 else if (kcd->prev.edge) {
765 /* Determine acute angle. */
766 kfe = kcd->prev.edge;
767 float angle1 = angle_v3v3v3(kcd->curr.cage, kcd->prev.cage, kfe->v1->cageco);
768 float angle2 = angle_v3v3v3(kcd->curr.cage, kcd->prev.cage, kfe->v2->cageco);
769
770 float angle;
771 float *end;
772 /* kcd->prev.edge can have one vertex part of cut and one part of mesh? */
773 /* This never seems to happen for kcd->curr.edge. */
774 if ((!kcd->prev.vert || kcd->prev.vert->v == kfe->v1->v) || kfe->v1->is_cut) {
775 angle = angle2;
776 end = kfe->v2->cageco;
777 }
778 else if ((!kcd->prev.vert || kcd->prev.vert->v == kfe->v2->v) || kfe->v2->is_cut) {
779 angle = angle1;
780 end = kfe->v1->cageco;
781 }
782 else {
783 if (angle1 < angle2) {
784 angle = angle1;
785 end = kfe->v1->cageco;
786 }
787 else {
788 angle = angle2;
789 end = kfe->v2->cageco;
790 }
791 }
792
793 /* Last vertex in screen space. */
794 float end_ss[2];
796
798 kcd, kcd->curr.cage, kcd->prev.cage, end, kcd->curr.mval, kcd->prev.mval, end_ss, angle);
799 }
800 else if (kcd->mdata.is_stored && !kcd->prev.is_space()) {
801 float angle = angle_v3v3v3(kcd->curr.cage, kcd->prev.cage, kcd->mdata.cage);
803 kcd->curr.cage,
804 kcd->prev.cage,
805 kcd->mdata.cage,
806 kcd->curr.mval,
807 kcd->prev.mval,
808 kcd->mdata.mval,
809 angle);
810 }
811}
812
814{
815 switch (kcd->dist_angle_mode) {
819 break;
820 }
823 break;
824 }
827 break;
828 }
829 }
830}
831
832/* Modal loop selection drawing callback. */
833static void knifetool_draw(const bContext * /*C*/, ARegion * /*region*/, void *arg)
834{
835 const KnifeTool_OpData *kcd = static_cast<const KnifeTool_OpData *>(arg);
837
839 GPU_polygon_offset(1.0f, 1.0f);
840
842 uint pos = GPU_vertformat_attr_add(format, "pos", blender::gpu::VertAttrType::SFLOAT_32_32_32);
843
844 /* Draw points. */
847
848 /* Needed for AA points. */
850
851 if (kcd->prev.vert) {
853 immUniform1f("size", 11 * UI_SCALE_FAC);
854
856 immVertex3fv(pos, kcd->prev.cage);
857 immEnd();
858 }
859
860 if (kcd->prev.bmface || kcd->prev.edge) {
862 immUniform1f("size", 9 * UI_SCALE_FAC);
863
865 immVertex3fv(pos, kcd->prev.cage);
866 immEnd();
867 }
868
869 if (kcd->curr.vert) {
871 immUniform1f("size", 11 * UI_SCALE_FAC);
872
874 immVertex3fv(pos, kcd->curr.cage);
875 immEnd();
876 }
877 else if (kcd->curr.edge) {
878 /* Lines (handled below.) */
879 }
880
881 if (kcd->curr.bmface || kcd->curr.edge) {
883 immUniform1f("size", 9 * UI_SCALE_FAC);
884
886 immVertex3fv(pos, kcd->curr.cage);
887 immEnd();
888 }
889
890 if (kcd->depth_test) {
892 }
893
894 if (kcd->totkvert > 0) {
895 BLI_mempool_iter iter;
896 KnifeVert *kfv;
897
899 immUniform1f("size", 5.0 * UI_SCALE_FAC);
900
902
903 BLI_mempool_iternew(kcd->kverts, &iter);
904 for (kfv = static_cast<KnifeVert *>(BLI_mempool_iterstep(&iter)); kfv;
905 kfv = static_cast<KnifeVert *>(BLI_mempool_iterstep(&iter)))
906 {
907 if (!kfv->is_cut || kfv->is_invalid) {
908 continue;
909 }
910
911 immVertex3fv(pos, kfv->cageco);
912 }
913
914 immEnd();
915
918 }
919
921
923
924 /* Draw lines. */
926
927 if (kcd->mode == MODE_DRAGGING) {
929 GPU_line_width(2.0);
930
932 immVertex3fv(pos, kcd->prev.cage);
933 immVertex3fv(pos, kcd->curr.cage);
934 immEnd();
935 }
936
937 if (kcd->curr.vert) {
938 /* Points (handled above). */
939 }
940 else if (kcd->curr.edge) {
942 GPU_line_width(2.0);
943
947 immEnd();
948 }
949
950 if (kcd->totkedge > 0) {
951 BLI_mempool_iter iter;
952 KnifeEdge *kfe;
953
955 GPU_line_width(1.0);
956
958
959 BLI_mempool_iternew(kcd->kedges, &iter);
960 for (kfe = static_cast<KnifeEdge *>(BLI_mempool_iterstep(&iter)); kfe;
961 kfe = static_cast<KnifeEdge *>(BLI_mempool_iterstep(&iter)))
962 {
963 if (!kfe->is_cut || kfe->is_invalid) {
964 continue;
965 }
966
967 immVertex3fv(pos, kfe->v1->cageco);
968 immVertex3fv(pos, kfe->v2->cageco);
969 }
970
971 immEnd();
972
975 }
976
977 /* Draw relative angle snapping reference edge. */
980 GPU_line_width(2.0);
981
985 immEnd();
986 }
987
988 const int64_t total_hits = kcd->linehits.size();
989 if (total_hits > 0) {
991
993 GPU_vertbuf_data_alloc(*vert, total_hits);
994
995 int other_verts_count = 0;
996 int snapped_verts_count = 0;
997 for (const KnifeLineHit &hit : kcd->linehits) {
998 if (hit.v) {
999 GPU_vertbuf_attr_set(vert, pos, snapped_verts_count++, hit.cagehit);
1000 }
1001 else {
1002 GPU_vertbuf_attr_set(vert, pos, total_hits - 1 - other_verts_count++, hit.cagehit);
1003 }
1004 }
1005
1006 gpu::Batch *batch = GPU_batch_create_ex(GPU_PRIM_POINTS, vert, nullptr, GPU_BATCH_OWNS_VBO);
1008
1009 /* Draw any snapped verts first. */
1010 float fcol[4];
1012 GPU_batch_uniform_4fv(batch, "color", fcol);
1014
1015 if (snapped_verts_count > 0) {
1016 GPU_batch_draw_range(batch, 0, snapped_verts_count);
1017 }
1018
1019 /* Now draw the rest. */
1021 GPU_batch_uniform_4fv(batch, "color", fcol);
1023
1024 if (other_verts_count > 0) {
1025 GPU_batch_draw_range(batch, snapped_verts_count, other_verts_count);
1026 }
1027
1029
1031 }
1032
1034
1036
1037 if (kcd->mode == MODE_DRAGGING) {
1038 if (kcd->is_angle_snapping) {
1040 }
1041 else if (kcd->axis_constrained) {
1043 }
1044
1045 if (kcd->show_dist_angle) {
1047 }
1048 }
1049
1051
1052 /* Reset default. */
1054}
1055
1057
1058/* -------------------------------------------------------------------- */
1061
1063{
1064 auto get_modal_key_str = [&](int id) {
1065 return WM_modalkeymap_operator_items_to_string(op->type, id, true).value_or("");
1066 };
1067
1069 status.opmodal(IFACE_("Cut"), op->type, KNF_MODAL_ADD_CUT);
1070 status.opmodal(IFACE_("Close"), op->type, KNF_MODAL_ADD_CUT_CLOSED);
1071 status.opmodal(IFACE_("Stop"), op->type, KNF_MODAL_NEW_CUT);
1072 status.opmodal(IFACE_("Confirm"), op->type, KNF_MODAL_CONFIRM);
1073 status.opmodal(IFACE_("Cancel"), op->type, KNF_MODAL_CANCEL);
1074 status.opmodal(IFACE_("Undo"), op->type, KNF_MODAL_UNDO);
1075 status.opmodal(IFACE_("Pan View"), op->type, KNF_MODAL_PANNING);
1076 status.opmodal(IFACE_("Midpoint Snap"), op->type, KNF_MODAL_MIDPOINT_ON, kcd->snap_midpoints);
1077 status.opmodal(
1078 IFACE_("Ignore Snap"), op->type, KNF_MODAL_IGNORE_SNAP_ON, kcd->ignore_edge_snapping);
1079 status.opmodal(IFACE_("Cut Through"), op->type, KNF_MODAL_CUT_THROUGH_TOGGLE, kcd->cut_through);
1080 status.opmodal({}, op->type, KNF_MODAL_X_AXIS, kcd->constrain_axis == 1);
1081 status.opmodal({}, op->type, KNF_MODAL_Y_AXIS, kcd->constrain_axis == 2);
1082 status.opmodal({}, op->type, KNF_MODAL_Z_AXIS, kcd->constrain_axis == 3);
1083 status.item(IFACE_("Axis"), ICON_NONE);
1084 status.opmodal(
1086 status.opmodal(IFACE_("X-Ray"), op->type, KNF_MODAL_DEPTH_TEST_TOGGLE, !kcd->depth_test);
1087
1088 const std::string angle = fmt::format(
1089 "{}: {:.2f}({:.2f}) ({}{}{}{})",
1090 IFACE_("Angle Constraint"),
1091 (kcd->angle >= 0.0f) ? RAD2DEGF(kcd->angle) : 360.0f + RAD2DEGF(kcd->angle),
1096 kcd->angle_snapping ?
1097 ((kcd->angle_snapping_mode == KNF_CONSTRAIN_ANGLE_MODE_SCREEN) ? "Screen" : "Relative") :
1098 "OFF", /* TODO: Can this be simplified? */
1101 get_modal_key_str(KNF_MODAL_CYCLE_ANGLE_SNAP_EDGE) :
1102 "",
1103 (kcd->angle_snapping_mode == KNF_CONSTRAIN_ANGLE_MODE_RELATIVE) ? ": Cycle Edge" : "");
1104
1106}
1107
1109
1110/* -------------------------------------------------------------------- */
1113
1114static const int *knife_bm_tri_index_get(const KnifeTool_OpData *kcd,
1115 int ob_index,
1116 int tri_index,
1117 int tri_index_buf[3])
1118{
1119 const KnifeObjectInfo *obinfo = &kcd->objects_info[ob_index];
1120 if (!obinfo->tri_indices.is_empty()) {
1121 return obinfo->tri_indices[tri_index];
1122 }
1123 const std::array<BMLoop *, 3> &ltri = obinfo->em->looptris[tri_index];
1124 for (int i = 0; i < 3; i++) {
1125 tri_index_buf[i] = BM_elem_index_get(ltri[i]->v);
1126 }
1127 return tri_index_buf;
1128}
1129
1131 int ob_index,
1132 int tri_index,
1133 float cos[3][3])
1134{
1135 const KnifeObjectInfo *obinfo = &kcd->objects_info[ob_index];
1136 int tri_ind_buf[3];
1137 const int *tri_ind = knife_bm_tri_index_get(kcd, ob_index, tri_index, tri_ind_buf);
1138 for (int i = 0; i < 3; i++) {
1139 copy_v3_v3(cos[i], obinfo->positions_cage[tri_ind[i]]);
1140 }
1141}
1142
1144 int ob_index,
1145 int tri_index,
1146 float cos[3][3])
1147{
1148 knife_bm_tri_cagecos_get(kcd, ob_index, tri_index, cos);
1149 const Object *ob = kcd->objects[ob_index];
1150 for (int i = 0; i < 3; i++) {
1151 mul_m4_v3(ob->object_to_world().ptr(), cos[i]);
1152 }
1153}
1154
1156
1157/* -------------------------------------------------------------------- */
1160
1162{
1163 return (BM_elem_flag_test(f, BM_ELEM_SELECT) != 0);
1164}
1165
1167{
1168 return (BM_elem_flag_test(f, BM_ELEM_HIDDEN) == 0);
1169}
1170
1172{
1173 Object *ob;
1174 BMEditMesh *em;
1175
1176 /* Test Function. */
1177 bool (*test_fn)(BMFace *);
1178 if (kcd->only_select && kcd->cut_through) {
1179 test_fn = knife_bm_face_is_select;
1180 }
1181 else {
1183 }
1184
1185 /* Construct BVH Tree. */
1186 const float epsilon = FLT_EPSILON * 2.0f;
1187 int tottri = 0;
1188 int ob_tottri = 0;
1190 BMFace *f_test = nullptr, *f_test_prev = nullptr;
1191 bool test_fn_ret = false;
1192
1193 /* Calculate tottri. */
1194 for (Object *ob : kcd->objects) {
1195 ob_tottri = 0;
1196 em = BKE_editmesh_from_object(ob);
1197
1198 for (int i = 0; i < em->looptris.size(); i++) {
1199 f_test = em->looptris[i][0]->f;
1200 if (f_test != f_test_prev) {
1201 test_fn_ret = test_fn(f_test);
1202 f_test_prev = f_test;
1203 }
1204
1205 if (test_fn_ret) {
1206 ob_tottri++;
1207 }
1208 }
1209
1210 tottri += ob_tottri;
1211 }
1212
1213 kcd->bvh.tree = BLI_bvhtree_new(tottri, epsilon, 8, 8);
1214
1215 f_test_prev = nullptr;
1216 test_fn_ret = false;
1217
1218 /* Add triangles for each object.
1219 * TODO:
1220 * test_fn can leave large gaps between bvh tree indices.
1221 * Compacting bvh tree indices may be possible.
1222 * Don't forget to update #knife_bvh_intersect_plane! */
1223 tottri = 0;
1224 for (const int ob_index : kcd->objects.index_range()) {
1225 ob = kcd->objects[ob_index];
1226 em = BKE_editmesh_from_object(ob);
1227 looptris = em->looptris;
1228
1229 for (int i = 0; i < em->looptris.size(); i++) {
1230
1231 f_test = looptris[i][0]->f;
1232 if (f_test != f_test_prev) {
1233 test_fn_ret = test_fn(f_test);
1234 f_test_prev = f_test;
1235 }
1236
1237 if (!test_fn_ret) {
1238 continue;
1239 }
1240
1241 float tri_cos[3][3];
1242 knife_bm_tri_cagecos_get_worldspace(kcd, ob_index, i, tri_cos);
1243 BLI_bvhtree_insert(kcd->bvh.tree, i + tottri, &tri_cos[0][0], 3);
1244 }
1245
1246 tottri += em->looptris.size();
1247 }
1248
1250}
1251
1252/* Wrapper for #BLI_bvhtree_free. */
1254{
1255 if (kcd->bvh.tree) {
1257 kcd->bvh.tree = nullptr;
1258 }
1259}
1260
1261static void knife_bvh_raycast_cb(void *userdata,
1262 int index,
1263 const BVHTreeRay *ray,
1264 BVHTreeRayHit *hit)
1265{
1266 if (index == -1) {
1267 return;
1268 }
1269
1270 KnifeTool_OpData *kcd = static_cast<KnifeTool_OpData *>(userdata);
1271 BMLoop *const *ltri = nullptr;
1272 Object *ob;
1273 BMEditMesh *em;
1274
1275 float dist;
1276 bool isect;
1277 int tottri;
1278
1279 tottri = 0;
1280 int ob_index = 0;
1281 for (; ob_index < kcd->objects.size(); ob_index++) {
1282 index -= tottri;
1283 ob = kcd->objects[ob_index];
1284 em = BKE_editmesh_from_object(ob);
1285 tottri = em->looptris.size();
1286 if (index < tottri) {
1287 ltri = em->looptris[index].data();
1288 break;
1289 }
1290 }
1291 BLI_assert(ltri != nullptr);
1292
1293 if (kcd->bvh.filter_cb) {
1294 if (!kcd->bvh.filter_cb(ltri[0]->f, kcd->bvh.filter_data)) {
1295 return;
1296 }
1297 }
1298
1299 float tri_cos[3][3];
1300 knife_bm_tri_cagecos_get_worldspace(kcd, ob_index, index, tri_cos);
1301 isect = (ray->radius > 0.0f ?
1303 ray->origin, ray->direction, UNPACK3(tri_cos), &dist, nullptr, ray->radius) :
1304#ifdef USE_KDOPBVH_WATERTIGHT
1306 ray->origin, ray->isect_precalc, UNPACK3(tri_cos), &dist, nullptr));
1307#else
1308isect_ray_tri_v3(ray->origin, ray->direction, UNPACK3(tri_cos), &dist, nullptr);
1309#endif
1310
1311 if (isect && dist < hit->dist) {
1312 madd_v3_v3v3fl(hit->co, ray->origin, ray->direction, dist);
1313
1314 /* Discard clipped points. */
1315 if (RV3D_CLIPPING_ENABLED(kcd->vc.v3d, kcd->vc.rv3d) &&
1316 ED_view3d_clipping_test(kcd->vc.rv3d, hit->co, false))
1317 {
1318 return;
1319 }
1320
1321 hit->dist = dist;
1322 hit->index = index;
1323
1324 copy_v3_v3(hit->no, ltri[0]->f->no);
1325
1326 kcd->bvh.looptris = em->looptris;
1327 kcd->bvh.ob_index = ob_index;
1328 }
1329}
1330
1331/* `co` is expected to be in world space. */
1333 const float co[3],
1334 const float dir[3],
1335 const float radius,
1336 float *r_dist,
1337 float r_cagehit[3],
1338 int *r_ob_index)
1339{
1340 BMFace *face;
1341 BVHTreeRayHit hit;
1342 const float dist = r_dist ? *r_dist : FLT_MAX;
1343 hit.dist = dist;
1344 hit.index = -1;
1345
1346 BLI_bvhtree_ray_cast(kcd->bvh.tree, co, dir, radius, &hit, knife_bvh_raycast_cb, kcd);
1347
1348 /* Handle Hit */
1349 if (hit.index != -1 && hit.dist != dist) {
1350 face = kcd->bvh.looptris[hit.index][0]->f;
1351
1352 if (r_cagehit) {
1353 copy_v3_v3(r_cagehit, hit.co);
1354 }
1355
1356 if (r_dist) {
1357 *r_dist = hit.dist;
1358 }
1359
1360 if (r_ob_index) {
1361 *r_ob_index = kcd->bvh.ob_index;
1362 }
1363
1364 return face;
1365 }
1366 return nullptr;
1367}
1368
1369/* `co` is expected to be in world space. */
1371 const float co[3],
1372 const float dir[3],
1373 const float radius,
1374 float *r_dist,
1375 float r_cagehit[3],
1376 int *r_ob_index,
1377 bool (*filter_cb)(BMFace *f, void *userdata),
1378 void *filter_userdata)
1379{
1380 kcd->bvh.filter_cb = filter_cb;
1381 kcd->bvh.filter_data = filter_userdata;
1382
1383 BMFace *face = knife_bvh_raycast(kcd, co, dir, radius, r_dist, r_cagehit, r_ob_index);
1384
1385 kcd->bvh.filter_cb = nullptr;
1386 kcd->bvh.filter_data = nullptr;
1387
1388 return face;
1389}
1390
1392
1393/* -------------------------------------------------------------------- */
1396
1397static void knife_project_v2(const KnifeTool_OpData *kcd, const float co[3], float sco[2])
1398{
1400}
1401
1402/* Ray is returned in world space. */
1404 const float mval[2],
1405 float r_origin[3],
1406 float r_end[3])
1407{
1408 /* Unproject to find view ray. */
1410 kcd->vc.depsgraph, kcd->region, kcd->vc.v3d, mval, r_origin, r_end, false);
1411}
1412
1413/* No longer used, but may be useful in the future. */
1415 float mval[3],
1416 float r_cage[3])
1417{
1418 float origin[3];
1419 float origin_ofs[3];
1420 float ray[3], ray_normal[3];
1421
1422 knife_input_ray_segment(kcd, mval, origin, origin_ofs);
1423
1424 sub_v3_v3v3(ray, origin_ofs, origin);
1425 normalize_v3_v3(ray_normal, ray);
1426
1427 knife_bvh_raycast(kcd, origin, ray_normal, 0.0f, nullptr, r_cage, nullptr);
1428}
1429
1431{
1432 bool v1_inside, v2_inside;
1433 bool v1_inface, v2_inface;
1434 BMLoop *l1, *l2;
1435
1436 if (!f || !v1 || !v2) {
1437 return false;
1438 }
1439
1440 l1 = v1->v ? BM_face_vert_share_loop(f, v1->v) : nullptr;
1441 l2 = v2->v ? BM_face_vert_share_loop(f, v2->v) : nullptr;
1442
1443 if ((l1 && l2) && BM_loop_is_adjacent(l1, l2)) {
1444 /* Boundary-case, always false to avoid edge-in-face checks below. */
1445 return false;
1446 }
1447
1448 /* Find out if v1 and v2, if set, are part of the face. */
1449 v1_inface = (l1 != nullptr);
1450 v2_inface = (l2 != nullptr);
1451
1452 /* BM_face_point_inside_test uses best-axis projection so this isn't most accurate test... */
1453 v1_inside = v1_inface ? false : BM_face_point_inside_test(f, v1->co);
1454 v2_inside = v2_inface ? false : BM_face_point_inside_test(f, v2->co);
1455 if ((v1_inface && v2_inside) || (v2_inface && v1_inside) || (v1_inside && v2_inside)) {
1456 return true;
1457 }
1458
1459 if (v1_inface && v2_inface) {
1460 float mid[3];
1461 /* Can have case where v1 and v2 are on shared chain between two faces.
1462 * BM_face_splits_check_legal does visibility and self-intersection tests,
1463 * but it is expensive and maybe a bit buggy, so use a simple
1464 * "is the midpoint in the face" test. */
1465 mid_v3_v3v3(mid, v1->co, v2->co);
1466 return BM_face_point_inside_test(f, mid);
1467 }
1468 return false;
1469}
1470
1472{
1474 kcd->vc.depsgraph, kcd->vc.v3d, kcd->vc.rv3d, true, &kcd->clipsta, &kcd->clipend);
1475}
1476
1478
1479/* -------------------------------------------------------------------- */
1484
1486{
1487 BMElem *ele_test;
1488 KnifeEdge *kfe = nullptr;
1489
1490 /* vert? */
1491 ele_test = (BMElem *)kfv->v;
1492
1493 if (r_kfe || ele_test == nullptr) {
1494 if (kfv->v == nullptr) {
1495 LISTBASE_FOREACH (LinkData *, ref, &kfv->edges) {
1496 kfe = static_cast<KnifeEdge *>(ref->data);
1497 if (kfe->e) {
1498 if (r_kfe) {
1499 *r_kfe = kfe;
1500 }
1501 break;
1502 }
1503 }
1504 }
1505 }
1506
1507 /* edge? */
1508 if (ele_test == nullptr) {
1509 if (kfe) {
1510 ele_test = (BMElem *)kfe->e;
1511 }
1512 }
1513
1514 /* face? */
1515 if (ele_test == nullptr) {
1516 if (BLI_listbase_is_single(&kfe->faces)) {
1517 ele_test = static_cast<BMElem *>(((LinkData *)kfe->faces.first)->data);
1518 }
1519 }
1520
1521 return ele_test;
1522}
1523
1525{
1526 BMElem *ele_test;
1527
1528 ele_test = (BMElem *)kfe->e;
1529
1530 if (ele_test == nullptr) {
1531 ele_test = (BMElem *)kfe->basef;
1532 }
1533
1534 return ele_test;
1535}
1536
1538
1539/* -------------------------------------------------------------------- */
1542
1544{
1545 ListBase *list;
1546
1547 list = static_cast<ListBase *>(BLI_memarena_alloc(kcd->arena, sizeof(ListBase)));
1548 BLI_listbase_clear(list);
1549 return list;
1550}
1551
1552static void knife_append_list(KnifeTool_OpData *kcd, ListBase *lst, void *elem)
1553{
1554 LinkData *ref;
1555
1556 ref = static_cast<LinkData *>(BLI_mempool_calloc(kcd->refs));
1557 ref->data = elem;
1558 BLI_addtail(lst, ref);
1559}
1560
1561static LinkData *find_ref(ListBase *lb, void *ref)
1562{
1563 LISTBASE_FOREACH (LinkData *, ref1, lb) {
1564 if (ref1->data == ref) {
1565 return ref1;
1566 }
1567 }
1568
1569 return nullptr;
1570}
1571
1572static void knife_append_list_no_dup(KnifeTool_OpData *kcd, ListBase *lst, void *elem)
1573{
1574 if (!find_ref(lst, elem)) {
1575 knife_append_list(kcd, lst, elem);
1576 }
1577}
1578
1580{
1581 knife_append_list(kcd, &kfe->v1->edges, kfe);
1582 knife_append_list(kcd, &kfe->v2->edges, kfe);
1583}
1584
1585/* Add faces of an edge to a KnifeVert's faces list. No checks for duplicates. */
1587{
1588 BMIter bmiter;
1589 BMFace *f;
1590
1591 BM_ITER_ELEM (f, &bmiter, e, BM_FACES_OF_EDGE) {
1592 knife_append_list(kcd, &kfv->faces, f);
1593 }
1594}
1595
1596/* Find a face in common in the two faces lists.
1597 * If more than one, return the first; if none, return nullptr. */
1599{
1600 LISTBASE_FOREACH (LinkData *, ref1, faces1) {
1601 LISTBASE_FOREACH (LinkData *, ref2, faces2) {
1602 if (ref1->data == ref2->data) {
1603 return (BMFace *)(ref1->data);
1604 }
1605 }
1606 }
1607 return nullptr;
1608}
1609
1611
1612/* -------------------------------------------------------------------- */
1615
1616static KnifeVert *new_knife_vert(KnifeTool_OpData *kcd, const float co[3], const float cageco[3])
1617{
1618 KnifeVert *kfv = static_cast<KnifeVert *>(BLI_mempool_calloc(kcd->kverts));
1619
1620 kcd->totkvert++;
1621
1622 copy_v3_v3(kfv->co, co);
1623 copy_v3_v3(kfv->cageco, cageco);
1624
1625 return kfv;
1626}
1627
1629{
1630 KnifeEdge *kfe = static_cast<KnifeEdge *>(BLI_mempool_calloc(kcd->kedges));
1631 kcd->totkedge++;
1632 return kfe;
1633}
1634
1635/* Get a KnifeVert wrapper for an existing BMVert. */
1637{
1638 KnifeVert *kfv = static_cast<KnifeVert *>(BLI_ghash_lookup(kcd->origvertmap, v));
1639 const float *cageco;
1640
1641 if (!kfv) {
1642 BMIter bmiter;
1643 BMFace *f;
1644
1645 if (BM_elem_index_get(v) >= 0) {
1646 cageco = kcd->objects_info[ob_index].positions_cage[BM_elem_index_get(v)];
1647 }
1648 else {
1649 cageco = v->co;
1650 }
1651
1652 float cageco_ws[3];
1653 Object *ob = kcd->objects[ob_index];
1654 mul_v3_m4v3(cageco_ws, ob->object_to_world().ptr(), cageco);
1655
1656 kfv = new_knife_vert(kcd, v->co, cageco_ws);
1657 kfv->v = v;
1658 kfv->ob_index = ob_index;
1659
1660 BLI_ghash_insert(kcd->origvertmap, v, kfv);
1661 BM_ITER_ELEM (f, &bmiter, v, BM_FACES_OF_VERT) {
1662 knife_append_list(kcd, &kfv->faces, f);
1663 }
1664 }
1665
1666 return kfv;
1667}
1668
1669/* Get a KnifeEdge wrapper for an existing BMEdge. */
1671{
1672 KnifeEdge *kfe = static_cast<KnifeEdge *>(BLI_ghash_lookup(kcd->origedgemap, e));
1673 if (!kfe) {
1674 BMIter bmiter;
1675 BMFace *f;
1676
1677 kfe = new_knife_edge(kcd);
1678 kfe->e = e;
1679 kfe->v1 = get_bm_knife_vert(kcd, e->v1, ob_index);
1680 kfe->v2 = get_bm_knife_vert(kcd, e->v2, ob_index);
1681
1682 knife_add_to_vert_edges(kcd, kfe);
1683
1684 BLI_ghash_insert(kcd->origedgemap, e, kfe);
1685
1686 BM_ITER_ELEM (f, &bmiter, e, BM_FACES_OF_EDGE) {
1687 knife_append_list(kcd, &kfe->faces, f);
1688 }
1689 }
1690
1691 return kfe;
1692}
1693
1695{
1696 ListBase *list = static_cast<ListBase *>(BLI_ghash_lookup(kcd->kedgefacemap, f));
1697
1698 if (!list) {
1699 BMIter bmiter;
1700 BMEdge *e;
1701
1702 list = knife_empty_list(kcd);
1703
1704 BM_ITER_ELEM (e, &bmiter, f, BM_EDGES_OF_FACE) {
1705 knife_append_list(kcd, list, get_bm_knife_edge(kcd, e, ob_index));
1706 }
1707
1708 BLI_ghash_insert(kcd->kedgefacemap, f, list);
1709 }
1710
1711 return list;
1712}
1713
1715{
1716 knife_append_list(kcd, knife_get_face_kedges(kcd, kfe->v1->ob_index, f), kfe);
1717 knife_append_list(kcd, &kfe->faces, f);
1718}
1719
1721 KnifeEdge *kfe,
1722 const float co[3],
1723 const float cageco[3],
1724 KnifeEdge **r_kfe)
1725{
1726 KnifeEdge *newkfe = new_knife_edge(kcd);
1727 LinkData *ref;
1728 BMFace *f;
1729
1730 newkfe->v1 = kfe->v1;
1731 newkfe->v2 = new_knife_vert(kcd, co, cageco);
1732 newkfe->v2->ob_index = kfe->v1->ob_index;
1733 newkfe->v2->is_cut = true;
1734 if (kfe->e) {
1735 knife_add_edge_faces_to_vert(kcd, newkfe->v2, kfe->e);
1736 }
1737 else {
1738 /* kfe cuts across an existing face.
1739 * If v1 and v2 are in multiple faces together (e.g., if they
1740 * are in doubled polys) then this arbitrarily chooses one of them. */
1741 f = knife_find_common_face(&kfe->v1->faces, &kfe->v2->faces);
1742 if (f) {
1743 knife_append_list(kcd, &newkfe->v2->faces, f);
1744 }
1745 }
1746 newkfe->basef = kfe->basef;
1747
1748 ref = find_ref(&kfe->v1->edges, kfe);
1749 BLI_remlink(&kfe->v1->edges, ref);
1750
1751 kfe->v1 = newkfe->v2;
1752 kfe->v1->is_splitting = true;
1753 BLI_addtail(&kfe->v1->edges, ref);
1754
1755 LISTBASE_FOREACH (LinkData *, ref, &kfe->faces) {
1756 knife_edge_append_face(kcd, newkfe, static_cast<BMFace *>(ref->data));
1757 }
1758
1759 knife_add_to_vert_edges(kcd, newkfe);
1760
1761 newkfe->is_cut = kfe->is_cut;
1762 newkfe->e = kfe->e;
1763
1764 newkfe->splits++;
1765 kfe->splits++;
1766
1767 kcd->undo->splits++;
1768
1769 BLI_stack_push(kcd->splitstack, (void *)&kfe);
1770 BLI_stack_push(kcd->splitstack, (void *)&newkfe);
1771
1772 *r_kfe = newkfe;
1773
1774 return newkfe->v2;
1775}
1776
1777/* Rejoin two edges split by #knife_split_edge. */
1778static void knife_join_edge(KnifeEdge *newkfe, KnifeEdge *kfe)
1779{
1780 newkfe->is_invalid = true;
1781 newkfe->v2->is_invalid = true;
1782
1783 kfe->v1 = newkfe->v1;
1784
1785 kfe->splits--;
1786 kfe->v1->is_splitting = false;
1787 kfe->v2->is_splitting = false;
1788}
1789
1791
1792/* -------------------------------------------------------------------- */
1795
1796static void knife_snap_curr(KnifeTool_OpData *kcd,
1797 const float2 &mval,
1798 const float3 &ray_orig,
1799 const float3 &ray_dir,
1800 const float3 *curr_cage_constrain,
1801 const float3 *fallback);
1802
1803/* User has just clicked for first time or first time after a restart (E key).
1804 * Copy the current position data into prev. */
1805static void knife_start_cut(KnifeTool_OpData *kcd, const float2 &mval)
1806{
1807 float3 ray_orig;
1808 float3 ray_dir;
1810 kcd->vc.depsgraph, kcd->region, kcd->vc.v3d, mval, ray_orig, ray_dir, false);
1811
1812 knife_snap_curr(kcd, mval, ray_orig, ray_dir, nullptr, nullptr);
1813 kcd->prev = kcd->curr;
1814 kcd->mdata.is_stored = false;
1815}
1816
1818{
1819 kpos->bmface = lh->f;
1820 kpos->vert = lh->v;
1821 kpos->edge = lh->kfe;
1822 copy_v3_v3(kpos->cage, lh->cagehit);
1823 copy_v2_v2(kpos->mval, lh->schit);
1824}
1825
1826/* Primary key: lambda along cut
1827 * Secondary key: lambda along depth
1828 * Tertiary key: pointer comparisons of verts if both snapped to verts
1829 */
1830static int linehit_compare(const KnifeLineHit &lh1, const KnifeLineHit &lh2)
1831{
1832 if (lh1.l < lh2.l) {
1833 return true;
1834 }
1835 if (lh1.l > lh2.l) {
1836 return false;
1837 }
1838 if (lh1.m < lh2.m) {
1839 return true;
1840 }
1841 if (lh1.m > lh2.m) {
1842 return false;
1843 }
1844 if (lh1.v < lh2.v) {
1845 return true;
1846 }
1847 if (lh1.v > lh2.v) {
1848 return false;
1849 }
1850 return false;
1851}
1852
1853/*
1854 * Sort linehits by distance along cut line, and secondarily from
1855 * front to back (from eye), and tertiarily by snap vertex,
1856 * and remove any duplicates.
1857 */
1859{
1860 bool is_double = false;
1861
1862 if (kcd->linehits.is_empty()) {
1863 return;
1864 }
1865
1866 std::sort(kcd->linehits.begin(), kcd->linehits.end(), linehit_compare);
1867
1868 /* Remove any edge hits that are preceded or followed
1869 * by a vertex hit that is very near. Mark such edge hits using
1870 * l == -1 and then do another pass to actually remove.
1871 * Also remove all but one of a series of vertex hits for the same vertex. */
1872 const int64_t total_hits = kcd->linehits.size();
1873 for (int i = 0; i < total_hits; i++) {
1874 KnifeLineHit *lhi = &kcd->linehits[i];
1875 if (lhi->v == nullptr) {
1876 continue;
1877 }
1878
1879 for (int j = i - 1; j >= 0; j--) {
1880 KnifeLineHit *lhj = &kcd->linehits[j];
1881 if (!lhj->kfe || fabsf(lhi->l - lhj->l) > KNIFE_FLT_EPSBIG ||
1882 fabsf(lhi->m - lhj->m) > KNIFE_FLT_EPSBIG)
1883 {
1884 break;
1885 }
1886
1887 if (lhi->kfe == lhj->kfe) {
1888 lhj->l = -1.0f;
1889 is_double = true;
1890 }
1891 }
1892 for (int j = i + 1; j < total_hits; j++) {
1893 KnifeLineHit *lhj = &kcd->linehits[j];
1894 if (fabsf(lhi->l - lhj->l) > KNIFE_FLT_EPSBIG || fabsf(lhi->m - lhj->m) > KNIFE_FLT_EPSBIG) {
1895 break;
1896 }
1897 if ((lhj->kfe && (lhi->kfe == lhj->kfe)) || (lhi->v == lhj->v)) {
1898 lhj->l = -1.0f;
1899 is_double = true;
1900 }
1901 }
1902 }
1903
1904 if (is_double) {
1905 /* Delete-in-place loop: copying from pos j to pos i+1. */
1906 int i = 0;
1907 int j = 1;
1908 while (j < total_hits) {
1909 KnifeLineHit *lhi = &kcd->linehits[i];
1910 KnifeLineHit *lhj = &kcd->linehits[j];
1911 if (lhj->l == -1.0f) {
1912 j++; /* Skip copying this one. */
1913 }
1914 else {
1915 /* Copy unless a no-op. */
1916 if (lhi->l == -1.0f) {
1917 /* Could happen if linehits[0] is being deleted. */
1918 memcpy(&kcd->linehits[i], &kcd->linehits[j], sizeof(KnifeLineHit));
1919 }
1920 else {
1921 if (i + 1 != j) {
1922 memcpy(&kcd->linehits[i + 1], &kcd->linehits[j], sizeof(KnifeLineHit));
1923 }
1924 i++;
1925 }
1926 j++;
1927 }
1928 }
1929 kcd->linehits.resize(i + 1);
1930 }
1931}
1932
1933/* Add hit to list of hits in facehits[f], where facehits is a map, if not already there. */
1935 GHash *facehits,
1936 BMFace *f,
1937 KnifeLineHit *hit)
1938{
1939 ListBase *list = static_cast<ListBase *>(BLI_ghash_lookup(facehits, f));
1940
1941 if (!list) {
1942 list = knife_empty_list(kcd);
1943 BLI_ghash_insert(facehits, f, list);
1944 }
1945 knife_append_list_no_dup(kcd, list, hit);
1946}
1947
1953 const KnifeLineHit *lh,
1954 const float co[3])
1955{
1956
1957 if (lh->v && lh->v->v) {
1958 BMLoop *l; /* side-of-loop */
1959 if ((l = BM_face_vert_share_loop(f, lh->v->v)) &&
1960 (BM_loop_point_side_of_loop_test(l, co) < 0.0f))
1961 {
1962 return true;
1963 }
1964 }
1965 else if (lh->kfe && lh->kfe->e) {
1966 BMLoop *l; /* side-of-edge */
1967 if ((l = BM_face_edge_share_loop(f, lh->kfe->e)) &&
1968 (BM_loop_point_side_of_edge_test(l, co) < 0.0f))
1969 {
1970 return true;
1971 }
1972 }
1973
1974 return false;
1975}
1976
1978 KnifeLineHit *lh1,
1979 KnifeLineHit *lh2,
1980 BMFace *f)
1981{
1982 KnifeEdge *kfe, *kfe2;
1983 BMEdge *e_base;
1984
1985 if ((lh1->v && lh1->v == lh2->v) || (lh1->kfe && lh1->kfe == lh2->kfe)) {
1986 return;
1987 }
1988
1989 /* If the cut is on an edge. */
1990 if ((lh1->v && lh2->v) && (lh1->v->v && lh2->v && lh2->v->v) &&
1991 (e_base = BM_edge_exists(lh1->v->v, lh2->v->v)))
1992 {
1993 return;
1994 }
1997 {
1998 return;
1999 }
2000
2001 /* Check if edge actually lies within face (might not, if this face is concave). */
2002 if ((lh1->v && !lh1->kfe) && (lh2->v && !lh2->kfe)) {
2003 if (!knife_verts_edge_in_face(lh1->v, lh2->v, f)) {
2004 return;
2005 }
2006 }
2007
2008 kfe = new_knife_edge(kcd);
2009 kfe->is_cut = true;
2010 kfe->basef = f;
2011
2012 if (lh1->v) {
2013 kfe->v1 = lh1->v;
2014 }
2015 else if (lh1->kfe) {
2016 kfe->v1 = knife_split_edge(kcd, lh1->kfe, lh1->hit, lh1->cagehit, &kfe2);
2017 lh1->v = kfe->v1; /* Record the #KnifeVert for this hit. */
2018 }
2019 else {
2020 BLI_assert(lh1->f);
2021 kfe->v1 = new_knife_vert(kcd, lh1->hit, lh1->cagehit);
2022 kfe->v1->ob_index = lh1->ob_index;
2023 kfe->v1->is_cut = true;
2024 knife_append_list(kcd, &kfe->v1->faces, lh1->f);
2025 lh1->v = kfe->v1; /* Record the #KnifeVert for this hit. */
2026 }
2027
2028 if (lh2->v) {
2029 kfe->v2 = lh2->v;
2030 }
2031 else if (lh2->kfe) {
2032 kfe->v2 = knife_split_edge(kcd, lh2->kfe, lh2->hit, lh2->cagehit, &kfe2);
2033 lh2->v = kfe->v2; /* Future uses of lh2 won't split again. */
2034 }
2035 else {
2036 BLI_assert(lh2->f);
2037 kfe->v2 = new_knife_vert(kcd, lh2->hit, lh2->cagehit);
2038 kfe->v2->ob_index = lh2->ob_index;
2039 kfe->v2->is_cut = true;
2040 knife_append_list(kcd, &kfe->v2->faces, lh2->f);
2041 lh2->v = kfe->v2; /* Record the KnifeVert for this hit. */
2042 }
2043
2044 knife_add_to_vert_edges(kcd, kfe);
2045
2046 if (kfe->basef && !find_ref(&kfe->faces, kfe->basef)) {
2047 knife_edge_append_face(kcd, kfe, kfe->basef);
2048 }
2049
2050 /* Update current undo frame cut count. */
2051 kcd->undo->cuts++;
2052}
2053
2054/* Given a list of KnifeLineHits for one face, sorted by l
2055 * and then by m, make the required KnifeVerts and
2056 * KnifeEdges.
2057 */
2059{
2060 LinkData *r;
2061
2062 if (BLI_listbase_count_at_most(hits, 2) != 2) {
2063 return;
2064 }
2065
2066 for (r = static_cast<LinkData *>(hits->first); r->next; r = r->next) {
2068 kcd, static_cast<KnifeLineHit *>(r->data), static_cast<KnifeLineHit *>(r->next->data), f);
2069 }
2070}
2071
2073{
2074 KnifeEdge *kfe;
2075 int edge_array_len = BLI_listbase_count(kfedges);
2076 int i;
2077
2078 BMEdge **edge_array = static_cast<BMEdge **>(BLI_array_alloca(edge_array, edge_array_len));
2079
2080 /* Point to knife edges we've created edges in, edge_array aligned. */
2081 KnifeEdge **kfe_array = static_cast<KnifeEdge **>(BLI_array_alloca(kfe_array, edge_array_len));
2082
2084
2085 i = 0;
2086 LISTBASE_FOREACH (LinkData *, ref, kfedges) {
2087 bool is_new_edge = false;
2088 kfe = static_cast<KnifeEdge *>(ref->data);
2089
2090 if (kfe->is_invalid) {
2091 continue;
2092 }
2093
2094 if (kfe->e == nullptr) {
2095 if (kfe->v1->v && kfe->v2->v) {
2096 kfe->e = BM_edge_exists(kfe->v1->v, kfe->v2->v);
2097 }
2098 }
2099
2100 if (kfe->e) {
2101 if (BM_edge_in_face(kfe->e, f)) {
2102 /* Shouldn't happen, but in this case just ignore. */
2103 continue;
2104 }
2105 }
2106 else {
2107 if (kfe->v1->v == nullptr) {
2108 kfe->v1->v = BM_vert_create(bm, kfe->v1->co, nullptr, eBMCreateFlag(0));
2109 }
2110 if (kfe->v2->v == nullptr) {
2111 kfe->v2->v = BM_vert_create(bm, kfe->v2->co, nullptr, eBMCreateFlag(0));
2112 }
2113 BLI_assert(kfe->e == nullptr);
2114 kfe->e = BM_edge_create(bm, kfe->v1->v, kfe->v2->v, nullptr, eBMCreateFlag(0));
2115 if (kfe->e) {
2117 BM_edge_select_set(bm, kfe->e, true);
2118 }
2119 is_new_edge = true;
2120 }
2121 }
2122
2123 BLI_assert(kfe->e);
2124
2125 if (BLI_gset_add(kcd->edgenet.edge_visit, kfe->e)) {
2126 kfe_array[i] = is_new_edge ? kfe : nullptr;
2127 edge_array[i] = kfe->e;
2128 i += 1;
2129 }
2130 }
2131
2132 if (i) {
2133 const int edge_array_len_orig = i;
2134 edge_array_len = i;
2135
2136#ifdef USE_NET_ISLAND_CONNECT
2137 uint edge_array_holes_len;
2138 BMEdge **edge_array_holes;
2140 f,
2141 edge_array,
2142 edge_array_len,
2143 true,
2144 kcd->edgenet.arena,
2145 &edge_array_holes,
2146 &edge_array_holes_len))
2147 {
2149 for (i = edge_array_len; i < edge_array_holes_len; i++) {
2150 BM_edge_select_set(bm, edge_array_holes[i], true);
2151 }
2152 }
2153
2154 edge_array_len = edge_array_holes_len;
2155 edge_array = edge_array_holes; /* Owned by the arena. */
2156 }
2157#endif
2158
2159 {
2160 BM_face_split_edgenet(bm, f, edge_array, edge_array_len, nullptr);
2161 }
2162
2163 /* Remove dangling edges, not essential - but nice for users. */
2164 for (i = 0; i < edge_array_len_orig; i++) {
2165 if (kfe_array[i] == nullptr) {
2166 continue;
2167 }
2168 if (BM_edge_is_wire(kfe_array[i]->e)) {
2169 BM_edge_kill(bm, kfe_array[i]->e);
2170 kfe_array[i]->e = nullptr;
2171 }
2172 }
2173
2174#ifdef USE_NET_ISLAND_CONNECT
2176#endif
2177 }
2178
2179 BLI_gset_clear(kcd->edgenet.edge_visit, nullptr);
2180}
2181
2182static int sort_verts_by_dist_cb(void *co_p, const void *cur_a_p, const void *cur_b_p)
2183{
2184 const KnifeVert *cur_a = static_cast<const KnifeVert *>(((const LinkData *)cur_a_p)->data);
2185 const KnifeVert *cur_b = static_cast<const KnifeVert *>(((const LinkData *)cur_b_p)->data);
2186 const float *co = static_cast<const float *>(co_p);
2187 const float a_sq = len_squared_v3v3(co, cur_a->co);
2188 const float b_sq = len_squared_v3v3(co, cur_b->co);
2189
2190 if (a_sq < b_sq) {
2191 return -1;
2192 }
2193 if (a_sq > b_sq) {
2194 return 1;
2195 }
2196 return 0;
2197}
2198
2199/* Use the network of KnifeEdges and KnifeVerts accumulated to make real BMVerts and BMEdedges. */
2200static void knife_make_cuts(KnifeTool_OpData *kcd, int ob_index)
2201{
2202 Object *ob = kcd->objects[ob_index];
2204 BMesh *bm = em->bm;
2205 KnifeEdge *kfe;
2206 KnifeVert *kfv;
2207 BMEdge *enew;
2208 ListBase *list;
2209 float pct;
2210 BLI_mempool_iter iter;
2213
2214 /* Put list of cutting edges for a face into fhash, keyed by face. */
2215 BLI_mempool_iternew(kcd->kedges, &iter);
2216 for (kfe = static_cast<KnifeEdge *>(BLI_mempool_iterstep(&iter)); kfe;
2217 kfe = static_cast<KnifeEdge *>(BLI_mempool_iterstep(&iter)))
2218 {
2219 if (kfe->is_invalid || kfe->v1->ob_index != ob_index) {
2220 continue;
2221 }
2222
2223 /* Select edges that lie directly on the cut. */
2224 if (kcd->select_result) {
2225 if (kfe->e && kfe->is_cut) {
2226 BM_edge_select_set(bm, kfe->e, true);
2227 }
2228 }
2229
2230 BMFace *f = kfe->basef;
2231 if (!f || kfe->e) {
2232 continue;
2233 }
2234 list = fhash.lookup_default(f, nullptr);
2235 if (!list) {
2236 list = knife_empty_list(kcd);
2237 fhash.add(f, list);
2238 }
2239 knife_append_list(kcd, list, kfe);
2240 }
2241
2242 /* Put list of splitting vertices for an edge into ehash, keyed by edge. */
2243 BLI_mempool_iternew(kcd->kverts, &iter);
2244 for (kfv = static_cast<KnifeVert *>(BLI_mempool_iterstep(&iter)); kfv;
2245 kfv = static_cast<KnifeVert *>(BLI_mempool_iterstep(&iter)))
2246 {
2247 if (kfv->v || kfv->is_invalid || kfv->ob_index != ob_index) {
2248 continue; /* Already have a BMVert. */
2249 }
2250 LISTBASE_FOREACH (LinkData *, ref, &kfv->edges) {
2251 kfe = static_cast<KnifeEdge *>(ref->data);
2252 BMEdge *e = kfe->e;
2253 if (!e) {
2254 continue;
2255 }
2256 list = ehash.lookup_default(e, nullptr);
2257 if (!list) {
2258 list = knife_empty_list(kcd);
2259 ehash.add(e, list);
2260 }
2261 /* There can be more than one kfe in kfv's list with same e. */
2262 if (!find_ref(list, kfv)) {
2263 knife_append_list(kcd, list, kfv);
2264 }
2265 }
2266 }
2267
2268 /* Split bmesh edges where needed. */
2269 for (auto [e, list] : ehash.items()) {
2271
2272 LISTBASE_FOREACH (LinkData *, ref, list) {
2273 kfv = static_cast<KnifeVert *>(ref->data);
2274 pct = line_point_factor_v3(kfv->co, e->v1->co, e->v2->co);
2275 kfv->v = BM_edge_split(bm, e, e->v1, &enew, pct);
2276 }
2277 }
2278
2279 if (kcd->only_select) {
2281 }
2282
2283 /* Do cuts for each face. */
2284 for (auto [f, list] : fhash.items()) {
2285 knife_make_face_cuts(kcd, bm, f, list);
2286 }
2287}
2288
2289/* User has just left-clicked after the first time.
2290 * Add all knife cuts implied by line from prev to curr.
2291 * If that line crossed edges then kcd->linehits will be non-null.
2292 * Make all of the KnifeVerts and KnifeEdges implied by this cut.
2293 */
2295{
2296 GHash *facehits;
2297 BMFace *f;
2298 GHashIterator giter;
2299 ListBase *list;
2300
2301 /* Allocate new undo frame on stack, unless cut is being dragged. */
2302 if (!kcd->is_drag_undo) {
2303 kcd->undo = static_cast<KnifeUndoFrame *>(BLI_stack_push_r(kcd->undostack));
2304 kcd->undo->pos = kcd->prev;
2305 kcd->undo->cuts = 0;
2306 kcd->undo->splits = 0;
2307 kcd->undo->mdata = kcd->mdata;
2308 kcd->is_drag_undo = true;
2309 }
2310
2311 /* Save values for angle drawing calculations. */
2312 copy_v3_v3(kcd->mdata.cage, kcd->prev.cage);
2313 copy_v2_v2(kcd->mdata.mval, kcd->prev.mval);
2314 kcd->mdata.is_stored = true;
2315
2317 if (kcd->linehits.is_empty()) {
2318 if (kcd->is_drag_hold == false) {
2319 kcd->prev = kcd->curr;
2320 }
2321 return;
2322 }
2323
2324 /* Consider most recent linehit in angle drawing calculations. */
2325 if (kcd->linehits.size() >= 2) {
2326 copy_v3_v3(kcd->mdata.cage, kcd->linehits[kcd->linehits.size() - 2].cagehit);
2327 }
2328
2329 /* Make facehits: map face -> list of linehits touching it. */
2330 facehits = BLI_ghash_ptr_new("knife facehits");
2331 for (KnifeLineHit &hit : kcd->linehits) {
2332 KnifeLineHit *lh = &hit;
2333 if (lh->f) {
2334 add_hit_to_facehits(kcd, facehits, lh->f, lh);
2335 }
2336 if (lh->v) {
2337 LISTBASE_FOREACH (LinkData *, r, &lh->v->faces) {
2338 add_hit_to_facehits(kcd, facehits, static_cast<BMFace *>(r->data), lh);
2339 }
2340 }
2341 if (lh->kfe) {
2342 LISTBASE_FOREACH (LinkData *, r, &lh->kfe->faces) {
2343 add_hit_to_facehits(kcd, facehits, static_cast<BMFace *>(r->data), lh);
2344 }
2345 }
2346 }
2347
2348 /* NOTE: as following loop progresses, the 'v' fields of
2349 * the linehits will be filled in (as edges are split or
2350 * in-face verts are made), so it may be true that both
2351 * the v and the kfe or f fields will be non-null. */
2352 GHASH_ITER (giter, facehits) {
2353 f = (BMFace *)BLI_ghashIterator_getKey(&giter);
2354 list = (ListBase *)BLI_ghashIterator_getValue(&giter);
2355 knife_cut_face(kcd, f, list);
2356 }
2357
2358 /* Set up for next cut. */
2359 kcd->prev = kcd->curr;
2360
2361 if (kcd->prev.bmface) {
2362 /* Was "in face" but now we have a KnifeVert it is snapped to. */
2363 KnifeLineHit *lh = &kcd->linehits.last();
2364 kcd->prev.vert = lh->v;
2365 kcd->prev.bmface = nullptr;
2366 }
2367
2368 if (kcd->is_drag_hold) {
2369 KnifeLineHit *lh = &kcd->linehits.last();
2370 linehit_to_knifepos(&kcd->prev, lh);
2371 }
2372
2373 BLI_ghash_free(facehits, nullptr, nullptr);
2375}
2376
2378{
2380}
2381
2383
2384/* -------------------------------------------------------------------- */
2387
2388/* Record the index in kcd->em->looptris of first looptri triple for a given face,
2389 * given an index for some triple in that array.
2390 * This assumes that all of the triangles for a given face are contiguous
2391 * in that array (as they are by the current tessellation routines).
2392 * Actually store index + 1 in the hash, because 0 looks like "no entry"
2393 * to hash lookup routine; will reverse this in the get routine.
2394 * Doing this lazily rather than all at once for all faces.
2395 */
2396static void set_lowest_face_tri(KnifeTool_OpData *kcd, BMEditMesh *em, BMFace *f, int index)
2397{
2398 int i;
2399
2400 if (BLI_ghash_lookup(kcd->facetrimap, f)) {
2401 return;
2402 }
2403
2404 BLI_assert(index >= 0 && index < em->looptris.size());
2405 BLI_assert(em->looptris[index][0]->f == f);
2406 for (i = index - 1; i >= 0; i--) {
2407 if (em->looptris[i][0]->f != f) {
2408 i++;
2409 break;
2410 }
2411 }
2412 if (i == -1) {
2413 i++;
2414 }
2415
2417}
2418
2419/* This should only be called for faces that have had a lowest face tri set by previous function.
2420 */
2422{
2423 int ans;
2424
2426 BLI_assert(ans != 0);
2427 return ans - 1;
2428}
2429
2439 const float s[2],
2440 const float v1[3],
2441 const float v2[3],
2442 int ob_index,
2443 BMFace *f,
2444 const float face_tol_sq,
2445 float hit_co[3],
2446 float hit_cageco[3])
2447{
2448 Object *ob = kcd->objects[ob_index];
2450
2451 int tottri, tri_i;
2452 float raydir[3];
2453 float tri_norm[3], tri_plane[4];
2454 float se1[2], se2[2];
2455 float d, lambda;
2456 ListBase *list;
2457 KnifeEdge *kfe;
2458
2459 sub_v3_v3v3(raydir, v2, v1);
2460 normalize_v3(raydir);
2461 tri_i = get_lowest_face_tri(kcd, f);
2462 tottri = em->looptris.size();
2463 BLI_assert(tri_i >= 0 && tri_i < tottri);
2464
2465 for (; tri_i < tottri; tri_i++) {
2466 float tri_cos[3][3];
2467 float ray_tri_uv[2];
2468
2469 const std::array<BMLoop *, 3> &ltri = em->looptris[tri_i];
2470 if (ltri[0]->f != f) {
2471 break;
2472 }
2473
2474 knife_bm_tri_cagecos_get_worldspace(kcd, ob_index, tri_i, tri_cos);
2475
2476 /* Using epsilon test in case ray is directly through an internal
2477 * tessellation edge and might not hit either tessellation tri with
2478 * an exact test;
2479 * We will exclude hits near real edges by a later test. */
2480 if (isect_ray_tri_epsilon_v3(v1, raydir, UNPACK3(tri_cos), &lambda, ray_tri_uv, KNIFE_FLT_EPS))
2481 {
2482 /* Check if line coplanar with tri. */
2483 normal_tri_v3(tri_norm, UNPACK3(tri_cos));
2484 plane_from_point_normal_v3(tri_plane, tri_cos[0], tri_norm);
2485 if ((dist_squared_to_plane_v3(v1, tri_plane) < KNIFE_FLT_EPS) &&
2487 {
2488 return false;
2489 }
2490 interp_v3_v3v3v3_uv(hit_cageco, UNPACK3(tri_cos), ray_tri_uv);
2491 /* Now check that far enough away from verts and edges. */
2492 list = knife_get_face_kedges(kcd, ob_index, f);
2493 LISTBASE_FOREACH (LinkData *, ref, list) {
2494 kfe = static_cast<KnifeEdge *>(ref->data);
2495 if (kfe->is_invalid) {
2496 continue;
2497 }
2498 knife_project_v2(kcd, kfe->v1->cageco, se1);
2499 knife_project_v2(kcd, kfe->v2->cageco, se2);
2500 d = dist_squared_to_line_segment_v2(s, se1, se2);
2501 if (d < face_tol_sq) {
2502 return false;
2503 }
2504 }
2505 interp_v3_v3v3v3_uv(hit_co, ltri[0]->v->co, ltri[1]->v->co, ltri[2]->v->co, ray_tri_uv);
2506 return true;
2507 }
2508 }
2509 return false;
2510}
2511
2517{
2518 Object *ob;
2519 BMEditMesh *em;
2520 BMIter iter;
2521 BMVert *v;
2522 float min[3], max[3];
2523 float ws[3];
2525
2526 for (int ob_index = 0; ob_index < kcd->objects.size(); ob_index++) {
2527 ob = kcd->objects[ob_index];
2528 em = BKE_editmesh_from_object(ob);
2529
2530 const Span<float3> positions_cage = kcd->objects_info[ob_index].positions_cage;
2531 if (!positions_cage.is_empty()) {
2532 for (int i = 0; i < em->bm->totvert; i++) {
2533 copy_v3_v3(ws, positions_cage[i]);
2534 mul_m4_v3(ob->object_to_world().ptr(), ws);
2535 minmax_v3v3_v3(min, max, ws);
2536 }
2537 }
2538 else {
2539 BM_ITER_MESH (v, &iter, em->bm, BM_VERTS_OF_MESH) {
2540 copy_v3_v3(ws, v->co);
2541 mul_m4_v3(ob->object_to_world().ptr(), ws);
2542 minmax_v3v3_v3(min, max, ws);
2543 }
2544 }
2545 }
2546
2547 kcd->ortho_extent = len_v3v3(min, max) / 2;
2549}
2550
2551/* Do edges e1 and e2 go between exactly the same coordinates? */
2552static bool coinciding_edges(BMEdge *e1, BMEdge *e2)
2553{
2554 const float *co11, *co12, *co21, *co22;
2555
2556 co11 = e1->v1->co;
2557 co12 = e1->v2->co;
2558 co21 = e2->v1->co;
2559 co22 = e2->v2->co;
2560 if ((equals_v3v3(co11, co21) && equals_v3v3(co12, co22)) ||
2561 (equals_v3v3(co11, co22) && equals_v3v3(co12, co21)))
2562 {
2563 return true;
2564 }
2565 return false;
2566}
2567
2568/* Callback used in point_is_visible to exclude hits on the faces that are the same
2569 * as or contain the hitting element (which is in user_data).
2570 * Also (see #44492) want to exclude hits on faces that butt up to the hitting element
2571 * (e.g., when you double an edge by an edge split).
2572 */
2573static bool bm_ray_cast_cb_elem_not_in_face_check(BMFace *f, void *user_data)
2574{
2575 bool ans;
2576 BMEdge *e, *e2;
2577 BMIter iter;
2578
2579 switch (((BMElem *)user_data)->head.htype) {
2580 case BM_FACE:
2581 ans = (BMFace *)user_data != f;
2582 break;
2583 case BM_EDGE:
2584 e = (BMEdge *)user_data;
2585 ans = !BM_edge_in_face(e, f);
2586 if (ans) {
2587 /* Is it a boundary edge, coincident with a split edge? */
2588 if (BM_edge_is_boundary(e)) {
2589 BM_ITER_ELEM (e2, &iter, f, BM_EDGES_OF_FACE) {
2590 if (coinciding_edges(e, e2)) {
2591 ans = false;
2592 break;
2593 }
2594 }
2595 }
2596 }
2597 break;
2598 case BM_VERT:
2599 ans = !BM_vert_in_face((BMVert *)user_data, f);
2600 break;
2601 default:
2602 ans = true;
2603 break;
2604 }
2605 return ans;
2606}
2607
2616 const float p[3],
2617 const float s[2],
2618 BMElem *ele_test)
2619{
2620 BMFace *f_hit;
2621
2622 /* If box clipping on, make sure p is not clipped. */
2623 if (RV3D_CLIPPING_ENABLED(kcd->vc.v3d, kcd->vc.rv3d) &&
2624 ED_view3d_clipping_test(kcd->vc.rv3d, p, false))
2625 {
2626 return false;
2627 }
2628
2629 /* If not cutting through, make sure no face is in front of p. */
2630 if (!kcd->cut_through) {
2631 float dist;
2632 float view[3], p_ofs[3];
2633
2634 /* TODO: I think there's a simpler way to get the required raycast ray. */
2635 ED_view3d_unproject_v3(kcd->vc.region, s[0], s[1], 0.0f, view);
2636
2637 /* Make p_ofs a little towards view, so ray doesn't hit p's face. */
2638 sub_v3_v3(view, p);
2639 dist = normalize_v3(view);
2640 copy_v3_v3(p_ofs, p);
2641
2642 /* Avoid projecting behind the viewpoint. */
2643 if (kcd->is_ortho && (kcd->vc.rv3d->persp != RV3D_CAMOB)) {
2644 dist = kcd->vc.v3d->clip_end * 2.0f;
2645 }
2646
2647 if (RV3D_CLIPPING_ENABLED(kcd->vc.v3d, kcd->vc.rv3d)) {
2648 float view_clip[2][3];
2649 /* NOTE: view_clip[0] should never get clipped. */
2650 copy_v3_v3(view_clip[0], p_ofs);
2651 madd_v3_v3v3fl(view_clip[1], p_ofs, view, dist);
2652
2654 view_clip[0], view_clip[1], kcd->vc.rv3d->clip_local, 6, view_clip[0], view_clip[1]))
2655 {
2656 dist = len_v3v3(p_ofs, view_clip[1]);
2657 }
2658 }
2659
2660 /* See if there's a face hit between p1 and the view. */
2661 if (ele_test) {
2662 f_hit = knife_bvh_raycast_filter(kcd,
2663 p_ofs,
2664 view,
2666 &dist,
2667 nullptr,
2668 nullptr,
2670 ele_test);
2671 }
2672 else {
2673 f_hit = knife_bvh_raycast(kcd, p_ofs, view, KNIFE_FLT_EPS, &dist, nullptr, nullptr);
2674 }
2675
2676 if (f_hit) {
2677 return false;
2678 }
2679 }
2680
2681 return true;
2682}
2683
2684/* Clip the line (v1, v2) to planes perpendicular to it and distances d from
2685 * the closest point on the line to the origin. */
2686static void clip_to_ortho_planes(float v1[3], float v2[3], const float center[3], const float d)
2687{
2688 float closest[3], dir[3];
2689
2690 sub_v3_v3v3(dir, v1, v2);
2691 normalize_v3(dir);
2692
2693 /* could be v1 or v2 */
2694 sub_v3_v3(v1, center);
2696 add_v3_v3(closest, center);
2697
2698 madd_v3_v3v3fl(v1, closest, dir, d);
2699 madd_v3_v3v3fl(v2, closest, dir, -d);
2700}
2701
2703 float s1[2],
2704 float s2[2],
2705 float sco[2],
2706 float cage[3],
2707 int ob_index,
2708 KnifeVert *v,
2709 KnifeEdge *kfe,
2710 KnifeLineHit *r_hit)
2711{
2712 memset(r_hit, 0, sizeof(*r_hit));
2713 copy_v3_v3(r_hit->cagehit, cage);
2714 copy_v2_v2(r_hit->schit, sco);
2715 r_hit->ob_index = ob_index;
2716
2717 /* Find position along screen line, used for sorting. */
2718 r_hit->l = len_v2v2(sco, s1) / len_v2v2(s2, s1);
2719
2720 r_hit->m = dot_m4_v3_row_z(kcd->vc.rv3d->persmatob, cage);
2721
2722 r_hit->v = v;
2723
2724 /* If this isn't from an existing BMVert, it may have been added to a BMEdge originally.
2725 * Knowing if the hit comes from an edge is important for edge-in-face checks later on.
2726 * See: #knife_add_single_cut -> #knife_verts_edge_in_face, #42611. */
2727 r_hit->kfe = kfe;
2728
2729 if (v) {
2730 copy_v3_v3(r_hit->hit, v->co);
2731 }
2732 else if (kfe) {
2734 r_hit->hit, cage, kfe->v1->co, kfe->v2->co, kfe->v1->cageco, kfe->v2->cageco);
2735 }
2736}
2737
2739 float s1[2],
2740 float s2[2],
2741 float sco[2],
2742 float ray_start[3],
2743 float ray_end[3],
2744 int ob_index,
2745 BMFace *f,
2746 float face_tol_sq,
2747 KnifeLineHit *r_hit)
2748{
2749 float3 p, cage;
2750 if (!knife_ray_intersect_face(kcd, sco, ray_start, ray_end, ob_index, f, face_tol_sq, p, cage)) {
2751 return false;
2752 }
2753 if (!point_is_visible(kcd, cage, sco, (BMElem *)f)) {
2754 return false;
2755 }
2756 knife_linehit_set(kcd, s1, s2, sco, cage, ob_index, nullptr, nullptr, r_hit);
2757 copy_v3_v3(r_hit->hit, p);
2758 r_hit->f = f;
2759 return true;
2760}
2761
2762/* Finds visible (or all, if cutting through) edges that intersects the current screen drag line.
2763 */
2765{
2766 float3 v1, v2;
2767 float2 s1, s2;
2768 int *results, *result;
2769 ListBase *list;
2770 KnifeLineHit hit;
2771 float s[2], se1[2], se2[2];
2772 float d1, d2;
2773 float vert_tol, vert_tol_sq;
2774 float line_tol, line_tol_sq;
2775 float face_tol, face_tol_sq;
2776 uint tot;
2777 int i;
2778
2780
2781 copy_v3_v3(v1, kcd->prev.cage);
2782 copy_v3_v3(v2, kcd->curr.cage);
2783
2784 /* Project screen line's 3d coordinates back into 2d. */
2785 knife_project_v2(kcd, v1, s1);
2786 knife_project_v2(kcd, v2, s2);
2787
2788 if (kcd->is_interactive) {
2789 if (len_squared_v2v2(s1, s2) < 1.0f) {
2790 return;
2791 }
2792 }
2793 else {
2795 return;
2796 }
2797 }
2798
2799 float4 plane;
2800 {
2801 if (kcd->is_ortho) {
2802 cross_v3_v3v3(plane, v2 - v1, kcd->vc.rv3d->viewinv[2]);
2803 }
2804 else {
2805 float3 orig = kcd->vc.rv3d->viewinv[3];
2806 float3 o_v1 = v1 - orig;
2807 float3 o_v2 = v2 - orig;
2808 cross_v3_v3v3(plane, o_v1, o_v2);
2809 }
2810 plane_from_point_normal_v3(plane, v1, plane);
2811 }
2812
2813 /* First use BVH tree to find faces, knife edges, and knife verts that might
2814 * intersect the cut plane with rays v1-v3 and v2-v4.
2815 * This de-duplicates the candidates before doing more expensive intersection tests. */
2816
2817 results = BLI_bvhtree_intersect_plane(kcd->bvh.tree, plane, &tot);
2818 if (!results) {
2819 return;
2820 }
2821
2824 Set<KnifeEdge *> kfes;
2825 Set<KnifeVert *> kfvs;
2826
2827 Object *ob;
2828 BMEditMesh *em;
2829
2830 for (i = 0, result = results; i < tot; i++, result++) {
2831 uint ob_index = 0;
2832 BMLoop *const *ltri = nullptr;
2833 for (ob_index = 0; ob_index < kcd->objects.size(); ob_index++) {
2834 ob = kcd->objects[ob_index];
2835 em = BKE_editmesh_from_object(ob);
2836 if (*result >= 0 && *result < em->looptris.size()) {
2837 ltri = em->looptris[*result].data();
2838 break;
2839 }
2840 *result -= em->looptris.size();
2841 }
2842 BLI_assert(ltri != nullptr);
2843 BMFace *f = ltri[0]->f;
2844 set_lowest_face_tri(kcd, em, f, *result);
2845
2846 /* Occlude but never cut unselected faces (when only_select is used). */
2847 if (kcd->only_select && !BM_elem_flag_test(f, BM_ELEM_SELECT)) {
2848 continue;
2849 }
2850 /* For faces, store index of lowest hit looptri in hash. */
2851 if (faces.contains(f)) {
2852 continue;
2853 }
2854 /* Don't care what the value is except that it is non-null, for iterator. */
2855 faces.add(f);
2856 fobs.add(f, ob_index);
2857
2858 list = knife_get_face_kedges(kcd, ob_index, f);
2859 LISTBASE_FOREACH (LinkData *, ref, list) {
2860 KnifeEdge *kfe = static_cast<KnifeEdge *>(ref->data);
2861 if (kfe->is_invalid) {
2862 continue;
2863 }
2864 if (kfes.contains(kfe)) {
2865 continue;
2866 }
2867 kfes.add(kfe);
2868 kfvs.add(kfe->v1);
2869 kfvs.add(kfe->v2);
2870 }
2871 }
2872
2873 /* Now go through the candidates and find intersections. */
2874 /* These tolerances, in screen space, are for intermediate hits,
2875 * as ends are already snapped to screen. */
2876
2877 if (kcd->is_interactive) {
2878 vert_tol = KNIFE_FLT_EPS_PX_VERT;
2879 line_tol = KNIFE_FLT_EPS_PX_EDGE;
2880 face_tol = KNIFE_FLT_EPS_PX_FACE;
2881 }
2882 else {
2883 /* Use 1/100th of a pixel, see #43896 (too big), #47910 (too small).
2884 *
2885 * Update, leave this as is until we investigate not using pixel coords
2886 * for geometry calculations: #48023. */
2887 vert_tol = line_tol = face_tol = 0.5f;
2888 }
2889
2890 vert_tol_sq = vert_tol * vert_tol;
2891 line_tol_sq = line_tol * line_tol;
2892 face_tol_sq = face_tol * face_tol;
2893
2894 /* Assume these tolerances swamp floating point rounding errors in calculations below. */
2895
2896 /* First look for vertex hits. */
2897 Vector<KnifeLineHit> linehits;
2898 for (KnifeVert *v : kfvs) {
2899 KnifeEdge *kfe_hit = nullptr;
2900
2901 bool kfv_is_in_cut = false;
2902 if (ELEM(v, kcd->prev.vert, kcd->curr.vert)) {
2903 /* This KnifeVert was captured by the snap system.
2904 * Since the tolerance distance can be different, add this vertex directly.
2905 * Otherwise, the cut may fail or a close cut on a connected edge can be performed. */
2906 bm_elem_from_knife_vert(v, &kfe_hit);
2907 copy_v2_v2(s, (v == kcd->prev.vert) ? kcd->prev.mval : kcd->curr.mval);
2908 kfv_is_in_cut = true;
2909 }
2910 else {
2911 knife_project_v2(kcd, v->cageco, s);
2912 float d = dist_squared_to_line_segment_v2(s, s1, s2);
2913 if ((d <= vert_tol_sq) &&
2914 point_is_visible(kcd, v->cageco, s, bm_elem_from_knife_vert(v, &kfe_hit)))
2915 {
2916 kfv_is_in_cut = true;
2917 }
2918 }
2919
2920 if (kfv_is_in_cut) {
2921 knife_linehit_set(kcd, s1, s2, s, v->cageco, v->ob_index, v, kfe_hit, &hit);
2922 linehits.append(hit);
2923 }
2924 else {
2925 /* This vertex isn't used so remove from `kfvs`.
2926 * This is useful to detect KnifeEdges that can be skipped.
2927 * And it optimizes iteration a little bit. */
2928 kfvs.remove(v);
2929 }
2930 }
2931
2932 /* Now edge hits; don't add if a vertex at end of edge should have hit. */
2933 for (KnifeEdge *kfe : kfes) {
2934 /* If we intersect any of the vertices, don't attempt to intersect the edge. */
2935 if (kfvs.contains(kfe->v1) || kfvs.contains(kfe->v2)) {
2936 continue;
2937 }
2938
2939 knife_project_v2(kcd, kfe->v1->cageco, se1);
2940 knife_project_v2(kcd, kfe->v2->cageco, se2);
2941 float3 p_cage;
2942 float2 p_cage_ss;
2943 bool kfe_is_in_cut = false;
2944 if (kfe == kcd->prev.edge) {
2945 /* This KnifeEdge was captured by the snap system. */
2946 p_cage = kcd->prev.cage;
2947 p_cage_ss = kcd->prev.mval;
2948 kfe_is_in_cut = true;
2949 }
2950 else if (kfe == kcd->curr.edge) {
2951 /* This KnifeEdge was captured by the snap system. */
2952 p_cage = kcd->curr.cage;
2953 p_cage_ss = kcd->curr.mval;
2954 kfe_is_in_cut = true;
2955 }
2956 else {
2957 int isect_kind = isect_seg_seg_v2_point_ex(s1, s2, se1, se2, 0.0f, p_cage_ss);
2958 if (isect_kind == -1) {
2959 /* isect_seg_seg_v2_point doesn't do tolerance test around ends of s1-s2. */
2960 closest_to_line_segment_v2(p_cage_ss, s1, se1, se2);
2961 if (len_squared_v2v2(p_cage_ss, s1) <= line_tol_sq) {
2962 isect_kind = 1;
2963 }
2964 else {
2965 closest_to_line_segment_v2(p_cage_ss, s2, se1, se2);
2966 if (len_squared_v2v2(p_cage_ss, s2) <= line_tol_sq) {
2967 isect_kind = 1;
2968 }
2969 }
2970 }
2971 if (isect_kind == 1) {
2972 d1 = len_v2v2(p_cage_ss, se1);
2973 d2 = len_v2v2(se2, se1);
2974 if (!(d1 <= line_tol || d2 <= line_tol || fabsf(d1 - d2) <= line_tol)) {
2975 /* Can't just interpolate between ends of `kfe` because
2976 * that doesn't work with perspective transformation. */
2977 float lambda;
2978 float3 kfe_dir = kfe->v2->cageco - kfe->v1->cageco;
2979 if (isect_ray_plane_v3(kfe->v1->cageco, kfe_dir, plane, &lambda, false)) {
2980 p_cage = kfe->v1->cageco + kfe_dir * lambda;
2981 if (point_is_visible(kcd, p_cage, p_cage_ss, bm_elem_from_knife_edge(kfe))) {
2982 if (kcd->snap_midpoints) {
2983 /* Choose intermediate point snap too. */
2984 mid_v3_v3v3(p_cage, kfe->v1->cageco, kfe->v2->cageco);
2985 mid_v2_v2v2(p_cage_ss, se1, se2);
2986 }
2987 kfe_is_in_cut = true;
2988 }
2989 }
2990 }
2991 }
2992 }
2993 if (kfe_is_in_cut) {
2994 knife_linehit_set(kcd, s1, s2, p_cage_ss, p_cage, kfe->v1->ob_index, nullptr, kfe, &hit);
2995 linehits.append(hit);
2996 }
2997 }
2998
2999 /* Now face hits; don't add if a vertex or edge in face should have hit. */
3000 const bool use_hit_prev = (kcd->prev.vert == nullptr) && (kcd->prev.edge == nullptr);
3001 const bool use_hit_curr = (kcd->curr.vert == nullptr) && (kcd->curr.edge == nullptr) &&
3002 !kcd->is_drag_hold;
3003 if (use_hit_prev || use_hit_curr) {
3004 float3 v3, v4;
3005
3006 /* Unproject screen line. */
3008 kcd->vc.depsgraph, kcd->region, kcd->vc.v3d, s1, v1, v3, true);
3010 kcd->vc.depsgraph, kcd->region, kcd->vc.v3d, s2, v2, v4, true);
3011
3012 /* Numeric error, 'v1' -> 'v2', 'v2' -> 'v4'
3013 * can end up being ~2000 units apart with an orthogonal perspective.
3014 *
3015 * (from ED_view3d_win_to_segment_clipped() above)
3016 * This gives precision error; rather than solving properly
3017 * (which may involve using doubles everywhere!),
3018 * limit the distance between these points. */
3019 if (kcd->is_ortho && (kcd->vc.rv3d->persp != RV3D_CAMOB)) {
3020 if (kcd->ortho_extent == 0.0f) {
3021 calc_ortho_extent(kcd);
3022 }
3023 clip_to_ortho_planes(v1, v3, kcd->ortho_extent_center, kcd->ortho_extent + 10.0f);
3025 }
3026
3027 for (BMFace *f : faces) {
3028 int ob_index = fobs.lookup(f);
3029 if (use_hit_prev &&
3030 knife_linehit_face_test(kcd, s1, s2, s1, v1, v3, ob_index, f, face_tol_sq, &hit))
3031 {
3032 linehits.append(hit);
3033 }
3034
3035 if (use_hit_curr &&
3036 knife_linehit_face_test(kcd, s1, s2, s2, v2, v4, ob_index, f, face_tol_sq, &hit))
3037 {
3038 linehits.append(hit);
3039 }
3040 }
3041 }
3042
3043 kcd->linehits = std::move(linehits);
3044
3045 MEM_freeN(results);
3046}
3047
3049
3050/* -------------------------------------------------------------------- */
3053
3055{
3056 zero_v3(kpd->cage);
3057 kpd->vert = nullptr;
3058 kpd->edge = nullptr;
3059 kpd->bmface = nullptr;
3060 kpd->ob_index = -1;
3061 zero_v2(kpd->mval);
3062}
3063
3065
3066/* -------------------------------------------------------------------- */
3069
3071 const float2 &mval,
3072 const float3 &ray_orig,
3073 const float3 &ray_dir,
3074 KnifePosData *r_kpd)
3075{
3076 float3 cage;
3077 int ob_index;
3078 BMFace *f;
3079 float dist = KMAXDIST;
3080
3081 f = knife_bvh_raycast(kcd, ray_orig, ray_dir, 0.0f, nullptr, cage, &ob_index);
3082
3083 if (f && kcd->only_select && BM_elem_flag_test(f, BM_ELEM_SELECT) == 0) {
3084 f = nullptr;
3085 ob_index = -1;
3086 }
3087
3088 if (f == nullptr) {
3089 if (kcd->is_interactive) {
3090 /* Try to use back-buffer selection method if ray casting failed.
3091 *
3092 * Apply the mouse coordinates to a copy of the view-context
3093 * since we don't want to rely on this being set elsewhere. */
3094 ViewContext vc = kcd->vc;
3095 vc.mval[0] = int(mval[0]);
3096 vc.mval[1] = int(mval[1]);
3097
3099 f = EDBM_face_find_nearest(&vc, &dist);
3100 }
3101
3102 if (f) {
3103 /* Cheat for now; just put in the origin instead
3104 * of a true coordinate on the face.
3105 * This just puts a point 1.0f in front of the view. */
3106 cage = ray_orig + ray_dir;
3107
3108 ob_index = 0;
3109 BLI_assert(ob_index == kcd->objects.first_index_of_try(vc.obact));
3110 }
3111 }
3112 }
3113
3114 if (f) {
3115 r_kpd->cage = cage;
3116 r_kpd->bmface = f;
3117 r_kpd->ob_index = ob_index;
3118 r_kpd->mval = mval;
3119
3120 return true;
3121 }
3122 return false;
3123}
3124
3132 KnifeTool_OpData *kcd, const float radius, int ob_index, BMFace *f, const float cageco[3])
3133{
3134 const float radius_sq = radius * radius;
3135 ListBase *list;
3136 float sco[2];
3137 float dis_sq;
3138 int c = 0;
3139
3140 knife_project_v2(kcd, cageco, sco);
3141
3142 list = knife_get_face_kedges(kcd, ob_index, f);
3143 LISTBASE_FOREACH (LinkData *, ref, list) {
3144 KnifeEdge *kfe = static_cast<KnifeEdge *>(ref->data);
3145 int i;
3146
3147 if (kfe->is_invalid) {
3148 continue;
3149 }
3150
3151 for (i = 0; i < 2; i++) {
3152 KnifeVert *kfv = i ? kfe->v2 : kfe->v1;
3153 float kfv_sco[2];
3154
3155 if (kfv->is_invalid) {
3156 continue;
3157 }
3158
3159 knife_project_v2(kcd, kfv->cageco, kfv_sco);
3160
3161 dis_sq = len_squared_v2v2(kfv_sco, sco);
3162 if (dis_sq < radius_sq) {
3163 if (RV3D_CLIPPING_ENABLED(kcd->vc.v3d, kcd->vc.rv3d)) {
3164 if (ED_view3d_clipping_test(kcd->vc.rv3d, kfv->cageco, false) == 0) {
3165 c++;
3166 }
3167 }
3168 else {
3169 c++;
3170 }
3171 }
3172 }
3173 }
3174
3175 return c;
3176}
3177
3184static float knife_snap_size(KnifeTool_OpData *kcd, float maxsize)
3185{
3186 BLI_assert(kcd->is_interactive == true);
3187 int density = 0;
3188
3189 if (!kcd->curr.is_space()) {
3191 kcd, maxsize * 2.0f, kcd->curr.ob_index, kcd->curr.bmface, kcd->curr.cage));
3192 }
3193
3194 return density ? min_ff(maxsize / (float(density) * 0.5f), maxsize) : maxsize;
3195}
3196
3202static bool knife_closest_constrain_to_edge(const float3 &cut_origin,
3203 const float3 &cut_dir,
3204 const float3 &kfv1_cageco,
3205 const float3 &kfv2_cageco,
3206 float r_close[3])
3207{
3208 /* If snapping, check we're in bounds. */
3209 float lambda;
3210 if (!isect_ray_line_v3(cut_origin, cut_dir, kfv1_cageco, kfv2_cageco, &lambda)) {
3211 return false;
3212 }
3213
3214 /* Be strict when constrained within edge. */
3215 if ((lambda < 0.0f - KNIFE_FLT_EPSBIG) || (lambda > 1.0f + KNIFE_FLT_EPSBIG)) {
3216 return false;
3217 }
3218
3219 interp_v3_v3v3(r_close, kfv1_cageco, kfv2_cageco, lambda);
3220 return true;
3221}
3222
3223/* `r_kpd->cage` is closest point on edge to the knife point. */
3225 int ob_index,
3226 BMFace *f,
3227 const float2 &curr_cage_ss,
3228 const float3 *curr_cage_constrain,
3229 const float3 &ray_orig,
3230 const float3 &ray_dir,
3231 KnifePosData *r_kpd)
3232{
3233 float maxdist;
3234
3235 if (kcd->is_interactive) {
3236 maxdist = knife_snap_size(kcd, kcd->ethresh);
3237
3238 if (kcd->ignore_vert_snapping) {
3239 maxdist *= 0.5f;
3240 }
3241 }
3242 else {
3243 maxdist = KNIFE_FLT_EPS;
3244 }
3245
3246 const float maxdist_sq = maxdist * maxdist;
3247 float cur_dist_sq = maxdist_sq;
3248 bool has_hit = false;
3249
3250 const float3 &cut_origin = kcd->prev.cage;
3251 const float3 cut_dir = math::normalize(
3252 (curr_cage_constrain ? *curr_cage_constrain : kcd->curr.cage) - kcd->prev.cage);
3253
3254 /* Look through all edges associated with this face. */
3255 ListBase *list = knife_get_face_kedges(kcd, ob_index, f);
3256 LISTBASE_FOREACH (LinkData *, ref, list) {
3257 KnifeEdge *kfe = static_cast<KnifeEdge *>(ref->data);
3258 float test_cagep[3];
3259
3260 if (kfe->is_invalid) {
3261 continue;
3262 }
3263
3264 /* Get the closest point on the edge. */
3265 if ((kcd->is_angle_snapping || kcd->axis_constrained) && (kfe != kcd->prev.edge) &&
3266 (kcd->mode == MODE_DRAGGING))
3267 {
3268 /* Check if it is within the edges' bounds. */
3270 cut_origin, cut_dir, kfe->v1->cageco, kfe->v2->cageco, test_cagep))
3271 {
3272 continue;
3273 }
3274 }
3275 else {
3276 closest_ray_to_segment_v3(ray_orig, ray_dir, kfe->v1->cageco, kfe->v2->cageco, test_cagep);
3277 }
3278
3279 /* Check if we're close enough. */
3280 float2 closest_ss;
3281 knife_project_v2(kcd, test_cagep, closest_ss);
3282 float dis_sq = len_squared_v2v2(closest_ss, curr_cage_ss);
3283 if (dis_sq >= cur_dist_sq) {
3284 continue;
3285 }
3286
3287 if (RV3D_CLIPPING_ENABLED(kcd->vc.v3d, kcd->vc.rv3d)) {
3288 /* Check we're in the view */
3289 if (ED_view3d_clipping_test(kcd->vc.rv3d, test_cagep, false)) {
3290 continue;
3291 }
3292 }
3293
3294 cur_dist_sq = dis_sq;
3295
3296 r_kpd->edge = kfe;
3297 if (kcd->snap_midpoints) {
3298 mid_v3_v3v3(r_kpd->cage, kfe->v1->cageco, kfe->v2->cageco);
3299 knife_project_v2(kcd, r_kpd->cage, r_kpd->mval);
3300 }
3301 else {
3302 copy_v3_v3(r_kpd->cage, test_cagep);
3303 r_kpd->mval = closest_ss;
3304 }
3305
3306 has_hit = true;
3307 }
3308
3309 return has_hit;
3310}
3311
3312/* Find a vertex near the mouse cursor, if it exists. */
3314 const KnifeEdge *kfe,
3315 const float2 &cage_ss,
3316 KnifePosData *r_kpd)
3317{
3318 float maxdist;
3319
3320 if (kcd->is_interactive) {
3321 maxdist = knife_snap_size(kcd, kcd->vthresh);
3322 if (kcd->ignore_vert_snapping) {
3323 maxdist *= 0.5f;
3324 }
3325 }
3326 else {
3327 maxdist = KNIFE_FLT_EPS;
3328 }
3329
3330 const float maxdist_sq = maxdist * maxdist;
3331 KnifeVert *curv = nullptr;
3332 float cur_kfv_sco[2];
3333 float dis_sq, curdis_sq = FLT_MAX;
3334
3335 for (int i = 0; i < 2; i++) {
3336 KnifeVert *kfv = i ? kfe->v2 : kfe->v1;
3337 float kfv_sco[2];
3338
3339 knife_project_v2(kcd, kfv->cageco, kfv_sco);
3340
3341 /* Be strict when in a constrained mode, the vertex needs to be very close to the cut line,
3342 * or we ignore. */
3343 if ((kcd->is_angle_snapping || kcd->axis_constrained) && (kcd->mode == MODE_DRAGGING)) {
3344 if (dist_squared_to_line_segment_v2(kfv_sco, kcd->prev.mval, kcd->curr.mval) >
3346 {
3347 continue;
3348 }
3349 }
3350
3351 dis_sq = len_squared_v2v2(kfv_sco, cage_ss);
3352 if (dis_sq < curdis_sq && dis_sq < maxdist_sq) {
3353 if (!RV3D_CLIPPING_ENABLED(kcd->vc.v3d, kcd->vc.rv3d) ||
3354 !ED_view3d_clipping_test(kcd->vc.rv3d, kfv->cageco, false))
3355 {
3356 curv = kfv;
3357 curdis_sq = dis_sq;
3358 copy_v2_v2(cur_kfv_sco, kfv_sco);
3359 }
3360 }
3361 }
3362
3363 if (curv) {
3364 r_kpd->cage = curv->cageco;
3365 r_kpd->vert = curv;
3366
3367 /* Update mouse coordinates to the snapped-to vertex's screen coordinates
3368 * this is important for angle snap, which uses the previous mouse position. */
3369 r_kpd->mval = cur_kfv_sco;
3370
3371 return true;
3372 }
3373
3374 return false;
3375}
3376
3381 float3 &r, const float3 &dvec, const float3 &vecx, const float3 &axis, float angle_snap)
3382{
3383 const float angle = angle_signed_on_axis_v3v3_v3(dvec, vecx, axis);
3384 const float angle_delta = (roundf(angle / angle_snap) * angle_snap) - angle;
3385 rotate_normalized_v3_v3v3fl(r, dvec, axis, angle_delta);
3386 return angle + angle_delta;
3387}
3388
3390 const float3 &vec_x,
3391 const float3 &axis,
3392 const float3 &ray_orig,
3393 const float3 &ray_dir,
3394 float3 &r_cage,
3395 float &r_angle)
3396{
3397 float3 curr_cage_projected;
3399 curr_cage_projected, ray_orig, ray_orig + ray_dir, kcd->prev.cage, axis))
3400 {
3401 return false;
3402 }
3403 const float3 dvec = curr_cage_projected - kcd->prev.cage;
3404 float snap_step;
3405 /* Currently user can input any float between 0 and 180. */
3408 {
3409 snap_step = DEG2RADF(kcd->angle_snapping_increment);
3410 }
3411 else {
3413 }
3414
3415 if (is_zero_v2(dvec)) {
3416 return false;
3417 }
3418
3419 float3 dvec_snap;
3420 r_angle = knife_snap_v3_angle(dvec_snap, dvec, vec_x, axis, snap_step);
3421 r_cage = kcd->prev.cage + dvec_snap;
3422 return true;
3423}
3424
3425/* Update both kcd->curr.mval and kcd->mval to snap to required angle. */
3427 const float3 &ray_orig,
3428 const float3 &ray_dir,
3429 float3 &r_cage,
3430 float &r_angle)
3431{
3432 const float3 &vec_x = kcd->vc.rv3d->viewinv[0];
3433 const float3 &vec_z = kcd->vc.rv3d->viewinv[2];
3434 return knife_snap_angle_impl(kcd, vec_x, vec_z, ray_orig, ray_dir, r_cage, r_angle);
3435}
3436
3437/* Snap to required angle along the plane of the face nearest to kcd->prev. */
3439 const float3 &ray_orig,
3440 const float3 &ray_dir,
3441 float3 &r_cage,
3442 float &r_angle)
3443{
3444 BMFace *fcurr = knife_bvh_raycast(kcd, ray_orig, ray_dir, 0.0f, nullptr, nullptr, nullptr);
3445
3446 if (!fcurr) {
3447 return false;
3448 }
3449
3450 /* Calculate a reference vector using previous cut segment, edge or vertex.
3451 * If none exists then exit. */
3452 float3 refv;
3453 if (kcd->prev.vert) {
3454 int count = 0;
3455 LISTBASE_FOREACH (LinkData *, ref, &kcd->prev.vert->edges) {
3456 KnifeEdge *kfe = ((KnifeEdge *)(ref->data));
3457 if (kfe->is_invalid) {
3458 continue;
3459 }
3460 if (kfe->e) {
3461 if (!BM_edge_in_face(kfe->e, fcurr)) {
3462 continue;
3463 }
3464 }
3465 if (count == kcd->snap_edge) {
3467 kfe->v2 :
3468 kfe->v1;
3469 refv = kfv->cageco - kcd->prev.cage;
3470 kcd->snap_ref_edge = kfe;
3471 break;
3472 }
3473 count++;
3474 }
3475 }
3476 else if (kcd->prev.edge) {
3478 kcd->prev.edge->v2 :
3479 kcd->prev.edge->v1;
3480 refv = kfv->cageco - kcd->prev.cage;
3481 kcd->snap_ref_edge = kcd->prev.edge;
3482 }
3483 else {
3484 return false;
3485 }
3486
3487 /* Choose best face for plane. */
3488 BMFace *fprev = nullptr;
3489 int fprev_ob_index = kcd->prev.ob_index;
3490 if (kcd->prev.vert && kcd->prev.vert->v) {
3491 LISTBASE_FOREACH (LinkData *, ref, &kcd->prev.vert->faces) {
3492 BMFace *f = ((BMFace *)(ref->data));
3493 if (f == fcurr) {
3494 fprev = f;
3495 }
3496 }
3497 }
3498 else if (kcd->prev.edge) {
3499 LISTBASE_FOREACH (LinkData *, ref, &kcd->prev.edge->faces) {
3500 BMFace *f = ((BMFace *)(ref->data));
3501 if (f == fcurr) {
3502 fprev = f;
3503 }
3504 }
3505 }
3506 else {
3507 /* Cut segment was started in a face. */
3508 float3 prev_ray_orig, prev_ray_dir;
3510 kcd->region,
3511 kcd->vc.v3d,
3512 kcd->prev.mval,
3513 prev_ray_orig,
3514 prev_ray_dir,
3515 false);
3516
3517 /* kcd->prev.face is usually not set. */
3518 fprev = knife_bvh_raycast(
3519 kcd, prev_ray_orig, prev_ray_dir, 0.0f, nullptr, nullptr, &fprev_ob_index);
3520 }
3521
3522 if (!fprev || fprev != fcurr) {
3523 return false;
3524 }
3525
3526 /* Use normal global direction. */
3527 Object *ob = kcd->objects[fprev_ob_index];
3528 float3 no_global = fprev->no;
3529 mul_transposed_mat3_m4_v3(ob->world_to_object().ptr(), no_global);
3530 normalize_v3(no_global);
3531
3532 return knife_snap_angle_impl(kcd, refv, no_global, ray_orig, ray_dir, r_cage, r_angle);
3533}
3534
3536 const float3 &ray_orig,
3537 const float3 &ray_dir)
3538{
3539 BMFace *fcurr = knife_bvh_raycast(kcd, ray_orig, ray_dir, 0.0f, nullptr, nullptr, nullptr);
3540
3541 int count = 0;
3542
3543 if (!fcurr) {
3544 return count;
3545 }
3546
3547 if (kcd->prev.vert) {
3548 LISTBASE_FOREACH (LinkData *, ref, &kcd->prev.vert->edges) {
3549 KnifeEdge *kfe = ((KnifeEdge *)(ref->data));
3550 if (kfe->is_invalid) {
3551 continue;
3552 }
3553 if (kfe->e) {
3554 if (!BM_edge_in_face(kfe->e, fcurr)) {
3555 continue;
3556 }
3557 }
3558 count++;
3559 }
3560 }
3561 else if (kcd->prev.edge) {
3562 return 1;
3563 }
3564 return count;
3565}
3566
3567/* Reset the snapping angle num input. */
3569{
3570 kcd->num.val[0] = 0;
3571 while (kcd->num.str_cur > 0) {
3572 kcd->num.str[kcd->num.str_cur - 1] = '\0';
3573 kcd->num.str_cur--;
3574 }
3575}
3576
3582 const float3 &ray_orig,
3583 const float3 &ray_dir,
3584 float3 &r_cage)
3585{
3586 float3 constrain_dir;
3587 {
3588 /* Constrain axes. */
3589 Scene *scene = kcd->scene;
3590 ViewLayer *view_layer = kcd->vc.view_layer;
3591 Object *obedit = (kcd->prev.ob_index != -1) ? kcd->objects[kcd->prev.ob_index] :
3592 kcd->vc.obedit;
3593 RegionView3D *rv3d = static_cast<RegionView3D *>(kcd->region->regiondata);
3594 const short scene_orientation = BKE_scene_orientation_get_index(scene, SCE_ORIENT_DEFAULT);
3595 /* Scene orientation takes priority. */
3596 const short orientation_type = scene_orientation ? scene_orientation :
3597 kcd->constrain_axis_mode - 1;
3598 const int pivot_point = scene->toolsettings->transform_pivot_point;
3599 float mat[3][3];
3601 scene, view_layer, kcd->vc.v3d, rv3d, obedit, obedit, orientation_type, pivot_point, mat);
3602
3603 constrain_dir = mat[kcd->constrain_axis - 1];
3604 }
3605
3606 float lambda;
3607 if (!isect_ray_ray_v3(kcd->prev.cage, constrain_dir, ray_orig, ray_dir, &lambda, nullptr)) {
3608 return;
3609 }
3610
3611 float3 cage_dir = constrain_dir * lambda;
3612 if (math::is_zero(cage_dir)) {
3613 return;
3614 }
3615
3616 r_cage = kcd->prev.cage + cage_dir;
3617}
3618
3625 const float2 &mval,
3626 const float3 &ray_orig,
3627 const float3 &ray_dir,
3628 const float3 *curr_cage_constrain,
3629 const float3 *fallback)
3630{
3632
3633 if (knife_find_closest_face(kcd, mval, ray_orig, ray_dir, &kcd->curr)) {
3634 if (!kcd->ignore_edge_snapping || !kcd->ignore_vert_snapping) {
3635 KnifePosData kpos_tmp = kcd->curr;
3637 kcd->curr.ob_index,
3638 kcd->curr.bmface,
3639 kcd->curr.mval,
3640 curr_cage_constrain,
3641 ray_orig,
3642 ray_dir,
3643 &kpos_tmp))
3644 {
3645 if (!kcd->ignore_edge_snapping) {
3646 kcd->curr = kpos_tmp;
3647 }
3648 if (!kcd->ignore_vert_snapping) {
3649 knife_find_closest_vert_of_edge(kcd, kpos_tmp.edge, kpos_tmp.mval, &kcd->curr);
3650 }
3651 }
3652 }
3653 }
3654
3655 if (kcd->curr.vert || kcd->curr.edge || kcd->curr.bmface) {
3656 return;
3657 }
3658
3659 kcd->curr.mval = mval;
3660 if (fallback) {
3661 /* If no geometry was found, use the fallback point. */
3662 kcd->curr.cage = *fallback;
3663 return;
3664 }
3665
3666 /* If no hits are found this would normally default to (0, 0, 0) so instead
3667 * get a point at the mouse ray closest to the previous point.
3668 * Note that drawing lines in `free-space` isn't properly supported
3669 * but there's no guarantee (0, 0, 0) has any geometry either - campbell */
3670
3672 kcd->curr.cage, ray_orig, ray_orig + ray_dir, kcd->prev.cage, kcd->vc.rv3d->viewinv[2]))
3673 {
3674 /* Should never fail! */
3675 kcd->curr.cage = kcd->prev.cage;
3676 BLI_assert(0);
3677 }
3678}
3679
3689{
3690 /* Mouse and ray with snapping applied. */
3691 float3 ray_orig;
3692 float3 ray_dir;
3693 float2 mval_constrain = mval;
3695 kcd->vc.depsgraph, kcd->region, kcd->vc.v3d, mval, ray_orig, ray_dir, false);
3696
3698
3699 /* view matrix may have changed, reproject */
3700 knife_project_v2(kcd, kcd->prev.cage, kcd->prev.mval);
3701
3702 bool is_constrained = false;
3703 kcd->is_angle_snapping = false;
3704 if (kcd->mode == MODE_DRAGGING) {
3705 if (kcd->angle_snapping) {
3708 kcd, ray_orig, ray_dir, kcd->curr.cage, kcd->angle);
3709 }
3712 kcd, ray_orig, ray_dir, kcd->curr.cage, kcd->angle);
3713 if (kcd->is_angle_snapping) {
3714 kcd->snap_ref_edges_count = knife_calculate_snap_ref_edges(kcd, ray_orig, ray_dir);
3715 }
3716 }
3717 }
3718
3719 if (kcd->is_angle_snapping) {
3720 is_constrained = true;
3721 }
3722 else if (kcd->axis_constrained) {
3723 knife_constrain_axis(kcd, ray_orig, ray_dir, kcd->curr.cage);
3724 is_constrained = true;
3725 }
3726 }
3727
3728 float3 fallback;
3729 float3 curr_cage_constrain;
3730 if (is_constrained) {
3731 /* Update ray and `mval_constrain`. */
3732 if (kcd->is_ortho) {
3733 float3 l1 = kcd->curr.cage - ray_dir;
3734 if (!isect_line_plane_v3(ray_orig, l1, kcd->curr.cage, ray_orig, ray_dir)) {
3735 /* Should never fail! */
3736 ray_orig = l1;
3738 }
3739 }
3740 else {
3741 ray_dir = math::normalize(kcd->curr.cage - ray_orig);
3742 }
3743 knife_project_v2(kcd, kcd->curr.cage, mval_constrain);
3744 curr_cage_constrain = kcd->curr.cage;
3745 fallback = kcd->curr.cage;
3746 }
3747
3748 knife_snap_curr(kcd,
3749 mval_constrain,
3750 ray_orig,
3751 ray_dir,
3752 is_constrained ? &curr_cage_constrain : nullptr,
3753 is_constrained ? &fallback : nullptr);
3754}
3755
3763{
3764 KnifeEdge *kfe, *newkfe;
3765 KnifeEdge *lastkfe = nullptr;
3766 KnifeVert *v1, *v2;
3767 KnifeUndoFrame *undo;
3768 BLI_mempool_iter iterkfe;
3769
3770 undo = static_cast<KnifeUndoFrame *>(BLI_stack_peek(kcd->undostack));
3771
3772 /* Undo edge splitting. */
3773 for (int i = 0; i < undo->splits; i++) {
3774 BLI_stack_pop(kcd->splitstack, &newkfe);
3775 BLI_stack_pop(kcd->splitstack, &kfe);
3776 knife_join_edge(newkfe, kfe);
3777 }
3778
3779 for (int i = 0; i < undo->cuts; i++) {
3780
3781 BLI_mempool_iternew(kcd->kedges, &iterkfe);
3782 for (kfe = static_cast<KnifeEdge *>(BLI_mempool_iterstep(&iterkfe)); kfe;
3783 kfe = static_cast<KnifeEdge *>(BLI_mempool_iterstep(&iterkfe)))
3784 {
3785 if (!kfe->is_cut || kfe->is_invalid || kfe->splits) {
3786 continue;
3787 }
3788 lastkfe = kfe;
3789 }
3790
3791 if (lastkfe) {
3792 lastkfe->is_invalid = true;
3793
3794 /* TODO: Are they always guaranteed to be in this order? */
3795 v1 = lastkfe->v1;
3796 v2 = lastkfe->v2;
3797
3798 /* Only remove first vertex if it is the start segment of the cut. */
3799 if (!v1->is_invalid && !v1->is_splitting) {
3800 v1->is_invalid = true;
3801 /* If the first vertex is touching any other cut edges don't remove it. */
3802 LISTBASE_FOREACH (LinkData *, ref, &v1->edges) {
3803 kfe = static_cast<KnifeEdge *>(ref->data);
3804 if (kfe->is_cut && !kfe->is_invalid) {
3805 v1->is_invalid = false;
3806 break;
3807 }
3808 }
3809 }
3810
3811 /* Only remove second vertex if it is the end segment of the cut. */
3812 if (!v2->is_invalid && !v2->is_splitting) {
3813 v2->is_invalid = true;
3814 /* If the second vertex is touching any other cut edges don't remove it. */
3815 LISTBASE_FOREACH (LinkData *, ref, &v2->edges) {
3816 kfe = static_cast<KnifeEdge *>(ref->data);
3817 if (kfe->is_cut && !kfe->is_invalid) {
3818 v2->is_invalid = false;
3819 break;
3820 }
3821 }
3822 }
3823 }
3824 }
3825
3826 if (ELEM(kcd->mode, MODE_DRAGGING, MODE_IDLE)) {
3827 /* Restore kcd->prev. */
3828 kcd->prev = undo->pos;
3829 }
3830
3831 /* Restore data for distance and angle measurements. */
3832 kcd->mdata = undo->mdata;
3833
3835}
3836
3838
3839/* -------------------------------------------------------------------- */
3842
3844 Object *ob,
3845 int ob_index,
3846 bool use_tri_indices)
3847{
3848 Scene *scene_eval = DEG_get_evaluated(kcd->vc.depsgraph, kcd->scene);
3849 Object *obedit_eval = DEG_get_evaluated(kcd->vc.depsgraph, ob);
3850 const Mesh &mesh_orig = *static_cast<const Mesh *>(ob->data);
3851 const Mesh &mesh_eval = *static_cast<const Mesh *>(obedit_eval->data);
3852
3853 KnifeObjectInfo *obinfo = &kcd->objects_info[ob_index];
3854
3855 if (BKE_editmesh_eval_orig_map_available(mesh_eval, &mesh_orig)) {
3856 BMEditMesh &em_eval = *mesh_eval.runtime->edit_mesh;
3857 obinfo->em = &em_eval;
3859 kcd->vc.depsgraph, &em_eval, scene_eval, obedit_eval);
3860 }
3861 else {
3862 obinfo->em = mesh_orig.runtime->edit_mesh.get();
3863 obinfo->positions_cage = BM_mesh_vert_coords_alloc(obinfo->em->bm);
3864 }
3865
3867
3868 if (use_tri_indices) {
3869 obinfo->tri_indices.reinitialize(obinfo->em->looptris.size());
3870 for (int i = 0; i < obinfo->em->looptris.size(); i++) {
3871 const std::array<BMLoop *, 3> &ltri = obinfo->em->looptris[i];
3872 obinfo->tri_indices[i][0] = BM_elem_index_get(ltri[0]->v);
3873 obinfo->tri_indices[i][1] = BM_elem_index_get(ltri[1]->v);
3874 obinfo->tri_indices[i][2] = BM_elem_index_get(ltri[2]->v);
3875 }
3876 }
3877}
3878
3880
3881/* -------------------------------------------------------------------- */
3884
3885static void knife_init_colors(KnifeColors *colors)
3886{
3887 /* Possible BMESH_TODO: add explicit themes or calculate these by
3888 * figuring out contrasting colors with grid / edges / verts
3889 * a la UI_make_axis_color. */
3895 colors->curpoint_a[3] = 102;
3898 colors->point_a[3] = 102;
3899
3904}
3905
3906/* called when modal loop selection gets set up... */
3908 KnifeTool_OpData *kcd,
3909 Vector<Object *> objects,
3910 const bool only_select,
3911 const bool cut_through,
3912 const bool xray,
3913 const int visible_measurements,
3914 const int angle_snapping,
3915 const float angle_snapping_increment,
3916 const bool is_interactive)
3917{
3918 /* Needed so multiple non-interactive cuts (also called knife-project)
3919 * doesn't access indices of loops that were created by cutting, see: #97153. */
3920 bool use_tri_indices = !is_interactive;
3921
3922 kcd->vc = *vc;
3923
3924 Scene *scene = vc->scene;
3925
3926 /* Assign the drawing handle for drawing preview line... */
3927 kcd->scene = scene;
3928 kcd->region = vc->region;
3929
3930 kcd->objects = std::move(objects);
3931
3932 Object *ob;
3933 BMEditMesh *em;
3935 for (int ob_index = 0; ob_index < kcd->objects.size(); ob_index++) {
3936 ob = kcd->objects[ob_index];
3937 em = BKE_editmesh_from_object(ob);
3938 knifetool_init_obinfo(kcd, ob, ob_index, use_tri_indices);
3939
3940 /* Can't usefully select resulting edges in face mode. */
3942 }
3943 knife_bvh_init(kcd);
3944
3945 /* Cut all the way through the mesh if use_occlude_geometry button not pushed. */
3946 kcd->is_interactive = is_interactive;
3947 kcd->cut_through = cut_through;
3948 kcd->only_select = only_select;
3949 kcd->depth_test = xray;
3950 kcd->dist_angle_mode = visible_measurements;
3952 kcd->angle_snapping_mode = angle_snapping;
3954 kcd->angle_snapping_increment = angle_snapping_increment;
3955
3956 kcd->arena = BLI_memarena_new(MEM_SIZE_OPTIMAL(1 << 15), "knife");
3957#ifdef USE_NET_ISLAND_CONNECT
3958 kcd->edgenet.arena = BLI_memarena_new(MEM_SIZE_OPTIMAL(1 << 15), __func__);
3959#endif
3960 kcd->edgenet.edge_visit = BLI_gset_ptr_new(__func__);
3961
3962 kcd->vthresh = KMAXDIST - 1;
3963 kcd->ethresh = KMAXDIST;
3964
3965 knife_recalc_ortho(kcd);
3966
3968
3969 kcd->refs = BLI_mempool_create(sizeof(LinkData), 0, 2048, 0);
3972
3973 kcd->undostack = BLI_stack_new(sizeof(KnifeUndoFrame), "knife undostack");
3974 kcd->splitstack = BLI_stack_new(sizeof(KnifeEdge *), "knife splitstack");
3975
3976 kcd->origedgemap = BLI_ghash_ptr_new("knife origedgemap");
3977 kcd->origvertmap = BLI_ghash_ptr_new("knife origvertmap");
3978 kcd->kedgefacemap = BLI_ghash_ptr_new("knife kedgefacemap");
3979 kcd->facetrimap = BLI_ghash_ptr_new("knife facetrimap");
3980
3983
3984 if (is_interactive) {
3987
3989 }
3990
3991 kcd->no_cuts = true;
3992
3993 kcd->axis_string[0] = ' ';
3994 kcd->axis_string[1] = '\0';
3995
3996 /* Initialize number input handling for angle snapping. */
3997 initNumInput(&kcd->num);
3998 kcd->num.idx_max = 0;
3999 kcd->num.val_flag[0] |= NUM_NO_NEGATIVE;
4000 kcd->num.unit_sys = scene->unit.system;
4001 kcd->num.unit_type[0] = B_UNIT_NONE;
4002}
4003
4004/* called when modal loop selection is done... */
4006{
4007 if (!kcd) {
4008 return;
4009 }
4010
4011 if (kcd->is_interactive) {
4013
4014 /* Deactivate the extra drawing stuff in 3D-View. */
4016 }
4017
4018 /* Free the custom data. */
4022
4025
4026 BLI_ghash_free(kcd->origedgemap, nullptr, nullptr);
4027 BLI_ghash_free(kcd->origvertmap, nullptr, nullptr);
4028 BLI_ghash_free(kcd->kedgefacemap, nullptr, nullptr);
4029 BLI_ghash_free(kcd->facetrimap, nullptr, nullptr);
4030
4032#ifdef USE_NET_ISLAND_CONNECT
4034#endif
4035 BLI_gset_free(kcd->edgenet.edge_visit, nullptr);
4036
4037 /* Tag for redraw. */
4039
4040 /* Knife BVH cleanup. */
4041 knife_bvh_free(kcd);
4042
4043 /* Destroy kcd itself. */
4044 MEM_delete(kcd);
4045}
4046
4048{
4049 KnifeTool_OpData *kcd = static_cast<KnifeTool_OpData *>(op->customdata);
4050 knifetool_exit_ex(kcd);
4051 op->customdata = nullptr;
4052}
4053
4055
4056/* -------------------------------------------------------------------- */
4059
4061static int knife_update_active(KnifeTool_OpData *kcd, const float2 &mval)
4062{
4063 knife_snap_update_from_mval(kcd, mval);
4064
4065 if (kcd->mode == MODE_DRAGGING) {
4067 }
4068 return 1;
4069}
4070
4071static void knifetool_update_mval(KnifeTool_OpData *kcd, const float2 &mval)
4072{
4073 knife_recalc_ortho(kcd);
4074
4075 if (knife_update_active(kcd, mval)) {
4077 }
4078}
4079
4081
4082/* -------------------------------------------------------------------- */
4085
4086static void knifetool_finish_single_pre(KnifeTool_OpData *kcd, int ob_index)
4087{
4088 knife_make_cuts(kcd, ob_index);
4089}
4090
4096{
4100
4102 params.calc_looptris = true;
4103 params.calc_normals = true;
4104 params.is_destructive = true;
4105 EDBM_update(static_cast<Mesh *>(ob->data), &params);
4106}
4107
4108/* Called on tool confirmation. */
4110{
4111 /* Separate pre/post passes are needed because `em->looptris` recalculation from the 'post' pass
4112 * causes triangle indices in #KnifeTool_OpData.bvh to get out of sync.
4113 * So perform all the cuts before doing any mesh recalculation, see: #101721. */
4114 for (int ob_index : kcd->objects.index_range()) {
4115 knifetool_finish_single_pre(kcd, ob_index);
4116 }
4117 for (Object *ob : kcd->objects) {
4119 }
4120}
4121
4123{
4124 KnifeTool_OpData *kcd = static_cast<KnifeTool_OpData *>(op->customdata);
4126}
4127
4129
4130/* -------------------------------------------------------------------- */
4133
4134static void knifetool_cancel(bContext * /*C*/, wmOperator *op)
4135{
4136 /* this is just a wrapper around exit() */
4137 knifetool_exit(op);
4138}
4139
4141{
4142 static const EnumPropertyItem modal_items[] = {
4143 {KNF_MODAL_CANCEL, "CANCEL", 0, "Cancel", ""},
4144 {KNF_MODAL_CONFIRM, "CONFIRM", 0, "Confirm", ""},
4145 {KNF_MODAL_UNDO, "UNDO", 0, "Undo", ""},
4146 {KNF_MODAL_MIDPOINT_ON, "SNAP_MIDPOINTS_ON", 0, "Snap to Midpoints On", ""},
4147 {KNF_MODAL_MIDPOINT_OFF, "SNAP_MIDPOINTS_OFF", 0, "Snap to Midpoints Off", ""},
4148 {KNF_MODAL_IGNORE_SNAP_ON, "IGNORE_SNAP_ON", 0, "Ignore Snapping On", ""},
4149 {KNF_MODAL_IGNORE_SNAP_OFF, "IGNORE_SNAP_OFF", 0, "Ignore Snapping Off", ""},
4150 {KNF_MODAL_ANGLE_SNAP_TOGGLE, "ANGLE_SNAP_TOGGLE", 0, "Toggle Angle Snapping", ""},
4152 "CYCLE_ANGLE_SNAP_EDGE",
4153 0,
4154 "Cycle Angle Snapping Relative Edge",
4155 ""},
4156 {KNF_MODAL_CUT_THROUGH_TOGGLE, "CUT_THROUGH_TOGGLE", 0, "Toggle Cut Through", ""},
4158 "SHOW_DISTANCE_ANGLE_TOGGLE",
4159 0,
4160 "Toggle Distance and Angle Measurements",
4161 ""},
4162 {KNF_MODAL_DEPTH_TEST_TOGGLE, "DEPTH_TEST_TOGGLE", 0, "Toggle Depth Testing", ""},
4163 {KNF_MODAL_NEW_CUT, "NEW_CUT", 0, "End Current Cut", ""},
4164 {KNF_MODAL_ADD_CUT, "ADD_CUT", 0, "Add Cut", ""},
4165 {KNF_MODAL_ADD_CUT_CLOSED, "ADD_CUT_CLOSED", 0, "Add Cut Closed", ""},
4166 {KNF_MODAL_PANNING, "PANNING", 0, "Panning", ""},
4167 {KNF_MODAL_X_AXIS, "X_AXIS", 0, "X Axis Locking", ""},
4168 {KNF_MODAL_Y_AXIS, "Y_AXIS", 0, "Y Axis Locking", ""},
4169 {KNF_MODAL_Z_AXIS, "Z_AXIS", 0, "Z Axis Locking", ""},
4170 {0, nullptr, 0, nullptr, nullptr},
4171 };
4172
4173 wmKeyMap *keymap = WM_modalkeymap_find(keyconf, "Knife Tool Modal Map");
4174
4175 /* This function is called for each space-type, only needs to add map once. */
4176 if (keymap && keymap->modal_items) {
4177 return nullptr;
4178 }
4179
4180 keymap = WM_modalkeymap_ensure(keyconf, "Knife Tool Modal Map", modal_items);
4181
4182 WM_modalkeymap_assign(keymap, "MESH_OT_knife_tool");
4183
4184 return keymap;
4185}
4186
4187/* Turn off angle snapping. */
4194
4195/* Turn off orientation locking. */
4202
4204{
4205 KnifeTool_OpData *kcd = static_cast<KnifeTool_OpData *>(op->customdata);
4206 bool do_refresh = false;
4207
4208 Object *ob = (kcd->curr.ob_index != -1) ? kcd->objects[kcd->curr.ob_index] : kcd->vc.obedit;
4209 if (!ob || ob->type != OB_MESH) {
4210 knifetool_exit(op);
4211 ED_workspace_status_text(C, nullptr);
4212 return OPERATOR_FINISHED;
4213 }
4214
4215 kcd->region = kcd->vc.region;
4216
4217 ED_view3d_init_mats_rv3d(ob, kcd->vc.rv3d); /* Needed to initialize clipping. */
4218
4219 if (kcd->mode == MODE_PANNING) {
4220 kcd->mode = KnifeMode(kcd->prevmode);
4221 }
4222
4223 bool handled = false;
4224 float snapping_increment_temp;
4225 const float2 mval = {float(event->mval[0]), float(event->mval[1])};
4226
4227 if (kcd->angle_snapping) {
4228 if (kcd->num.str_cur >= 3 ||
4230 {
4232 }
4233 knife_update_header(C, op, kcd); /* Update the angle multiple. */
4234 /* Modal numinput active, try to handle numeric inputs first... */
4235 if (event->val == KM_PRESS && hasNumInput(&kcd->num) && handleNumInput(C, &kcd->num, event)) {
4236 handled = true;
4237 applyNumInput(&kcd->num, &snapping_increment_temp);
4238 /* Restrict number key input to 0 - 180 degree range. */
4239 if (snapping_increment_temp > KNIFE_MIN_ANGLE_SNAPPING_INCREMENT &&
4240 snapping_increment_temp <= KNIFE_MAX_ANGLE_SNAPPING_INCREMENT)
4241 {
4242 kcd->angle_snapping_increment = snapping_increment_temp;
4243 }
4244 knife_update_active(kcd, mval);
4245 knife_update_header(C, op, kcd);
4248 }
4249 }
4250
4251 /* Handle modal keymap. */
4252 if (event->type == EVT_MODAL_MAP) {
4253 switch (event->val) {
4254 case KNF_MODAL_CANCEL:
4255 /* finish */
4257
4258 knifetool_exit(op);
4259 ED_workspace_status_text(C, nullptr);
4260
4261 return OPERATOR_CANCELLED;
4262 case KNF_MODAL_CONFIRM: {
4263 const bool changed = (kcd->totkvert != 0);
4264 /* finish */
4266
4267 knifetool_finish(op);
4268 knifetool_exit(op);
4269 ED_workspace_status_text(C, nullptr);
4270
4271 /* Cancel to prevent undo push for empty cuts. */
4272 if (!changed) {
4273 return OPERATOR_CANCELLED;
4274 }
4275 return OPERATOR_FINISHED;
4276 }
4277 case KNF_MODAL_UNDO:
4278 if (BLI_stack_is_empty(kcd->undostack)) {
4280 knifetool_exit(op);
4281 ED_workspace_status_text(C, nullptr);
4282 return OPERATOR_CANCELLED;
4283 }
4284 knifetool_undo(kcd);
4285 knife_update_active(kcd, mval);
4287 handled = true;
4288 break;
4290 kcd->snap_midpoints = true;
4291
4292 knife_recalc_ortho(kcd);
4293 knife_update_active(kcd, mval);
4294 do_refresh = true;
4295 handled = true;
4296 break;
4298 kcd->snap_midpoints = false;
4299
4300 knife_recalc_ortho(kcd);
4301 knife_update_active(kcd, mval);
4302 do_refresh = true;
4303 handled = true;
4304 break;
4306 kcd->ignore_vert_snapping = kcd->ignore_edge_snapping = true;
4307 do_refresh = true;
4308 handled = true;
4309 break;
4311 kcd->ignore_vert_snapping = kcd->ignore_edge_snapping = false;
4312 do_refresh = true;
4313 handled = true;
4314 break;
4317 kcd->angle_snapping_mode++;
4318 kcd->snap_ref_edges_count = 0;
4319 kcd->snap_edge = 0;
4320 }
4321 else {
4323 }
4326 RNA_float_get(op->ptr, "angle_snapping_increment"));
4329 knife_update_active(kcd, mval);
4330 do_refresh = true;
4331 handled = true;
4332 break;
4335 if (kcd->snap_ref_edges_count) {
4336 kcd->snap_edge++;
4337 kcd->snap_edge %= kcd->snap_ref_edges_count;
4339 do_refresh = true;
4340 handled = true;
4341 }
4342 }
4343 break;
4345 kcd->cut_through = !kcd->cut_through;
4346 knife_update_active(kcd, mval);
4347 do_refresh = true;
4348 handled = true;
4349 break;
4352 kcd->dist_angle_mode++;
4353 }
4354 else {
4356 }
4358 do_refresh = true;
4359 handled = true;
4360 break;
4362 kcd->depth_test = !kcd->depth_test;
4363 do_refresh = true;
4364 handled = true;
4365 break;
4366 case KNF_MODAL_NEW_CUT:
4367 /* If no cuts have been made, exit.
4368 * Preserves right click cancel workflow which most tools use,
4369 * but stops accidentally deleting entire cuts with right click.
4370 */
4371 if (kcd->no_cuts) {
4373 knifetool_exit(op);
4374 ED_workspace_status_text(C, nullptr);
4375 return OPERATOR_CANCELLED;
4376 }
4378 knife_finish_cut(kcd);
4379 kcd->mode = MODE_IDLE;
4380 handled = true;
4381 break;
4382 case KNF_MODAL_ADD_CUT:
4383 kcd->no_cuts = false;
4384 knife_recalc_ortho(kcd);
4385
4386 /* Get the value of the event which triggered this one. */
4387 if (event->prev_val != KM_RELEASE) {
4388 if (kcd->mode == MODE_DRAGGING) {
4389 knife_add_cut(kcd);
4390 }
4391 else if (kcd->mode != MODE_PANNING) {
4392 knife_start_cut(kcd, mval);
4393 kcd->mode = MODE_DRAGGING;
4394 kcd->init = kcd->curr;
4395 }
4396
4397 /* Freehand drawing is incompatible with cut-through. */
4398 if (kcd->cut_through == false) {
4399 kcd->is_drag_hold = true;
4400 /* No edge snapping while dragging (edges are too sticky when cuts are immediate). */
4401 kcd->ignore_edge_snapping = true;
4402 }
4403 }
4404 else {
4405 kcd->is_drag_hold = false;
4406 kcd->ignore_edge_snapping = false;
4407 kcd->is_drag_undo = false;
4408
4409 /* Needed because the last face 'hit' is ignored when dragging. */
4410 knifetool_update_mval(kcd, kcd->curr.mval);
4411 }
4412
4414 handled = true;
4415 break;
4417 if (kcd->mode == MODE_DRAGGING) {
4418
4419 /* Shouldn't be possible with default key-layout, just in case. */
4420 if (kcd->is_drag_hold) {
4421 kcd->is_drag_hold = false;
4422 kcd->is_drag_undo = false;
4423 knifetool_update_mval(kcd, kcd->curr.mval);
4424 }
4425
4426 kcd->prev = kcd->curr;
4427 kcd->curr = kcd->init;
4428
4429 knife_project_v2(kcd, kcd->curr.cage, kcd->curr.mval);
4430 knifetool_update_mval(kcd, kcd->curr.mval);
4431
4432 knife_add_cut(kcd);
4433
4434 /* KNF_MODAL_NEW_CUT */
4435 knife_finish_cut(kcd);
4436 kcd->mode = MODE_IDLE;
4437 }
4438 handled = true;
4439 break;
4440 case KNF_MODAL_PANNING:
4441 if (event->val != KM_RELEASE) {
4442 if (kcd->mode != MODE_PANNING) {
4443 kcd->prevmode = kcd->mode;
4444 kcd->mode = MODE_PANNING;
4445 }
4446 }
4447 else {
4448 kcd->mode = KnifeMode(kcd->prevmode);
4449 }
4450
4452 return OPERATOR_PASS_THROUGH;
4453 }
4454 }
4455 else { /* non-modal-mapped events */
4456 switch (event->type) {
4457 case MOUSEPAN:
4458 case MOUSEZOOM:
4459 case MOUSEROTATE:
4460 case WHEELUPMOUSE:
4461 case WHEELDOWNMOUSE:
4462 case NDOF_MOTION:
4463 return OPERATOR_PASS_THROUGH;
4464 case MOUSEMOVE: /* Mouse moved somewhere to select another loop. */
4465 if (kcd->mode != MODE_PANNING) {
4466 knifetool_update_mval(kcd, mval);
4467 do_refresh = true;
4468
4469 if (kcd->is_drag_hold) {
4470 if (kcd->linehits.size() >= 2) {
4471 knife_add_cut(kcd);
4472 }
4473 }
4474 }
4475
4476 break;
4477 default: {
4478 break;
4479 }
4480 }
4481 }
4482
4483 if (kcd->angle_snapping) {
4484 if (kcd->num.str_cur >= 3 ||
4486 {
4488 }
4489 if (event->type != EVT_MODAL_MAP) {
4490 /* Modal number-input inactive, try to handle numeric inputs last. */
4491 if (!handled && event->val == KM_PRESS && handleNumInput(C, &kcd->num, event)) {
4492 applyNumInput(&kcd->num, &snapping_increment_temp);
4493 /* Restrict number key input to 0 - 180 degree range. */
4494 if (snapping_increment_temp > KNIFE_MIN_ANGLE_SNAPPING_INCREMENT &&
4495 snapping_increment_temp <= KNIFE_MAX_ANGLE_SNAPPING_INCREMENT)
4496 {
4497 kcd->angle_snapping_increment = snapping_increment_temp;
4498 }
4499 knife_update_active(kcd, mval);
4500 knife_update_header(C, op, kcd);
4503 }
4504 }
4505 }
4506
4507 /* Constrain axes with X,Y,Z keys. */
4508 if (event->type == EVT_MODAL_MAP) {
4510 if (event->val == KNF_MODAL_X_AXIS && kcd->constrain_axis != KNF_CONSTRAIN_AXIS_X) {
4513 kcd->axis_string[0] = 'X';
4514 }
4515 else if (event->val == KNF_MODAL_Y_AXIS && kcd->constrain_axis != KNF_CONSTRAIN_AXIS_Y) {
4518 kcd->axis_string[0] = 'Y';
4519 }
4520 else if (event->val == KNF_MODAL_Z_AXIS && kcd->constrain_axis != KNF_CONSTRAIN_AXIS_Z) {
4523 kcd->axis_string[0] = 'Z';
4524 }
4525 else {
4526 /* Cycle through modes with repeated key presses. */
4528 kcd->constrain_axis_mode++;
4529 kcd->axis_string[0] += 32; /* Lower case. */
4530 }
4531 else {
4534 }
4535 }
4538
4539 /* Needed so changes to constraints are re-evaluated without any cursor motion. */
4540 knifetool_update_mval(kcd, mval);
4541
4542 do_refresh = true;
4543 }
4544 }
4545
4546 if (kcd->mode == MODE_DRAGGING) {
4548 }
4549 else {
4551 }
4552
4553 if (do_refresh) {
4555 knife_update_header(C, op, kcd);
4556 }
4557
4558 /* Keep going until the user confirms. */
4560}
4561
4563{
4564 const bool only_select = RNA_boolean_get(op->ptr, "only_selected");
4565 const bool cut_through = !RNA_boolean_get(op->ptr, "use_occlude_geometry");
4566 const bool xray = !RNA_boolean_get(op->ptr, "xray");
4567 const int visible_measurements = RNA_enum_get(op->ptr, "visible_measurements");
4568 const int angle_snapping = RNA_enum_get(op->ptr, "angle_snapping");
4569 const bool wait_for_input = RNA_boolean_get(op->ptr, "wait_for_input");
4570 const float angle_snapping_increment = RAD2DEGF(
4571 RNA_float_get(op->ptr, "angle_snapping_increment"));
4572
4574
4575 /* alloc new customdata */
4576 KnifeTool_OpData *kcd = MEM_new<KnifeTool_OpData>(__func__);
4577 op->customdata = kcd;
4579 &vc,
4580 kcd,
4582 only_select,
4583 cut_through,
4584 xray,
4585 visible_measurements,
4586 angle_snapping,
4587 angle_snapping_increment,
4588 true);
4589
4590 if (only_select) {
4591 bool faces_selected = false;
4592 for (Object *obedit : kcd->objects) {
4594 if (em->bm->totfacesel != 0) {
4595 faces_selected = true;
4596 }
4597 }
4598
4599 if (!faces_selected) {
4600 BKE_report(op->reports, RPT_ERROR, "Selected faces required");
4601 knifetool_cancel(C, op);
4602 return OPERATOR_CANCELLED;
4603 }
4604 }
4605
4607
4608 /* Add a modal handler for this operator - handles loop selection. */
4611
4612 if (wait_for_input == false) {
4613 /* Avoid copy-paste logic. */
4614 wmEvent event_modal{};
4615 event_modal.prev_val = KM_NOTHING;
4616 event_modal.type = EVT_MODAL_MAP;
4617 event_modal.val = KNF_MODAL_ADD_CUT;
4618
4619 copy_v2_v2_int(event_modal.mval, event->mval);
4620
4621 wmOperatorStatus retval = knifetool_modal(C, op, &event_modal);
4622 OPERATOR_RETVAL_CHECK(retval);
4624 UNUSED_VARS_NDEBUG(retval);
4625 }
4626
4627 knife_update_header(C, op, kcd);
4628
4630}
4631
4633{
4634 /* Description. */
4635 ot->name = "Knife Topology Tool";
4636 ot->idname = "MESH_OT_knife_tool";
4637 ot->description = "Cut new topology";
4638
4639 /* Callbacks. */
4640 ot->invoke = knifetool_invoke;
4641 ot->modal = knifetool_modal;
4642 ot->cancel = knifetool_cancel;
4644
4645 /* Flags. */
4647
4648 /* Properties. */
4649 PropertyRNA *prop;
4650 static const EnumPropertyItem visible_measurements_items[] = {
4651 {KNF_MEASUREMENT_NONE, "NONE", 0, "None", "Show no measurements"},
4652 {KNF_MEASUREMENT_BOTH, "BOTH", 0, "Both", "Show both distances and angles"},
4653 {KNF_MEASUREMENT_DISTANCE, "DISTANCE", 0, "Distance", "Show just distance measurements"},
4654 {KNF_MEASUREMENT_ANGLE, "ANGLE", 0, "Angle", "Show just angle measurements"},
4655 {0, nullptr, 0, nullptr, nullptr},
4656 };
4657
4658 static const EnumPropertyItem angle_snapping_items[] = {
4659 {KNF_CONSTRAIN_ANGLE_MODE_NONE, "NONE", 0, "None", "No angle snapping"},
4660 {KNF_CONSTRAIN_ANGLE_MODE_SCREEN, "SCREEN", 0, "Screen", "Screen space angle snapping"},
4662 "RELATIVE",
4663 0,
4664 "Relative",
4665 "Angle snapping relative to the previous cut edge"},
4666 {0, nullptr, 0, nullptr, nullptr},
4667 };
4668
4669 RNA_def_boolean(ot->srna,
4670 "use_occlude_geometry",
4671 true,
4672 "Occlude Geometry",
4673 "Only cut the front most geometry");
4674 RNA_def_boolean(ot->srna, "only_selected", false, "Only Selected", "Only cut selected geometry");
4675 RNA_def_boolean(ot->srna, "xray", true, "X-Ray", "Show cuts hidden by geometry");
4676
4677 RNA_def_enum(ot->srna,
4678 "visible_measurements",
4679 visible_measurements_items,
4681 "Measurements",
4682 "Visible distance and angle measurements");
4683 prop = RNA_def_enum(ot->srna,
4684 "angle_snapping",
4685 angle_snapping_items,
4687 "Angle Snapping",
4688 "Angle snapping mode");
4690
4691 prop = RNA_def_float(ot->srna,
4692 "angle_snapping_increment",
4696 "Angle Snap Increment",
4697 "The angle snap increment used when in constrained angle mode",
4701
4702 prop = RNA_def_boolean(ot->srna, "wait_for_input", true, "Wait for Input", "");
4704}
4705
4707
4708/* -------------------------------------------------------------------- */
4713
4714static bool edbm_mesh_knife_point_isect(LinkNode *polys, const float cent_ss[2])
4715{
4716 LinkNode *p = polys;
4717 int isect = 0;
4718
4719 while (p) {
4720 const float (*mval_fl)[2] = static_cast<const float (*)[2]>(p->link);
4721 const int mval_tot = MEM_allocN_len(mval_fl) / sizeof(*mval_fl);
4722 isect += int(isect_point_poly_v2(cent_ss, mval_fl, mval_tot - 1));
4723 p = p->next;
4724 }
4725
4726 if (isect % 2) {
4727 return true;
4728 }
4729 return false;
4730}
4731
4733 ViewContext *vc, const Span<Object *> objects, LinkNode *polys, bool use_tag, bool cut_through)
4734{
4735 KnifeTool_OpData *kcd;
4736
4737 /* Init. */
4738 {
4739 const bool only_select = false;
4740 const bool is_interactive = false; /* Can enable for testing. */
4741 const bool xray = false;
4742 const int visible_measurements = KNF_MEASUREMENT_NONE;
4743 const int angle_snapping = KNF_CONSTRAIN_ANGLE_MODE_NONE;
4744 const float angle_snapping_increment = KNIFE_DEFAULT_ANGLE_SNAPPING_INCREMENT;
4745
4746 kcd = MEM_new<KnifeTool_OpData>(__func__);
4747
4748 knifetool_init(vc,
4749 kcd,
4750 {objects},
4751 only_select,
4752 cut_through,
4753 xray,
4754 visible_measurements,
4755 angle_snapping,
4756 angle_snapping_increment,
4757 is_interactive);
4758
4759 kcd->ignore_edge_snapping = true;
4760 kcd->ignore_vert_snapping = true;
4761 }
4762
4763 /* Execute. */
4764 {
4765 LinkNode *p = polys;
4766
4767 knife_recalc_ortho(kcd);
4768
4769 while (p) {
4770 const float (*mval_fl)[2] = static_cast<const float (*)[2]>(p->link);
4771 const int mval_tot = MEM_allocN_len(mval_fl) / sizeof(*mval_fl);
4772 int i;
4773
4774 knife_start_cut(kcd, mval_fl[0]);
4775 kcd->mode = MODE_DRAGGING;
4776
4777 for (i = 1; i < mval_tot; i++) {
4778 knifetool_update_mval(kcd, mval_fl[i]);
4779 knife_add_cut(kcd);
4780 }
4781
4782 knife_finish_cut(kcd);
4783 kcd->mode = MODE_IDLE;
4784 p = p->next;
4785 }
4786 }
4787
4788 /* Finish. */
4789 {
4790 /* See #knifetool_finish_ex for why multiple passes are needed. */
4791 for (int ob_index : kcd->objects.index_range()) {
4792 Object *ob = kcd->objects[ob_index];
4794
4795 if (use_tag) {
4797 }
4798
4799 knifetool_finish_single_pre(kcd, ob_index);
4800 }
4801
4802 for (Object *ob : kcd->objects) {
4804
4805 /* Tag faces inside! */
4806 if (use_tag) {
4807 BMesh *bm = em->bm;
4808 BMEdge *e;
4809 BMIter iter;
4810 bool keep_search;
4811
4812/* Use face-loop tag to store if we have intersected. */
4813#define F_ISECT_IS_UNKNOWN(f) BM_elem_flag_test(BM_FACE_FIRST_LOOP(f), BM_ELEM_TAG)
4814#define F_ISECT_SET_UNKNOWN(f) BM_elem_flag_enable(BM_FACE_FIRST_LOOP(f), BM_ELEM_TAG)
4815#define F_ISECT_SET_OUTSIDE(f) BM_elem_flag_disable(BM_FACE_FIRST_LOOP(f), BM_ELEM_TAG)
4816 {
4817 BMFace *f;
4818 BM_ITER_MESH (f, &iter, bm, BM_FACES_OF_MESH) {
4821 }
4822 }
4823
4824 /* Tag all faces linked to cut edges. */
4825 BM_ITER_MESH (e, &iter, bm, BM_EDGES_OF_MESH) {
4826 /* Check are we tagged?, then we are an original face. */
4828 continue;
4829 }
4830
4831 BMFace *f;
4832 BMIter fiter;
4833 BM_ITER_ELEM (f, &fiter, e, BM_FACES_OF_EDGE) {
4834 float cent[3], cent_ss[2];
4836 mul_m4_v3(ob->object_to_world().ptr(), cent);
4837 knife_project_v2(kcd, cent, cent_ss);
4838 if (edbm_mesh_knife_point_isect(polys, cent_ss)) {
4840 }
4841 }
4842 }
4843
4844 /* Expand tags for faces which are not cut, but are inside the polys. */
4845 do {
4846 BMFace *f;
4847 keep_search = false;
4848 BM_ITER_MESH (f, &iter, bm, BM_FACES_OF_MESH) {
4850 continue;
4851 }
4852
4853 /* Am I connected to a tagged face via an un-tagged edge
4854 * (ie, not across a cut)? */
4855 BMLoop *l_first = BM_FACE_FIRST_LOOP(f);
4856 BMLoop *l_iter = l_first;
4857 bool found = false;
4858
4859 do {
4860 if (BM_elem_flag_test(l_iter->e, BM_ELEM_TAG) != false) {
4861 /* Now check if the adjacent faces is tagged. */
4862 BMLoop *l_radial_iter = l_iter->radial_next;
4863 if (l_radial_iter != l_iter) {
4864 do {
4865 if (BM_elem_flag_test(l_radial_iter->f, BM_ELEM_TAG)) {
4866 found = true;
4867 }
4868 } while ((l_radial_iter = l_radial_iter->radial_next) != l_iter &&
4869 (found == false));
4870 }
4871 }
4872 } while ((l_iter = l_iter->next) != l_first && (found == false));
4873
4874 if (found) {
4875 float cent[3], cent_ss[2];
4877 mul_m4_v3(ob->object_to_world().ptr(), cent);
4878 knife_project_v2(kcd, cent, cent_ss);
4879 if ((kcd->cut_through || point_is_visible(kcd, cent, cent_ss, (BMElem *)f)) &&
4880 edbm_mesh_knife_point_isect(polys, cent_ss))
4881 {
4883 keep_search = true;
4884 }
4885 else {
4886 /* Don't lose time on this face again, set it as outside. */
4888 }
4889 }
4890 }
4891 } while (keep_search);
4892
4893#undef F_ISECT_IS_UNKNOWN
4894#undef F_ISECT_SET_UNKNOWN
4895#undef F_ISECT_SET_OUTSIDE
4896 }
4897 }
4898
4899 for (Object *ob : kcd->objects) {
4900 /* Defer freeing data until the BVH tree is finished with, see: #point_is_visible and
4901 * the doc-string for #knifetool_finish_single_post. */
4903 }
4904
4905 knifetool_exit_ex(kcd);
4906 kcd = nullptr;
4907 }
4908}
4909
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)
@ RPT_ERROR
Definition BKE_report.hh:39
void BKE_report(ReportList *reports, eReportType type, const char *message)
Definition report.cc:153
int BKE_scene_orientation_get_index(Scene *scene, int slot_index)
Definition scene.cc:2439
@ B_UNIT_LENGTH
Definition BKE_unit.hh:137
@ B_UNIT_ROTATION
Definition BKE_unit.hh:141
@ B_UNIT_NONE
Definition BKE_unit.hh:136
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:1882
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:1895
void BLF_size(int fontid, float size)
Definition blf.cc:443
void BLF_enable(int fontid, FontFlags flag)
Definition blf.cc:320
void BLF_color3ubv(int fontid, const unsigned char rgb[3])
Definition blf.cc:476
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:789
void BLF_rotation(int fontid, float angle)
Definition blf.cc:903
void BLF_draw(int fontid, const char *str, size_t str_len, ResultBLF *r_info=nullptr) ATTR_NONNULL(2)
Definition blf.cc:585
int blf_mono_font
Definition blf.cc:48
void BLF_disable(int fontid, FontFlags flag)
Definition blf.cc:329
void BLF_position(int fontid, float x, float y, float z)
Definition blf.cc:388
@ BLF_ROTATION
Definition BLF_enums.hh:33
#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)
MINLINE 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_ALLOW_ITER
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)
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_UTF8(dst, format,...)
#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
@ SCE_ORIENT_DEFAULT
@ SCE_SELECT_FACE
@ USER_UNIT_NONE
@ SPACE_VIEW3D
#define UI_SCALE_FAC
#define RV3D_CLIPPING_ENABLED(v3d, rv3d)
@ RV3D_CAMOB
@ 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)
bool EDBM_uvselect_clear(BMEditMesh *em)
BMFace * EDBM_face_find_nearest(ViewContext *vc, float *dist_px_manhattan_p)
ViewContext em_setup_viewcontext(bContext *C)
void EDBM_selectmode_flush(BMEditMesh *em)
void initNumInput(NumInput *n)
Definition numinput.cc:70
bool handleNumInput(bContext *C, NumInput *n, const wmEvent *event)
Definition numinput.cc:312
bool applyNumInput(NumInput *n, float *vec)
Definition numinput.cc:190
@ NUM_NO_NEGATIVE
bool hasNumInput(const NumInput *n)
Definition numinput.cc:171
bool ED_operator_editmesh_view3d(bContext *C)
void ED_workspace_status_text(bContext *C, const char *str)
Definition area.cc:1024
void ED_region_tag_redraw(ARegion *region)
Definition area.cc:618
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
#define GPU_batch_uniform_1f(batch, name, x)
Definition GPU_batch.hh:271
void GPU_batch_discard(blender::gpu::Batch *batch)
void GPU_batch_program_set_builtin(blender::gpu::Batch *batch, GPUBuiltinShader shader_id)
blender::gpu::Batch * GPU_batch_create_ex(GPUPrimType primitive_type, blender::gpu::VertBuf *vertex_buf, blender::gpu::IndexBuf *index_buf, GPUBatchFlag owns_flag)
Definition gpu_batch.cc:51
@ GPU_BATCH_OWNS_VBO
Definition GPU_batch.hh:42
void GPU_batch_draw(blender::gpu::Batch *batch)
#define GPU_batch_uniform_4fv(batch, name, val)
Definition GPU_batch.hh:279
void GPU_batch_draw_range(blender::gpu::Batch *batch, int vertex_first, int vertex_count)
void immEnd()
void immUnbindProgram()
void immBindBuiltinProgram(GPUBuiltinShader 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_DEPTH_LESS_EQUAL
Definition GPU_state.hh:114
@ GPU_DEPTH_NONE
Definition GPU_state.hh:111
void GPU_line_width(float width)
Definition gpu_state.cc:166
@ GPU_BLEND_NONE
Definition GPU_state.hh:85
@ GPU_BLEND_ALPHA
Definition GPU_state.hh:87
void GPU_blend(GPUBlend blend)
Definition gpu_state.cc:42
void GPU_depth_test(GPUDepthTest test)
Definition gpu_state.cc:68
static blender::gpu::VertBuf * GPU_vertbuf_create_with_format(const GPUVertFormat &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)
uint GPU_vertformat_attr_add(GPUVertFormat *format, blender::StringRef name, blender::gpu::VertAttrType type)
static double angle(const Eigen::Vector3d &v1, const Eigen::Vector3d &v2)
Definition IK_Math.h:117
Read Guarded memory(de)allocation.
#define MEM_SIZE_OPTIMAL(size)
@ PROP_SKIP_SAVE
Definition RNA_types.hh:344
@ PROP_HIDDEN
Definition RNA_types.hh:338
@ PROP_ANGLE
Definition RNA_types.hh:252
#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])
@ KM_NOTHING
Definition WM_types.hh:310
@ KM_PRESS
Definition WM_types.hh:311
@ KM_RELEASE
Definition WM_types.hh:312
@ OPTYPE_BLOCKING
Definition WM_types.hh:184
@ OPTYPE_UNDO
Definition WM_types.hh:182
@ OPTYPE_REGISTER
Definition WM_types.hh:180
#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:256
const T * data() const
Definition BLI_array.hh:312
void reinitialize(const int64_t new_size)
Definition BLI_array.hh:419
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
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)
nullptr float
#define roundf(x)
@ KNF_CONSTRAIN_ANGLE_MODE_NONE
@ KNF_CONSTRAIN_ANGLE_MODE_SCREEN
@ KNF_CONSTRAIN_ANGLE_MODE_RELATIVE
static bool knife_find_closest_face(KnifeTool_OpData *kcd, const float2 &mval, const float3 &ray_orig, const float3 &ray_dir, KnifePosData *r_kpd)
@ 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 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)
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)
@ KNF_CONSTRAIN_AXIS_Y
@ KNF_CONSTRAIN_AXIS_Z
@ KNF_CONSTRAIN_AXIS_X
@ KNF_CONSTRAIN_AXIS_NONE
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)
static void knifetool_draw_angle_snapping(const KnifeTool_OpData *kcd)
@ KNF_MEASUREMENT_NONE
@ KNF_MEASUREMENT_BOTH
@ KNF_MEASUREMENT_DISTANCE
@ KNF_MEASUREMENT_ANGLE
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)
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)
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)
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
@ KNF_CONSTRAIN_AXIS_MODE_LOCAL
@ KNF_CONSTRAIN_AXIS_MODE_GLOBAL
@ KNF_CONSTRAIN_AXIS_MODE_NONE
static int sort_verts_by_dist_cb(void *co_p, const void *cur_a_p, const void *cur_b_p)
static KnifeEdge * get_bm_knife_edge(KnifeTool_OpData *kcd, BMEdge *e, int ob_index)
static void knifetool_disable_orientation_locking(KnifeTool_OpData *kcd)
static int knife_sample_screen_density_from_closest_face(KnifeTool_OpData *kcd, const float radius, int ob_index, BMFace *f, const float cageco[3])
static int linehit_compare(const KnifeLineHit &lh1, const KnifeLineHit &lh2)
static void knife_add_edge_faces_to_vert(KnifeTool_OpData *kcd, KnifeVert *kfv, BMEdge *e)
static void knife_start_cut(KnifeTool_OpData *kcd, const float2 &mval)
static bool coinciding_edges(BMEdge *e1, BMEdge *e2)
static bool knife_bm_face_is_select(BMFace *f)
static bool edbm_mesh_knife_point_isect(LinkNode *polys, const float cent_ss[2])
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)
struct @021025263243242147216143265077100330027142264337::@225245033123204053237120173316075113304004012000 batch
uint pos
#define cos
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, 2 > float2
VecBase< float, 3 > float3
const int status
#define fabsf
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
BLI_mempool * refs
KnifeUndoFrame * undo
Array< KnifeObjectInfo > objects_info
BLI_mempool * kverts
BLI_Stack * splitstack
struct KnifeTool_OpData::@240051365323366117076354054023250343146140107334 edgenet
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:757
short val
Definition WM_types.hh:759
int mval[2]
Definition WM_types.hh:763
short prev_val
Definition WM_types.hh:814
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:4238
wmOperatorType * ot
Definition wm_files.cc:4237
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:932
void WM_modalkeymap_assign(wmKeyMap *km, const char *opname)
wmKeyMap * WM_modalkeymap_find(wmKeyConfig *keyconf, const char *idname)
Definition wm_keymap.cc:959
void wmOrtho2_region_pixelspace(const ARegion *region)