1 /*
2  * This program is free software; you can redistribute it and/or
3  * modify it under the terms of the GNU General Public License
4  * as published by the Free Software Foundation; either version 2
5  * of the License, or (at your option) any later version.
6  *
7  * This program is distributed in the hope that it will be useful,
8  * but WITHOUT ANY WARRANTY; without even the implied warranty of
9  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
10  * GNU General Public License for more details.
11  *
12  * You should have received a copy of the GNU General Public License
13  * along with this program; if not, write to the Free Software Foundation,
14  * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
15  */
16 
17 /** \file
18  * \ingroup spview3d
19  */
20 
21 #include "DNA_armature_types.h"
22 #include "DNA_curve_types.h"
23 #include "DNA_lattice_types.h"
24 #include "DNA_mesh_types.h"
25 #include "DNA_meshdata_types.h"
26 #include "DNA_meta_types.h"
27 #include "DNA_object_types.h"
28 #include "DNA_scene_types.h"
29 
30 #include "BLI_math_geom.h"
31 #include "BLI_rect.h"
32 #include "BLI_utildefines.h"
33 
34 #include "BKE_DerivedMesh.h"
35 #include "BKE_action.h"
36 #include "BKE_armature.h"
37 #include "BKE_curve.h"
38 #include "BKE_displist.h"
39 #include "BKE_editmesh.h"
40 #include "BKE_mesh_iterators.h"
41 #include "BKE_mesh_runtime.h"
42 #include "BKE_modifier.h"
43 
44 #include "DEG_depsgraph.h"
45 #include "DEG_depsgraph_query.h"
46 
47 #include "bmesh.h"
48 
49 #include "ED_armature.h"
50 #include "ED_screen.h"
51 #include "ED_view3d.h"
52 
53 typedef struct foreachScreenObjectVert_userData {
54   void (*func)(void *userData, MVert *mv, const float screen_co_b[2], int index);
55   void *userData;
56   ViewContext vc;
57   eV3DProjTest clip_flag;
58 } foreachScreenObjectVert_userData;
59 
60 typedef struct foreachScreenVert_userData {
61   void (*func)(void *userData, BMVert *eve, const float screen_co_b[2], int index);
62   void *userData;
63   ViewContext vc;
64   eV3DProjTest clip_flag;
65 } foreachScreenVert_userData;
66 
67 /* user data structures for derived mesh callbacks */
68 typedef struct foreachScreenEdge_userData {
69   void (*func)(void *userData,
70                BMEdge *eed,
71                const float screen_co_a[2],
72                const float screen_co_b[2],
73                int index);
74   void *userData;
75   ViewContext vc;
76   rctf win_rect; /* copy of: vc.region->winx/winy, use for faster tests, minx/y will always be 0 */
77   eV3DProjTest clip_flag;
78 } foreachScreenEdge_userData;
79 
80 typedef struct foreachScreenFace_userData {
81   void (*func)(void *userData, BMFace *efa, const float screen_co_b[2], int index);
82   void *userData;
83   ViewContext vc;
84   eV3DProjTest clip_flag;
85 } foreachScreenFace_userData;
86 
87 /**
88  * \note foreach funcs should be called while drawing or directly after
89  * if not, #ED_view3d_init_mats_rv3d() can be used for selection tools
90  * but would not give correct results with dupli's for eg. which don't
91  * use the object matrix in the usual way.
92  */
93 
94 /* ------------------------------------------------------------------------ */
95 
meshobject_foreachScreenVert__mapFunc(void * userData,int index,const float co[3],const float UNUSED (no_f[3]),const short UNUSED (no_s[3]))96 static void meshobject_foreachScreenVert__mapFunc(void *userData,
97                                                   int index,
98                                                   const float co[3],
99                                                   const float UNUSED(no_f[3]),
100                                                   const short UNUSED(no_s[3]))
101 {
102   foreachScreenObjectVert_userData *data = userData;
103   struct MVert *mv = &((Mesh *)(data->vc.obact->data))->mvert[index];
104 
105   if (!(mv->flag & ME_HIDE)) {
106     float screen_co[2];
107 
108     if (ED_view3d_project_float_object(data->vc.region, co, screen_co, data->clip_flag) !=
109         V3D_PROJ_RET_OK) {
110       return;
111     }
112 
113     data->func(data->userData, mv, screen_co, index);
114   }
115 }
116 
meshobject_foreachScreenVert(ViewContext * vc,void (* func)(void * userData,MVert * eve,const float screen_co[2],int index),void * userData,eV3DProjTest clip_flag)117 void meshobject_foreachScreenVert(
118     ViewContext *vc,
119     void (*func)(void *userData, MVert *eve, const float screen_co[2], int index),
120     void *userData,
121     eV3DProjTest clip_flag)
122 {
123   foreachScreenObjectVert_userData data;
124   Mesh *me;
125 
126   Scene *scene_eval = DEG_get_evaluated_scene(vc->depsgraph);
127   Object *ob_eval = DEG_get_evaluated_object(vc->depsgraph, vc->obact);
128 
129   me = mesh_get_eval_final(vc->depsgraph, scene_eval, ob_eval, &CD_MASK_BAREMESH);
130 
131   ED_view3d_check_mats_rv3d(vc->rv3d);
132 
133   data.vc = *vc;
134   data.func = func;
135   data.userData = userData;
136   data.clip_flag = clip_flag;
137 
138   if (clip_flag & V3D_PROJ_TEST_CLIP_BB) {
139     ED_view3d_clipping_local(vc->rv3d, vc->obact->obmat);
140   }
141 
142   BKE_mesh_foreach_mapped_vert(me, meshobject_foreachScreenVert__mapFunc, &data, MESH_FOREACH_NOP);
143 }
144 
mesh_foreachScreenVert__mapFunc(void * userData,int index,const float co[3],const float UNUSED (no_f[3]),const short UNUSED (no_s[3]))145 static void mesh_foreachScreenVert__mapFunc(void *userData,
146                                             int index,
147                                             const float co[3],
148                                             const float UNUSED(no_f[3]),
149                                             const short UNUSED(no_s[3]))
150 {
151   foreachScreenVert_userData *data = userData;
152   BMVert *eve = BM_vert_at_index(data->vc.em->bm, index);
153 
154   if (!BM_elem_flag_test(eve, BM_ELEM_HIDDEN)) {
155     float screen_co[2];
156 
157     if (ED_view3d_project_float_object(data->vc.region, co, screen_co, data->clip_flag) !=
158         V3D_PROJ_RET_OK) {
159       return;
160     }
161 
162     data->func(data->userData, eve, screen_co, index);
163   }
164 }
165 
mesh_foreachScreenVert(ViewContext * vc,void (* func)(void * userData,BMVert * eve,const float screen_co[2],int index),void * userData,eV3DProjTest clip_flag)166 void mesh_foreachScreenVert(
167     ViewContext *vc,
168     void (*func)(void *userData, BMVert *eve, const float screen_co[2], int index),
169     void *userData,
170     eV3DProjTest clip_flag)
171 {
172   foreachScreenVert_userData data;
173 
174   Mesh *me = editbmesh_get_eval_cage_from_orig(
175       vc->depsgraph, vc->scene, vc->obedit, &CD_MASK_BAREMESH);
176 
177   ED_view3d_check_mats_rv3d(vc->rv3d);
178 
179   data.vc = *vc;
180   data.func = func;
181   data.userData = userData;
182   data.clip_flag = clip_flag;
183 
184   if (clip_flag & V3D_PROJ_TEST_CLIP_BB) {
185     ED_view3d_clipping_local(vc->rv3d, vc->obedit->obmat); /* for local clipping lookups */
186   }
187 
188   BM_mesh_elem_table_ensure(vc->em->bm, BM_VERT);
189   BKE_mesh_foreach_mapped_vert(me, mesh_foreachScreenVert__mapFunc, &data, MESH_FOREACH_NOP);
190 }
191 
192 /* ------------------------------------------------------------------------ */
193 
mesh_foreachScreenEdge__mapFunc(void * userData,int index,const float v0co[3],const float v1co[3])194 static void mesh_foreachScreenEdge__mapFunc(void *userData,
195                                             int index,
196                                             const float v0co[3],
197                                             const float v1co[3])
198 {
199   foreachScreenEdge_userData *data = userData;
200   BMEdge *eed = BM_edge_at_index(data->vc.em->bm, index);
201 
202   if (!BM_elem_flag_test(eed, BM_ELEM_HIDDEN)) {
203     float screen_co_a[2];
204     float screen_co_b[2];
205     eV3DProjTest clip_flag_nowin = data->clip_flag & ~V3D_PROJ_TEST_CLIP_WIN;
206 
207     if (ED_view3d_project_float_object(data->vc.region, v0co, screen_co_a, clip_flag_nowin) !=
208         V3D_PROJ_RET_OK) {
209       return;
210     }
211     if (ED_view3d_project_float_object(data->vc.region, v1co, screen_co_b, clip_flag_nowin) !=
212         V3D_PROJ_RET_OK) {
213       return;
214     }
215 
216     if (data->clip_flag & V3D_PROJ_TEST_CLIP_WIN) {
217       if (!BLI_rctf_isect_segment(&data->win_rect, screen_co_a, screen_co_b)) {
218         return;
219       }
220     }
221 
222     data->func(data->userData, eed, screen_co_a, screen_co_b, index);
223   }
224 }
225 
mesh_foreachScreenEdge(ViewContext * vc,void (* func)(void * userData,BMEdge * eed,const float screen_co_a[2],const float screen_co_b[2],int index),void * userData,eV3DProjTest clip_flag)226 void mesh_foreachScreenEdge(ViewContext *vc,
227                             void (*func)(void *userData,
228                                          BMEdge *eed,
229                                          const float screen_co_a[2],
230                                          const float screen_co_b[2],
231                                          int index),
232                             void *userData,
233                             eV3DProjTest clip_flag)
234 {
235   foreachScreenEdge_userData data;
236 
237   Mesh *me = editbmesh_get_eval_cage_from_orig(
238       vc->depsgraph, vc->scene, vc->obedit, &CD_MASK_BAREMESH);
239 
240   ED_view3d_check_mats_rv3d(vc->rv3d);
241 
242   data.vc = *vc;
243 
244   data.win_rect.xmin = 0;
245   data.win_rect.ymin = 0;
246   data.win_rect.xmax = vc->region->winx;
247   data.win_rect.ymax = vc->region->winy;
248 
249   data.func = func;
250   data.userData = userData;
251   data.clip_flag = clip_flag;
252 
253   if (clip_flag & V3D_PROJ_TEST_CLIP_BB) {
254     ED_view3d_clipping_local(vc->rv3d, vc->obedit->obmat); /* for local clipping lookups */
255   }
256 
257   BM_mesh_elem_table_ensure(vc->em->bm, BM_EDGE);
258   BKE_mesh_foreach_mapped_edge(me, mesh_foreachScreenEdge__mapFunc, &data);
259 }
260 
261 /* ------------------------------------------------------------------------ */
262 
263 /**
264  * Only call for bound-box clipping.
265  * Otherwise call #mesh_foreachScreenEdge__mapFunc
266  */
mesh_foreachScreenEdge_clip_bb_segment__mapFunc(void * userData,int index,const float v0co[3],const float v1co[3])267 static void mesh_foreachScreenEdge_clip_bb_segment__mapFunc(void *userData,
268                                                             int index,
269                                                             const float v0co[3],
270                                                             const float v1co[3])
271 {
272   foreachScreenEdge_userData *data = userData;
273   BMEdge *eed = BM_edge_at_index(data->vc.em->bm, index);
274 
275   BLI_assert(data->clip_flag & V3D_PROJ_TEST_CLIP_BB);
276 
277   if (!BM_elem_flag_test(eed, BM_ELEM_HIDDEN)) {
278     float v0co_clip[3];
279     float v1co_clip[3];
280 
281     if (!clip_segment_v3_plane_n(v0co, v1co, data->vc.rv3d->clip_local, 4, v0co_clip, v1co_clip)) {
282       return;
283     }
284 
285     float screen_co_a[2];
286     float screen_co_b[2];
287 
288     /* Clipping already handled, no need to check in projection. */
289     eV3DProjTest clip_flag_nowin = data->clip_flag &
290                                    ~(V3D_PROJ_TEST_CLIP_WIN | V3D_PROJ_TEST_CLIP_BB);
291 
292     if (ED_view3d_project_float_object(data->vc.region, v0co_clip, screen_co_a, clip_flag_nowin) !=
293         V3D_PROJ_RET_OK) {
294       return;
295     }
296     if (ED_view3d_project_float_object(data->vc.region, v1co_clip, screen_co_b, clip_flag_nowin) !=
297         V3D_PROJ_RET_OK) {
298       return;
299     }
300 
301     if (data->clip_flag & V3D_PROJ_TEST_CLIP_WIN) {
302       if (!BLI_rctf_isect_segment(&data->win_rect, screen_co_a, screen_co_b)) {
303         return;
304       }
305     }
306 
307     data->func(data->userData, eed, screen_co_a, screen_co_b, index);
308   }
309 }
310 
311 /**
312  * A version of #mesh_foreachScreenEdge that clips the segment when
313  * there is a clipping bounding box.
314  */
mesh_foreachScreenEdge_clip_bb_segment(ViewContext * vc,void (* func)(void * userData,BMEdge * eed,const float screen_co_a[2],const float screen_co_b[2],int index),void * userData,eV3DProjTest clip_flag)315 void mesh_foreachScreenEdge_clip_bb_segment(ViewContext *vc,
316                                             void (*func)(void *userData,
317                                                          BMEdge *eed,
318                                                          const float screen_co_a[2],
319                                                          const float screen_co_b[2],
320                                                          int index),
321                                             void *userData,
322                                             eV3DProjTest clip_flag)
323 {
324   foreachScreenEdge_userData data;
325 
326   Mesh *me = editbmesh_get_eval_cage_from_orig(
327       vc->depsgraph, vc->scene, vc->obedit, &CD_MASK_BAREMESH);
328 
329   ED_view3d_check_mats_rv3d(vc->rv3d);
330 
331   data.vc = *vc;
332 
333   data.win_rect.xmin = 0;
334   data.win_rect.ymin = 0;
335   data.win_rect.xmax = vc->region->winx;
336   data.win_rect.ymax = vc->region->winy;
337 
338   data.func = func;
339   data.userData = userData;
340   data.clip_flag = clip_flag;
341 
342   BM_mesh_elem_table_ensure(vc->em->bm, BM_EDGE);
343 
344   if ((clip_flag & V3D_PROJ_TEST_CLIP_BB) && (vc->rv3d->clipbb != NULL)) {
345     ED_view3d_clipping_local(vc->rv3d, vc->obedit->obmat); /* for local clipping lookups. */
346     BKE_mesh_foreach_mapped_edge(me, mesh_foreachScreenEdge_clip_bb_segment__mapFunc, &data);
347   }
348   else {
349     BKE_mesh_foreach_mapped_edge(me, mesh_foreachScreenEdge__mapFunc, &data);
350   }
351 }
352 
353 /* ------------------------------------------------------------------------ */
354 
mesh_foreachScreenFace__mapFunc(void * userData,int index,const float cent[3],const float UNUSED (no[3]))355 static void mesh_foreachScreenFace__mapFunc(void *userData,
356                                             int index,
357                                             const float cent[3],
358                                             const float UNUSED(no[3]))
359 {
360   foreachScreenFace_userData *data = userData;
361   BMFace *efa = BM_face_at_index(data->vc.em->bm, index);
362 
363   if (!BM_elem_flag_test(efa, BM_ELEM_HIDDEN)) {
364     float screen_co[2];
365     if (ED_view3d_project_float_object(data->vc.region, cent, screen_co, data->clip_flag) ==
366         V3D_PROJ_RET_OK) {
367       data->func(data->userData, efa, screen_co, index);
368     }
369   }
370 }
371 
mesh_foreachScreenFace(ViewContext * vc,void (* func)(void * userData,BMFace * efa,const float screen_co_b[2],int index),void * userData,const eV3DProjTest clip_flag)372 void mesh_foreachScreenFace(
373     ViewContext *vc,
374     void (*func)(void *userData, BMFace *efa, const float screen_co_b[2], int index),
375     void *userData,
376     const eV3DProjTest clip_flag)
377 {
378   foreachScreenFace_userData data;
379 
380   Mesh *me = editbmesh_get_eval_cage_from_orig(
381       vc->depsgraph, vc->scene, vc->obedit, &CD_MASK_BAREMESH);
382   ED_view3d_check_mats_rv3d(vc->rv3d);
383 
384   data.vc = *vc;
385   data.func = func;
386   data.userData = userData;
387   data.clip_flag = clip_flag;
388 
389   BM_mesh_elem_table_ensure(vc->em->bm, BM_FACE);
390 
391   if (BKE_modifiers_uses_subsurf_facedots(vc->scene, vc->obedit)) {
392     BKE_mesh_foreach_mapped_subdiv_face_center(
393         me, mesh_foreachScreenFace__mapFunc, &data, MESH_FOREACH_NOP);
394   }
395   else {
396     BKE_mesh_foreach_mapped_face_center(
397         me, mesh_foreachScreenFace__mapFunc, &data, MESH_FOREACH_NOP);
398   }
399 }
400 
401 /* ------------------------------------------------------------------------ */
402 
nurbs_foreachScreenVert(ViewContext * vc,void (* func)(void * userData,Nurb * nu,BPoint * bp,BezTriple * bezt,int beztindex,bool handles_visible,const float screen_co_b[2]),void * userData,const eV3DProjTest clip_flag)403 void nurbs_foreachScreenVert(ViewContext *vc,
404                              void (*func)(void *userData,
405                                           Nurb *nu,
406                                           BPoint *bp,
407                                           BezTriple *bezt,
408                                           int beztindex,
409                                           bool handles_visible,
410                                           const float screen_co_b[2]),
411                              void *userData,
412                              const eV3DProjTest clip_flag)
413 {
414   Curve *cu = vc->obedit->data;
415   Nurb *nu;
416   int i;
417   ListBase *nurbs = BKE_curve_editNurbs_get(cu);
418   /* If no point in the triple is selected, the handles are invisible. */
419   const bool only_selected = (vc->v3d->overlay.handle_display == CURVE_HANDLE_SELECTED);
420 
421   ED_view3d_check_mats_rv3d(vc->rv3d);
422 
423   if (clip_flag & V3D_PROJ_TEST_CLIP_BB) {
424     ED_view3d_clipping_local(vc->rv3d, vc->obedit->obmat); /* for local clipping lookups */
425   }
426 
427   for (nu = nurbs->first; nu; nu = nu->next) {
428     if (nu->type == CU_BEZIER) {
429       for (i = 0; i < nu->pntsu; i++) {
430         BezTriple *bezt = &nu->bezt[i];
431 
432         if (bezt->hide == 0) {
433           const bool handles_visible = (vc->v3d->overlay.handle_display != CURVE_HANDLE_NONE) &&
434                                        (!only_selected || BEZT_ISSEL_ANY(bezt));
435           float screen_co[2];
436 
437           if (!handles_visible) {
438             if (ED_view3d_project_float_object(vc->region,
439                                                bezt->vec[1],
440                                                screen_co,
441                                                V3D_PROJ_RET_CLIP_BB | V3D_PROJ_RET_CLIP_WIN) ==
442                 V3D_PROJ_RET_OK) {
443               func(userData, nu, NULL, bezt, 1, false, screen_co);
444             }
445           }
446           else {
447             if (ED_view3d_project_float_object(vc->region,
448                                                bezt->vec[0],
449                                                screen_co,
450                                                V3D_PROJ_RET_CLIP_BB | V3D_PROJ_RET_CLIP_WIN) ==
451                 V3D_PROJ_RET_OK) {
452               func(userData, nu, NULL, bezt, 0, true, screen_co);
453             }
454             if (ED_view3d_project_float_object(vc->region,
455                                                bezt->vec[1],
456                                                screen_co,
457                                                V3D_PROJ_RET_CLIP_BB | V3D_PROJ_RET_CLIP_WIN) ==
458                 V3D_PROJ_RET_OK) {
459               func(userData, nu, NULL, bezt, 1, true, screen_co);
460             }
461             if (ED_view3d_project_float_object(vc->region,
462                                                bezt->vec[2],
463                                                screen_co,
464                                                V3D_PROJ_RET_CLIP_BB | V3D_PROJ_RET_CLIP_WIN) ==
465                 V3D_PROJ_RET_OK) {
466               func(userData, nu, NULL, bezt, 2, true, screen_co);
467             }
468           }
469         }
470       }
471     }
472     else {
473       for (i = 0; i < nu->pntsu * nu->pntsv; i++) {
474         BPoint *bp = &nu->bp[i];
475 
476         if (bp->hide == 0) {
477           float screen_co[2];
478           if (ED_view3d_project_float_object(
479                   vc->region, bp->vec, screen_co, V3D_PROJ_RET_CLIP_BB | V3D_PROJ_RET_CLIP_WIN) ==
480               V3D_PROJ_RET_OK) {
481             func(userData, nu, bp, NULL, -1, false, screen_co);
482           }
483         }
484       }
485     }
486   }
487 }
488 
489 /* ------------------------------------------------------------------------ */
490 
491 /* ED_view3d_init_mats_rv3d must be called first */
mball_foreachScreenElem(struct ViewContext * vc,void (* func)(void * userData,struct MetaElem * ml,const float screen_co_b[2]),void * userData,const eV3DProjTest clip_flag)492 void mball_foreachScreenElem(struct ViewContext *vc,
493                              void (*func)(void *userData,
494                                           struct MetaElem *ml,
495                                           const float screen_co_b[2]),
496                              void *userData,
497                              const eV3DProjTest clip_flag)
498 {
499   MetaBall *mb = (MetaBall *)vc->obedit->data;
500   MetaElem *ml;
501 
502   ED_view3d_check_mats_rv3d(vc->rv3d);
503 
504   for (ml = mb->editelems->first; ml; ml = ml->next) {
505     float screen_co[2];
506     if (ED_view3d_project_float_object(vc->region, &ml->x, screen_co, clip_flag) ==
507         V3D_PROJ_RET_OK) {
508       func(userData, ml, screen_co);
509     }
510   }
511 }
512 
513 /* ------------------------------------------------------------------------ */
514 
lattice_foreachScreenVert(ViewContext * vc,void (* func)(void * userData,BPoint * bp,const float screen_co[2]),void * userData,const eV3DProjTest clip_flag)515 void lattice_foreachScreenVert(ViewContext *vc,
516                                void (*func)(void *userData, BPoint *bp, const float screen_co[2]),
517                                void *userData,
518                                const eV3DProjTest clip_flag)
519 {
520   Object *obedit = vc->obedit;
521   Lattice *lt = obedit->data;
522   BPoint *bp = lt->editlatt->latt->def;
523   DispList *dl = obedit->runtime.curve_cache ?
524                      BKE_displist_find(&obedit->runtime.curve_cache->disp, DL_VERTS) :
525                      NULL;
526   const float *co = dl ? dl->verts : NULL;
527   int i, N = lt->editlatt->latt->pntsu * lt->editlatt->latt->pntsv * lt->editlatt->latt->pntsw;
528 
529   ED_view3d_check_mats_rv3d(vc->rv3d);
530 
531   if (clip_flag & V3D_PROJ_TEST_CLIP_BB) {
532     ED_view3d_clipping_local(vc->rv3d, obedit->obmat); /* for local clipping lookups */
533   }
534 
535   for (i = 0; i < N; i++, bp++, co += 3) {
536     if (bp->hide == 0) {
537       float screen_co[2];
538       if (ED_view3d_project_float_object(vc->region, dl ? co : bp->vec, screen_co, clip_flag) ==
539           V3D_PROJ_RET_OK) {
540         func(userData, bp, screen_co);
541       }
542     }
543   }
544 }
545 
546 /* ------------------------------------------------------------------------ */
547 
548 /* ED_view3d_init_mats_rv3d must be called first */
armature_foreachScreenBone(struct ViewContext * vc,void (* func)(void * userData,struct EditBone * ebone,const float screen_co_a[2],const float screen_co_b[2]),void * userData,const eV3DProjTest clip_flag)549 void armature_foreachScreenBone(struct ViewContext *vc,
550                                 void (*func)(void *userData,
551                                              struct EditBone *ebone,
552                                              const float screen_co_a[2],
553                                              const float screen_co_b[2]),
554                                 void *userData,
555                                 const eV3DProjTest clip_flag)
556 {
557   bArmature *arm = vc->obedit->data;
558   EditBone *ebone;
559 
560   ED_view3d_check_mats_rv3d(vc->rv3d);
561 
562   for (ebone = arm->edbo->first; ebone; ebone = ebone->next) {
563     if (EBONE_VISIBLE(arm, ebone)) {
564       float screen_co_a[2], screen_co_b[2];
565       int points_proj_tot = 0;
566 
567       /* project head location to screenspace */
568       if (ED_view3d_project_float_object(vc->region, ebone->head, screen_co_a, clip_flag) ==
569           V3D_PROJ_RET_OK) {
570         points_proj_tot++;
571       }
572       else {
573         screen_co_a[0] = IS_CLIPPED; /* weak */
574         /* screen_co_a[1]: intentionally dont set this so we get errors on misuse */
575       }
576 
577       /* project tail location to screenspace */
578       if (ED_view3d_project_float_object(vc->region, ebone->tail, screen_co_b, clip_flag) ==
579           V3D_PROJ_RET_OK) {
580         points_proj_tot++;
581       }
582       else {
583         screen_co_b[0] = IS_CLIPPED; /* weak */
584         /* screen_co_b[1]: intentionally dont set this so we get errors on misuse */
585       }
586 
587       if (points_proj_tot) { /* at least one point's projection worked */
588         func(userData, ebone, screen_co_a, screen_co_b);
589       }
590     }
591   }
592 }
593 
594 /* ------------------------------------------------------------------------ */
595 
596 /* ED_view3d_init_mats_rv3d must be called first */
597 /* almost _exact_ copy of #armature_foreachScreenBone */
pose_foreachScreenBone(struct ViewContext * vc,void (* func)(void * userData,struct bPoseChannel * pchan,const float screen_co_a[2],const float screen_co_b[2]),void * userData,const eV3DProjTest clip_flag)598 void pose_foreachScreenBone(struct ViewContext *vc,
599                             void (*func)(void *userData,
600                                          struct bPoseChannel *pchan,
601                                          const float screen_co_a[2],
602                                          const float screen_co_b[2]),
603                             void *userData,
604                             const eV3DProjTest clip_flag)
605 {
606   const Object *ob_eval = DEG_get_evaluated_object(vc->depsgraph, vc->obact);
607   const bArmature *arm_eval = ob_eval->data;
608   bPose *pose = vc->obact->pose;
609   bPoseChannel *pchan;
610 
611   ED_view3d_check_mats_rv3d(vc->rv3d);
612 
613   for (pchan = pose->chanbase.first; pchan; pchan = pchan->next) {
614     if (PBONE_VISIBLE(arm_eval, pchan->bone)) {
615       bPoseChannel *pchan_eval = BKE_pose_channel_find_name(ob_eval->pose, pchan->name);
616       float screen_co_a[2], screen_co_b[2];
617       int points_proj_tot = 0;
618 
619       /* project head location to screenspace */
620       if (ED_view3d_project_float_object(
621               vc->region, pchan_eval->pose_head, screen_co_a, clip_flag) == V3D_PROJ_RET_OK) {
622         points_proj_tot++;
623       }
624       else {
625         screen_co_a[0] = IS_CLIPPED; /* weak */
626         /* screen_co_a[1]: intentionally dont set this so we get errors on misuse */
627       }
628 
629       /* project tail location to screenspace */
630       if (ED_view3d_project_float_object(
631               vc->region, pchan_eval->pose_tail, screen_co_b, clip_flag) == V3D_PROJ_RET_OK) {
632         points_proj_tot++;
633       }
634       else {
635         screen_co_b[0] = IS_CLIPPED; /* weak */
636         /* screen_co_b[1]: intentionally dont set this so we get errors on misuse */
637       }
638 
639       if (points_proj_tot) { /* at least one point's projection worked */
640         func(userData, pchan, screen_co_a, screen_co_b);
641       }
642     }
643   }
644 }
645