Blender V4.5
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_gpencil_legacy.h"
28#include "BKE_paint.hh"
29#include "BKE_tracking.h"
30
31#include "WM_api.hh"
32#include "WM_toolsystem.hh"
33#include "WM_types.hh"
34
35#include "RNA_access.hh"
36#include "RNA_enum_types.hh"
37#include "RNA_prototypes.hh"
38
39#include "UI_view2d.hh"
40
41#include "ED_clip.hh"
42#include "ED_gpencil_legacy.hh"
43#include "ED_object.hh"
44#include "ED_select_utils.hh"
45#include "ED_view3d.hh"
46
48
49#include "gpencil_intern.hh"
50
51/* ******************************************************** */
52/* Context Wrangling... */
53
55 ScrArea *area,
56 Scene *scene,
57 PointerRNA *r_ptr)
58{
59 /* If there's an active area, check if the particular editor may
60 * have defined any special Grease Pencil context for editing. */
61 if (area) {
62 SpaceLink *sl = static_cast<SpaceLink *>(area->spacedata.first);
63
64 switch (area->spacetype) {
65 case SPACE_INFO: /* header info */
66 {
67 return nullptr;
68 }
69
70 case SPACE_TOPBAR: /* Top-bar */
71 case SPACE_VIEW3D: /* 3D-View */
72 case SPACE_PROPERTIES: /* properties */
73 {
74 if (r_ptr) {
75 *r_ptr = RNA_id_pointer_create(&scene->id);
76 }
77 return &scene->gpd;
78 }
79 case SPACE_NODE: /* Nodes Editor */
80 {
81 SpaceNode *snode = (SpaceNode *)sl;
82
83 /* return the GP data for the active node block/node */
84 if (snode && snode->nodetree) {
85 /* for now, as long as there's an active node tree,
86 * default to using that in the Nodes Editor */
87 if (r_ptr) {
88 *r_ptr = RNA_id_pointer_create(&snode->nodetree->id);
89 }
90 return &snode->nodetree->gpd;
91 }
92
93 /* Even when there is no node-tree, don't allow this to flow to scene. */
94 return nullptr;
95 }
96 case SPACE_SEQ: /* Sequencer */
97 {
98 SpaceSeq *sseq = (SpaceSeq *)sl;
99
100 /* For now, Grease Pencil data is associated with the space
101 * (actually preview region only). */
102 if (r_ptr) {
103 *r_ptr = RNA_pointer_create_discrete(screen_id, &RNA_SpaceSequenceEditor, sseq);
104 }
105 return &sseq->gpd;
106 }
107 case SPACE_IMAGE: /* Image/UV Editor */
108 {
109 SpaceImage *sima = (SpaceImage *)sl;
110
111 /* For now, Grease Pencil data is associated with the space... */
112 if (r_ptr) {
113 *r_ptr = RNA_pointer_create_discrete(screen_id, &RNA_SpaceImageEditor, sima);
114 }
115 return &sima->gpd;
116 }
117 case SPACE_CLIP: /* Nodes Editor */
118 {
119 SpaceClip *sc = (SpaceClip *)sl;
121
122 if (clip) {
125 &clip->tracking);
126 MovieTrackingTrack *track = tracking_object->active_track;
127
128 if (!track) {
129 return nullptr;
130 }
131
132 if (r_ptr) {
133 *r_ptr = RNA_pointer_create_discrete(&clip->id, &RNA_MovieTrackingTrack, track);
134 }
135 return &track->gpd;
136 }
137 if (r_ptr) {
138 *r_ptr = RNA_id_pointer_create(&clip->id);
139 }
140 return &clip->gpd;
141 }
142 break;
143 }
144 default: /* unsupported space */
145 return nullptr;
146 }
147 }
148
149 return nullptr;
150}
151
153{
154 ID *screen_id = (ID *)CTX_wm_screen(C);
155 Scene *scene = CTX_data_scene(C);
156 ScrArea *area = CTX_wm_area(C);
157
158 return ED_annotation_data_get_pointers_direct(screen_id, area, scene, r_ptr);
159}
160/* -------------------------------------------------------- */
161
163{
164 bGPdata **gpd_ptr = ED_annotation_data_get_pointers_direct(screen_id, area, scene, nullptr);
165 return (gpd_ptr) ? *(gpd_ptr) : nullptr;
166}
167
169{
170 bGPdata **gpd_ptr = ED_annotation_data_get_pointers(C, nullptr);
171 return (gpd_ptr) ? *(gpd_ptr) : nullptr;
172}
173
174/* ******************************************************** */
175/* Brush Tool Core */
176
177bool gpencil_stroke_inside_circle(const float mval[2], int rad, int x0, int y0, int x1, int y1)
178{
179 /* simple within-radius check for now */
180 const float screen_co_a[2] = {float(x0), float(y0)};
181 const float screen_co_b[2] = {float(x1), float(y1)};
182
183 if (edge_inside_circle(mval, rad, screen_co_a, screen_co_b)) {
184 return true;
185 }
186
187 /* not inside */
188 return false;
189}
190
191/* ******************************************************** */
192/* Stroke Validity Testing */
193
195{
196 /* sanity check */
197 if (ELEM(nullptr, area, gps)) {
198 return false;
199 }
200
201 /* filter stroke types by flags + spacetype */
202 if (gps->flag & GP_STROKE_3DSPACE) {
203 /* 3D strokes - only in 3D view */
205 }
206 if (gps->flag & GP_STROKE_2DIMAGE) {
207 /* Special "image" strokes - only in Image Editor */
208 return (area->spacetype == SPACE_IMAGE);
209 }
210 if (gps->flag & GP_STROKE_2DSPACE) {
211 /* 2D strokes (data-space) - for any 2D view (i.e. everything other than 3D view). */
212 return (area->spacetype != SPACE_VIEW3D);
213 }
214 /* view aligned - anything goes */
215 return true;
216}
217
218/* ******************************************************** */
219/* Space Conversion */
220
222 const GP_SpaceConversion *gsc, const bGPDstroke *gps, const bGPDspoint *pt, int *r_x, int *r_y)
223{
224 const ARegion *region = gsc->region;
225 const View2D *v2d = gsc->v2d;
226 const rctf *subrect = gsc->subrect;
227 int xyval[2];
228
229 /* sanity checks */
232
233 if (gps->flag & GP_STROKE_3DSPACE) {
235 {
236 *r_x = xyval[0];
237 *r_y = xyval[1];
238 }
239 else {
240 *r_x = V2D_IS_CLIPPED;
241 *r_y = V2D_IS_CLIPPED;
242 }
243 }
244 else if (gps->flag & GP_STROKE_2DSPACE) {
245 float vec[3] = {pt->x, pt->y, 0.0f};
246 mul_m4_v3(gsc->mat, vec);
247 UI_view2d_view_to_region_clip(v2d, vec[0], vec[1], r_x, r_y);
248 }
249 else {
250 if (subrect == nullptr) {
251 /* normal 3D view (or view space) */
252 *r_x = int(pt->x / 100 * region->winx);
253 *r_y = int(pt->y / 100 * region->winy);
254 }
255 else {
256 /* camera view, use subrect */
257 *r_x = int((pt->x / 100) * BLI_rctf_size_x(subrect)) + subrect->xmin;
258 *r_y = int((pt->y / 100) * BLI_rctf_size_y(subrect)) + subrect->ymin;
259 }
260 }
261}
262
267 const tGPspoint *point2D,
268 const float origin[3],
269 float out[3])
270{
271 float mval_prj[2];
272 float rvec[3];
273
274 copy_v3_v3(rvec, origin);
275
276 const float zfac = ED_view3d_calc_zfac(static_cast<const RegionView3D *>(region->regiondata),
277 rvec);
278
280 {
281 float dvec[3];
282 float xy_delta[2];
283 sub_v2_v2v2(xy_delta, mval_prj, point2D->m_xy);
284 ED_view3d_win_to_delta(region, xy_delta, zfac, dvec);
285 sub_v3_v3v3(out, rvec, dvec);
286 }
287 else {
288 zero_v3(out);
289 }
290}
291
293 float origin[3],
294 const tGPspoint *tpt,
295 bGPDspoint *pt)
296{
297 float p3d[3];
298 /* conversion to 3d format */
299 gpencil_stroke_convertcoords(region, tpt, origin, p3d);
300 copy_v3_v3(&pt->x, p3d);
301 zero_v4(pt->vert_color);
302
303 pt->pressure = tpt->pressure;
304 pt->strength = tpt->strength;
305 pt->uv_fac = tpt->uv_fac;
306 pt->uv_rot = tpt->uv_rot;
307}
308
310 int *buffer_size,
311 int *buffer_used,
312 const bool clear)
313{
314 tGPspoint *p = nullptr;
315
316 /* By default a buffer is created with one block with a predefined number of free points,
317 * if the size is not enough, the cache is reallocated adding a new block of free points.
318 * This is done in order to keep cache small and improve speed. */
319 if (*buffer_used + 1 > *buffer_size) {
320 if ((*buffer_size == 0) || (buffer_array == nullptr)) {
322 *buffer_size = GP_STROKE_BUFFER_CHUNK;
323 }
324 else {
325 *buffer_size += GP_STROKE_BUFFER_CHUNK;
326 p = static_cast<tGPspoint *>(MEM_recallocN(buffer_array, sizeof(tGPspoint) * *buffer_size));
327 }
328
329 if (p == nullptr) {
330 *buffer_size = *buffer_used = 0;
331 }
332
333 buffer_array = p;
334 }
335
336 /* clear old data */
337 if (clear) {
338 *buffer_used = 0;
339 if (buffer_array != nullptr) {
340 memset(buffer_array, 0, sizeof(tGPspoint) * *buffer_size);
341 }
342 }
343
344 return buffer_array;
345}
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])
MINLINE void sub_v3_v3v3(float r[3], const float a[3], const float b[3])
MINLINE void copy_v3_v3(float r[3], const float a[3])
MINLINE void zero_v4(float r[4])
MINLINE void sub_v2_v2v2(float r[2], const float a[2], const float b[2])
MINLINE void zero_v3(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
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])
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)
eV3DProjStatus ED_view3d_project_float_global(const ARegion *region, const float co[3], float r_co[2], eV3DProjTest flag)
Read Guarded memory(de)allocation.
#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:1701
#define V2D_IS_CLIPPED
Definition UI_view2d.hh:21
#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)
static void gpencil_stroke_convertcoords(ARegion *region, const tGPspoint *point2D, const float origin[3], float out[3])
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 ED_gpencil_tpoint_to_point(ARegion *region, float origin[3], const tGPspoint *tpt, bGPDspoint *pt)
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)
#define out
#define MEM_recallocN(vmemh, len)
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)
void * regiondata
Definition DNA_ID.h:404
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