Welcome to mirror list, hosted at ThFree Co, Russian Federation.

git.blender.org/blender.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
Diffstat (limited to 'source/blender/src/gpencil.c')
-rw-r--r--source/blender/src/gpencil.c272
1 files changed, 196 insertions, 76 deletions
diff --git a/source/blender/src/gpencil.c b/source/blender/src/gpencil.c
index b9488d20200..f6703fbe66b 100644
--- a/source/blender/src/gpencil.c
+++ b/source/blender/src/gpencil.c
@@ -63,6 +63,7 @@
#include "BKE_armature.h"
#include "BKE_curve.h"
#include "BKE_image.h"
+#include "BKE_library.h"
#include "BIF_gl.h"
#include "BIF_glutil.h"
@@ -111,7 +112,7 @@ void free_gpencil_strokes (bGPDframe *gpf)
gpsn= gps->next;
/* free stroke memory arrays, then stroke itself */
- MEM_freeN(gps->points);
+ if (gps->points) MEM_freeN(gps->points);
BLI_freelinkN(&gpf->strokes, gps);
}
}
@@ -134,7 +135,7 @@ void free_gpencil_frames (bGPDlayer *gpl)
}
}
-/* Free all of the gp-layers for a viewport (list should be &G.vd->gpd or so) */
+/* Free all of the gp-layers for a viewport (list should be &gpd->layers or so) */
void free_gpencil_layers (ListBase *list)
{
bGPDlayer *gpl, *gpln;
@@ -254,13 +255,63 @@ bGPdata *gpencil_data_addnew (void)
/* -------- Data Duplication ---------- */
+/* make a copy of a given gpencil frame */
+bGPDframe *gpencil_frame_duplicate (bGPDframe *src)
+{
+ bGPDstroke *gps, *gpsd;
+ bGPDframe *dst;
+
+ /* error checking */
+ if (src == NULL)
+ return NULL;
+
+ /* make a copy of the source frame */
+ dst= MEM_dupallocN(src);
+
+ /* copy strokes */
+ dst->strokes.first = dst->strokes.last= NULL;
+ for (gps= src->strokes.first; gps; gps= gps->next) {
+ /* make copy of source stroke, then adjust pointer to points too */
+ gpsd= MEM_dupallocN(gps);
+ gpsd->points= MEM_dupallocN(gps->points);
+
+ BLI_addtail(&dst->strokes, gpsd);
+ }
+
+ /* return new frame */
+ return dst;
+}
+
+/* make a copy of a given gpencil layer */
+bGPDlayer *gpencil_layer_duplicate (bGPDlayer *src)
+{
+ bGPDframe *gpf, *gpfd;
+ bGPDlayer *dst;
+
+ /* error checking */
+ if (src == NULL)
+ return NULL;
+
+ /* make a copy of source layer */
+ dst= MEM_dupallocN(src);
+
+ /* copy frames */
+ dst->frames.first= dst->frames.last= NULL;
+ for (gpf= src->frames.first; gpf; gpf= gpf->next) {
+ /* make a copy of source stroke */
+ gpfd= gpencil_frame_duplicate(gpf);
+ BLI_addtail(&dst->frames, gpfd);
+ }
+
+ /* return new layer */
+ return dst;
+}
+
/* make a copy of a given gpencil datablock */
bGPdata *gpencil_data_duplicate (bGPdata *src)
{
+ bGPDlayer *gpl, *gpld;
bGPdata *dst;
- bGPDlayer *gpld, *gpls;
- bGPDframe *gpfd, *gpfs;
- bGPDstroke *gps;
/* error checking */
if (src == NULL)
@@ -270,25 +321,11 @@ bGPdata *gpencil_data_duplicate (bGPdata *src)
dst= MEM_dupallocN(src);
/* copy layers */
- duplicatelist(&dst->layers, &src->layers);
-
- for (gpld=dst->layers.first, gpls=src->layers.first; gpld && gpls;
- gpld=gpld->next, gpls=gpls->next)
- {
- /* copy frames */
- duplicatelist(&gpld->frames, &gpls->frames);
-
- for (gpfd=gpld->frames.first, gpfs=gpls->frames.first; gpfd && gpfs;
- gpfd=gpfd->next, gpfs=gpfs->next)
- {
- /* copy strokes */
- duplicatelist(&gpfd->strokes, &gpfs->strokes);
-
- for (gps= gpfd->strokes.first; gps; gps= gps->next)
- {
- gps->points= MEM_dupallocN(gps->points);
- }
- }
+ dst->layers.first= dst->layers.last= NULL;
+ for (gpl= src->layers.first; gpl; gpl= gpl->next) {
+ /* make a copy of source layer and its data */
+ gpld= gpencil_layer_duplicate(gpl);
+ BLI_addtail(&dst->layers, gpld);
}
/* return new */
@@ -414,6 +451,30 @@ short gpencil_data_setactive (ScrArea *sa, bGPdata *gpd)
return 0;
}
+/* return the ScrArea that has the given GP-datablock
+ * - assumes that only searching in current screen
+ * - is based on GP-datablocks only being able to
+ * exist for one area at a time (i.e. not multiuser)
+ */
+ScrArea *gpencil_data_findowner (bGPdata *gpd)
+{
+ ScrArea *sa;
+
+ /* error checking */
+ if (gpd == NULL)
+ return NULL;
+
+ /* loop over all scrareas for current screen, and check if that area has this gpd */
+ for (sa= G.curscreen->areabase.first; sa; sa= sa->next) {
+ /* use get-active func to see if match */
+ if (gpencil_data_getactive(sa) == gpd)
+ return sa;
+ }
+
+ /* not found */
+ return NULL;
+}
+
/* -------- GP-Frame API ---------- */
/* delete the last stroke of the given frame */
@@ -538,6 +599,7 @@ bGPDframe *gpencil_layer_getframe (bGPDlayer *gpl, int cframe, short addnew)
else {
/* unresolved errogenous situation! */
printf("Error: cannot find appropriate gp-frame \n");
+ /* gpl->actframe should still be NULL */
}
}
else {
@@ -546,6 +608,7 @@ bGPDframe *gpencil_layer_getframe (bGPDlayer *gpl, int cframe, short addnew)
gpl->actframe= gpencil_frame_addnew(gpl, cframe);
else {
/* don't do anything... this may be when no frames yet! */
+ /* gpl->actframe should still be NULL */
}
}
@@ -791,7 +854,7 @@ static void gp_stroke_to_bezier (bGPDlayer *gpl, bGPDstroke *gps, Curve *cu)
/* set settings */
bezt->h1= bezt->h2= HD_FREE;
bezt->f1= bezt->f2= bezt->f3= SELECT;
- bezt->radius = bezt->weight = pt->pressure * gpl->thickness;
+ bezt->radius = bezt->weight = pt->pressure * gpl->thickness * 0.1;
}
/* must calculate handles or else we crash */
@@ -816,17 +879,20 @@ static void gp_layer_to_curve (bGPdata *gpd, bGPDlayer *gpl, short mode)
/* only convert if there are any strokes on this layer's frame to convert */
if (gpf->strokes.first == NULL)
return;
-
- /* initialise the curve */
- cu= add_curve(gpl->info, 1);
- cu->flag |= CU_3D;
- /* init the curve object (remove rotation and assign curve data to it) */
+ /* init the curve object (remove rotation and get curve data from it)
+ * - must clear transforms set on object, as those skew our results
+ */
add_object_draw(OB_CURVE);
ob= OBACT;
ob->loc[0]= ob->loc[1]= ob->loc[2]= 0;
ob->rot[0]= ob->rot[1]= ob->rot[2]= 0;
- ob->data= cu;
+ cu= ob->data;
+ cu->flag |= CU_3D;
+
+ /* rename object and curve to layer name */
+ rename_id((ID *)ob, gpl->info);
+ rename_id((ID *)cu, gpl->info);
/* add points to curve */
for (gps= gpf->strokes.first; gps; gps= gps->next) {
@@ -865,25 +931,26 @@ static void gp_stroke_to_bonechain (bGPDlayer *gpl, bGPDstroke *gps, bArmature *
VecCopyf(ebo->tail, p3db);
/* add new bone - note: sync with editarmature.c::add_editbone() */
- BLI_strncpy(ebo->name, "Stroke", 32);
- unique_editbone_name(bones, ebo->name);
-
- BLI_addtail(bones, ebo);
-
- ebo->flag |= BONE_CONNECTED;
- ebo->weight= 1.0F;
- ebo->dist= 0.25F;
- ebo->xwidth= 0.1;
- ebo->zwidth= 0.1;
- ebo->ease1= 1.0;
- ebo->ease2= 1.0;
- ebo->rad_head= pt->pressure * gpl->thickness * 0.1;
- ebo->rad_tail= ptn->pressure * gpl->thickness * 0.1;
- ebo->segments= 1;
- ebo->layer= arm->layer;
+ {
+ BLI_strncpy(ebo->name, "Stroke", 32);
+ unique_editbone_name(bones, ebo->name);
+
+ BLI_addtail(bones, ebo);
+
+ ebo->flag |= BONE_CONNECTED;
+ ebo->weight= 1.0F;
+ ebo->dist= 0.25F;
+ ebo->xwidth= 0.1;
+ ebo->zwidth= 0.1;
+ ebo->ease1= 1.0;
+ ebo->ease2= 1.0;
+ ebo->rad_head= pt->pressure * gpl->thickness * 0.1;
+ ebo->rad_tail= ptn->pressure * gpl->thickness * 0.1;
+ ebo->segments= 1;
+ ebo->layer= arm->layer;
+ }
/* set parenting */
- // TODO: also adjust roll....
ebo->parent= prev;
}
}
@@ -904,22 +971,43 @@ static void gp_layer_to_armature (bGPdata *gpd, bGPDlayer *gpl, short mode)
/* only convert if there are any strokes on this layer's frame to convert */
if (gpf->strokes.first == NULL)
return;
-
- /* initialise the armature */
- arm= add_armature(gpl->info);
- /* init the armature object (remove rotation and assign armature data to it) */
+ /* init the armature object (remove rotation and assign armature data to it)
+ * - must clear transforms set on object, as those skew our results
+ */
add_object_draw(OB_ARMATURE);
ob= OBACT;
ob->loc[0]= ob->loc[1]= ob->loc[2]= 0;
ob->rot[0]= ob->rot[1]= ob->rot[2]= 0;
- ob->data= arm;
+ arm= ob->data;
+
+ /* rename object and armature to layer name */
+ rename_id((ID *)ob, gpl->info);
+ rename_id((ID *)arm, gpl->info);
/* convert segments to bones, strokes to bone chains */
for (gps= gpf->strokes.first; gps; gps= gps->next) {
gp_stroke_to_bonechain(gpl, gps, arm, &bones);
}
+ /* adjust roll of bones
+ * - set object as EditMode object, but need to clear afterwards!
+ * - use 'align to world z-up' option
+ */
+ {
+ /* set our data as if we're in editmode to fool auto_align_armature() */
+ G.obedit= ob;
+ G.edbo.first = bones.first;
+ G.edbo.last = bones.last;
+
+ /* WARNING: need to make sure this magic number doesn't change */
+ auto_align_armature(2);
+
+ /* clear editbones (not needed anymore) */
+ G.edbo.first= G.edbo.last= NULL;
+ G.obedit= NULL;
+ }
+
/* flush editbones to armature */
editbones_to_armature(&bones, ob);
if (bones.first) BLI_freelistN(&bones);
@@ -990,12 +1078,15 @@ void gpencil_convert_menu (void)
/* maximum sizes of gp-session buffer */
#define GP_STROKE_BUFFER_MAX 5000
-/* Hardcoded sensitivity thresholds... */
+/* Macros for accessing sensitivity thresholds... */
/* minimum number of pixels mouse should move before new point created */
-#define MIN_MANHATTEN_PX U.gp_manhattendist
+#define MIN_MANHATTEN_PX (U.gp_manhattendist)
/* minimum length of new segment before new point can be added */
-#define MIN_EUCLIDEAN_PX U.gp_euclideandist
+#define MIN_EUCLIDEAN_PX (U.gp_euclideandist)
+/* macro to test if only converting endpoints - only for use when converting! */
+#define GP_BUFFER2STROKE_ENDPOINTS ((gpd->flag & GP_DATA_EDITPAINT) && (G.qual & LR_CTRLKEY))
+
/* ------ */
/* Temporary 'Stroke' Operation data */
@@ -1013,6 +1104,10 @@ typedef struct tGPsdata {
short mval[2]; /* current mouse-position */
short mvalo[2]; /* previous recorded mouse-position */
+
+ short pressure; /* current stylus pressure */
+ short opressure; /* previous stylus pressure */
+
short radius; /* radius of influence for eraser */
} tGPsdata;
@@ -1180,6 +1275,34 @@ static short gp_stroke_addpoint (tGPsdata *p, short mval[2], float pressure)
return GP_STROKEADD_NORMAL;
}
+/* smooth a stroke (in buffer) before storing it */
+static void gp_stroke_smooth (tGPsdata *p)
+{
+ bGPdata *gpd= p->gpd;
+ int i=0, cmx=gpd->sbuffer_size;
+
+ /* only smooth if smoothing is enabled, and we're not doing a straight line */
+ if ( !(U.gp_settings & GP_PAINT_DOSMOOTH) || GP_BUFFER2STROKE_ENDPOINTS)
+ return;
+
+ /* don't try if less than 2 points in buffer */
+ if ((cmx <= 2) || (gpd->sbuffer == NULL))
+ return;
+
+ /* apply weighting-average (note doing this along path sequentially does introduce slight error) */
+ for (i=0; i < gpd->sbuffer_size; i++) {
+ tGPspoint *pc= (((tGPspoint *)gpd->sbuffer) + i);
+ tGPspoint *pb= (i-1 > 0)?(pc-1):(pc);
+ tGPspoint *pa= (i-2 > 0)?(pc-2):(pb);
+ tGPspoint *pd= (i+1 < cmx)?(pc+1):(pc);
+ tGPspoint *pe= (i+2 < cmx)?(pc+2):(pd);
+
+ pc->x= (short)(0.1*pa->x + 0.2*pb->x + 0.4*pc->x + 0.2*pd->x + 0.1*pe->x);
+ pc->y= (short)(0.1*pa->y + 0.2*pb->y + 0.4*pc->y + 0.2*pd->y + 0.1*pe->y);
+ }
+}
+
+
/* make a new stroke from the buffer data */
static void gp_stroke_newfrombuffer (tGPsdata *p)
{
@@ -1188,9 +1311,6 @@ static void gp_stroke_newfrombuffer (tGPsdata *p)
bGPDspoint *pt;
tGPspoint *ptc;
int i, totelem;
-
- /* macro to test if only converting endpoints */
- #define GP_BUFFER2STROKE_ENDPOINTS ((gpd->flag & GP_DATA_EDITPAINT) && (G.qual & LR_CTRLKEY))
/* get total number of points to allocate space for:
* - in 'Draw Mode', holding the Ctrl-Modifier will only take endpoints
@@ -1261,9 +1381,6 @@ static void gp_stroke_newfrombuffer (tGPsdata *p)
/* add stroke to frame */
BLI_addtail(&p->gpf->strokes, gps);
-
- /* undefine macro to test if only converting endpoints */
- #undef GP_BUFFER2STROKE_ENDPOINTS
}
/* --- 'Eraser' for 'Paint' Tool ------ */
@@ -1662,7 +1779,6 @@ static void gp_paint_initstroke (tGPsdata *p, short paintmode)
break;
case SPACE_SEQ:
{
- /* for now, this is not applicable here... */
p->gpd->sbuffer_sflag |= GP_STROKE_2DIMAGE;
}
break;
@@ -1682,6 +1798,9 @@ static void gp_paint_strokeend (tGPsdata *p)
{
/* check if doing eraser or not */
if ((p->gpd->sbuffer_sflag & GP_STROKE_ERASER) == 0) {
+ /* smooth stroke before transferring? */
+ gp_stroke_smooth(p);
+
/* transfer stroke to frame */
gp_stroke_newfrombuffer(p);
}
@@ -1715,7 +1834,6 @@ static void gp_paint_cleanup (tGPsdata *p)
short gpencil_paint (short mousebutton, short paintmode)
{
tGPsdata p;
- float opressure, pressure;
short ok = GP_STROKEADD_NORMAL;
/* init paint-data */
@@ -1735,14 +1853,15 @@ short gpencil_paint (short mousebutton, short paintmode)
/* init drawing-device settings */
getmouseco_areawin(p.mval);
- pressure = get_pressure();
+ p.pressure = get_pressure();
p.mvalo[0]= p.mval[0];
p.mvalo[1]= p.mval[1];
- opressure= pressure;
+ p.opressure= p.pressure;
- /* radius for eraser circle is thickness^2 */
- p.radius= p.gpl->thickness * p.gpl->thickness;
+ /* radius for eraser circle is defined in userprefs now */
+ // TODO: make this more easily tweaked...
+ p.radius= U.gp_eraser;
/* start drawing eraser-circle (if applicable) */
if (paintmode == GP_PAINTMODE_ERASER)
@@ -1754,8 +1873,8 @@ short gpencil_paint (short mousebutton, short paintmode)
* - not erasing
*/
if (paintmode != GP_PAINTMODE_ERASER) {
- if (!(pressure >= 0.99f) || (p.gpd->flag & GP_DATA_EDITPAINT)) {
- gp_stroke_addpoint(&p, p.mval, pressure);
+ if (!(p.pressure >= 0.99f) || (p.gpd->flag & GP_DATA_EDITPAINT)) {
+ gp_stroke_addpoint(&p, p.mval, p.pressure);
}
}
@@ -1763,7 +1882,7 @@ short gpencil_paint (short mousebutton, short paintmode)
do {
/* get current user input */
getmouseco_areawin(p.mval);
- pressure = get_pressure();
+ p.pressure = get_pressure();
/* only add current point to buffer if mouse moved (otherwise wait until it does) */
if (paintmode == GP_PAINTMODE_ERASER) {
@@ -1775,10 +1894,11 @@ short gpencil_paint (short mousebutton, short paintmode)
p.mvalo[0]= p.mval[0];
p.mvalo[1]= p.mval[1];
+ p.opressure= p.pressure;
}
else if (gp_stroke_filtermval(&p, p.mval, p.mvalo)) {
/* try to add point */
- ok= gp_stroke_addpoint(&p, p.mval, pressure);
+ ok= gp_stroke_addpoint(&p, p.mval, p.pressure);
/* handle errors while adding point */
if ((ok == GP_STROKEADD_FULL) || (ok == GP_STROKEADD_OVERFLOW)) {
@@ -1786,8 +1906,8 @@ short gpencil_paint (short mousebutton, short paintmode)
gp_paint_strokeend(&p);
/* start a new stroke, starting from previous point */
- gp_stroke_addpoint(&p, p.mvalo, opressure);
- ok= gp_stroke_addpoint(&p, p.mval, pressure);
+ gp_stroke_addpoint(&p, p.mvalo, p.opressure);
+ ok= gp_stroke_addpoint(&p, p.mval, p.pressure);
}
else if (ok == GP_STROKEADD_INVALID) {
/* the painting operation cannot continue... */
@@ -1802,7 +1922,7 @@ short gpencil_paint (short mousebutton, short paintmode)
p.mvalo[0]= p.mval[0];
p.mvalo[1]= p.mval[1];
- opressure= pressure;
+ p.opressure= p.pressure;
}
else
BIF_wait_for_statechange();
@@ -1820,7 +1940,7 @@ short gpencil_paint (short mousebutton, short paintmode)
/* check size of buffer before cleanup, to determine if anything happened here */
if (paintmode == GP_PAINTMODE_ERASER) {
- ok= 1; // fixme
+ ok= 1; /* assume that we did something... */
draw_sel_circle(NULL, p.mvalo, 0, p.radius, 0);
}
else