Blender V5.0
gpencil_utils.cc
Go to the documentation of this file.
1/* SPDX-FileCopyrightText: 2014 Blender Authors
2 *
3 * SPDX-License-Identifier: GPL-2.0-or-later */
4
8
9#include <cstdlib>
10#include <cstring>
11
12#include "MEM_guardedalloc.h"
13
14#include "BLI_math_matrix.h"
15#include "BLI_math_vector.h"
16#include "BLI_utildefines.h"
17
19#include "DNA_material_types.h"
20#include "DNA_object_types.h"
21#include "DNA_scene_types.h"
22#include "DNA_screen_types.h"
23#include "DNA_space_types.h"
24#include "DNA_view3d_types.h"
25
26#include "BKE_context.hh"
27#include "BKE_paint.hh"
28#include "BKE_tracking.h"
29
30#include "WM_api.hh"
31#include "WM_toolsystem.hh"
32#include "WM_types.hh"
33
34#include "RNA_access.hh"
35#include "RNA_enum_types.hh"
36#include "RNA_prototypes.hh"
37
38#include "UI_view2d.hh"
39
40#include "ED_clip.hh"
41#include "ED_gpencil_legacy.hh"
42#include "ED_object.hh"
43#include "ED_select_utils.hh"
44#include "ED_view3d.hh"
45
47
48#include "gpencil_intern.hh"
49
50/* ******************************************************** */
51/* Context Wrangling... */
52
54 ScrArea *area,
55 Scene *scene,
56 PointerRNA *r_ptr)
57{
58 /* If there's an active area, check if the particular editor may
59 * have defined any special Grease Pencil context for editing. */
60 if (area) {
61 SpaceLink *sl = static_cast<SpaceLink *>(area->spacedata.first);
62
63 switch (area->spacetype) {
64 case SPACE_INFO: /* header info */
65 {
66 return nullptr;
67 }
68
69 case SPACE_TOPBAR: /* Top-bar */
70 case SPACE_VIEW3D: /* 3D-View */
71 case SPACE_PROPERTIES: /* properties */
72 {
73 if (r_ptr) {
74 *r_ptr = RNA_id_pointer_create(&scene->id);
75 }
76 return &scene->gpd;
77 }
78 case SPACE_NODE: /* Nodes Editor */
79 {
80 SpaceNode *snode = (SpaceNode *)sl;
81
82 /* return the GP data for the active node block/node */
83 if (snode && snode->nodetree) {
84 /* for now, as long as there's an active node tree,
85 * default to using that in the Nodes Editor */
86 if (r_ptr) {
87 *r_ptr = RNA_id_pointer_create(&snode->nodetree->id);
88 }
89 return &snode->nodetree->gpd;
90 }
91
92 /* Even when there is no node-tree, don't allow this to flow to scene. */
93 return nullptr;
94 }
95 case SPACE_SEQ: /* Sequencer */
96 {
97 SpaceSeq *sseq = (SpaceSeq *)sl;
98
99 /* For now, Grease Pencil data is associated with the space
100 * (actually preview region only). */
101 if (r_ptr) {
102 *r_ptr = RNA_pointer_create_discrete(screen_id, &RNA_SpaceSequenceEditor, sseq);
103 }
104 return &sseq->gpd;
105 }
106 case SPACE_IMAGE: /* Image/UV Editor */
107 {
108 SpaceImage *sima = (SpaceImage *)sl;
109
110 /* For now, Grease Pencil data is associated with the space... */
111 if (r_ptr) {
112 *r_ptr = RNA_pointer_create_discrete(screen_id, &RNA_SpaceImageEditor, sima);
113 }
114 return &sima->gpd;
115 }
116 case SPACE_CLIP: /* Nodes Editor */
117 {
118 SpaceClip *sc = (SpaceClip *)sl;
120
121 if (clip) {
124 &clip->tracking);
125 MovieTrackingTrack *track = tracking_object->active_track;
126
127 if (!track) {
128 return nullptr;
129 }
130
131 if (r_ptr) {
132 *r_ptr = RNA_pointer_create_discrete(&clip->id, &RNA_MovieTrackingTrack, track);
133 }
134 return &track->gpd;
135 }
136 if (r_ptr) {
137 *r_ptr = RNA_id_pointer_create(&clip->id);
138 }
139 return &clip->gpd;
140 }
141 break;
142 }
143 default: /* unsupported space */
144 return nullptr;
145 }
146 }
147
148 return nullptr;
149}
150
152{
153 ID *screen_id = (ID *)CTX_wm_screen(C);
154 Scene *scene = CTX_data_scene(C);
155 ScrArea *area = CTX_wm_area(C);
156
157 return ED_annotation_data_get_pointers_direct(screen_id, area, scene, r_ptr);
158}
159/* -------------------------------------------------------- */
160
162{
163 bGPdata **gpd_ptr = ED_annotation_data_get_pointers_direct(screen_id, area, scene, nullptr);
164 return (gpd_ptr) ? *(gpd_ptr) : nullptr;
165}
166
168{
169 bGPdata **gpd_ptr = ED_annotation_data_get_pointers(C, nullptr);
170 return (gpd_ptr) ? *(gpd_ptr) : nullptr;
171}
172
173/* ******************************************************** */
174/* Brush Tool Core */
175
176bool gpencil_stroke_inside_circle(const float mval[2], int rad, int x0, int y0, int x1, int y1)
177{
178 /* simple within-radius check for now */
179 const float screen_co_a[2] = {float(x0), float(y0)};
180 const float screen_co_b[2] = {float(x1), float(y1)};
181
182 if (edge_inside_circle(mval, rad, screen_co_a, screen_co_b)) {
183 return true;
184 }
185
186 /* not inside */
187 return false;
188}
189
190/* ******************************************************** */
191/* Stroke Validity Testing */
192
194{
195 /* sanity check */
196 if (ELEM(nullptr, area, gps)) {
197 return false;
198 }
199
200 /* filter stroke types by flags + spacetype */
201 if (gps->flag & GP_STROKE_3DSPACE) {
202 /* 3D strokes - only in 3D view */
204 }
205 if (gps->flag & GP_STROKE_2DIMAGE) {
206 /* Special "image" strokes - only in Image Editor */
207 return (area->spacetype == SPACE_IMAGE);
208 }
209 if (gps->flag & GP_STROKE_2DSPACE) {
210 /* 2D strokes (data-space) - for any 2D view (i.e. everything other than 3D view). */
211 return (area->spacetype != SPACE_VIEW3D);
212 }
213 /* view aligned - anything goes */
214 return true;
215}
216
217/* ******************************************************** */
218/* Space Conversion */
219
221 const GP_SpaceConversion *gsc, const bGPDstroke *gps, const bGPDspoint *pt, int *r_x, int *r_y)
222{
223 const ARegion *region = gsc->region;
224 const View2D *v2d = gsc->v2d;
225 const rctf *subrect = gsc->subrect;
226 int xyval[2];
227
228 /* sanity checks */
231
232 if (gps->flag & GP_STROKE_3DSPACE) {
234 {
235 *r_x = xyval[0];
236 *r_y = xyval[1];
237 }
238 else {
239 *r_x = V2D_IS_CLIPPED;
240 *r_y = V2D_IS_CLIPPED;
241 }
242 }
243 else if (gps->flag & GP_STROKE_2DSPACE) {
244 float vec[3] = {pt->x, pt->y, 0.0f};
245 mul_m4_v3(gsc->mat, vec);
246 UI_view2d_view_to_region_clip(v2d, vec[0], vec[1], r_x, r_y);
247 }
248 else {
249 if (subrect == nullptr) {
250 /* normal 3D view (or view space) */
251 *r_x = int(pt->x / 100 * region->winx);
252 *r_y = int(pt->y / 100 * region->winy);
253 }
254 else {
255 /* camera view, use subrect */
256 *r_x = int((pt->x / 100) * BLI_rctf_size_x(subrect)) + subrect->xmin;
257 *r_y = int((pt->y / 100) * BLI_rctf_size_y(subrect)) + subrect->ymin;
258 }
259 }
260}
261
263 int *buffer_size,
264 int *buffer_used,
265 const bool clear)
266{
267 tGPspoint *p = nullptr;
268
269 /* By default a buffer is created with one block with a predefined number of free points,
270 * if the size is not enough, the cache is reallocated adding a new block of free points.
271 * This is done in order to keep cache small and improve speed. */
272 if (*buffer_used + 1 > *buffer_size) {
273 if ((*buffer_size == 0) || (buffer_array == nullptr)) {
275 *buffer_size = GP_STROKE_BUFFER_CHUNK;
276 }
277 else {
278 *buffer_size += GP_STROKE_BUFFER_CHUNK;
279 p = static_cast<tGPspoint *>(MEM_recallocN(buffer_array, sizeof(tGPspoint) * *buffer_size));
280 }
281
282 if (p == nullptr) {
283 *buffer_size = *buffer_used = 0;
284 }
285
286 buffer_array = p;
287 }
288
289 /* clear old data */
290 if (clear) {
291 *buffer_used = 0;
292 if (buffer_array != nullptr) {
293 memset(buffer_array, 0, sizeof(tGPspoint) * *buffer_size);
294 }
295 }
296
297 return buffer_array;
298}
bScreen * CTX_wm_screen(const bContext *C)
ScrArea * CTX_wm_area(const bContext *C)
Scene * CTX_data_scene(const bContext *C)
struct MovieTrackingObject * BKE_tracking_object_get_active(const struct MovieTracking *tracking)
#define BLI_assert(a)
Definition BLI_assert.h:46
void mul_m4_v3(const float M[4][4], float r[3])
BLI_INLINE float BLI_rctf_size_x(const struct rctf *rct)
Definition BLI_rect.h:202
BLI_INLINE float BLI_rctf_size_y(const struct rctf *rct)
Definition BLI_rect.h:206
#define ELEM(...)
Object is a sort of wrapper for general info.
@ SPACE_CLIP
@ SPACE_TOPBAR
@ SPACE_NODE
@ SPACE_PROPERTIES
@ SPACE_SEQ
@ SPACE_IMAGE
@ SPACE_VIEW3D
@ SPACE_INFO
@ SC_GPENCIL_SRC_TRACK
MovieClip * ED_space_clip_get_clip(const SpaceClip *sc)
@ V3D_PROJ_TEST_NOP
Definition ED_view3d.hh:279
@ V3D_PROJ_RET_OK
Definition ED_view3d.hh:256
bool edge_inside_circle(const float cent[2], float radius, const float screen_co_a[2], const float screen_co_b[2])
eV3DProjStatus ED_view3d_project_int_global(const ARegion *region, const float co[3], int r_co[2], eV3DProjTest flag)
Read Guarded memory(de)allocation.
#define MEM_recallocN(vmemh, len)
#define C
Definition RandGen.cpp:29
bool UI_view2d_view_to_region_clip(const View2D *v2d, float x, float y, int *r_region_x, int *r_region_y) ATTR_NONNULL()
Definition view2d.cc:1702
#define V2D_IS_CLIPPED
Definition UI_view2d.hh:21
nullptr float
#define GP_STROKE_BUFFER_CHUNK
bool ED_gpencil_stroke_can_use_direct(const ScrArea *area, const bGPDstroke *gps)
bGPdata * ED_annotation_data_get_active(const bContext *C)
bGPdata * ED_annotation_data_get_active_direct(ID *screen_id, ScrArea *area, Scene *scene)
bGPdata ** ED_annotation_data_get_pointers_direct(ID *screen_id, ScrArea *area, Scene *scene, PointerRNA *r_ptr)
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)
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)
void * MEM_calloc_arrayN(size_t len, size_t size, const char *str)
Definition mallocn.cc:123
static void clear(Message &msg)
Definition msgfmt.cc:213
PointerRNA RNA_pointer_create_discrete(ID *id, StructRNA *type, void *data)
PointerRNA RNA_id_pointer_create(ID *id)
Definition DNA_ID.h:414
void * first
struct MovieTracking tracking
struct bGPdata * gpd
MovieTrackingTrack * active_track
struct bGPdata * gpd
ListBase spacedata
struct bGPdata * gpd
struct bNodeTree * nodetree
struct bGPdata * gpd
struct bGPdata * gpd
float xmin
float ymin