Blender V4.3
annotate_paint.cc
Go to the documentation of this file.
1/* SPDX-FileCopyrightText: 2008-2018 Blender Authors
2 *
3 * SPDX-License-Identifier: GPL-2.0-or-later */
4
9#include <cmath>
10#include <cstddef>
11#include <cstdio>
12#include <cstdlib>
13#include <cstring>
14
15#include "MEM_guardedalloc.h"
16
17#include "BLI_math_matrix.h"
18#include "BLI_time.h"
19#include "BLI_utildefines.h"
20
21#include "BLT_translation.hh"
22
23#include "BKE_callbacks.hh"
24#include "BKE_context.hh"
26#include "BKE_gpencil_legacy.h"
27#include "BKE_report.hh"
28#include "BKE_screen.hh"
29#include "BKE_tracking.h"
30
32#include "DNA_object_types.h"
33#include "DNA_scene_types.h"
35
36#include "UI_view2d.hh"
37
38#include "ED_clip.hh"
39#include "ED_gpencil_legacy.hh"
40#include "ED_screen.hh"
41#include "ED_view3d.hh"
42
43#include "GPU_immediate.hh"
44#include "GPU_immediate_util.hh"
45#include "GPU_state.hh"
46
47#include "RNA_access.hh"
48#include "RNA_define.hh"
49#include "RNA_prototypes.hh"
50
51#include "WM_api.hh"
52#include "WM_types.hh"
53
54#include "gpencil_intern.hh"
55
56/* ******************************************* */
57/* 'Globals' and Defines */
58
59#define DEPTH_INVALID 1.0f
60
61/* values for tGPsdata->status */
63 GP_STATUS_IDLING = 0, /* stroke isn't in progress yet */
64 GP_STATUS_PAINTING, /* a stroke is in progress */
65 GP_STATUS_ERROR, /* something wasn't correctly set up */
66 GP_STATUS_DONE, /* painting done */
67 GP_STATUS_CAPTURE /* capture event, but cancel */
68};
69
70/* Return flags for adding points to stroke buffer */
72 GP_STROKEADD_INVALID = -2, /* error occurred - insufficient info to do so */
73 GP_STROKEADD_OVERFLOW = -1, /* error occurred - cannot fit any more points */
74 GP_STROKEADD_NORMAL, /* point was successfully added */
75 GP_STROKEADD_FULL, /* cannot add any more points to buffer */
76};
77
78/* Runtime flags */
80 GP_PAINTFLAG_FIRSTRUN = (1 << 0), /* operator just started */
84 /* Flags used to indicate if stabilization is being used. */
87};
89
90/* Temporary 'Stroke' Operation data
91 * "p" = op->customdata
92 */
93struct tGPsdata {
97 Depsgraph *depsgraph;
98
110 const rctf *subrect;
112
115
124
127
134
136 short radius;
137
138 /* Stabilizer. */
142
144 float mval[2];
146 float mvalo[2];
147
149 float pressure;
152
153 /* These need to be doubles, as (at least under unix) they are in seconds since epoch,
154 * float (and its 7 digits precision) is definitively not enough here!
155 * double, with its 15 digits precision,
156 * ensures us millisecond precision for a few centuries at least.
157 */
159 double inittime;
161 double curtime;
163 double ocurtime;
164
167 float imat[4][4];
168 float mat[4][4];
169
171 float custom_color[4];
172
175
177 short straight[2];
178
181};
182
183/* ------ */
184
185/* Macros for accessing sensitivity thresholds... */
186/* minimum number of pixels mouse should move before new point created */
187#define MIN_MANHATTAN_PX (U.gp_manhattandist)
188/* minimum length of new segment before new point can be added */
189#define MIN_EUCLIDEAN_PX (U.gp_euclideandist)
190
192{
193 return (p->gpf && p->gpf->strokes.last && p->flags & GP_PAINTFLAG_STROKEADDED);
194}
195
197{
198 BLI_assert(p->gpf->strokes.last != nullptr);
200}
201
202/* ------ */
203/* Forward defines for some functions... */
204
206
207/* ******************************************* */
208/* Context Wrangling... */
209
210/* check if context is suitable for drawing */
212{
214 /* check if current context can support GPencil data */
215 if (ED_annotation_data_get_pointers(C, nullptr) != nullptr) {
216 /* check if Grease Pencil isn't already running */
217 if (ED_gpencil_session_active() == 0) {
218 return true;
219 }
220 CTX_wm_operator_poll_msg_set(C, "Annotation operator is already active");
221 }
222 else {
223 CTX_wm_operator_poll_msg_set(C, "Failed to find Annotation data to draw into");
224 }
225 }
226 else {
227 CTX_wm_operator_poll_msg_set(C, "Active region not set");
228 }
229
230 return false;
231}
232
233/* check if projecting strokes into 3d-geometry in the 3D-View */
235{
236 bGPdata *gpd = p->gpd;
237 return ((gpd->runtime.sbuffer_sflag & GP_STROKE_3DSPACE) &&
239}
240
241/* ******************************************* */
242/* Calculations/Conversions */
243
244/* Utilities --------------------------------- */
245
246/* get the reference point for stroke-point conversions */
247static void annotation_get_3d_reference(tGPsdata *p, float vec[3])
248{
249 const float *fp = p->scene->cursor.location;
250
251 /* use 3D-cursor */
252 copy_v3_v3(vec, fp);
253}
254
255/* Stroke Editing ---------------------------- */
256
257/* check if the current mouse position is suitable for adding a new point */
258static bool annotation_stroke_filtermval(tGPsdata *p, const float mval[2], const float pmval[2])
259{
260 int dx = int(fabsf(mval[0] - pmval[0]));
261 int dy = int(fabsf(mval[1] - pmval[1]));
262
263 /* if buffer is empty, just let this go through (i.e. so that dots will work) */
264 if (p->gpd->runtime.sbuffer_used == 0) {
265 return true;
266 }
267
268 /* check if mouse moved at least certain distance on both axes (best case)
269 * - aims to eliminate some jitter-noise from input when trying to draw straight lines freehand
270 */
271
272 /* If lazy mouse, check minimum distance. */
274 if ((dx * dx + dy * dy) > (p->stabilizer_radius * p->stabilizer_radius)) {
275 return true;
276 }
277
278 /* If the mouse is moving within the radius of the last move,
279 * don't update the mouse position. This allows sharp turns. */
280 copy_v2_v2(p->mval, p->mvalo);
281 return false;
282 }
283
284 if ((dx > MIN_MANHATTAN_PX) && (dy > MIN_MANHATTAN_PX)) {
285 return true;
286 }
287
288 /* Check if the distance since the last point is significant enough:
289 * - Prevents points being added too densely
290 * - Distance here doesn't use `sqrt` to prevent slowness.
291 * We should still be safe from overflows though.
292 */
293 if ((dx * dx + dy * dy) > MIN_EUCLIDEAN_PX * MIN_EUCLIDEAN_PX) {
294 return true;
295 }
296
297 /* mouse 'didn't move' */
298 return false;
299}
300
301/* convert screen-coordinates to buffer-coordinates */
303 const float mval[2],
304 float out[3],
305 const float *depth)
306{
307 bGPdata *gpd = p->gpd;
308 if (depth && (*depth == DEPTH_INVALID)) {
309 depth = nullptr;
310 }
311
312 /* in 3d-space - pt->x/y/z are 3 side-by-side floats */
314 int mval_i[2];
315 round_v2i_v2fl(mval_i, mval);
316 if (annotation_project_check(p) && ED_view3d_autodist_simple(p->region, mval_i, out, 0, depth))
317 {
318 /* projecting onto 3D-Geometry
319 * - nothing more needs to be done here, since view_autodist_simple() has already done it
320 */
321 }
322 else {
323 float mval_prj[2];
324 float rvec[3];
325
326 /* Current method just converts each point in screen-coordinates to
327 * 3D-coordinates using the 3D-cursor as reference. In general, this
328 * works OK, but it could of course be improved.
329 *
330 * TODO:
331 * - investigate using nearest point(s) on a previous stroke as
332 * reference point instead or as offset, for easier stroke matching
333 */
334
336 const float zfac = ED_view3d_calc_zfac(
337 static_cast<const RegionView3D *>(p->region->regiondata), rvec);
338
341 {
342 float dvec[3];
343 float xy_delta[2];
344 sub_v2_v2v2(xy_delta, mval_prj, mval);
345 ED_view3d_win_to_delta(p->region, xy_delta, zfac, dvec);
346 sub_v3_v3v3(out, rvec, dvec);
347 }
348 else {
349 zero_v3(out);
350 }
351 }
352 }
353
354 /* 2d - on 'canvas' (assume that p->v2d is set) */
355 else if ((gpd->runtime.sbuffer_sflag & GP_STROKE_2DSPACE) && (p->v2d)) {
356 UI_view2d_region_to_view(p->v2d, mval[0], mval[1], &out[0], &out[1]);
357 mul_v3_m4v3(out, p->imat, out);
358 }
359
360 /* 2d - relative to screen (viewport area) */
361 else {
362 if (p->subrect == nullptr) { /* normal 3D view */
363 out[0] = float(mval[0]) / float(p->region->winx) * 100;
364 out[1] = float(mval[1]) / float(p->region->winy) * 100;
365 }
366 else { /* camera view, use subrect */
367 out[0] = ((mval[0] - p->subrect->xmin) / BLI_rctf_size_x(p->subrect)) * 100;
368 out[1] = ((mval[1] - p->subrect->ymin) / BLI_rctf_size_y(p->subrect)) * 100;
369 }
370 }
371}
372
383static void annotation_smooth_buffer(tGPsdata *p, float inf, int idx)
384{
385 bGPdata *gpd = p->gpd;
386 short num_points = gpd->runtime.sbuffer_used;
387
388 /* Do nothing if not enough points to smooth out */
389 if ((num_points < 3) || (idx < 3) || (inf == 0.0f)) {
390 return;
391 }
392
393 tGPspoint *points = (tGPspoint *)gpd->runtime.sbuffer;
394 float steps = 4.0f;
395 if (idx < 4) {
396 steps--;
397 }
398
399 tGPspoint *pta = idx >= 4 ? &points[idx - 4] : nullptr;
400 tGPspoint *ptb = idx >= 3 ? &points[idx - 3] : nullptr;
401 tGPspoint *ptc = idx >= 2 ? &points[idx - 2] : nullptr;
402 tGPspoint *ptd = &points[idx - 1];
403
404 float sco[2] = {0.0f};
405 float a[2], b[2], c[2], d[2];
406 const float average_fac = 1.0f / steps;
407
408 /* Compute smoothed coordinate by taking the ones nearby */
409 if (pta) {
410 copy_v2_v2(a, pta->m_xy);
411 madd_v2_v2fl(sco, a, average_fac);
412 }
413 if (ptb) {
414 copy_v2_v2(b, ptb->m_xy);
415 madd_v2_v2fl(sco, b, average_fac);
416 }
417 if (ptc) {
418 copy_v2_v2(c, ptc->m_xy);
419 madd_v2_v2fl(sco, c, average_fac);
420 }
421 if (ptd) {
422 copy_v2_v2(d, ptd->m_xy);
423 madd_v2_v2fl(sco, d, average_fac);
424 }
425
426 /* Based on influence factor, blend between original and optimal smoothed coordinate */
427 interp_v2_v2v2(c, c, sco, inf);
428 copy_v2_v2(ptc->m_xy, c);
429}
430
431static void annotation_stroke_arrow_calc_points_segment(float stroke_points[8],
432 const float ref_point[2],
433 const float dir_cw[2],
434 const float dir_ccw[2],
435 const float length,
436 const float sign)
437{
438 stroke_points[0] = ref_point[0] + dir_cw[0] * length * sign;
439 stroke_points[1] = ref_point[1] + dir_cw[1] * length * sign;
440 stroke_points[2] = ref_point[0] + dir_ccw[0] * length * sign;
441 stroke_points[3] = ref_point[1] + dir_ccw[1] * length * sign;
442}
443
445 const float stroke_dir[2],
446 float corner[2],
447 float stroke_points[8],
448 const int arrow_style)
449{
450 const int arrow_length = 8;
451 float norm_dir[2];
452 copy_v2_v2(norm_dir, stroke_dir);
453 normalize_v2(norm_dir);
454 const float inv_norm_dir_clockwise[2] = {norm_dir[1], -norm_dir[0]};
455 const float inv_norm_dir_counterclockwise[2] = {-norm_dir[1], norm_dir[0]};
456
457 switch (arrow_style) {
459 mul_v2_fl(norm_dir, arrow_length);
460 stroke_points[0] = corner[0] + inv_norm_dir_clockwise[0] * arrow_length + norm_dir[0];
461 stroke_points[1] = corner[1] + inv_norm_dir_clockwise[1] * arrow_length + norm_dir[1];
462 stroke_points[2] = corner[0] + inv_norm_dir_counterclockwise[0] * arrow_length + norm_dir[0];
463 stroke_points[3] = corner[1] + inv_norm_dir_counterclockwise[1] * arrow_length + norm_dir[1];
464 break;
467 corner,
468 inv_norm_dir_clockwise,
469 inv_norm_dir_counterclockwise,
470 arrow_length,
471 1.0f);
472 break;
474 mul_v2_fl(norm_dir, arrow_length);
475 if (point != nullptr) {
476 add_v2_v2(point->m_xy, norm_dir);
477 copy_v2_v2(corner, point->m_xy);
478 }
480 corner,
481 inv_norm_dir_clockwise,
482 inv_norm_dir_counterclockwise,
483 arrow_length,
484 -1.0f);
485 stroke_points[4] = corner[0] - norm_dir[0];
486 stroke_points[5] = corner[1] - norm_dir[1];
487 break;
489 mul_v2_fl(norm_dir, arrow_length * 1.5f);
490 if (point != nullptr) {
491 add_v2_v2(point->m_xy, norm_dir);
492 copy_v2_v2(corner, point->m_xy);
493 }
495 corner,
496 inv_norm_dir_clockwise,
497 inv_norm_dir_counterclockwise,
498 arrow_length * 0.75f,
499 -1.0f);
500 stroke_points[4] = stroke_points[0] - norm_dir[0];
501 stroke_points[5] = stroke_points[1] - norm_dir[1];
502 stroke_points[6] = stroke_points[2] - norm_dir[0];
503 stroke_points[7] = stroke_points[3] - norm_dir[1];
504 break;
505 default:
506 break;
507 }
508}
509
510/* add current stroke-point to buffer (returns whether point was successfully added) */
512 const float mval[2],
513 float pressure,
514 double curtime)
515{
516 bGPdata *gpd = p->gpd;
517 tGPspoint *pt;
519
520 /* check painting mode */
522 /* straight lines only - i.e. only store start and end point in buffer */
523 if (gpd->runtime.sbuffer_used == 0) {
524 /* first point in buffer (start point) */
525 pt = (tGPspoint *)(gpd->runtime.sbuffer);
526
527 /* store settings */
528 copy_v2_v2(pt->m_xy, mval);
529 /* Pressure values are unreliable, so ignore for now, see #44932. */
530 pt->pressure = 1.0f;
531 pt->strength = 1.0f;
532 pt->time = float(curtime - p->inittime);
533
534 /* increment buffer size */
535 gpd->runtime.sbuffer_used++;
536 }
537 else {
538 /* just reset the endpoint to the latest value
539 * - assume that pointers for this are always valid...
540 */
541 pt = ((tGPspoint *)(gpd->runtime.sbuffer) + 1);
542
543 /* store settings */
544 copy_v2_v2(pt->m_xy, mval);
545 /* Pressure values are unreliable, so ignore for now, see #44932. */
546 pt->pressure = 1.0f;
547 pt->strength = 1.0f;
548 pt->time = float(curtime - p->inittime);
549
550 /* now the buffer has 2 points (and shouldn't be allowed to get any larger) */
551 gpd->runtime.sbuffer_used = 2;
552
553 /* Arrows. */
555 /* Store start and end point coords for arrows. */
556 float end[2];
557 copy_v2_v2(end, pt->m_xy);
558 pt = ((tGPspoint *)(gpd->runtime.sbuffer));
559 float start[2];
560 copy_v2_v2(start, pt->m_xy);
561
562 /* Arrow end corner. */
564 pt++;
565 const float e_heading[2] = {start[0] - end[0], start[1] - end[1]};
566 /* Calculate points for ending arrow. */
568 pt, e_heading, end, gpd->runtime.arrow_end, gpd->runtime.arrow_end_style);
569 }
570 /* Arrow start corner. */
572 const float s_heading[2] = {end[0] - start[0], end[1] - start[1]};
573 /* Calculate points for starting arrow. */
575 nullptr, s_heading, start, gpd->runtime.arrow_start, gpd->runtime.arrow_start_style);
576 }
577 }
578 }
579
580 /* can keep carrying on this way :) */
581 return GP_STROKEADD_NORMAL;
582 }
583
584 if (p->paintmode == GP_PAINTMODE_DRAW) { /* normal drawing */
585 /* check if still room in buffer or add more */
587 static_cast<tGPspoint *>(gpd->runtime.sbuffer),
588 &gpd->runtime.sbuffer_size,
589 &gpd->runtime.sbuffer_used,
590 false);
591
592 /* get pointer to destination point */
593 pt = ((tGPspoint *)(gpd->runtime.sbuffer) + gpd->runtime.sbuffer_used);
594
595 /* store settings */
596 copy_v2_v2(pt->m_xy, mval);
597 pt->pressure = pressure;
598 /* Unused for annotations, but initialize for easier conversions to GP Object. */
599 pt->strength = 1.0f;
600
601 /* point time */
602 pt->time = float(curtime - p->inittime);
603
604 /* increment counters */
605 gpd->runtime.sbuffer_used++;
606
607 /* Don't smooth if stabilizer is on. */
608 if ((p->flags & GP_PAINTFLAG_USE_STABILIZER_TEMP) == 0) {
609 /* smooth while drawing previous points with a reduction factor for previous */
610 for (int s = 0; s < 3; s++) {
611 annotation_smooth_buffer(p, 0.5f * ((3.0f - s) / 3.0f), gpd->runtime.sbuffer_used - s);
612 }
613 }
614
615 return GP_STROKEADD_NORMAL;
616 }
617
619 /* get pointer to destination point */
620 pt = (tGPspoint *)gpd->runtime.sbuffer;
621
622 /* store settings */
623 copy_v2_v2(pt->m_xy, mval);
624 /* Pressure values are unreliable, so ignore for now, see #44932. */
625 pt->pressure = 1.0f;
626 pt->strength = 1.0f;
627 pt->time = float(curtime - p->inittime);
628
629 /* if there's stroke for this poly line session add (or replace last) point
630 * to stroke. This allows to draw lines more interactively (see new segment
631 * during mouse slide, e.g.)
632 */
634 bGPDstroke *gps = static_cast<bGPDstroke *>(p->gpf->strokes.last);
635 bGPDspoint *pts;
636
637 /* first time point is adding to temporary buffer -- need to allocate new point in stroke */
638 if (gpd->runtime.sbuffer_used == 0) {
639 gps->points = static_cast<bGPDspoint *>(
640 MEM_reallocN(gps->points, sizeof(bGPDspoint) * (gps->totpoints + 1)));
641 gps->totpoints++;
642 }
643
644 pts = &gps->points[gps->totpoints - 1];
645
646 /* special case for poly lines: normally,
647 * depth is needed only when creating new stroke from buffer,
648 * but poly lines are converting to stroke instantly,
649 * so initialize depth buffer before converting coordinates
650 */
652 View3D *v3d = static_cast<View3D *>(p->area->spacedata.first);
653
655
659 }
660 else {
662 }
663 }
664
666 ED_view3d_depth_override(p->depsgraph, p->region, v3d, nullptr, mode, nullptr);
667 }
668
669 /* convert screen-coordinates to appropriate coordinates (and store them) */
670 annotation_stroke_convertcoords(p, pt->m_xy, &pts->x, nullptr);
671
672 /* copy pressure and time */
673 pts->pressure = pt->pressure;
674 pts->strength = pt->strength;
675 pts->time = pt->time;
676 gps->tot_triangles = 0;
677 }
678
679 /* increment counters */
680 if (gpd->runtime.sbuffer_used == 0) {
681 gpd->runtime.sbuffer_used++;
682 }
683
684 return GP_STROKEADD_NORMAL;
685 }
686
687 /* return invalid state for now... */
689}
690
692{
693 pt->pressure = 1.0f;
694 pt->strength = 1.0f;
695 pt->time = 1.0f;
696}
697
698static void annotation_stroke_arrow_init_conv_point(bGPDspoint *pt, const float point[3])
699{
700 copy_v3_v3(&pt->x, point);
702}
703
705 tGPsdata *p, tGPspoint *ptc, bGPDspoint *pt, const float co[8], const int co_idx)
706{
707 /* NOTE: provided co_idx should be always pair number as it's [x1, y1, x2, y2, x3, y3]. */
708 const float real_co[2] = {co[co_idx], co[co_idx + 1]};
709 copy_v2_v2(ptc->m_xy, real_co);
710 annotation_stroke_convertcoords(p, ptc->m_xy, &pt->x, nullptr);
712}
713
714static void annotation_stroke_arrow_allocate(bGPDstroke *gps, const int totpoints)
715{
716 /* Copy appropriate settings for stroke. */
717 gps->totpoints = totpoints;
718 /* Allocate enough memory for a continuous array for storage points. */
719 gps->points = static_cast<bGPDspoint *>(
720 MEM_callocN(sizeof(bGPDspoint) * gps->totpoints, "annotation_stroke_points"));
721}
722
724 tGPspoint *ptc,
725 bGPDspoint *pt,
726 const float corner_point[3],
727 const float arrow_points[8])
728{
729 annotation_stroke_arrow_init_point(p, ptc, pt, arrow_points, 0);
730 pt++;
732 pt++;
733 annotation_stroke_arrow_init_point(p, ptc, pt, arrow_points, 2);
734}
735
737 tGPspoint *ptc,
738 bGPDspoint *pt,
739 const float arrow_points[8])
740{
741 annotation_stroke_arrow_init_point(p, ptc, pt, arrow_points, 0);
742 pt++;
743 annotation_stroke_arrow_init_point(p, ptc, pt, arrow_points, 2);
744}
745
747 tGPspoint *ptc,
748 bGPDspoint *pt,
749 const float arrow_points[8])
750{
751 annotation_stroke_arrow_init_point(p, ptc, pt, arrow_points, 0);
752 pt++;
753 annotation_stroke_arrow_init_point(p, ptc, pt, arrow_points, 2);
754 pt++;
755 annotation_stroke_arrow_init_point(p, ptc, pt, arrow_points, 4);
756 pt++;
757 annotation_stroke_arrow_init_point(p, ptc, pt, arrow_points, 0);
758}
759
761 tGPspoint *ptc,
762 bGPDspoint *pt,
763 const float corner_point[3],
764 const float arrow_points[8])
765{
767 pt++;
768 annotation_stroke_arrow_init_point(p, ptc, pt, arrow_points, 0);
769 pt++;
770 annotation_stroke_arrow_init_point(p, ptc, pt, arrow_points, 4);
771 pt++;
772 annotation_stroke_arrow_init_point(p, ptc, pt, arrow_points, 6);
773 pt++;
774 annotation_stroke_arrow_init_point(p, ptc, pt, arrow_points, 2);
775 pt++;
777}
778
780 tGPspoint *ptc,
781 bGPDspoint *pt,
782 bGPDstroke *arrow_stroke,
783 const float arrow_points[8],
784 const int style)
785{
786 float corner_conv[3];
787 copy_v3_v3(corner_conv, &pt->x);
788
789 switch (style) {
791 annotation_arrow_create_segm(p, ptc, pt, arrow_points);
792 break;
794 annotation_arrow_create_closed(p, ptc, pt, arrow_points);
795 break;
797 annotation_arrow_create_open(p, ptc, pt, corner_conv, arrow_points);
798 break;
800 annotation_arrow_create_square(p, ptc, pt, corner_conv, arrow_points);
801 break;
802 default:
803 break;
804 }
805 /* Link stroke to frame. */
806 BLI_addtail(&p->gpf->strokes, arrow_stroke);
807}
808
809/* make a new stroke from the buffer data */
811{
812 bGPdata *gpd = p->gpd;
813 bGPDlayer *gpl = p->gpl;
814 bGPDstroke *gps;
815 bGPDspoint *pt;
816 tGPspoint *ptc;
818
819 int i, totelem;
820 /* Since strokes are so fine, when using their depth we need a margin
821 * otherwise they might get missed. */
822 int depth_margin = (ts->annotate_v3d_align & GP_PROJECT_DEPTH_STROKE) ? 4 : 0;
823
824 /* get total number of points to allocate space for
825 * - drawing straight-lines only requires the endpoints
826 */
828 totelem = (gpd->runtime.sbuffer_used >= 2) ? 2 : gpd->runtime.sbuffer_used;
829 }
830 else {
831 totelem = gpd->runtime.sbuffer_used;
832 }
833
834 /* exit with error if no valid points from this stroke */
835 if (totelem == 0) {
836 return;
837 }
838
839 /* special case for poly line -- for already added stroke during session
840 * coordinates are getting added to stroke immediately to allow more
841 * interactive behavior
842 */
845 return;
846 }
847 }
848
849 /* allocate memory for a new stroke */
850 gps = static_cast<bGPDstroke *>(MEM_callocN(sizeof(bGPDstroke), "annotation_stroke"));
851
852 /* copy appropriate settings for stroke */
853 gps->totpoints = totelem;
854 gps->thickness = gpl->thickness;
855 gps->fill_opacity_fac = 1.0f;
856 gps->hardness = 1.0f;
857 copy_v2_fl(gps->aspect_ratio, 1.0f);
858 gps->uv_scale = 1.0f;
859 gps->flag = gpd->runtime.sbuffer_sflag;
860 gps->inittime = p->inittime;
861 gps->tot_triangles = 0;
862
863 /* allocate enough memory for a continuous array for storage points */
864 gps->points = static_cast<bGPDspoint *>(
865 MEM_callocN(sizeof(bGPDspoint) * gps->totpoints, "annotation_stroke_points"));
866 gps->tot_triangles = 0;
867
868 /* set pointer to first non-initialized point */
869 pt = gps->points + (gps->totpoints - totelem);
870
871 /* copy points from the buffer to the stroke */
873 /* straight lines only -> only endpoints */
874 {
875 /* first point */
876 ptc = static_cast<tGPspoint *>(gpd->runtime.sbuffer);
877
878 /* convert screen-coordinates to appropriate coordinates (and store them) */
879 annotation_stroke_convertcoords(p, ptc->m_xy, &pt->x, nullptr);
880
881 /* copy pressure and time */
882 pt->pressure = ptc->pressure;
883 pt->strength = ptc->strength;
885 pt->time = ptc->time;
886
887 pt++;
888 }
889
890 if (totelem == 2) {
891 bGPdata_Runtime runtime = blender::dna::shallow_copy(gpd->runtime);
892
893 /* Last point if applicable. */
894 ptc = ((tGPspoint *)runtime.sbuffer) + (runtime.sbuffer_used - 1);
895
896 /* Convert screen-coordinates to appropriate coordinates (and store them). */
897 annotation_stroke_convertcoords(p, ptc->m_xy, &pt->x, nullptr);
898
899 /* Copy pressure and time. */
900 pt->pressure = ptc->pressure;
901 pt->strength = ptc->strength;
903 pt->time = ptc->time;
904
906 /* End arrow stroke. */
907 if ((runtime.sbuffer_sflag & GP_STROKE_USE_ARROW_END) &&
909 {
910 int totarrowpoints = runtime.arrow_end_style;
911
912 /* Setting up arrow stroke. */
913 bGPDstroke *e_arrow_gps = BKE_gpencil_stroke_duplicate(gps, false, false);
914 annotation_stroke_arrow_allocate(e_arrow_gps, totarrowpoints);
915
916 /* Set pointer to first non-initialized point. */
917 pt = e_arrow_gps->points + (e_arrow_gps->totpoints - totarrowpoints);
918
919 /* End point. */
920 ptc = ((tGPspoint *)runtime.sbuffer) + (runtime.sbuffer_used - 1);
921 annotation_stroke_convertcoords(p, ptc->m_xy, &pt->x, nullptr);
923
924 /* Fill and convert arrow points to create arrow shape. */
926 p, ptc, pt, e_arrow_gps, runtime.arrow_end, runtime.arrow_end_style);
927 }
928 /* Start arrow stroke. */
931 {
932 int totarrowpoints = runtime.arrow_start_style;
933
934 /* Setting up arrow stroke. */
935 bGPDstroke *s_arrow_gps = BKE_gpencil_stroke_duplicate(gps, false, false);
936 annotation_stroke_arrow_allocate(s_arrow_gps, totarrowpoints);
937
938 /* Set pointer to first non-initialized point. */
939 pt = s_arrow_gps->points + (s_arrow_gps->totpoints - totarrowpoints);
940
941 /* Start point. */
942 ptc = static_cast<tGPspoint *>(runtime.sbuffer);
943 annotation_stroke_convertcoords(p, ptc->m_xy, &pt->x, nullptr);
945
946 /* Fill and convert arrow points to create arrow shape. */
948 p, ptc, pt, s_arrow_gps, runtime.arrow_start, runtime.arrow_start_style);
949 }
950 }
951 }
952 else if (p->paintmode == GP_PAINTMODE_DRAW_POLY) {
953 /* first point */
954 ptc = static_cast<tGPspoint *>(gpd->runtime.sbuffer);
955
956 /* convert screen-coordinates to appropriate coordinates (and store them) */
957 annotation_stroke_convertcoords(p, ptc->m_xy, &pt->x, nullptr);
958
959 /* copy pressure and time */
960 pt->pressure = ptc->pressure;
961 pt->strength = ptc->strength;
962 pt->time = ptc->time;
963 }
964 else {
965 float *depth_arr = nullptr;
966
967 /* get an array of depths, far depths are blended */
969 int mval_i[2], mval_prev[2] = {0};
970 int interp_depth = 0;
971 int found_depth = 0;
972
973 depth_arr = static_cast<float *>(
974 MEM_mallocN(sizeof(float) * gpd->runtime.sbuffer_used, "depth_points"));
975
976 const ViewDepths *depths = p->depths;
977 for (i = 0, ptc = static_cast<tGPspoint *>(gpd->runtime.sbuffer);
978 i < gpd->runtime.sbuffer_used;
979 i++, ptc++, pt++)
980 {
981 round_v2i_v2fl(mval_i, ptc->m_xy);
982
983 if ((ED_view3d_depth_read_cached(depths, mval_i, depth_margin, depth_arr + i) == 0) &&
985 depths, mval_i, mval_prev, depth_margin + 1, depth_arr + i) == 0)))
986 {
987 interp_depth = true;
988 }
989 else {
990 found_depth = true;
991 }
992
993 copy_v2_v2_int(mval_prev, mval_i);
994 }
995
996 if (found_depth == false) {
997 /* Unfortunately there is not much we can do when the depth isn't found,
998 * ignore depth in this case, use the 3D cursor. */
999 for (i = gpd->runtime.sbuffer_used - 1; i >= 0; i--) {
1000 depth_arr[i] = 0.9999f;
1001 }
1002 }
1003 else {
1005 /* remove all info between the valid endpoints */
1006 int first_valid = 0;
1007 int last_valid = 0;
1008
1009 for (i = 0; i < gpd->runtime.sbuffer_used; i++) {
1010 if (depth_arr[i] != DEPTH_INVALID) {
1011 break;
1012 }
1013 }
1014 first_valid = i;
1015
1016 for (i = gpd->runtime.sbuffer_used - 1; i >= 0; i--) {
1017 if (depth_arr[i] != DEPTH_INVALID) {
1018 break;
1019 }
1020 }
1021 last_valid = i;
1022
1023 /* invalidate non-endpoints, so only blend between first and last */
1024 for (i = first_valid + 1; i < last_valid; i++) {
1025 depth_arr[i] = DEPTH_INVALID;
1026 }
1027
1028 interp_depth = true;
1029 }
1030
1031 if (interp_depth) {
1033 }
1034 }
1035 }
1036
1037 pt = gps->points;
1038
1039 /* convert all points (normal behavior) */
1040 for (i = 0, ptc = static_cast<tGPspoint *>(gpd->runtime.sbuffer);
1041 i < gpd->runtime.sbuffer_used && ptc;
1042 i++, ptc++, pt++)
1043 {
1044 /* convert screen-coordinates to appropriate coordinates (and store them) */
1045 annotation_stroke_convertcoords(p, ptc->m_xy, &pt->x, depth_arr ? depth_arr + i : nullptr);
1046
1047 /* copy pressure and time */
1048 pt->pressure = ptc->pressure;
1049 pt->strength = ptc->strength;
1051 pt->time = ptc->time;
1052 }
1053
1054 if (depth_arr) {
1055 MEM_freeN(depth_arr);
1056 }
1057 }
1058
1059 /* add stroke to frame */
1060 BLI_addtail(&p->gpf->strokes, gps);
1062}
1063
1064/* --- 'Eraser' for 'Paint' Tool ------ */
1065
1066/* helper to free a stroke
1067 * NOTE: gps->dvert and gps->triangles should be nullptr, but check anyway for good measure
1068 */
1070{
1071 if (gps->points) {
1072 MEM_freeN(gps->points);
1073 }
1074
1075 if (gps->dvert) {
1077 MEM_freeN(gps->dvert);
1078 }
1079
1080 if (gps->triangles) {
1081 MEM_freeN(gps->triangles);
1082 }
1083
1084 BLI_freelinkN(&gpf->strokes, gps);
1085}
1086
1087/* only erase stroke points that are visible (3d view) */
1089 const bGPDspoint *pt,
1090 const int x,
1091 const int y)
1092{
1094 RegionView3D *rv3d = static_cast<RegionView3D *>(p->region->regiondata);
1095 const int mval_i[2] = {x, y};
1096 float mval_3d[3];
1097
1098 float p_depth;
1099 if (ED_view3d_depth_read_cached(p->depths, mval_i, 0, &p_depth)) {
1100 ED_view3d_depth_unproject_v3(p->region, mval_i, double(p_depth), mval_3d);
1101
1102 const float depth_mval = ED_view3d_calc_depth_for_comparison(rv3d, mval_3d);
1103 const float depth_pt = ED_view3d_calc_depth_for_comparison(rv3d, &pt->x);
1104
1105 if (depth_pt > depth_mval) {
1106 return true;
1107 }
1108 }
1109 }
1110 return false;
1111}
1112
1113/* Eraser tool - evaluation per stroke. */
1115 bGPDframe *gpf,
1116 bGPDstroke *gps,
1117 const float mval[2],
1118 const int radius,
1119 const rcti *rect)
1120{
1121 bGPDspoint *pt1, *pt2;
1122 int pc1[2] = {0};
1123 int pc2[2] = {0};
1124 int mval_i[2];
1125 round_v2i_v2fl(mval_i, mval);
1126
1127 if (gps->totpoints == 0) {
1128 /* just free stroke */
1129 annotation_free_stroke(gpf, gps);
1130 }
1131 else if (gps->totpoints == 1) {
1132 /* only process if it hasn't been masked out... */
1133 if (!(p->flags & GP_PAINTFLAG_SELECTMASK) || (gps->points->flag & GP_SPOINT_SELECT)) {
1134 gpencil_point_to_xy(&p->gsc, gps, gps->points, &pc1[0], &pc1[1]);
1135
1136 /* Do bound-box check first. */
1137 if (!ELEM(V2D_IS_CLIPPED, pc1[0], pc1[1]) && BLI_rcti_isect_pt(rect, pc1[0], pc1[1])) {
1138 /* only check if point is inside */
1139 if (len_v2v2_int(mval_i, pc1) <= radius) {
1140 /* free stroke */
1141 annotation_free_stroke(gpf, gps);
1142 }
1143 }
1144 }
1145 }
1146 else {
1147 /* Perform culling? */
1148 bool do_cull = false;
1149
1150 /* Clear Tags
1151 *
1152 * NOTE: It's better this way, as we are sure that
1153 * we don't miss anything, though things will be
1154 * slightly slower as a result
1155 */
1156 for (int i = 0; i < gps->totpoints; i++) {
1157 bGPDspoint *pt = &gps->points[i];
1158 pt->flag &= ~GP_SPOINT_TAG;
1159 }
1160
1161 /* First Pass: Loop over the points in the stroke
1162 * 1) Thin out parts of the stroke under the brush
1163 * 2) Tag "too thin" parts for removal (in second pass)
1164 */
1165 for (int i = 0; (i + 1) < gps->totpoints; i++) {
1166 /* get points to work with */
1167 pt1 = gps->points + i;
1168 pt2 = gps->points + i + 1;
1169
1170 /* only process if it hasn't been masked out... */
1171 if ((p->flags & GP_PAINTFLAG_SELECTMASK) && !(gps->points->flag & GP_SPOINT_SELECT)) {
1172 continue;
1173 }
1174
1175 gpencil_point_to_xy(&p->gsc, gps, pt1, &pc1[0], &pc1[1]);
1176 gpencil_point_to_xy(&p->gsc, gps, pt2, &pc2[0], &pc2[1]);
1177
1178 /* Check that point segment of the bound-box of the eraser stroke. */
1179 if ((!ELEM(V2D_IS_CLIPPED, pc1[0], pc1[1]) && BLI_rcti_isect_pt(rect, pc1[0], pc1[1])) ||
1180 (!ELEM(V2D_IS_CLIPPED, pc2[0], pc2[1]) && BLI_rcti_isect_pt(rect, pc2[0], pc2[1])))
1181 {
1182 /* Check if point segment of stroke had anything to do with
1183 * eraser region (either within stroke painted, or on its lines)
1184 * - this assumes that line-width is irrelevant.
1185 */
1186 if (gpencil_stroke_inside_circle(mval, radius, pc1[0], pc1[1], pc2[0], pc2[1])) {
1187 if ((annotation_stroke_eraser_is_occluded(p, pt1, pc1[0], pc1[1]) == false) ||
1188 (annotation_stroke_eraser_is_occluded(p, pt2, pc2[0], pc2[1]) == false))
1189 {
1190 /* Edge is affected - Check individual points now */
1191 if (len_v2v2_int(mval_i, pc1) <= radius) {
1192 pt1->flag |= GP_SPOINT_TAG;
1193 }
1194 if (len_v2v2_int(mval_i, pc2) <= radius) {
1195 pt2->flag |= GP_SPOINT_TAG;
1196 }
1197 do_cull = true;
1198 }
1199 }
1200 }
1201 }
1202
1203 /* Second Pass: Remove any points that are tagged */
1204 if (do_cull) {
1206 p->gpd, gpf, gps, gps->next, GP_SPOINT_TAG, false, false, 0);
1207 }
1208 }
1209}
1210
1211/* erase strokes which fall under the eraser strokes */
1213{
1214 bGPDframe *gpf = p->gpf;
1215 bGPDstroke *gps, *gpn;
1216 rcti rect;
1217
1218 /* rect is rectangle of eraser */
1219 rect.xmin = p->mval[0] - p->radius;
1220 rect.ymin = p->mval[1] - p->radius;
1221 rect.xmax = p->mval[0] + p->radius;
1222 rect.ymax = p->mval[1] + p->radius;
1223
1224 if (p->area->spacetype == SPACE_VIEW3D) {
1226 View3D *v3d = static_cast<View3D *>(p->area->spacedata.first);
1229 p->depsgraph, p->region, v3d, nullptr, V3D_DEPTH_NO_GPENCIL, &p->depths);
1230 }
1231 }
1232
1233 /* loop over strokes of active layer only (session init already took care of ensuring validity),
1234 * checking segments for intersections to remove
1235 */
1236 for (gps = static_cast<bGPDstroke *>(gpf->strokes.first); gps; gps = gpn) {
1237 gpn = gps->next;
1238 /* Not all strokes in the datablock may be valid in the current editor/context
1239 * (e.g. 2D space strokes in the 3D view, if the same datablock is shared)
1240 */
1242 annotation_stroke_eraser_dostroke(p, gpf, gps, p->mval, p->radius, &rect);
1243 }
1244 }
1245}
1246
1247/* ******************************************* */
1248/* Sketching Operator */
1249
1250/* clear the session buffers (call this before AND after a paint operation) */
1252{
1253 bGPdata *gpd = p->gpd;
1254
1256 &gpd->runtime.sbuffer_size,
1257 &gpd->runtime.sbuffer_used,
1258 true);
1259
1260 /* reset flags */
1261 gpd->runtime.sbuffer_sflag = 0;
1262
1263 /* reset inittime */
1264 p->inittime = 0.0;
1265}
1266
1267/* (re)init new painting data */
1269{
1270 Main *bmain = CTX_data_main(C);
1271 bGPdata **gpd_ptr = nullptr;
1272 ScrArea *curarea = CTX_wm_area(C);
1273 ARegion *region = CTX_wm_region(C);
1275
1276 /* make sure the active view (at the starting time) is a 3d-view */
1277 if (curarea == nullptr) {
1279 return false;
1280 }
1281
1282 /* pass on current scene and window */
1283 p->bmain = CTX_data_main(C);
1284 p->scene = CTX_data_scene(C);
1286 p->win = CTX_wm_window(C);
1287
1288 unit_m4(p->imat);
1289 unit_m4(p->mat);
1290
1291 switch (curarea->spacetype) {
1292 /* supported views first */
1293 case SPACE_VIEW3D: {
1294 // View3D *v3d = curarea->spacedata.first;
1295 // RegionView3D *rv3d = region->regiondata;
1296
1297 /* set current area
1298 * - must verify that region data is 3D-view (and not something else)
1299 */
1300 /* CAUTION: If this is the "toolbar", then this will change on the first stroke */
1301 p->area = curarea;
1302 p->region = region;
1304
1305 if (region->regiondata == nullptr) {
1307 return false;
1308 }
1309 break;
1310 }
1311 case SPACE_NODE: {
1312 // SpaceNode *snode = curarea->spacedata.first;
1313
1314 /* set current area */
1315 p->area = curarea;
1316 p->region = region;
1317 p->v2d = &region->v2d;
1318 p->align_flag = &ts->gpencil_v2d_align;
1319 break;
1320 }
1321 case SPACE_SEQ: {
1322 SpaceSeq *sseq = static_cast<SpaceSeq *>(curarea->spacedata.first);
1323
1324 /* set current area */
1325 p->area = curarea;
1326 p->region = region;
1327 p->v2d = &region->v2d;
1328 p->align_flag = &ts->gpencil_v2d_align;
1329
1330 /* check that gpencil data is allowed to be drawn */
1331 if (!((sseq->mainb == SEQ_DRAW_IMG_IMBUF) && (region->regiontype == RGN_TYPE_PREVIEW))) {
1333 return false;
1334 }
1335 break;
1336 }
1337 case SPACE_IMAGE: {
1338 // SpaceImage *sima = curarea->spacedata.first;
1339
1340 /* set the current area */
1341 p->area = curarea;
1342 p->region = region;
1343 p->v2d = &region->v2d;
1344 p->align_flag = &ts->gpencil_v2d_align;
1345 break;
1346 }
1347 case SPACE_CLIP: {
1348 SpaceClip *sc = static_cast<SpaceClip *>(curarea->spacedata.first);
1350
1351 if (clip == nullptr) {
1353 return false;
1354 }
1355
1356 /* set the current area */
1357 p->area = curarea;
1358 p->region = region;
1359 p->v2d = &region->v2d;
1360 p->align_flag = &ts->gpencil_v2d_align;
1361
1362 invert_m4_m4(p->imat, sc->unistabmat);
1363
1364 /* custom color for new layer */
1365 p->custom_color[0] = 1.0f;
1366 p->custom_color[1] = 0.0f;
1367 p->custom_color[2] = 0.5f;
1368 p->custom_color[3] = 0.9f;
1369
1370 if (sc->gpencil_src == SC_GPENCIL_SRC_TRACK) {
1371 int framenr = ED_space_clip_get_clip_frame_number(sc);
1372 const MovieTrackingObject *tracking_object = BKE_tracking_object_get_active(
1373 &clip->tracking);
1374 MovieTrackingTrack *track = tracking_object->active_track;
1375 MovieTrackingMarker *marker = track ? BKE_tracking_marker_get(track, framenr) : nullptr;
1376
1377 if (marker) {
1378 p->imat[3][0] -= marker->pos[0];
1379 p->imat[3][1] -= marker->pos[1];
1380 }
1381 else {
1383 return false;
1384 }
1385 }
1386
1387 invert_m4_m4(p->mat, p->imat);
1388 copy_m4_m4(p->gsc.mat, p->mat);
1389 break;
1390 }
1391 /* unsupported views */
1392 default: {
1394 return false;
1395 }
1396 }
1397
1398 /* get gp-data */
1399 gpd_ptr = ED_annotation_data_get_pointers(C, &p->ownerPtr);
1400 if ((gpd_ptr == nullptr) || !ED_gpencil_data_owner_is_annotation(&p->ownerPtr)) {
1402 return false;
1403 }
1404
1405 /* if no existing GPencil block exists, add one */
1406 if (*gpd_ptr == nullptr) {
1407 bGPdata *gpd = BKE_gpencil_data_addnew(bmain, "Annotations");
1408 *gpd_ptr = gpd;
1409
1410 /* mark datablock as being used for annotations */
1411 gpd->flag |= GP_DATA_ANNOTATIONS;
1412 }
1413 p->gpd = *gpd_ptr;
1414
1415 if (ED_gpencil_session_active() == 0) {
1416 /* initialize undo stack,
1417 * also, existing undo stack would make buffer drawn
1418 */
1420 }
1421
1422 /* clear out buffer (stored in gp-data), in case something contaminated it */
1424
1425 return true;
1426}
1427
1428/* Enable the annotations in the current space. */
1430{
1431 ScrArea *area = p->area;
1432 switch (area->spacetype) {
1433 case SPACE_VIEW3D: {
1434 View3D *v3d = (View3D *)area->spacedata.first;
1435 v3d->flag2 |= V3D_SHOW_ANNOTATION;
1436 break;
1437 }
1438 case SPACE_SEQ: {
1439 SpaceSeq *sseq = (SpaceSeq *)area->spacedata.first;
1441 break;
1442 }
1443 case SPACE_IMAGE: {
1444 SpaceImage *sima = (SpaceImage *)area->spacedata.first;
1445 sima->flag |= SI_SHOW_GPENCIL;
1446 break;
1447 }
1448 case SPACE_NODE: {
1449 SpaceNode *snode = (SpaceNode *)area->spacedata.first;
1450 snode->flag |= SNODE_SHOW_GPENCIL;
1451 break;
1452 }
1453 case SPACE_CLIP: {
1454 SpaceClip *sclip = (SpaceClip *)area->spacedata.first;
1455 sclip->flag |= SC_SHOW_ANNOTATION;
1456 break;
1457 }
1458 default:
1459 break;
1460 }
1461}
1462
1463/* init new painting session */
1465{
1466 tGPsdata *p = nullptr;
1467
1468 /* create new context data */
1469 p = static_cast<tGPsdata *>(MEM_callocN(sizeof(tGPsdata), "Annotation Drawing Data"));
1470
1471 /* Try to initialize context data
1472 * WARNING: This may not always succeed (e.g. using GP in an annotation-only context)
1473 */
1474 if (annotation_session_initdata(C, p) == 0) {
1475 /* Invalid state - Exit
1476 * NOTE: It should be safe to just free the data, since failing context checks should
1477 * only happen when no data has been allocated.
1478 */
1479 MEM_freeN(p);
1480 return nullptr;
1481 }
1482
1483 /* Radius for eraser circle is defined in user-preferences. */
1484 /* NOTE: we do this here, so that if we exit immediately,
1485 * erase size won't get lost
1486 */
1487 p->radius = U.gp_eraser;
1488
1489 /* Annotations must be always visible when use it. */
1491
1492 /* return context data for running paint operator */
1493 return p;
1494}
1495
1496/* cleanup after a painting session */
1498{
1499 bGPdata *gpd = (p) ? p->gpd : nullptr;
1500
1501 /* error checking */
1502 if (gpd == nullptr) {
1503 return;
1504 }
1505
1506 /* free stroke buffer */
1507 if (gpd->runtime.sbuffer) {
1509 gpd->runtime.sbuffer = nullptr;
1510 }
1511
1512 /* clear flags */
1513 gpd->runtime.sbuffer_used = 0;
1514 gpd->runtime.sbuffer_size = 0;
1515 gpd->runtime.sbuffer_sflag = 0;
1516 p->inittime = 0.0;
1517}
1518
1520{
1521 if (p->depths) {
1523 }
1524 MEM_freeN(p);
1525}
1526
1527/* init new stroke */
1529 eGPencil_PaintModes paintmode,
1530 Depsgraph *depsgraph)
1531{
1532 Scene *scene = p->scene;
1533 ToolSettings *ts = scene->toolsettings;
1534
1535 /* Call to the annotation pre handler to notify python the annotation starts. */
1537
1538 /* get active layer (or add a new one if non-existent) */
1540 if (p->gpl == nullptr) {
1541 /* tag for annotations */
1543 p->gpl = BKE_gpencil_layer_addnew(p->gpd, DATA_("Note"), true, false);
1544
1545 if (p->custom_color[3]) {
1547 }
1548 }
1549 if (p->gpl->flag & GP_LAYER_LOCKED) {
1551 return;
1552 }
1553
1554 /* get active frame (add a new one if not matching frame) */
1555 if (paintmode == GP_PAINTMODE_ERASER) {
1556 /* Eraser mode:
1557 * 1) Only allow erasing on the active layer (unlike for 3d-art Grease Pencil),
1558 * since we won't be exposing layer locking in the UI
1559 * 2) Ensure that p->gpf refers to the frame used for the active layer
1560 * (to avoid problems with other tools which expect it to exist)
1561 */
1562 bool has_layer_to_erase = false;
1563
1565 /* Ensure that there's stuff to erase here (not including selection mask below)... */
1566 if (p->gpl->actframe && p->gpl->actframe->strokes.first) {
1567 has_layer_to_erase = true;
1568 }
1569 }
1570
1571 /* Ensure active frame is set correctly... */
1572 p->gpf = p->gpl->actframe;
1573
1574 if (has_layer_to_erase == false) {
1576 printf("Error: Eraser will not be affecting anything (gpencil_paint_init)\n");
1577 return;
1578 }
1579 }
1580 else {
1581 /* Drawing Modes - Add a new frame if needed on the active layer */
1582 short add_frame_mode = GP_GETFRAME_ADD_NEW;
1583
1585 add_frame_mode = GP_GETFRAME_ADD_COPY;
1586 }
1587 else {
1588 add_frame_mode = GP_GETFRAME_ADD_NEW;
1589 }
1590
1591 p->gpf = BKE_gpencil_layer_frame_get(p->gpl, scene->r.cfra, eGP_GetFrame_Mode(add_frame_mode));
1592
1593 if (p->gpf == nullptr) {
1595 return;
1596 }
1597
1598 p->gpf->flag |= GP_FRAME_PAINT;
1599 }
1600
1601 /* set 'eraser' for this stroke if using eraser */
1602 p->paintmode = paintmode;
1603 if (p->paintmode == GP_PAINTMODE_ERASER) {
1605
1606 /* check if we should respect depth while erasing */
1607 if (p->area->spacetype == SPACE_VIEW3D) {
1608 if (p->gpl->flag & GP_LAYER_NO_XRAY) {
1610 }
1611 }
1612 }
1613 else {
1614 /* disable eraser flags - so that we can switch modes during a session */
1615 p->gpd->runtime.sbuffer_sflag &= ~GP_STROKE_ERASER;
1616
1617 if (p->area->spacetype == SPACE_VIEW3D) {
1618 if (p->gpl->flag & GP_LAYER_NO_XRAY) {
1619 p->flags &= ~GP_PAINTFLAG_V3D_ERASER_DEPTH;
1620 }
1621 }
1622 }
1623
1624 /* set 'initial run' flag, which is only used to denote when a new stroke is starting */
1626
1627 /* when drawing in the camera view, in 2D space, set the subrect */
1628 p->subrect = nullptr;
1629 if ((*p->align_flag & GP_PROJECT_VIEWSPACE) == 0) {
1630 if (p->area->spacetype == SPACE_VIEW3D) {
1631 View3D *v3d = static_cast<View3D *>(p->area->spacedata.first);
1632 RegionView3D *rv3d = static_cast<RegionView3D *>(p->region->regiondata);
1633
1634 /* for camera view set the subrect */
1635 if (rv3d->persp == RV3D_CAMOB) {
1636 /* no shift */
1638 p->scene, depsgraph, p->region, v3d, rv3d, true, &p->subrect_data);
1639 p->subrect = &p->subrect_data;
1640 }
1641 }
1642 }
1643
1644 /* init stroke point space-conversion settings... */
1645 p->gsc.gpd = p->gpd;
1646 p->gsc.gpl = p->gpl;
1647
1648 p->gsc.area = p->area;
1649 p->gsc.region = p->region;
1650 p->gsc.v2d = p->v2d;
1651
1653 p->gsc.subrect = p->subrect;
1654
1655 copy_m4_m4(p->gsc.mat, p->mat);
1656
1657 /* check if points will need to be made in view-aligned space */
1658 if (*p->align_flag & GP_PROJECT_VIEWSPACE) {
1659 switch (p->area->spacetype) {
1660 case SPACE_VIEW3D: {
1662 break;
1663 }
1664 case SPACE_NODE:
1665 case SPACE_SEQ:
1666 case SPACE_IMAGE:
1667 case SPACE_CLIP: {
1669 break;
1670 }
1671 }
1672 }
1673}
1674
1675/* finish off a stroke (clears buffer, but doesn't finish the paint operation) */
1677{
1679 const bool is_eraser = (p->gpd->runtime.sbuffer_sflag & GP_STROKE_ERASER) != 0;
1680 /* for surface sketching, need to set the right OpenGL context stuff so that
1681 * the conversions will project the values correctly...
1682 */
1683 if (annotation_project_check(p)) {
1684 View3D *v3d = static_cast<View3D *>(p->area->spacedata.first);
1685
1687
1691 }
1692 else {
1693 mode = V3D_DEPTH_NO_OVERLAYS;
1694 }
1695 }
1696 /* need to restore the original projection settings before packing up */
1699 p->depsgraph, p->region, v3d, nullptr, mode, is_eraser ? nullptr : &p->depths);
1700 }
1701
1702 /* check if doing eraser or not */
1703 if (!is_eraser) {
1704 /* transfer stroke to frame */
1706 }
1707
1708 /* Call to the annotation post handler to notify python the annotation is done. */
1710
1711 /* clean up buffer now */
1713}
1714
1715/* finish off stroke painting operation */
1717{
1718 /* p->gpd==nullptr happens when stroke failed to initialize,
1719 * for example when GP is hidden in current space (sergey)
1720 */
1721 if (p->gpd) {
1722 /* finish off a stroke */
1724 }
1725
1726 /* "unlock" frame */
1727 if (p->gpf) {
1728 p->gpf->flag &= ~GP_FRAME_PAINT;
1729 }
1730}
1731
1732/* ------------------------------- */
1733
1734/* Helper callback for drawing the cursor itself */
1735static void annotation_draw_eraser(bContext * /*C*/, int x, int y, void *p_ptr)
1736{
1737 tGPsdata *p = (tGPsdata *)p_ptr;
1738
1739 if (p->paintmode == GP_PAINTMODE_ERASER) {
1741 const uint shdr_pos = GPU_vertformat_attr_add(format, "pos", GPU_COMP_F32, 2, GPU_FETCH_FLOAT);
1743
1744 GPU_line_smooth(true);
1746
1747 immUniformColor4ub(255, 100, 100, 20);
1748 imm_draw_circle_fill_2d(shdr_pos, x, y, p->radius, 40);
1749
1751
1753
1754 float viewport_size[4];
1755 GPU_viewport_size_get_f(viewport_size);
1756 immUniform2f("viewport_size", viewport_size[2], viewport_size[3]);
1757
1758 immUniformColor4f(1.0f, 0.39f, 0.39f, 0.78f);
1759 immUniform1i("colors_len", 0); /* "simple" mode */
1760 immUniform1f("dash_width", 12.0f);
1761 immUniform1f("udash_factor", 0.5f);
1762
1763 imm_draw_circle_wire_2d(shdr_pos,
1764 x,
1765 y,
1766 p->radius,
1767 /* XXX Dashed shader gives bad results with sets of small segments
1768 * currently, temp hack around the issue. :( */
1769 max_ii(8, p->radius / 2)); /* was fixed 40 */
1770
1772
1774 GPU_line_smooth(false);
1775 }
1776}
1777
1778/* Turn brush cursor in 3D view on/off */
1780{
1781 if (p->erasercursor && !enable) {
1782 /* clear cursor */
1784 p->erasercursor = nullptr;
1785 }
1786 else if (enable && !p->erasercursor) {
1787 /* enable cursor */
1790 nullptr, /* XXX */
1792 p);
1793 }
1794}
1795static void annotation_draw_stabilizer(bContext *C, int x, int y, void *p_ptr)
1796{
1797 ARegion *region = CTX_wm_region(C);
1798 tGPsdata *p = (tGPsdata *)p_ptr;
1799 bGPdata_Runtime runtime = blender::dna::shallow_copy(p->gpd->runtime);
1800 const tGPspoint *points = static_cast<const tGPspoint *>(runtime.sbuffer);
1801 int totpoints = runtime.sbuffer_used;
1802 if (totpoints < 2) {
1803 return;
1804 }
1805 const tGPspoint *pt = &points[totpoints - 1];
1806
1810 GPU_line_smooth(true);
1812 GPU_line_width(1.25f);
1813 const float color[3] = {1.0f, 0.39f, 0.39f};
1814
1815 /* default radius and color */
1816 float darkcolor[3];
1817 const float radius = 4.0f;
1818
1819 /* Inner Ring: Color from UI panel */
1820 immUniformColor4f(color[0], color[1], color[2], 0.8f);
1821 imm_draw_circle_wire_2d(pos, x, y, radius, 40);
1822
1823 /* Outer Ring: Dark color for contrast on light backgrounds (e.g. gray on white) */
1824 mul_v3_v3fl(darkcolor, color, 0.40f);
1825 immUniformColor4f(darkcolor[0], darkcolor[1], darkcolor[2], 0.8f);
1826 imm_draw_circle_wire_2d(pos, x, y, radius + 1, 40);
1827
1828 /* Rope Simple. */
1829 immUniformColor4f(color[0], color[1], color[2], 0.8f);
1831 immVertex2f(pos, pt->m_xy[0] + region->winrct.xmin, pt->m_xy[1] + region->winrct.ymin);
1832 immVertex2f(pos, x, y);
1833 immEnd();
1834
1835 /* Returns back all GPU settings */
1837 GPU_line_smooth(false);
1838
1840}
1841
1842/* Turn *stabilizer* brush cursor in 3D view on/off */
1844{
1845 if (p->stabilizer_cursor && !enable) {
1846 /* clear cursor */
1848 p->stabilizer_cursor = nullptr;
1849 }
1850 else if (enable && !p->stabilizer_cursor) {
1851 /* enable cursor */
1854 }
1855}
1856
1857/* Check if tablet eraser is being used (when processing events) */
1859{
1860 return (event->tablet.active == EVT_TABLET_ERASER);
1861}
1862
1863/* ------------------------------- */
1864
1866{
1867 tGPsdata *p = static_cast<tGPsdata *>(op->customdata);
1868
1869 /* restore cursor to indicate end of drawing */
1871
1872 /* don't assume that operator data exists at all */
1873 if (p) {
1874 /* check size of buffer before cleanup, to determine if anything happened here */
1875 if (p->paintmode == GP_PAINTMODE_ERASER) {
1876 /* turn off radial brush cursor */
1878 }
1879 else if (p->paintmode == GP_PAINTMODE_DRAW) {
1881 }
1882
1883 /* always store the new eraser size to be used again next time
1884 * NOTE: Do this even when not in eraser mode, as eraser may
1885 * have been toggled at some point.
1886 */
1887 U.gp_eraser = p->radius;
1888
1889 /* clear undo stack */
1891
1892 /* cleanup */
1896 p = nullptr;
1897 }
1898
1899 op->customdata = nullptr;
1900}
1901
1903{
1904 /* this is just a wrapper around exit() */
1905 annotation_draw_exit(C, op);
1906}
1907
1908/* ------------------------------- */
1909
1910static int annotation_draw_init(bContext *C, wmOperator *op, const wmEvent *event)
1911{
1912 tGPsdata *p;
1913 eGPencil_PaintModes paintmode = eGPencil_PaintModes(RNA_enum_get(op->ptr, "mode"));
1914
1915 /* check context */
1916 p = static_cast<tGPsdata *>(op->customdata = annotation_session_initpaint(C));
1917 if ((p == nullptr) || (p->status == GP_STATUS_ERROR)) {
1918 /* something wasn't set correctly in context */
1919 annotation_draw_exit(C, op);
1920 return 0;
1921 }
1922
1923 /* init painting data */
1925 if (p->status == GP_STATUS_ERROR) {
1926 annotation_draw_exit(C, op);
1927 return 0;
1928 }
1929
1930 if (event != nullptr) {
1931 p->keymodifier = event->keymodifier;
1932 }
1933 else {
1934 p->keymodifier = -1;
1935 }
1936
1937 /* everything is now setup ok */
1938 return 1;
1939}
1940
1941/* ------------------------------- */
1942
1943/* ensure that the correct cursor icon is set */
1945{
1946 if (p->paintmode == GP_PAINTMODE_ERASER) {
1948 }
1949 else {
1951 }
1952}
1953
1954/* update UI indicators of status, including cursor and header prints */
1956{
1957 /* header prints */
1958 switch (p->status) {
1959 case GP_STATUS_PAINTING:
1960 switch (p->paintmode) {
1962 /* Provide usage tips, since this is modal, and unintuitive without hints */
1964 C,
1965 IFACE_("Annotation Create Poly: LMB click to place next stroke vertex | "
1966 "ESC/Enter to end (or click outside this area)"));
1967 break;
1968 default:
1969 /* Do nothing - the others are self explanatory, exit quickly once the mouse is
1970 * released Showing any text would just be annoying as it would flicker.
1971 */
1972 break;
1973 }
1974 break;
1975
1976 case GP_STATUS_IDLING:
1977 /* print status info */
1978 switch (p->paintmode) {
1981 IFACE_("Annotation Eraser: Hold and drag LMB or RMB to erase | "
1982 "ESC/Enter to end (or click outside this area)"));
1983 break;
1986 IFACE_("Annotation Line Draw: Hold and drag LMB to draw | "
1987 "ESC/Enter to end (or click outside this area)"));
1988 break;
1989 case GP_PAINTMODE_DRAW:
1991 IFACE_("Annotation Freehand Draw: Hold and drag LMB to draw | "
1992 "E/ESC/Enter to end (or click outside this area)"));
1993 break;
1996 C,
1997 IFACE_("Annotation Create Poly: LMB click to place next stroke vertex | "
1998 "ESC/Enter to end (or click outside this area)"));
1999 break;
2000
2001 default: /* unhandled future cases */
2003 C, IFACE_("Annotation Session: ESC/Enter to end (or click outside this area)"));
2004 break;
2005 }
2006 break;
2007
2008 case GP_STATUS_ERROR:
2009 case GP_STATUS_DONE:
2010 case GP_STATUS_CAPTURE:
2011 /* clear status string */
2012 ED_workspace_status_text(C, nullptr);
2013 break;
2014 }
2015}
2016
2017/* ------------------------------- */
2018
2019/* create a new stroke point at the point indicated by the painting context */
2020static void annotation_draw_apply(wmOperator *op, tGPsdata *p, Depsgraph *depsgraph)
2021{
2022 /* handle drawing/erasing -> test for erasing first */
2023 if (p->paintmode == GP_PAINTMODE_ERASER) {
2024 /* do 'live' erasing now */
2026
2027 /* store used values */
2028 p->mvalo[0] = p->mval[0];
2029 p->mvalo[1] = p->mval[1];
2030 p->opressure = p->pressure;
2031 }
2032 /* Only add current point to buffer if mouse moved
2033 * (even though we got an event, it might be just noise). */
2034 else if (annotation_stroke_filtermval(p, p->mval, p->mvalo)) {
2035 /* If lazy mouse, interpolate the last and current mouse positions. */
2037 float now_mouse[2];
2038 float last_mouse[2];
2039 copy_v2_v2(now_mouse, p->mval);
2040 copy_v2_v2(last_mouse, p->mvalo);
2041 interp_v2_v2v2(now_mouse, now_mouse, last_mouse, min_ff(p->stabilizer_factor, .995f));
2042 copy_v2_v2(p->mval, now_mouse);
2043 }
2044
2045 /* try to add point */
2046 short ok = annotation_stroke_addpoint(p, p->mval, p->pressure, p->curtime);
2047
2048 /* handle errors while adding point */
2050 /* finish off old stroke */
2052 /* And start a new one!!! Else, projection errors! */
2054
2055 /* start a new stroke, starting from previous point */
2056 if (ok == GP_STROKEADD_OVERFLOW) {
2057 p->inittime = p->ocurtime;
2059 }
2060 else {
2061 p->inittime = p->curtime;
2062 }
2064 }
2065 else if (ok == GP_STROKEADD_INVALID) {
2066 /* the painting operation cannot continue... */
2067 BKE_report(op->reports, RPT_ERROR, "Cannot paint stroke");
2069
2070 return;
2071 }
2072
2073 /* store used values */
2074 p->mvalo[0] = p->mval[0];
2075 p->mvalo[1] = p->mval[1];
2076 p->opressure = p->pressure;
2077 p->ocurtime = p->curtime;
2078 }
2079}
2080
2081/* handle draw event */
2083 wmOperator *op, const wmEvent *event, Depsgraph *depsgraph, float x, float y)
2084{
2085 tGPsdata *p = static_cast<tGPsdata *>(op->customdata);
2086 PointerRNA itemptr;
2087 float mousef[2];
2088
2089 /* Convert from window-space to area-space mouse coordinates
2090 * add any x,y override position for fake events. */
2091 if (p->flags & GP_PAINTFLAG_FIRSTRUN) {
2092 /* The first run may be a drag event, see: #99368. */
2094 p->mval[0] -= x;
2095 p->mval[1] -= y;
2096 }
2097 else {
2098 p->mval[0] = float(event->mval[0]) - x;
2099 p->mval[1] = float(event->mval[1]) - y;
2100 }
2101
2102 /* Key to toggle stabilization. */
2103 if ((event->modifier & KM_SHIFT) && (p->paintmode == GP_PAINTMODE_DRAW)) {
2104 /* Using permanent stabilization, shift will deactivate the flag. */
2108 p->flags &= ~GP_PAINTFLAG_USE_STABILIZER_TEMP;
2109 }
2110 }
2111 /* Not using any stabilization flag. Activate temporal one. */
2112 else if ((p->flags & GP_PAINTFLAG_USE_STABILIZER_TEMP) == 0) {
2115 }
2116 }
2117 /* verify key status for straight lines */
2118 else if (event->modifier & (KM_CTRL | KM_ALT)) {
2119 if (p->straight[0] == 0) {
2120 int dx = abs(int(p->mval[0] - p->mvalo[0]));
2121 int dy = abs(int(p->mval[1] - p->mvalo[1]));
2122 if ((dx > 0) || (dy > 0)) {
2123 /* check mouse direction to replace the other coordinate with previous values */
2124 if (dx >= dy) {
2125 /* horizontal */
2126 p->straight[0] = 1;
2127 p->straight[1] = p->mval[1]; /* save y */
2128 }
2129 else {
2130 /* vertical */
2131 p->straight[0] = 2;
2132 p->straight[1] = p->mval[0]; /* save x */
2133 }
2134 }
2135 }
2136 }
2137 else {
2138 p->straight[0] = 0;
2139 /* We were using shift while having permanent stabilization active,
2140 * so activate the temp flag back again. */
2142 if ((p->flags & GP_PAINTFLAG_USE_STABILIZER_TEMP) == 0) {
2145 }
2146 }
2147 /* We are using the temporal stabilizer flag at the moment,
2148 * but shift is not pressed as well as the permanent flag is not used,
2149 * so we don't need the cursor anymore. */
2151 /* Reset temporal stabilizer flag and remove cursor. */
2152 p->flags &= ~GP_PAINTFLAG_USE_STABILIZER_TEMP;
2154 }
2155 }
2156
2158
2159 /* handle pressure sensitivity (which is supplied by tablets or otherwise 1.0) */
2160 p->pressure = event->tablet.pressure;
2161
2162 /* Hack for pressure sensitive eraser on D+RMB when using a tablet:
2163 * The pen has to float over the tablet surface, resulting in
2164 * zero pressure (#47101). Ignore pressure values if floating
2165 * (i.e. "effectively zero" pressure), and only when the "active"
2166 * end is the stylus (i.e. the default when not eraser)
2167 */
2168 if (p->paintmode == GP_PAINTMODE_ERASER) {
2169 if ((event->tablet.active != EVT_TABLET_ERASER) && (p->pressure < 0.001f)) {
2170 p->pressure = 1.0f;
2171 }
2172 }
2173
2174 /* special exception for start of strokes (i.e. maybe for just a dot) */
2175 if (p->flags & GP_PAINTFLAG_FIRSTRUN) {
2176 p->flags &= ~GP_PAINTFLAG_FIRSTRUN;
2177
2178 p->mvalo[0] = p->mval[0];
2179 p->mvalo[1] = p->mval[1];
2180 p->opressure = p->pressure;
2181 p->inittime = p->ocurtime = p->curtime;
2182 p->straight[0] = 0;
2183 p->straight[1] = 0;
2184
2185 /* special exception here for too high pressure values on first touch in
2186 * windows for some tablets, then we just skip first touch...
2187 */
2188 if ((event->tablet.active != EVT_TABLET_NONE) && (p->pressure >= 0.99f)) {
2189 return;
2190 }
2191 }
2192
2193 /* check if alt key is pressed and limit to straight lines */
2194 if ((p->paintmode != GP_PAINTMODE_ERASER) && (p->straight[0] != 0)) {
2195 if (p->straight[0] == 1) {
2196 /* horizontal */
2197 p->mval[1] = p->straight[1]; /* replace y */
2198 }
2199 else {
2200 /* vertical */
2201 p->mval[0] = p->straight[1]; /* replace x */
2202 }
2203 }
2204
2205 /* fill in stroke data (not actually used directly by gpencil_draw_apply) */
2206 RNA_collection_add(op->ptr, "stroke", &itemptr);
2207
2208 mousef[0] = p->mval[0];
2209 mousef[1] = p->mval[1];
2210 RNA_float_set_array(&itemptr, "mouse", mousef);
2211 RNA_float_set(&itemptr, "pressure", p->pressure);
2212 RNA_boolean_set(&itemptr, "is_start", (p->flags & GP_PAINTFLAG_FIRSTRUN) != 0);
2213
2214 RNA_float_set(&itemptr, "time", p->curtime - p->inittime);
2215
2216 /* apply the current latest drawing point */
2218
2219 /* force refresh */
2220 /* just active area for now, since doing whole screen is too slow */
2222}
2223
2224/* ------------------------------- */
2225
2226/* operator 'redo' (i.e. after changing some properties, but also for repeat last) */
2228{
2229 tGPsdata *p = nullptr;
2231
2232 /* try to initialize context data needed while drawing */
2233 if (!annotation_draw_init(C, op, nullptr)) {
2234 if (op->customdata) {
2235 MEM_freeN(op->customdata);
2236 }
2237 return OPERATOR_CANCELLED;
2238 }
2239
2240 p = static_cast<tGPsdata *>(op->customdata);
2241
2242 /* loop over the stroke RNA elements recorded (i.e. progress of mouse movement),
2243 * setting the relevant values in context at each step, then applying
2244 */
2245 RNA_BEGIN (op->ptr, itemptr, "stroke") {
2246 float mousef[2];
2247
2248 /* get relevant data for this point from stroke */
2249 RNA_float_get_array(&itemptr, "mouse", mousef);
2250 p->mval[0] = int(mousef[0]);
2251 p->mval[1] = int(mousef[1]);
2252 p->pressure = RNA_float_get(&itemptr, "pressure");
2253 p->curtime = double(RNA_float_get(&itemptr, "time")) + p->inittime;
2254
2255 if (RNA_boolean_get(&itemptr, "is_start")) {
2256 /* if first-run flag isn't set already (i.e. not true first stroke),
2257 * then we must terminate the previous one first before continuing
2258 */
2259 if ((p->flags & GP_PAINTFLAG_FIRSTRUN) == 0) {
2260 /* TODO: both of these ops can set error-status, but we probably don't need to worry */
2263 }
2264 }
2265
2266 /* if first run, set previous data too */
2267 if (p->flags & GP_PAINTFLAG_FIRSTRUN) {
2268 p->flags &= ~GP_PAINTFLAG_FIRSTRUN;
2269
2270 p->mvalo[0] = p->mval[0];
2271 p->mvalo[1] = p->mval[1];
2272 p->opressure = p->pressure;
2273 p->ocurtime = p->curtime;
2274 }
2275
2276 /* apply this data as necessary now (as per usual) */
2278 }
2279 RNA_END;
2280
2281 /* cleanup */
2282 annotation_draw_exit(C, op);
2283
2284 /* refreshes */
2286
2287 /* done */
2288 return OPERATOR_FINISHED;
2289}
2290
2291/* ------------------------------- */
2292
2293/* start of interactive drawing part of operator */
2294static int annotation_draw_invoke(bContext *C, wmOperator *op, const wmEvent *event)
2295{
2296 tGPsdata *p = nullptr;
2297
2298 /* support for tablets eraser pen */
2300 RNA_enum_set(op->ptr, "mode", GP_PAINTMODE_ERASER);
2301 }
2302
2303 /* try to initialize context data needed while drawing */
2304 if (!annotation_draw_init(C, op, event)) {
2305 if (op->customdata) {
2306 MEM_freeN(op->customdata);
2307 }
2308 return OPERATOR_CANCELLED;
2309 }
2310
2311 p = static_cast<tGPsdata *>(op->customdata);
2312
2313 /* if empty erase capture and finish */
2314 if (p->status == GP_STATUS_CAPTURE) {
2315 annotation_draw_exit(C, op);
2316
2317 BKE_report(op->reports, RPT_ERROR, "Nothing to erase");
2318 return OPERATOR_FINISHED;
2319 }
2320
2321 /* if eraser is on, draw radial aid */
2322 if (p->paintmode == GP_PAINTMODE_ERASER) {
2324 }
2325 else if (p->paintmode == GP_PAINTMODE_DRAW_STRAIGHT) {
2326 if (RNA_enum_get(op->ptr, "arrowstyle_start") != GP_STROKE_ARROWSTYLE_NONE) {
2328 p->gpd->runtime.arrow_start_style = RNA_enum_get(op->ptr, "arrowstyle_start");
2329 }
2330 if (RNA_enum_get(op->ptr, "arrowstyle_end") != GP_STROKE_ARROWSTYLE_NONE) {
2332 p->gpd->runtime.arrow_end_style = RNA_enum_get(op->ptr, "arrowstyle_end");
2333 }
2334 }
2335 else if (p->paintmode == GP_PAINTMODE_DRAW) {
2336 p->stabilizer_factor = RNA_float_get(op->ptr, "stabilizer_factor");
2337 p->stabilizer_radius = RNA_int_get(op->ptr, "stabilizer_radius");
2338 if (RNA_boolean_get(op->ptr, "use_stabilizer")) {
2341 }
2342 else if (event->modifier & KM_SHIFT) {
2345 }
2346 }
2347 /* set cursor
2348 * NOTE: This may change later (i.e. intentionally via brush toggle,
2349 * or unintentionally if the user scrolls outside the area)...
2350 */
2352
2353 /* only start drawing immediately if we're allowed to do so... */
2354 if (RNA_boolean_get(op->ptr, "wait_for_input") == false) {
2355 /* hotkey invoked - start drawing */
2357
2358 /* handle the initial drawing - i.e. for just doing a simple dot */
2361 }
2362 else {
2363 /* toolbar invoked - don't start drawing yet... */
2365 }
2366
2368 /* add a modal handler for this operator, so that we can then draw continuous strokes */
2371}
2372
2373/* gpencil modal operator stores area, which can be removed while using it (like full-screen). */
2374static bool annotation_area_exists(bContext *C, ScrArea *area_test)
2375{
2376 bScreen *screen = CTX_wm_screen(C);
2377 return (BLI_findindex(&screen->areabase, area_test) != -1);
2378}
2379
2381{
2382 tGPsdata *p = static_cast<tGPsdata *>(op->customdata);
2383
2384 /* we must check that we're still within the area that we're set up to work from
2385 * otherwise we could crash (see bug #20586)
2386 */
2387 if (CTX_wm_area(C) != p->area) {
2388 printf("\t\t\tGP - wrong area execution abort!\n");
2390 }
2391
2392 /* we may need to set up paint env again if we're resuming */
2393 /* XXX: watch it with the paintmode! in future,
2394 * it'd be nice to allow changing paint-mode when in sketching-sessions */
2395
2396 if (annotation_session_initdata(C, p)) {
2398 }
2399
2400 if (p->status != GP_STATUS_ERROR) {
2402 op->flag &= ~OP_IS_MODAL_CURSOR_REGION;
2403 }
2404
2405 return static_cast<tGPsdata *>(op->customdata);
2406}
2407
2409{
2410 tGPsdata *p = static_cast<tGPsdata *>(op->customdata);
2411
2413
2415
2417
2420
2421 p->gpd = nullptr;
2422 p->gpl = nullptr;
2423 p->gpf = nullptr;
2424}
2425
2426/* add events for missing mouse movements when the artist draw very fast */
2428 wmOperator *op,
2429 const wmEvent *event,
2430 tGPsdata *p)
2431{
2433 float pt[2], a[2], b[2];
2434 float factor = 10.0f;
2435
2436 copy_v2_v2(a, p->mvalo);
2437 b[0] = float(event->mval[0]) + 1.0f;
2438 b[1] = float(event->mval[1]) + 1.0f;
2439
2440 /* get distance in pixels */
2441 float dist = len_v2v2(a, b);
2442
2443 /* for very small distances, add a half way point */
2444 if (dist <= 2.0f) {
2445 interp_v2_v2v2(pt, a, b, 0.5f);
2446 sub_v2_v2v2(pt, b, pt);
2447 /* create fake event */
2448 annotation_draw_apply_event(op, event, depsgraph, pt[0], pt[1]);
2449 }
2450 else if (dist >= factor) {
2451 int slices = 2 + int((dist - 1.0) / factor);
2452 float n = 1.0f / slices;
2453 for (int i = 1; i < slices; i++) {
2454 interp_v2_v2v2(pt, a, b, n * i);
2455 sub_v2_v2v2(pt, b, pt);
2456 /* create fake event */
2457 annotation_draw_apply_event(op, event, depsgraph, pt[0], pt[1]);
2458 }
2459 }
2460}
2461
2462/* events handling during interactive drawing part of operator */
2463static int annotation_draw_modal(bContext *C, wmOperator *op, const wmEvent *event)
2464{
2465 tGPsdata *p = static_cast<tGPsdata *>(op->customdata);
2466 /* Default exit state - pass through to support MMB view navigation, etc. */
2467 int estate = OPERATOR_PASS_THROUGH;
2468
2469/* NOTE(mike erwin): Not quite what I was looking for, but a good start!
2470 * grease-pencil continues to draw on the screen while the 3D mouse moves the viewpoint.
2471 * Problem is that the stroke is converted to 3D only after it is finished.
2472 * This approach should work better in tools that immediately apply in 3D space. */
2473#if 0
2474 if (event->type == NDOF_MOTION) {
2475 return OPERATOR_PASS_THROUGH;
2476 }
2477#endif
2478
2479 if (p->status == GP_STATUS_IDLING) {
2480 ARegion *region = CTX_wm_region(C);
2481 p->region = region;
2482 }
2483
2484 /* We don't pass on key events, GP is used with key-modifiers -
2485 * prevents D-key to insert drivers. */
2486 if (ISKEYBOARD(event->type)) {
2487 if (ELEM(event->type,
2492 EVT_ZKEY))
2493 {
2494 /* allow some keys:
2495 * - For frame changing #33412.
2496 * - For undo (during sketching sessions).
2497 */
2498 }
2499 else if (ELEM(event->type,
2500 EVT_PAD0,
2501 EVT_PAD1,
2502 EVT_PAD2,
2503 EVT_PAD3,
2504 EVT_PAD4,
2505 EVT_PAD5,
2506 EVT_PAD6,
2507 EVT_PAD7,
2508 EVT_PAD8,
2509 EVT_PAD9))
2510 {
2511 /* Allow numpad keys so that camera/view manipulations can still take place
2512 * - #EVT_PAD0 in particular is really important for Grease Pencil drawing,
2513 * as animators may be working "to camera", so having this working
2514 * is essential for ensuring that they can quickly return to that view.
2515 */
2516 }
2517 else if ((event->type == EVT_BKEY) && (event->val == KM_RELEASE)) {
2518 /* Add Blank Frame
2519 * - Since this operator is non-modal, we can just call it here, and keep going...
2520 * - This operator is especially useful when animating
2521 */
2523 C, "GPENCIL_OT_layer_annotation_add", WM_OP_EXEC_DEFAULT, nullptr, event);
2524 estate = OPERATOR_RUNNING_MODAL;
2525 }
2526 else {
2527 estate = OPERATOR_RUNNING_MODAL;
2528 }
2529 }
2530
2531 /* Exit painting mode (and/or end current stroke)
2532 *
2533 * NOTE: cannot do RIGHTMOUSE (as is standard for canceling)
2534 * as that would break polyline #32647.
2535 */
2536 if (event->val == KM_PRESS &&
2538 {
2539 /* exit() ends the current stroke before cleaning up */
2541 estate = OPERATOR_FINISHED;
2542 }
2543
2544 /* Toggle painting mode upon mouse-button movement
2545 * - #RIGHTMOUSE: eraser (all).
2546 * (Disabling #RIGHTMOUSE case here results in bugs like #32647)
2547 * - Others (typically LMB): standard drawing (all) / straight line drawing (all).
2548 * Also making sure we have a valid event value, to not exit too early. */
2549
2550 if (ISMOUSE_BUTTON(event->type) && ELEM(event->val, KM_PRESS, KM_RELEASE)) {
2551 /* if painting, end stroke */
2552 if (p->status == GP_STATUS_PAINTING) {
2553 int sketch = 0;
2554
2555 /* basically, this should be mouse-button up = end stroke
2556 * BUT, polyline drawing is an exception -- all knots should be added during one session
2557 */
2558 sketch |= (p->paintmode == GP_PAINTMODE_DRAW_POLY);
2559
2560 if (sketch) {
2561 /* end stroke only, and then wait to resume painting soon */
2563
2564 /* If eraser mode is on, turn it off after the stroke finishes
2565 * NOTE: This just makes it nicer to work with drawing sessions
2566 */
2567 if (p->paintmode == GP_PAINTMODE_ERASER) {
2568 p->paintmode = eGPencil_PaintModes(RNA_enum_get(op->ptr, "mode"));
2569
2570 /* if the original mode was *still* eraser,
2571 * we'll let it say for now, since this gives
2572 * users an opportunity to have visual feedback
2573 * when adjusting eraser size
2574 */
2575 if (p->paintmode != GP_PAINTMODE_ERASER) {
2576 /* turn off cursor...
2577 * NOTE: this should be enough for now
2578 * Just hiding this makes it seem like
2579 * you can paint again...
2580 */
2582 }
2583 }
2584
2585 /* we've just entered idling state, so this event was processed (but no others yet) */
2586 estate = OPERATOR_RUNNING_MODAL;
2587
2588 /* stroke could be smoothed, send notifier to refresh screen */
2590 }
2591 else {
2593 estate = OPERATOR_FINISHED;
2594 }
2595 }
2596 else if (event->val == KM_PRESS) {
2597 bool in_bounds = false;
2598
2599 /* Check if we're outside the bounds of the active region
2600 * NOTE: An exception here is that if launched from the toolbar,
2601 * whatever region we're now in should become the new region
2602 */
2603 if ((p->region) && (p->region->regiontype == RGN_TYPE_TOOLS)) {
2604 /* Change to whatever region is now under the mouse */
2605 ARegion *current_region = BKE_area_find_region_xy(p->area, RGN_TYPE_ANY, event->xy);
2606
2607 if (current_region) {
2608 /* Assume that since we found the cursor in here, it is in bounds
2609 * and that this should be the region that we begin drawing in
2610 */
2611 p->region = current_region;
2612 in_bounds = true;
2613 }
2614 else {
2615 /* Out of bounds, or invalid in some other way */
2617 estate = OPERATOR_CANCELLED;
2618 }
2619 }
2620 else if (p->region) {
2621 /* Perform bounds check. */
2622 const rcti *region_rect = ED_region_visible_rect(p->region);
2623 in_bounds = BLI_rcti_isect_pt_v(region_rect, event->mval);
2624 }
2625 else {
2626 /* No region */
2628 estate = OPERATOR_CANCELLED;
2629 }
2630
2631 if (in_bounds) {
2632 /* Switch paintmode (temporarily if need be) based on which button was used
2633 * NOTE: This is to make it more convenient to erase strokes when using drawing sessions
2634 */
2635 if ((event->type == RIGHTMOUSE) || annotation_is_tablet_eraser_active(event)) {
2636 /* turn on eraser */
2638 }
2639 else { /* Any mouse button besides right. */
2640 /* restore drawmode to default */
2641 p->paintmode = eGPencil_PaintModes(RNA_enum_get(op->ptr, "mode"));
2642 }
2643
2645
2646 /* not painting, so start stroke (this should be mouse-button down) */
2647 p = annotation_stroke_begin(C, op);
2648
2649 if (p->status == GP_STATUS_ERROR) {
2650 estate = OPERATOR_CANCELLED;
2651 }
2652 }
2653 else if (p->status != GP_STATUS_ERROR) {
2654 /* User clicked outside bounds of window while idling, so exit paintmode
2655 * NOTE: Don't enter this case if an error occurred while finding the
2656 * region (as above)
2657 */
2659 estate = OPERATOR_FINISHED;
2660 }
2661 }
2662 else if (event->val == KM_RELEASE) {
2665 }
2666 }
2667
2668 /* handle mode-specific events */
2669 if (p->status == GP_STATUS_PAINTING) {
2670 /* handle painting mouse-movements? */
2671 if (ISMOUSE_MOTION(event->type) || (p->flags & GP_PAINTFLAG_FIRSTRUN)) {
2672 /* handle drawing event */
2673 if ((p->flags & GP_PAINTFLAG_FIRSTRUN) == 0) {
2674 annotation_add_missing_events(C, op, event, p);
2675 }
2676
2677 /* TODO(sergey): Possibly evaluating dependency graph from modal operator? */
2679
2680 /* finish painting operation if anything went wrong just now */
2681 if (p->status == GP_STATUS_ERROR) {
2682 printf("\t\t\t\tGP - add error done!\n");
2683 estate = OPERATOR_CANCELLED;
2684 }
2685 else {
2686 /* event handled, so just tag as running modal */
2687 estate = OPERATOR_RUNNING_MODAL;
2688 }
2689 }
2690 /* eraser size */
2691 else if ((p->paintmode == GP_PAINTMODE_ERASER) &&
2693 {
2694 /* just resize the brush (local version)
2695 * TODO: fix the hardcoded size jumps (set to make a visible difference) and hardcoded keys
2696 */
2697 switch (event->type) {
2698 case WHEELDOWNMOUSE: /* larger */
2699 case EVT_PADPLUSKEY:
2700 p->radius += 5;
2701 break;
2702
2703 case WHEELUPMOUSE: /* smaller */
2704 case EVT_PADMINUS:
2705 p->radius -= 5;
2706
2707 if (p->radius <= 0) {
2708 p->radius = 1;
2709 }
2710 break;
2711 }
2712
2713 /* force refresh */
2714 /* just active area for now, since doing whole screen is too slow */
2716
2717 /* event handled, so just tag as running modal */
2718 estate = OPERATOR_RUNNING_MODAL;
2719 }
2720 /* there shouldn't be any other events, but just in case there are, let's swallow them
2721 * (i.e. to prevent problems with undo)
2722 */
2723 else {
2724 /* swallow event to save ourselves trouble */
2725 estate = OPERATOR_RUNNING_MODAL;
2726 }
2727 }
2728
2729 /* gpencil modal operator stores area, which can be removed while using it (like full-screen). */
2730 if (0 == annotation_area_exists(C, p->area)) {
2731 estate = OPERATOR_CANCELLED;
2732 }
2733 else {
2734 /* update status indicators - cursor, header, etc. */
2736 /* cursor may have changed outside our control - #44084 */
2738 }
2739
2740 /* process last operations before exiting */
2741 switch (estate) {
2742 case OPERATOR_FINISHED:
2743 /* one last flush before we're done */
2744 annotation_draw_exit(C, op);
2746 break;
2747
2748 case OPERATOR_CANCELLED:
2749 annotation_draw_exit(C, op);
2750 break;
2751
2753 /* event doesn't need to be handled */
2754 break;
2755 }
2756
2757 /* return status code */
2758 return estate;
2759}
2760
2761/* ------------------------------- */
2762
2764 {GP_PAINTMODE_DRAW, "DRAW", 0, "Draw Freehand", "Draw freehand stroke(s)"},
2766 "DRAW_STRAIGHT",
2767 0,
2768 "Draw Straight Lines",
2769 "Draw straight line segment(s)"},
2771 "DRAW_POLY",
2772 0,
2773 "Draw Poly Line",
2774 "Click to place endpoints of straight line segments (connected)"},
2775 {GP_PAINTMODE_ERASER, "ERASER", 0, "Eraser", "Erase Annotation strokes"},
2776 {0, nullptr, 0, nullptr, nullptr},
2777};
2778
2780 {GP_STROKE_ARROWSTYLE_NONE, "NONE", 0, "None", "Don't use any arrow/style in corner"},
2781 {GP_STROKE_ARROWSTYLE_CLOSED, "ARROW", 0, "Arrow", "Use closed arrow style"},
2782 {GP_STROKE_ARROWSTYLE_OPEN, "ARROW_OPEN", 0, "Open Arrow", "Use open arrow style"},
2784 "ARROW_OPEN_INVERTED",
2785 0,
2786 "Segment",
2787 "Use perpendicular segment style"},
2788 {GP_STROKE_ARROWSTYLE_SQUARE, "DIAMOND", 0, "Square", "Use square style"},
2789 {0, nullptr, 0, nullptr, nullptr},
2790};
2791
2793{
2794 PropertyRNA *prop;
2795
2796 /* identifiers */
2797 ot->name = "Annotation Draw";
2798 ot->idname = "GPENCIL_OT_annotate";
2799 ot->description = "Make annotations on the active data";
2800
2801 /* api callbacks */
2807
2808 /* flags */
2810
2811 /* settings for drawing */
2812 ot->prop = RNA_def_enum(
2813 ot->srna, "mode", prop_gpencil_drawmodes, 0, "Mode", "Way to interpret mouse movements");
2814
2815 /* properties */
2816 prop = RNA_def_enum(
2817 ot->srna, "arrowstyle_start", arrow_types, 0, "Start Arrow Style", "Stroke start style");
2818 prop = RNA_def_enum(
2819 ot->srna, "arrowstyle_end", arrow_types, 0, "End Arrow Style", "Stroke end style");
2820 prop = RNA_def_boolean(ot->srna,
2821 "use_stabilizer",
2822 false,
2823 "Stabilize Stroke",
2824 "Helper to draw smooth and clean lines. Press Shift for an invert effect "
2825 "(even if this option is not active)");
2826 prop = RNA_def_float(ot->srna,
2827 "stabilizer_factor",
2828 0.75f,
2829 0.0f,
2830 1.0f,
2831 "Stabilizer Stroke Factor",
2832 "Higher values gives a smoother stroke",
2833 0.0f,
2834 1.0f);
2835 prop = RNA_def_int(ot->srna,
2836 "stabilizer_radius",
2837 35,
2838 0,
2839 200,
2840 "Stabilizer Stroke Radius",
2841 "Minimum distance from last point before stroke continues",
2842 1,
2843 100);
2845
2846 prop = RNA_def_collection_runtime(ot->srna, "stroke", &RNA_OperatorStrokeElement, "Stroke", "");
2848
2849 /* NOTE: wait for input is enabled by default,
2850 * so that all UI code can work properly without needing users to know about this */
2851 prop = RNA_def_boolean(ot->srna,
2852 "wait_for_input",
2853 true,
2854 "Wait for Input",
2855 "Wait for first click instead of painting immediately");
2857}
@ BKE_CB_EVT_ANNOTATION_POST
@ BKE_CB_EVT_ANNOTATION_PRE
void BKE_callback_exec_id_depsgraph(Main *bmain, ID *id, Depsgraph *depsgraph, eCbEvent evt)
Definition callbacks.cc:53
bScreen * CTX_wm_screen(const bContext *C)
void CTX_wm_operator_poll_msg_set(bContext *C, const char *msg)
Depsgraph * CTX_data_ensure_evaluated_depsgraph(const bContext *C)
ScrArea * CTX_wm_area(const bContext *C)
wmWindow * CTX_wm_window(const bContext *C)
Scene * CTX_data_scene(const bContext *C)
Main * CTX_data_main(const bContext *C)
ToolSettings * CTX_data_tool_settings(const bContext *C)
ARegion * CTX_wm_region(const bContext *C)
struct bGPDstroke * BKE_gpencil_stroke_delete_tagged_points(struct bGPdata *gpd, struct bGPDframe *gpf, struct bGPDstroke *gps, struct bGPDstroke *next_stroke, int tag_flags, bool select, bool flat_cap, int limit)
void BKE_gpencil_free_stroke_weights(struct bGPDstroke *gps)
struct bGPDlayer * BKE_gpencil_layer_addnew(struct bGPdata *gpd, const char *name, bool setactive, bool add_to_header)
bool BKE_gpencil_layer_is_editable(const struct bGPDlayer *gpl)
struct bGPDframe * BKE_gpencil_layer_frame_get(struct bGPDlayer *gpl, int cframe, eGP_GetFrame_Mode addnew)
struct bGPDlayer * BKE_gpencil_layer_active_get(struct bGPdata *gpd)
struct bGPDstroke * BKE_gpencil_stroke_duplicate(struct bGPDstroke *gps_src, bool dup_points, bool dup_curve)
#define GPENCIL_STRENGTH_MIN
struct bGPdata * BKE_gpencil_data_addnew(struct Main *bmain, const char name[])
eGP_GetFrame_Mode
@ GP_GETFRAME_ADD_NEW
@ GP_GETFRAME_ADD_COPY
void BKE_report(ReportList *reports, eReportType type, const char *message)
Definition report.cc:125
ARegion * BKE_area_find_region_xy(const ScrArea *area, int regiontype, const int xy[2]) ATTR_NONNULL(3)
Definition screen.cc:844
struct MovieTrackingMarker * BKE_tracking_marker_get(struct MovieTrackingTrack *track, int framenr)
Definition tracking.cc:1358
struct MovieTrackingObject * BKE_tracking_object_get_active(const struct MovieTracking *tracking)
#define BLI_assert(a)
Definition BLI_assert.h:50
void BLI_freelinkN(struct ListBase *listbase, void *vlink) ATTR_NONNULL(1)
Definition listbase.cc:269
void BLI_addtail(struct ListBase *listbase, void *vlink) ATTR_NONNULL(1)
Definition listbase.cc:110
int BLI_findindex(const struct ListBase *listbase, const void *vlink) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL(1)
MINLINE float min_ff(float a, float b)
MINLINE int max_ii(int a, int b)
int interp_sparse_array(float *array, int list_size, float skipval)
void unit_m4(float m[4][4])
Definition rct.c:1127
void copy_m4_m4(float m1[4][4], const float m2[4][4])
void mul_v3_m4v3(float r[3], const float mat[4][4], const float vec[3])
bool invert_m4_m4(float inverse[4][4], const float mat[4][4])
MINLINE void round_v2i_v2fl(int r[2], const float a[2])
MINLINE void madd_v2_v2fl(float r[2], const float a[2], float f)
MINLINE float len_v2v2_int(const int v1[2], const int v2[2])
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 mul_v2_fl(float r[2], float f)
void interp_v2_v2v2(float r[2], const float a[2], const float b[2], float t)
Definition math_vector.c:21
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 void copy_v3_v3(float r[3], const float a[3])
MINLINE void add_v2_v2(float r[2], const float a[2])
MINLINE void sub_v2_v2v2(float r[2], const float a[2], const float b[2])
MINLINE float normalize_v2(float n[2])
MINLINE void zero_v3(float r[3])
MINLINE void mul_v3_v3fl(float r[3], const float a[3], float f)
MINLINE void copy_v2_fl(float r[2], float f)
bool BLI_rcti_isect_pt_v(const struct rcti *rect, const int xy[2])
bool BLI_rcti_isect_pt(const struct rcti *rect, int x, int y)
BLI_INLINE float BLI_rctf_size_x(const struct rctf *rct)
Definition BLI_rect.h:197
BLI_INLINE float BLI_rctf_size_y(const struct rctf *rct)
Definition BLI_rect.h:201
unsigned int uint
Platform independent time functions.
double BLI_time_now_seconds(void)
Definition time.c:65
#define CLAMP(a, b, c)
#define ENUM_OPERATORS(_type, _max)
#define ELEM(...)
#define IFACE_(msgid)
#define DATA_(msgid)
typedef double(DMatrix)[4][4]
@ GP_STROKE_ARROWSTYLE_NONE
@ GP_STROKE_ARROWSTYLE_SEGMENT
@ GP_STROKE_ARROWSTYLE_CLOSED
@ GP_STROKE_ARROWSTYLE_OPEN
@ GP_STROKE_ARROWSTYLE_SQUARE
@ GP_STROKE_USE_ARROW_END
@ GP_STROKE_USE_ARROW_START
Object is a sort of wrapper for general info.
@ GP_PROJECT_VIEWSPACE
@ GP_PROJECT_DEPTH_VIEW
@ GP_PROJECT_DEPTH_STROKE_ENDPOINTS
@ GP_PROJECT_DEPTH_STROKE
@ GP_PROJECT_DEPTH_ONLY_SELECTED
@ GP_TOOL_FLAG_RETAIN_LAST
@ RGN_TYPE_PREVIEW
@ RGN_TYPE_TOOLS
#define RGN_TYPE_ANY
@ SI_SHOW_GPENCIL
@ SNODE_SHOW_GPENCIL
@ SPACE_CLIP
@ SPACE_NODE
@ SPACE_SEQ
@ SPACE_IMAGE
@ SPACE_VIEW3D
@ SEQ_PREVIEW_SHOW_GPENCIL
@ SC_SHOW_ANNOTATION
@ SEQ_DRAW_IMG_IMBUF
@ SC_GPENCIL_SRC_TRACK
#define SPACE_TYPE_ANY
@ RV3D_CAMOB
@ V3D_SHOW_ANNOTATION
@ OP_IS_MODAL_CURSOR_REGION
@ OPERATOR_RUNNING_MODAL
@ OPERATOR_PASS_THROUGH
MovieClip * ED_space_clip_get_clip(const SpaceClip *sc)
int ED_space_clip_get_clip_frame_number(const SpaceClip *sc)
bool ED_operator_regionactive(bContext *C)
Definition screen_ops.cc:94
const rcti * ED_region_visible_rect(ARegion *region)
Definition area.cc:4010
void ED_workspace_status_text(bContext *C, const char *str)
Definition area.cc:966
void ED_region_tag_redraw(ARegion *region)
Definition area.cc:634
bool ED_view3d_depth_read_cached(const ViewDepths *vd, const int mval[2], int margin, float *r_depth)
void view3d_region_operator_needs_opengl(wmWindow *win, ARegion *region)
bool ED_view3d_depth_read_cached_seg(const ViewDepths *vd, const int mval_sta[2], const int mval_end[2], int margin, float *r_depth)
@ V3D_PROJ_TEST_NOP
Definition ED_view3d.hh:266
float ED_view3d_calc_depth_for_comparison(const RegionView3D *rv3d, const float co[3])
@ V3D_PROJ_RET_OK
Definition ED_view3d.hh:243
void ED_view3d_depth_override(Depsgraph *depsgraph, ARegion *region, View3D *v3d, Object *obact, eV3DDepthOverrideMode mode, ViewDepths **r_depths)
void ED_view3d_win_to_delta(const ARegion *region, const float xy_delta[2], float zfac, float r_out[3])
float ED_view3d_calc_zfac(const RegionView3D *rv3d, const float co[3])
void ED_view3d_depths_free(ViewDepths *depths)
eV3DDepthOverrideMode
Definition ED_view3d.hh:184
@ V3D_DEPTH_SELECTED_ONLY
Definition ED_view3d.hh:194
@ V3D_DEPTH_NO_GPENCIL
Definition ED_view3d.hh:188
@ V3D_DEPTH_NO_OVERLAYS
Definition ED_view3d.hh:186
@ V3D_DEPTH_GPENCIL_ONLY
Definition ED_view3d.hh:190
bool ED_view3d_autodist_simple(ARegion *region, const int mval[2], float mouse_worldloc[3], int margin, const float *force_depth)
void ED_view3d_calc_camera_border(const Scene *scene, const Depsgraph *depsgraph, const ARegion *region, const View3D *v3d, const RegionView3D *rv3d, bool no_shift, rctf *r_viewborder)
bool ED_view3d_depth_unproject_v3(const ARegion *region, const int mval[2], double depth, float r_location_world[3])
eV3DProjStatus ED_view3d_project_float_global(const ARegion *region, const float co[3], float r_co[2], eV3DProjTest flag)
void immEnd()
void immUnbindProgram()
void immUniform2f(const char *name, float x, float y)
void immUniformColor4f(float r, float g, float b, float a)
void immVertex2f(uint attr_id, float x, float y)
void immBindBuiltinProgram(eGPUBuiltinShader shader_id)
void immUniform1i(const char *name, int x)
void immUniform1f(const char *name, float x)
void immUniformColor4ub(unsigned char r, unsigned char g, unsigned char b, unsigned char a)
GPUVertFormat * immVertexFormat()
void immBegin(GPUPrimType, uint vertex_len)
void imm_draw_circle_fill_2d(uint shdr_pos, float x, float y, float radius, int nsegments)
void imm_draw_circle_wire_2d(uint shdr_pos, float x, float y, float radius, int nsegments)
@ GPU_PRIM_LINES
@ GPU_SHADER_3D_LINE_DASHED_UNIFORM_COLOR
@ GPU_SHADER_3D_UNIFORM_COLOR
@ GPU_BLEND_NONE
Definition GPU_state.hh:85
@ GPU_BLEND_ALPHA
Definition GPU_state.hh:87
void GPU_blend(eGPUBlend blend)
Definition gpu_state.cc:42
void GPU_line_width(float width)
Definition gpu_state.cc:161
void GPU_line_smooth(bool enable)
Definition gpu_state.cc:78
void GPU_viewport_size_get_f(float coords[4])
Definition gpu_state.cc:262
@ GPU_FETCH_FLOAT
uint GPU_vertformat_attr_add(GPUVertFormat *, const char *name, GPUVertCompType, uint comp_len, GPUVertFetchMode)
@ GPU_COMP_F32
Read Guarded memory(de)allocation.
#define MEM_reallocN(vmemh, len)
#define RNA_BEGIN(sptr, itemptr, propname)
#define RNA_END
@ PROP_SKIP_SAVE
Definition RNA_types.hh:245
@ PROP_HIDDEN
Definition RNA_types.hh:239
@ PROP_PIXEL
Definition RNA_types.hh:151
void UI_view2d_region_to_view(const View2D *v2d, float x, float y, float *r_view_x, float *r_view_y) ATTR_NONNULL()
Definition view2d.cc:1663
#define V2D_IS_CLIPPED
Definition UI_view2d.hh:21
@ OPTYPE_BLOCKING
Definition WM_types.hh:164
@ OPTYPE_UNDO
Definition WM_types.hh:162
@ KM_PRESS
Definition WM_types.hh:284
@ KM_RELEASE
Definition WM_types.hh:285
#define NA_EDITED
Definition WM_types.hh:550
#define NC_GPENCIL
Definition WM_types.hh:366
@ WM_OP_EXEC_DEFAULT
Definition WM_types.hh:225
@ KM_CTRL
Definition WM_types.hh:256
@ KM_ALT
Definition WM_types.hh:257
@ KM_SHIFT
Definition WM_types.hh:255
void GPENCIL_OT_annotate(wmOperatorType *ot)
eGPencil_PaintStatus
@ GP_STATUS_PAINTING
@ GP_STATUS_IDLING
@ GP_STATUS_CAPTURE
@ GP_STATUS_ERROR
@ GP_STATUS_DONE
static void annotation_stroke_arrow_calc_points(tGPspoint *point, const float stroke_dir[2], float corner[2], float stroke_points[8], const int arrow_style)
static short annotation_stroke_addpoint(tGPsdata *p, const float mval[2], float pressure, double curtime)
static void annotation_arrow_create(tGPsdata *p, tGPspoint *ptc, bGPDspoint *pt, bGPDstroke *arrow_stroke, const float arrow_points[8], const int style)
static void annotation_arrow_create_square(tGPsdata *p, tGPspoint *ptc, bGPDspoint *pt, const float corner_point[3], const float arrow_points[8])
static bool annotation_stroke_eraser_is_occluded(tGPsdata *p, const bGPDspoint *pt, const int x, const int y)
static void annotation_stroke_eraser_dostroke(tGPsdata *p, bGPDframe *gpf, bGPDstroke *gps, const float mval[2], const int radius, const rcti *rect)
static void annotation_get_3d_reference(tGPsdata *p, float vec[3])
static void annotation_smooth_buffer(tGPsdata *p, float inf, int idx)
static bool annotation_session_initdata(bContext *C, tGPsdata *p)
eGPencil_PaintFlags
@ GP_PAINTFLAG_SELECTMASK
@ GP_PAINTFLAG_USE_STABILIZER
@ GP_PAINTFLAG_FIRSTRUN
@ GP_PAINTFLAG_STROKEADDED
@ GP_PAINTFLAG_V3D_ERASER_DEPTH
@ GP_PAINTFLAG_USE_STABILIZER_TEMP
static void annotation_stroke_end(wmOperator *op)
static int annotation_draw_modal(bContext *C, wmOperator *op, const wmEvent *event)
static void annotation_stroke_newfrombuffer(tGPsdata *p)
static bool annotation_draw_poll(bContext *C)
static bool annotation_stroke_filtermval(tGPsdata *p, const float mval[2], const float pmval[2])
static int annotation_draw_invoke(bContext *C, wmOperator *op, const wmEvent *event)
static void annotation_arrow_create_segm(tGPsdata *p, tGPspoint *ptc, bGPDspoint *pt, const float arrow_points[8])
static void annotation_stroke_added_enable(tGPsdata *p)
static bool annotation_area_exists(bContext *C, ScrArea *area_test)
static void annotation_add_missing_events(bContext *C, wmOperator *op, const wmEvent *event, tGPsdata *p)
static void annotation_paint_initstroke(tGPsdata *p, eGPencil_PaintModes paintmode, Depsgraph *depsgraph)
static void annotation_stroke_arrow_init_point(tGPsdata *p, tGPspoint *ptc, bGPDspoint *pt, const float co[8], const int co_idx)
static void annotation_draw_stabilizer(bContext *C, int x, int y, void *p_ptr)
static void annotation_draw_exit(bContext *C, wmOperator *op)
static bool annotation_stroke_added_check(tGPsdata *p)
static void annotation_stroke_arrow_allocate(bGPDstroke *gps, const int totpoints)
static void annotation_stroke_arrow_calc_points_segment(float stroke_points[8], const float ref_point[2], const float dir_cw[2], const float dir_ccw[2], const float length, const float sign)
static void annotation_paint_strokeend(tGPsdata *p)
static void annotation_draw_toggle_stabilizer_cursor(tGPsdata *p, short enable)
static bool annotation_project_check(tGPsdata *p)
static bool annotation_is_tablet_eraser_active(const wmEvent *event)
static void annotation_draw_apply(wmOperator *op, tGPsdata *p, Depsgraph *depsgraph)
static int annotation_draw_init(bContext *C, wmOperator *op, const wmEvent *event)
static void annotation_session_cleanup(tGPsdata *p)
static int annotation_draw_exec(bContext *C, wmOperator *op)
static void annotation_draw_apply_event(wmOperator *op, const wmEvent *event, Depsgraph *depsgraph, float x, float y)
static void annotation_arrow_create_closed(tGPsdata *p, tGPspoint *ptc, bGPDspoint *pt, const float arrow_points[8])
static tGPsdata * annotation_stroke_begin(bContext *C, wmOperator *op)
#define MIN_EUCLIDEAN_PX
static void annotation_draw_cancel(bContext *C, wmOperator *op)
static void annotation_stroke_convertcoords(tGPsdata *p, const float mval[2], float out[3], const float *depth)
static const EnumPropertyItem arrow_types[]
static void annotation_draw_cursor_set(tGPsdata *p)
static void annotation_draw_status_indicators(bContext *C, tGPsdata *p)
static void annotation_visible_on_space(tGPsdata *p)
#define MIN_MANHATTAN_PX
static void annotation_arrow_create_open(tGPsdata *p, tGPspoint *ptc, bGPDspoint *pt, const float corner_point[3], const float arrow_points[8])
eGP_StrokeAdd_Result
@ GP_STROKEADD_INVALID
@ GP_STROKEADD_FULL
@ GP_STROKEADD_NORMAL
@ GP_STROKEADD_OVERFLOW
static void annotation_session_validatebuffer(tGPsdata *p)
static void annotation_stroke_doeraser(tGPsdata *p)
static void annotation_draw_toggle_eraser_cursor(tGPsdata *p, short enable)
static tGPsdata * annotation_session_initpaint(bContext *C)
static void annotation_session_free(tGPsdata *p)
#define DEPTH_INVALID
static void annotation_stroke_arrow_init_point_default(bGPDspoint *pt)
static void annotation_draw_eraser(bContext *, int x, int y, void *p_ptr)
static const EnumPropertyItem prop_gpencil_drawmodes[]
static void annotation_free_stroke(bGPDframe *gpf, bGPDstroke *gps)
static void annotation_paint_cleanup(tGPsdata *p)
static void annotation_stroke_arrow_init_conv_point(bGPDspoint *pt, const float point[3])
unsigned int U
Definition btGjkEpa3.h:78
local_group_size(16, 16) .push_constant(Type b
#define printf
const Depsgraph * depsgraph
#define fabsf(x)
draw_view in_light_buf[] float
draw_view push_constant(Type::INT, "radiance_src") .push_constant(Type capture_info_buf storage_buf(1, Qualifier::READ, "ObjectBounds", "bounds_buf[]") .push_constant(Type draw_view int
void gpencil_undo_push(bGPdata *gpd)
void gpencil_undo_init(bGPdata *gpd)
eGPencil_PaintModes
@ GP_PAINTMODE_DRAW_POLY
@ GP_PAINTMODE_ERASER
@ GP_PAINTMODE_DRAW
@ GP_PAINTMODE_DRAW_STRAIGHT
void gpencil_undo_finish()
void gpencil_point_to_xy(const GP_SpaceConversion *gsc, const bGPDstroke *gps, const bGPDspoint *pt, int *r_x, int *r_y)
bool gpencil_stroke_inside_circle(const float mval[2], int rad, int x0, int y0, int x1, int y1)
int ED_gpencil_session_active()
bool ED_gpencil_stroke_can_use_direct(const ScrArea *area, const bGPDstroke *gps)
bool ED_gpencil_data_owner_is_annotation(PointerRNA *owner_ptr)
tGPspoint * ED_gpencil_sbuffer_ensure(tGPspoint *buffer_array, int *buffer_size, int *buffer_used, const bool clear)
bGPdata ** ED_annotation_data_get_pointers(const bContext *C, PointerRNA *r_ptr)
format
void *(* MEM_mallocN)(size_t len, const char *str)
Definition mallocn.cc:44
void MEM_freeN(void *vmemh)
Definition mallocn.cc:105
void *(* MEM_callocN)(size_t len, const char *str)
Definition mallocn.cc:42
void RNA_boolean_set(PointerRNA *ptr, const char *name, bool value)
void RNA_float_get_array(PointerRNA *ptr, const char *name, float *values)
void RNA_collection_add(PointerRNA *ptr, const char *name, PointerRNA *r_value)
int RNA_int_get(PointerRNA *ptr, const char *name)
float RNA_float_get(PointerRNA *ptr, const char *name)
void RNA_float_set(PointerRNA *ptr, const char *name, float value)
bool RNA_boolean_get(PointerRNA *ptr, const char *name)
void RNA_enum_set(PointerRNA *ptr, const char *name, int value)
void RNA_float_set_array(PointerRNA *ptr, const char *name, const float *values)
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_collection_runtime(StructOrFunctionRNA *cont_, const char *identifier, StructRNA *type, 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_flag(PropertyRNA *prop, PropertyFlag flag)
void RNA_def_property_subtype(PropertyRNA *prop, PropertySubType subtype)
PropertyRNA * RNA_def_int(StructOrFunctionRNA *cont_, const char *identifier, const int default_value, const int hardmin, const int hardmax, const char *ui_name, const char *ui_description, const int softmin, const int softmax)
static const int steps
void * regiondata
void * last
void * first
MovieTrackingTrack * active_track
struct ToolSettings * toolsettings
View3DCursor cursor
ListBase spacedata
float unistabmat[4][4]
bGPDtriangle * triangles
struct MDeformVert * dvert
struct bGPDstroke * next
bGPdata_Runtime runtime
float xmin
float ymin
int ymin
int ymax
int xmin
int xmax
bGPDframe * gpf
float mat[4][4]
ScrArea * area
short straight[2]
float stabilizer_factor
Scene * scene
PointerRNA ownerPtr
wmWindow * win
eGPencil_PaintStatus status
Depsgraph * depsgraph
eGPencil_PaintModes paintmode
short keymodifier
bGPDlayer * gpl
const rctf * subrect
double inittime
View2D * v2d
eGPencil_PaintFlags flags
float custom_color[4]
double ocurtime
ARegion * region
ViewDepths * depths
GP_SpaceConversion gsc
char * align_flag
float mval[2]
bGPdata * gpd
char stabilizer_radius
float mvalo[2]
void * stabilizer_cursor
float imat[4][4]
void * erasercursor
short val
Definition WM_types.hh:724
int xy[2]
Definition WM_types.hh:726
int mval[2]
Definition WM_types.hh:728
uint8_t modifier
Definition WM_types.hh:739
wmTabletData tablet
Definition WM_types.hh:751
short type
Definition WM_types.hh:722
const char * name
Definition WM_types.hh:990
bool(* poll)(bContext *C) ATTR_WARN_UNUSED_RESULT
Definition WM_types.hh:1042
const char * idname
Definition WM_types.hh:992
int(* modal)(bContext *C, wmOperator *op, const wmEvent *event) ATTR_WARN_UNUSED_RESULT
Definition WM_types.hh:1036
int(* invoke)(bContext *C, wmOperator *op, const wmEvent *event) ATTR_WARN_UNUSED_RESULT
Definition WM_types.hh:1022
int(* exec)(bContext *C, wmOperator *op) ATTR_WARN_UNUSED_RESULT
Definition WM_types.hh:1006
const char * description
Definition WM_types.hh:996
PropertyRNA * prop
Definition WM_types.hh:1092
StructRNA * srna
Definition WM_types.hh:1080
void(* cancel)(bContext *C, wmOperator *op)
Definition WM_types.hh:1028
struct ReportList * reports
struct PointerRNA * ptr
ccl_device_inline int abs(int x)
Definition util/math.h:120
void WM_cursor_modal_set(wmWindow *win, int val)
void WM_cursor_modal_restore(wmWindow *win)
@ WM_CURSOR_PAINT_BRUSH
Definition wm_cursors.hh:33
@ WM_CURSOR_ERASER
Definition wm_cursors.hh:34
void WM_event_drag_start_mval_fl(const wmEvent *event, const ARegion *region, float r_mval[2])
wmEventHandler_Op * WM_event_add_modal_handler(bContext *C, wmOperator *op)
void WM_event_add_notifier(const bContext *C, uint type, void *reference)
int WM_operator_name_call(bContext *C, const char *opstring, wmOperatorCallContext context, PointerRNA *properties, const wmEvent *event)
#define ISMOUSE_BUTTON(event_type)
#define ISMOUSE_MOTION(event_type)
#define ISKEYBOARD(event_type)
@ EVT_PAD8
@ EVT_PAD2
@ RIGHTMOUSE
@ EVT_EKEY
@ EVT_PAD4
@ EVT_PAD0
@ EVT_PAD9
@ EVT_DOWNARROWKEY
@ EVT_PAD3
@ WHEELUPMOUSE
@ EVT_RIGHTARROWKEY
@ EVT_PADENTER
@ EVT_SPACEKEY
@ WHEELDOWNMOUSE
@ EVT_PAD6
@ EVT_PAD5
@ EVT_PADMINUS
@ EVT_UPARROWKEY
@ EVT_LEFTARROWKEY
@ NDOF_MOTION
@ EVT_ZKEY
@ EVT_ESCKEY
@ EVT_PAD1
@ EVT_PAD7
@ EVT_PADPLUSKEY
@ EVT_BKEY
@ EVT_RETKEY
@ EVT_TABLET_NONE
@ EVT_TABLET_ERASER
wmOperatorType * ot
Definition wm_files.cc:4125
bool WM_paint_cursor_end(wmPaintCursor *handle)
wmPaintCursor * WM_paint_cursor_activate(short space_type, short region_type, bool(*poll)(bContext *C), wmPaintCursorDraw draw, void *customdata)