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
8
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 {
661 mode = V3D_DEPTH_ALL;
662 }
663 }
664
666 ED_view3d_depth_override(p->depsgraph, p->region, v3d, nullptr, mode, false, 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
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, false, &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 */
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 */
1616
1617 if (p->area->spacetype == SPACE_VIEW3D) {
1618 if (p->gpl->flag & GP_LAYER_NO_XRAY) {
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_ALL;
1694 }
1695 }
1696 /* need to restore the original projection settings before packing up */
1699 p->depsgraph, p->region, v3d, nullptr, mode, false, 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() */
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 */
1920 return 0;
1921 }
1922
1923 /* init painting data */
1925 if (p->status == GP_STATUS_ERROR) {
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. */
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. */
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) {
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) {
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 */
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) {
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
2398 }
2399
2400 if (p->status != GP_STATUS_ERROR) {
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 */
2746 break;
2747
2748 case OPERATOR_CANCELLED:
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 */
2802 ot->exec = annotation_draw_exec;
2803 ot->invoke = annotation_draw_invoke;
2804 ot->modal = annotation_draw_modal;
2805 ot->cancel = annotation_draw_cancel;
2806 ot->poll = annotation_draw_poll;
2807
2808 /* flags */
2809 ot->flag = OPTYPE_UNDO | OPTYPE_BLOCKING;
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
@ V3D_SHOW_ANNOTATION
@ RV3D_CAMOB
@ 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:4014
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:275
void ED_view3d_depth_override(Depsgraph *depsgraph, ARegion *region, View3D *v3d, Object *obact, eV3DDepthOverrideMode mode, bool use_overlay, ViewDepths **r_depths)
float ED_view3d_calc_depth_for_comparison(const RegionView3D *rv3d, const float co[3])
@ V3D_PROJ_RET_OK
Definition ED_view3d.hh:252
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_ALL
Definition ED_view3d.hh:186
@ V3D_DEPTH_SELECTED_ONLY
Definition ED_view3d.hh:194
@ V3D_DEPTH_NO_GPENCIL
Definition ED_view3d.hh:188
@ 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)
Group Output data from inside of a node group A color picker Mix two input colors RGB to Convert a color s luminance to a grayscale value Generate a normal vector and a dot product Brightness Control the brightness and contrast of the input color Vector Map input vector components with curves Camera Retrieve information about the camera and how it relates to the current shading point s position Clamp a value between a minimum and a maximum Vector Perform vector math operation Invert Invert a color
in reality light always falls off quadratically Particle Retrieve the data of the particle that spawned the object for example to give variation to multiple instances of an object Point Retrieve information about points in a point cloud Retrieve the edges of an object as it appears to Cycles topology will always appear triangulated Convert a blackbody temperature to an RGB value Normal Generate a perturbed normal from an RGB normal map image Typically used for faking highly detailed surfaces Generate an OSL shader from a file or text data block Image Sample an image file as a texture Gabor Generate Gabor noise Gradient Generate interpolated color and intensity values based on the input vector Magic Generate a psychedelic color texture Voronoi Generate Worley noise based on the distance to random points Typically used to generate textures such as or biological cells Brick Generate a procedural texture producing bricks Texture Retrieve multiple types of texture coordinates nTypically used as inputs for texture nodes Vector Convert a point
#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
#define C
Definition RandGen.cpp:29
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
#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_PRESS
Definition WM_types.hh:284
@ KM_RELEASE
Definition WM_types.hh:285
@ 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])
#define U
BPy_StructRNA * depsgraph
SIMD_FORCE_INLINE btScalar length(const btQuaternion &q)
Return the length of a quaternion.
local_group_size(16, 16) .push_constant(Type b
#define printf
#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
struct MovieTracking tracking
MovieTrackingTrack * active_track
struct ToolSettings * toolsettings
struct RenderData r
View3DCursor cursor
ListBase spacedata
float unistabmat[4][4]
bGPDtriangle * triangles
struct MDeformVert * dvert
struct bGPDstroke * next
bGPdata_Runtime runtime
ListBase areabase
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
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)
@ EVT_TABLET_NONE
@ EVT_TABLET_ERASER
@ 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
#define ISKEYBOARD(event_type)
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)