diff options
author | Joshua Leung <aligorith@gmail.com> | 2009-04-20 14:45:25 +0400 |
---|---|---|
committer | Joshua Leung <aligorith@gmail.com> | 2009-04-20 14:45:25 +0400 |
commit | 4b45c57bb6fe974f30e62a226a587a4db4c17375 (patch) | |
tree | fb8e05e1d5d1568c65555c6f92a9e671670644a3 | |
parent | f2f95caf30977fcca97d168e190f1bdb3176dab4 (diff) |
Drivers - Rotational Difference
Restored the code to get this working. I haven't tested this to verify it, but it 'should' work...
Also, deleted old gpencil.c file in editors, since it was causing compiling errors.
-rw-r--r-- | source/blender/blenkernel/intern/fcurve.c | 62 | ||||
-rw-r--r-- | source/blender/editors/gpencil/gpencil.c | 2161 |
2 files changed, 58 insertions, 2165 deletions
diff --git a/source/blender/blenkernel/intern/fcurve.c b/source/blender/blenkernel/intern/fcurve.c index 49100be5216..b4cec927e30 100644 --- a/source/blender/blenkernel/intern/fcurve.c +++ b/source/blender/blenkernel/intern/fcurve.c @@ -709,6 +709,41 @@ float driver_get_target_value (ChannelDriver *driver, DriverTarget *dtar) return value; } +/* Get two PoseChannels from the targets of the given Driver */ +static void driver_get_target_pchans2 (ChannelDriver *driver, bPoseChannel **pchan1, bPoseChannel **pchan2) +{ + DriverTarget *dtar; + short i = 0; + + /* before doing anything */ + *pchan1= NULL; + *pchan2= NULL; + + /* only take the first two targets */ + for (dtar= driver->targets.first; (dtar) && (i < 2); dtar=dtar->next, i++) { + PointerRNA id_ptr, ptr; + PropertyRNA *prop; + + /* get RNA-pointer for the ID-block given in target */ + if (dtar->id) + RNA_id_pointer_create(dtar->id, &id_ptr); + else + continue; + + /* resolve path so that we have pointer to the right posechannel */ + if (RNA_path_resolve(&id_ptr, dtar->rna_path, &ptr, &prop)) { + /* is pointer valid (i.e. pointing to an actual posechannel */ + if ((ptr.type == &RNA_PoseChannel) && (ptr.data)) { + /* first or second target? */ + if (i) + *pchan1= ptr.data; + else + *pchan2= ptr.data; + } + } + } +} + /* Evaluate an Channel-Driver to get a 'time' value to use instead of "evaltime" * - "evaltime" is the frame at which F-Curve is being evaluated * - has to return a float value @@ -746,7 +781,8 @@ static float evaluate_driver (ChannelDriver *driver, float evaltime) return (value / (float)tot); } } - + break; + case DRIVER_TYPE_PYTHON: /* expression */ { #ifndef DISABLE_PYTHON @@ -766,11 +802,30 @@ static float evaluate_driver (ChannelDriver *driver, float evaltime) break; - case DRIVER_TYPE_ROTDIFF: /* difference of rotations of 2 bones (should be in same armature) */ + case DRIVER_TYPE_ROTDIFF: /* difference of rotations of 2 bones (should ideally be in same armature) */ { - /* + bPoseChannel *pchan, *pchan2; float q1[4], q2[4], quat[4], angle; + /* get pose channels, and check if we've got two */ + driver_get_target_pchans2(driver, &pchan, &pchan2); + if (ELEM(NULL, pchan, pchan2)) { + /* disable this driver, since it doesn't work correctly... */ + driver->flag |= DRIVER_FLAG_INVALID; + + /* check what the error was */ + if ((pchan == NULL) && (pchan2 == NULL)) + printf("Driver Evaluation Error: Rotational difference failed - first 2 targets invalid \n"); + else if (pchan == NULL) + printf("Driver Evaluation Error: Rotational difference failed - first target not valid PoseChannel \n"); + else if (pchan2 == NULL) + printf("Driver Evaluation Error: Rotational difference failed - second target not valid PoseChannel \n"); + + /* stop here... */ + return 0.0f; + } + + /* use the final posed locations */ Mat4ToQuat(pchan->pose_mat, q1); Mat4ToQuat(pchan2->pose_mat, q2); @@ -780,7 +835,6 @@ static float evaluate_driver (ChannelDriver *driver, float evaltime) angle= ABS(angle); return (angle > M_PI) ? (float)((2.0f * M_PI) - angle) : (float)(angle); - */ } break; diff --git a/source/blender/editors/gpencil/gpencil.c b/source/blender/editors/gpencil/gpencil.c deleted file mode 100644 index f842cde72d9..00000000000 --- a/source/blender/editors/gpencil/gpencil.c +++ /dev/null @@ -1,2161 +0,0 @@ -/** - * $Id$ - * - * ***** BEGIN GPL LICENSE BLOCK ***** - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * as published by the Free Software Foundation; either version 2 - * of the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software Foundation, - * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. - * - * The Original Code is Copyright (C) 2008, Blender Foundation - * This is a new part of Blender - * - * Contributor(s): Joshua Leung - * - * ***** END GPL LICENSE BLOCK ***** - */ - -#include <stdio.h> -#include <string.h> -#include <stdlib.h> -#include <stddef.h> -#include <math.h> - -#include "MEM_guardedalloc.h" - -#include "BMF_Api.h" - -#include "IMB_imbuf.h" -#include "IMB_imbuf_types.h" - -#include "BLI_arithb.h" -#include "BLI_blenlib.h" - -#include "DNA_listBase.h" -#include "DNA_armature_types.h" -#include "DNA_curve_types.h" -#include "DNA_gpencil_types.h" -#include "DNA_object_types.h" -#include "DNA_scene_types.h" -#include "DNA_screen_types.h" -#include "DNA_space_types.h" -#include "DNA_userdef_types.h" -#include "DNA_vec_types.h" -#include "DNA_view3d_types.h" - -#include "BKE_armature.h" -#include "BKE_blender.h" -#include "BKE_context.h" -#include "BKE_curve.h" -#include "BKE_global.h" -#include "BKE_image.h" -#include "BKE_library.h" -#include "BKE_utildefines.h" - -#include "BIF_gl.h" -#include "BIF_glutil.h" - -#include "WM_api.h" -#include "WM_types.h" - -#include "UI_view2d.h" - -#include "ED_armature.h" -#include "ED_gpencil.h" -#include "ED_sequencer.h" -#include "ED_view3d.h" - -#include "PIL_time.h" /* sleep */ - -#include "gpencil_intern.h" - -/* XXX */ -static void BIF_undo_push() {} -static void error() {} -static int pupmenu() {return 0;} -static void add_object_draw() {} -static int get_activedevice() {return 0;} -#define L_MOUSE 0 -#define R_MOUSE 0 - -/* XXX */ - -/* ************************************************** */ -/* GENERAL STUFF */ - -/* --------- Memory Management ------------ */ - -/* Free strokes belonging to a gp-frame */ -void free_gpencil_strokes (bGPDframe *gpf) -{ - bGPDstroke *gps, *gpsn; - - /* error checking */ - if (gpf == NULL) return; - - /* free strokes */ - for (gps= gpf->strokes.first; gps; gps= gpsn) { - gpsn= gps->next; - - /* free stroke memory arrays, then stroke itself */ - if (gps->points) MEM_freeN(gps->points); - BLI_freelinkN(&gpf->strokes, gps); - } -} - -/* Free all of a gp-layer's frames */ -void free_gpencil_frames (bGPDlayer *gpl) -{ - bGPDframe *gpf, *gpfn; - - /* error checking */ - if (gpl == NULL) return; - - /* free frames */ - for (gpf= gpl->frames.first; gpf; gpf= gpfn) { - gpfn= gpf->next; - - /* free strokes and their associated memory */ - free_gpencil_strokes(gpf); - BLI_freelinkN(&gpl->frames, gpf); - } -} - -/* 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; - - /* error checking */ - if (list == NULL) return; - - /* delete layers*/ - for (gpl= list->first; gpl; gpl= gpln) { - gpln= gpl->next; - - /* free layers and their data */ - free_gpencil_frames(gpl); - BLI_freelinkN(list, gpl); - } -} - -/* Free gp-data and all it's related data */ -void free_gpencil_data (bGPdata *gpd) -{ - /* free layers then data itself */ - free_gpencil_layers(&gpd->layers); - MEM_freeN(gpd); -} - -/* -------- Container Creation ---------- */ - -/* add a new gp-frame to the given layer */ -bGPDframe *gpencil_frame_addnew (bGPDlayer *gpl, int cframe) -{ - bGPDframe *gpf, *gf; - short state=0; - - /* error checking */ - if ((gpl == NULL) || (cframe <= 0)) - return NULL; - - /* allocate memory for this frame */ - gpf= MEM_callocN(sizeof(bGPDframe), "bGPDframe"); - gpf->framenum= cframe; - - /* find appropriate place to add frame */ - if (gpl->frames.first) { - for (gf= gpl->frames.first; gf; gf= gf->next) { - /* check if frame matches one that is supposed to be added */ - if (gf->framenum == cframe) { - state= -1; - break; - } - - /* if current frame has already exceeded the frame to add, add before */ - if (gf->framenum > cframe) { - BLI_insertlinkbefore(&gpl->frames, gf, gpf); - state= 1; - break; - } - } - } - - /* check whether frame was added successfully */ - if (state == -1) { - MEM_freeN(gpf); - printf("Error: frame (%d) existed already for this layer \n", cframe); - } - else if (state == 0) { - /* add to end then! */ - BLI_addtail(&gpl->frames, gpf); - } - - /* return frame */ - return gpf; -} - -/* add a new gp-layer and make it the active layer */ -bGPDlayer *gpencil_layer_addnew (bGPdata *gpd) -{ - bGPDlayer *gpl; - - /* check that list is ok */ - if (gpd == NULL) - return NULL; - - /* allocate memory for frame and add to end of list */ - gpl= MEM_callocN(sizeof(bGPDlayer), "bGPDlayer"); - - /* add to datablock */ - BLI_addtail(&gpd->layers, gpl); - - /* set basic settings */ - gpl->color[3]= 0.9f; - gpl->thickness = 3; - - /* auto-name */ - sprintf(gpl->info, "GP_Layer"); - BLI_uniquename(&gpd->layers, gpl, "GP_Layer", '.', offsetof(bGPDlayer, info[0]), 128); - - /* make this one the active one */ - gpencil_layer_setactive(gpd, gpl); - - /* return layer */ - return gpl; -} - -/* add a new gp-datablock */ -bGPdata *gpencil_data_addnew (void) -{ - bGPdata *gpd; - - /* allocate memory for a new block */ - gpd= MEM_callocN(sizeof(bGPdata), "GreasePencilData"); - - /* initial settings */ - gpd->flag = (GP_DATA_DISPINFO|GP_DATA_EXPAND); - - return gpd; -} - -/* -------- 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); - dst->prev= dst->next= NULL; - - /* 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); - dst->prev= dst->next= NULL; - - /* copy frames */ - dst->frames.first= dst->frames.last= NULL; - for (gpf= src->frames.first; gpf; gpf= gpf->next) { - /* make a copy of source frame */ - gpfd= gpencil_frame_duplicate(gpf); - BLI_addtail(&dst->frames, gpfd); - - /* if source frame was the current layer's 'active' frame, reassign that too */ - if (gpf == dst->actframe) - dst->actframe= 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; - - /* error checking */ - if (src == NULL) - return NULL; - - /* make a copy of the base-data */ - dst= MEM_dupallocN(src); - - /* copy layers */ - 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 */ - return dst; -} - -/* ----------- GP-Datablock API ------------- */ - -/* get the appropriate bGPdata from the active/given context */ -// XXX region or region data? -bGPdata *gpencil_data_getactive (ScrArea *sa) -{ - ScrArea *curarea= NULL; // XXX - - /* error checking */ - if ((sa == NULL) && (curarea == NULL)) - return NULL; - if (sa == NULL) - sa= curarea; - - /* handle depending on spacetype */ - switch (sa->spacetype) { - case SPACE_VIEW3D: - { - View3D *v3d= sa->spacedata.first; - return v3d->gpd; - } - break; - case SPACE_NODE: - { - SpaceNode *snode= sa->spacedata.first; - return snode->gpd; - } - break; - case SPACE_SEQ: - { - SpaceSeq *sseq= sa->spacedata.first; - - /* only applicable for image modes */ - if (sseq->mainb != SEQ_DRAW_SEQUENCE) - return sseq->gpd; - } - break; - case SPACE_IMAGE: - { - SpaceImage *sima= sa->spacedata.first; - return sima->gpd; - } - break; - } - - /* nothing found */ - return NULL; -} - -/* set bGPdata for the active/given context, and return success/fail */ -short gpencil_data_setactive (ScrArea *sa, bGPdata *gpd) -{ - ScrArea *curarea= NULL; // XXX - - /* error checking */ - if ((sa == NULL) && (curarea == NULL)) - return 0; - if (gpd == NULL) - return 0; - if (sa == NULL) - sa= curarea; - - /* handle depending on spacetype */ - // TODO: someday we should have multi-user data, so no need to loose old data - switch (sa->spacetype) { - case SPACE_VIEW3D: - { - View3D *v3d= sa->spacedata.first; - - /* free the existing block */ - if (v3d->gpd) - free_gpencil_data(v3d->gpd); - v3d->gpd= gpd; - - return 1; - } - break; - case SPACE_NODE: - { - SpaceNode *snode= sa->spacedata.first; - - /* free the existing block */ - if (snode->gpd) - free_gpencil_data(snode->gpd); - snode->gpd= gpd; - - /* set special settings */ - gpd->flag |= GP_DATA_VIEWALIGN; - - return 1; - } - break; - case SPACE_SEQ: - { - SpaceSeq *sseq= sa->spacedata.first; - - /* only applicable if right mode */ - if (sseq->mainb != SEQ_DRAW_SEQUENCE) { - /* free the existing block */ - if (sseq->gpd) - free_gpencil_data(sseq->gpd); - sseq->gpd= gpd; - - return 1; - } - } - break; - case SPACE_IMAGE: - { - SpaceImage *sima= sa->spacedata.first; - - if (sima->gpd) - free_gpencil_data(sima->gpd); - sima->gpd= gpd; - - return 1; - } - break; - } - - /* failed to add */ - 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) -{ - bScreen *curscreen= NULL; // XXX - 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= 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 */ -void gpencil_frame_delete_laststroke (bGPDframe *gpf) -{ - bGPDstroke *gps= (gpf) ? gpf->strokes.last : NULL; - - /* error checking */ - if (ELEM(NULL, gpf, gps)) - return; - - /* free the stroke and its data */ - MEM_freeN(gps->points); - BLI_freelinkN(&gpf->strokes, gps); -} - -/* -------- GP-Layer API ---------- */ - -/* get the appropriate gp-frame from a given layer - * - this sets the layer's actframe var (if allowed to) - * - extension beyond range (if first gp-frame is after all frame in interest and cannot add) - */ -bGPDframe *gpencil_layer_getframe (bGPDlayer *gpl, int cframe, short addnew) -{ - bGPDframe *gpf = NULL; - short found = 0; - - /* error checking */ - if (gpl == NULL) return NULL; - if (cframe <= 0) cframe = 1; - - /* check if there is already an active frame */ - if (gpl->actframe) { - gpf= gpl->actframe; - - /* do not allow any changes to layer's active frame if layer is locked */ - if (gpl->flag & GP_LAYER_LOCKED) - return gpf; - /* do not allow any changes to actframe if frame has painting tag attached to it */ - if (gpf->flag & GP_FRAME_PAINT) - return gpf; - - /* try to find matching frame */ - if (gpf->framenum < cframe) { - for (; gpf; gpf= gpf->next) { - if (gpf->framenum == cframe) { - found= 1; - break; - } - else if ((gpf->next) && (gpf->next->framenum > cframe)) { - found= 1; - break; - } - } - - /* set the appropriate frame */ - if (addnew) { - if ((found) && (gpf->framenum == cframe)) - gpl->actframe= gpf; - else - gpl->actframe= gpencil_frame_addnew(gpl, cframe); - } - else if (found) - gpl->actframe= gpf; - else - gpl->actframe= gpl->frames.last; - } - else { - for (; gpf; gpf= gpf->prev) { - if (gpf->framenum <= cframe) { - found= 1; - break; - } - } - - /* set the appropriate frame */ - if (addnew) { - if ((found) && (gpf->framenum == cframe)) - gpl->actframe= gpf; - else - gpl->actframe= gpencil_frame_addnew(gpl, cframe); - } - else if (found) - gpl->actframe= gpf; - else - gpl->actframe= gpl->frames.first; - } - } - else if (gpl->frames.first) { - /* check which of the ends to start checking from */ - const int first= ((bGPDframe *)(gpl->frames.first))->framenum; - const int last= ((bGPDframe *)(gpl->frames.last))->framenum; - - if (abs(cframe-first) > abs(cframe-last)) { - /* find gp-frame which is less than or equal to cframe */ - for (gpf= gpl->frames.last; gpf; gpf= gpf->prev) { - if (gpf->framenum <= cframe) { - found= 1; - break; - } - } - } - else { - /* find gp-frame which is less than or equal to cframe */ - for (gpf= gpl->frames.first; gpf; gpf= gpf->next) { - if (gpf->framenum <= cframe) { - found= 1; - break; - } - } - } - - /* set the appropriate frame */ - if (addnew) { - if ((found) && (gpf->framenum == cframe)) - gpl->actframe= gpf; - else - gpl->actframe= gpencil_frame_addnew(gpl, cframe); - } - else if (found) - gpl->actframe= gpf; - else { - /* unresolved errogenous situation! */ - printf("Error: cannot find appropriate gp-frame \n"); - /* gpl->actframe should still be NULL */ - } - } - else { - /* currently no frames (add if allowed to) */ - if (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 */ - } - } - - /* return */ - return gpl->actframe; -} - -/* delete the given frame from a layer */ -void gpencil_layer_delframe (bGPDlayer *gpl, bGPDframe *gpf) -{ - /* error checking */ - if (ELEM(NULL, gpl, gpf)) - return; - - /* free the frame and its data */ - free_gpencil_strokes(gpf); - BLI_freelinkN(&gpl->frames, gpf); - gpl->actframe = NULL; -} - -/* get the active gp-layer for editing */ -bGPDlayer *gpencil_layer_getactive (bGPdata *gpd) -{ - bGPDlayer *gpl; - - /* error checking */ - if (ELEM(NULL, gpd, gpd->layers.first)) - return NULL; - - /* loop over layers until found (assume only one active) */ - for (gpl=gpd->layers.first; gpl; gpl=gpl->next) { - if (gpl->flag & GP_LAYER_ACTIVE) - return gpl; - } - - /* no active layer found */ - return NULL; -} - -/* set the active gp-layer */ -void gpencil_layer_setactive (bGPdata *gpd, bGPDlayer *active) -{ - bGPDlayer *gpl; - - /* error checking */ - if (ELEM3(NULL, gpd, gpd->layers.first, active)) - return; - - /* loop over layers deactivating all */ - for (gpl=gpd->layers.first; gpl; gpl=gpl->next) - gpl->flag &= ~GP_LAYER_ACTIVE; - - /* set as active one */ - active->flag |= GP_LAYER_ACTIVE; -} - -/* delete the active gp-layer */ -void gpencil_layer_delactive (bGPdata *gpd) -{ - bGPDlayer *gpl= gpencil_layer_getactive(gpd); - - /* error checking */ - if (ELEM(NULL, gpd, gpl)) - return; - - /* free layer */ - free_gpencil_frames(gpl); - BLI_freelinkN(&gpd->layers, gpl); -} - -/* ************************************************** */ -/* GREASE-PENCIL EDITING - Tools */ - -/* --------- Data Deletion ---------- */ - -/* delete the last stroke on the active layer */ -void gpencil_delete_laststroke (bGPdata *gpd, int cfra) -{ - bGPDlayer *gpl= gpencil_layer_getactive(gpd); - bGPDframe *gpf= gpencil_layer_getframe(gpl, cfra, 0); - - gpencil_frame_delete_laststroke(gpf); -} - -/* delete the active frame */ -void gpencil_delete_actframe (bGPdata *gpd, int cfra) -{ - bGPDlayer *gpl= gpencil_layer_getactive(gpd); - bGPDframe *gpf= gpencil_layer_getframe(gpl, cfra, 0); - - gpencil_layer_delframe(gpl, gpf); -} - - - -/* delete various grase-pencil elements - * mode: 1 - last stroke - * 2 - active frame - * 3 - active layer - */ -void gpencil_delete_operation (int cfra, short mode) -{ - bGPdata *gpd; - - /* get datablock to work on */ - gpd= gpencil_data_getactive(NULL); - if (gpd == NULL) return; - - switch (mode) { - case 1: /* last stroke */ - gpencil_delete_laststroke(gpd, cfra); - break; - case 2: /* active frame */ - gpencil_delete_actframe(gpd, cfra); - break; - case 3: /* active layer */ - gpencil_layer_delactive(gpd); - break; - } - - /* redraw and undo-push */ - BIF_undo_push("GPencil Delete"); -} - -/* display a menu for deleting different grease-pencil elements */ -void gpencil_delete_menu (void) -{ - bGPdata *gpd= gpencil_data_getactive(NULL); - int cfra= 0; // XXX - short mode; - - /* only show menu if it will be relevant */ - if (gpd == NULL) return; - - mode= pupmenu("Grease Pencil Erase...%t|Last Stroke%x1|Active Frame%x2|Active Layer%x3"); - if (mode <= 0) return; - - gpencil_delete_operation(cfra, mode); -} - -/* --------- Data Conversion ---------- */ - -/* convert the coordinates from the given stroke point into 3d-coordinates */ -static void gp_strokepoint_convertcoords (bGPDstroke *gps, bGPDspoint *pt, float p3d[3]) -{ - ARegion *ar= NULL; // XXX - - if (gps->flag & GP_STROKE_3DSPACE) { - /* directly use 3d-coordinates */ - VecCopyf(p3d, &pt->x); - } - else { - short mval[2]; - int mx, my; - float *fp= give_cursor(NULL, NULL); // XXX should be scene, v3d - float dvec[3]; - - /* get screen coordinate */ - if (gps->flag & GP_STROKE_2DSPACE) { - // XXX - // View2D *v2d= spacelink_get_view2d(curarea->spacedata.first); - // UI_view2d_view_to_region(v2d, pt->x, pt->y, &mx, &my); - } - else { - // XXX - // mx= (short)(pt->x / 1000 * curarea->winx); - // my= (short)(pt->y / 1000 * curarea->winy); - } - - /* convert screen coordinate to 3d coordinates - * - method taken from editview.c - mouse_cursor() - */ - project_short_noclip(ar, fp, mval); - window_to_3d_delta(ar, dvec, mval[0]-mx, mval[1]-my); - VecSubf(p3d, fp, dvec); - } -} - -/* --- */ - -/* convert stroke to 3d path */ -static void gp_stroke_to_path (bGPDlayer *gpl, bGPDstroke *gps, Curve *cu) -{ - bGPDspoint *pt; - Nurb *nu; - BPoint *bp; - int i; - - /* create new 'nurb' within the curve */ - nu = (Nurb *)MEM_callocN(sizeof(Nurb), "gpstroke_to_path(nurb)"); - - nu->pntsu= gps->totpoints; - nu->pntsv= 1; - nu->orderu= gps->totpoints; - nu->flagu= 2; /* endpoint */ - nu->resolu= 32; - - nu->bp= (BPoint *)MEM_callocN(sizeof(BPoint)*gps->totpoints, "bpoints"); - - /* add points */ - for (i=0, pt=gps->points, bp=nu->bp; i < gps->totpoints; i++, pt++, bp++) { - float p3d[3]; - - /* get coordinates to add at */ - gp_strokepoint_convertcoords(gps, pt, p3d); - VecCopyf(bp->vec, p3d); - - /* set settings */ - bp->f1= SELECT; - bp->radius = bp->weight = pt->pressure * gpl->thickness; - } - - /* add nurb to curve */ - BLI_addtail(&cu->nurb, nu); -} - -/* convert stroke to 3d bezier */ -static void gp_stroke_to_bezier (bGPDlayer *gpl, bGPDstroke *gps, Curve *cu) -{ - bGPDspoint *pt; - Nurb *nu; - BezTriple *bezt; - int i; - - /* create new 'nurb' within the curve */ - nu = (Nurb *)MEM_callocN(sizeof(Nurb), "gpstroke_to_bezier(nurb)"); - - nu->pntsu= gps->totpoints; - nu->resolu= 12; - nu->resolv= 12; - nu->type= CU_BEZIER; - nu->bezt = (BezTriple *)MEM_callocN(gps->totpoints*sizeof(BezTriple), "bezts"); - - /* add points */ - for (i=0, pt=gps->points, bezt=nu->bezt; i < gps->totpoints; i++, pt++, bezt++) { - float p3d[3]; - - /* get coordinates to add at */ - gp_strokepoint_convertcoords(gps, pt, p3d); - - /* TODO: maybe in future the handles shouldn't be in same place */ - VecCopyf(bezt->vec[0], p3d); - VecCopyf(bezt->vec[1], p3d); - VecCopyf(bezt->vec[2], p3d); - - /* set settings */ - bezt->h1= bezt->h2= HD_FREE; - bezt->f1= bezt->f2= bezt->f3= SELECT; - bezt->radius = bezt->weight = pt->pressure * gpl->thickness * 0.1f; - } - - /* must calculate handles or else we crash */ - calchandlesNurb(nu); - - /* add nurb to curve */ - BLI_addtail(&cu->nurb, nu); -} - -/* convert a given grease-pencil layer to a 3d-curve representation (using current view if appropriate) */ -static void gp_layer_to_curve (bGPdata *gpd, bGPDlayer *gpl, Scene *scene, short mode) -{ - bGPDframe *gpf= gpencil_layer_getframe(gpl, scene->r.cfra, 0); - bGPDstroke *gps; - Object *ob; - Curve *cu; - - /* error checking */ - if (ELEM3(NULL, gpd, gpl, gpf)) - return; - - /* only convert if there are any strokes on this layer's frame to convert */ - if (gpf->strokes.first == NULL) - return; - - /* 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; - 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) { - switch (mode) { - case 1: - gp_stroke_to_path(gpl, gps, cu); - break; - case 2: - gp_stroke_to_bezier(gpl, gps, cu); - break; - } - } -} - -/* --- */ - -/* convert a stroke to a bone chain */ -static void gp_stroke_to_bonechain (bGPDlayer *gpl, bGPDstroke *gps, bArmature *arm, ListBase *bones) -{ - EditBone *ebo, *prev=NULL; - bGPDspoint *pt, *ptn; - int i; - - /* add each segment separately */ - for (i=0, pt=gps->points, ptn=gps->points+1; i < (gps->totpoints-1); prev=ebo, i++, pt++, ptn++) { - float p3da[3], p3db[3]; - - /* get coordinates to add at */ - gp_strokepoint_convertcoords(gps, pt, p3da); - gp_strokepoint_convertcoords(gps, ptn, p3db); - - /* allocate new bone */ - ebo= MEM_callocN(sizeof(EditBone), "eBone"); - - VecCopyf(ebo->head, p3da); - 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, NULL); - - BLI_addtail(bones, ebo); - - if (i > 0) - { - ebo->flag |= BONE_CONNECTED; - } - ebo->weight= 1.0f; - ebo->dist= 0.25f; - ebo->xwidth= 0.1f; - ebo->zwidth= 0.1f; - ebo->ease1= 1.0f; - ebo->ease2= 1.0f; - ebo->rad_head= pt->pressure * gpl->thickness * 0.1f; - ebo->rad_tail= ptn->pressure * gpl->thickness * 0.1f; - ebo->segments= 1; - ebo->layer= arm->layer; - } - - /* set parenting */ - ebo->parent= prev; - } -} - -/* convert a given grease-pencil layer to a 3d-curve representation (using current view if appropriate) */ -static void gp_layer_to_armature (bGPdata *gpd, bGPDlayer *gpl, Scene *scene, View3D *v3d, short mode) -{ - bGPDframe *gpf= gpencil_layer_getframe(gpl, scene->r.cfra, 0); - bGPDstroke *gps; - Object *ob; - bArmature *arm; - - /* error checking */ - if (ELEM3(NULL, gpd, gpl, gpf)) - return; - - /* only convert if there are any strokes on this layer's frame to convert */ - if (gpf->strokes.first == NULL) - return; - - /* 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; - arm= ob->data; - - /* rename object and armature to layer name */ - rename_id((ID *)ob, gpl->info); - rename_id((ID *)arm, gpl->info); - - /* this is editmode armature */ - arm->edbo= MEM_callocN(sizeof(ListBase), "arm edbo"); - - /* convert segments to bones, strokes to bone chains */ - for (gps= gpf->strokes.first; gps; gps= gps->next) { - gp_stroke_to_bonechain(gpl, gps, arm, arm->edbo); - } - - /* 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() */ - scene->obedit= ob; - - /* WARNING: need to make sure this magic number doesn't change */ - auto_align_armature(scene, v3d, 2); - - scene->obedit= NULL; - } - - /* flush editbones to armature */ - ED_armature_from_edit(scene, ob); - ED_armature_edit_free(ob); -} - -/* --- */ - -/* convert grease-pencil strokes to another representation - * mode: 1 - Active layer to path - * 2 - Active layer to bezier - * 3 - Active layer to armature - */ -void gpencil_convert_operation (short mode) -{ - Scene *scene= NULL; // XXX - View3D *v3d= NULL; // XXX - RegionView3D *rv3d= NULL; // XXX - bGPdata *gpd; - float *fp= give_cursor(scene, v3d); - - /* get datablock to work on */ - gpd= gpencil_data_getactive(NULL); - if (gpd == NULL) return; - - /* initialise 3d-cursor correction globals */ - initgrabz(rv3d, fp[0], fp[1], fp[2]); - - /* handle selection modes */ - switch (mode) { - case 1: /* active layer only (to path) */ - case 2: /* active layer only (to bezier) */ - { - bGPDlayer *gpl= gpencil_layer_getactive(gpd); - gp_layer_to_curve(gpd, gpl, scene, mode); - } - break; - case 3: /* active layer only (to armature) */ - { - bGPDlayer *gpl= gpencil_layer_getactive(gpd); - gp_layer_to_armature(gpd, gpl, scene, v3d, mode); - } - break; - } - - /* redraw and undo-push */ - BIF_undo_push("GPencil Convert"); -} - -/* display a menu for converting grease-pencil strokes */ -void gpencil_convert_menu (void) -{ - bGPdata *gpd= gpencil_data_getactive(NULL); - short mode; - - /* only show menu if it will be relevant */ - if (gpd == NULL) return; - - mode= pupmenu("Grease Pencil Convert %t|Active Layer To Path%x1|Active Layer to Bezier%x2|Active Layer to Armature%x3"); - if (mode <= 0) return; - - gpencil_convert_operation(mode); -} - -/* ************************************************** */ -/* GREASE-PENCIL EDITING MODE - Painting */ - -/* ---------- 'Globals' and Defines ----------------- */ - -/* maximum sizes of gp-session buffer */ -#define GP_STROKE_BUFFER_MAX 5000 - -/* Macros for accessing sensitivity thresholds... */ - /* minimum number of pixels mouse should move before new point created */ -#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) - -/* macro to test if only converting endpoints - only for use when converting! */ -#define GP_BUFFER2STROKE_ENDPOINTS ((gpd->flag & GP_DATA_EDITPAINT) && (ctrl)) - -/* ------ */ - -/* Temporary 'Stroke' Operation data */ -typedef struct tGPsdata { - Scene *scene; /* current scene from context */ - ScrArea *sa; /* area where painting originated */ - ARegion *ar; /* region where painting originated */ - View2D *v2d; /* needed for GP_STROKE_2DSPACE */ - - ImBuf *ibuf; /* needed for GP_STROKE_2DIMAGE */ - struct IBufViewSettings { - int offsx, offsy; /* offsets */ - int sizex, sizey; /* dimensions to use as scale-factor */ - } im2d_settings; /* needed for GP_STROKE_2DIMAGE */ - - bGPdata *gpd; /* gp-datablock layer comes from */ - bGPDlayer *gpl; /* layer we're working on */ - bGPDframe *gpf; /* frame we're working on */ - - short status; /* current status of painting */ - short paintmode; /* mode for painting */ - - short mval[2]; /* current mouse-position */ - short mvalo[2]; /* previous recorded mouse-position */ - - float pressure; /* current stylus pressure */ - float opressure; /* previous stylus pressure */ - - short radius; /* radius of influence for eraser */ -} tGPsdata; - -/* values for tGPsdata->status */ -enum { - GP_STATUS_NORMAL = 0, /* running normally */ - GP_STATUS_ERROR, /* something wasn't correctly set up */ - GP_STATUS_DONE /* painting done */ -}; - -/* values for tGPsdata->paintmode */ -enum { - GP_PAINTMODE_DRAW = 0, - GP_PAINTMODE_ERASER -}; - -/* Return flags for adding points to stroke buffer */ -enum { - GP_STROKEADD_INVALID = -2, /* error occurred - insufficient info to do so */ - GP_STROKEADD_OVERFLOW = -1, /* error occurred - cannot fit any more points */ - GP_STROKEADD_NORMAL, /* point was successfully added */ - GP_STROKEADD_FULL /* cannot add any more points to buffer */ -}; - -/* ---------- Stroke Editing ------------ */ - -/* clear the session buffers (call this before AND after a paint operation) */ -static void gp_session_validatebuffer (tGPsdata *p) -{ - bGPdata *gpd= p->gpd; - - /* clear memory of buffer (or allocate it if starting a new session) */ - if (gpd->sbuffer) - memset(gpd->sbuffer, 0, sizeof(tGPspoint)*GP_STROKE_BUFFER_MAX); - else - gpd->sbuffer= MEM_callocN(sizeof(tGPspoint)*GP_STROKE_BUFFER_MAX, "gp_session_strokebuffer"); - - /* reset indices */ - gpd->sbuffer_size = 0; - - /* reset flags */ - gpd->sbuffer_sflag= 0; -} - -/* check if the current mouse position is suitable for adding a new point */ -static short gp_stroke_filtermval (tGPsdata *p, short mval[2], short pmval[2]) -{ - short dx= abs(mval[0] - pmval[0]); - short dy= abs(mval[1] - pmval[1]); - - /* check if mouse moved at least certain distance on both axes (best case) */ - if ((dx > MIN_MANHATTEN_PX) && (dy > MIN_MANHATTEN_PX)) - return 1; - - /* check if the distance since the last point is significant enough */ - // future optimisation: sqrt here may be too slow? - else if (sqrt(dx*dx + dy*dy) > MIN_EUCLIDEAN_PX) - return 1; - - /* mouse 'didn't move' */ - else - return 0; -} - -/* convert screen-coordinates to buffer-coordinates */ -static void gp_stroke_convertcoords (tGPsdata *p, short mval[], float out[]) -{ - bGPdata *gpd= p->gpd; - - /* in 3d-space - pt->x/y/z are 3 side-by-side floats */ - if (gpd->sbuffer_sflag & GP_STROKE_3DSPACE) { - const short mx=mval[0], my=mval[1]; - float *fp= give_cursor(p->scene, NULL); // XXX NULL could be v3d - float dvec[3]; - - /* Current method just converts each point in screen-coordinates to - * 3D-coordinates using the 3D-cursor as reference. In general, this - * works OK, but it could of course be improved. - * - * TODO: - * - investigate using nearest point(s) on a previous stroke as - * reference point instead or as offset, for easier stroke matching - * - investigate projection onto geometry (ala retopo) - */ - - /* method taken from editview.c - mouse_cursor() */ - project_short_noclip(p->ar, fp, mval); - window_to_3d_delta(p->ar, dvec, mval[0]-mx, mval[1]-my); - VecSubf(out, fp, dvec); - } - - /* 2d - on 'canvas' (assume that p->v2d is set) */ - else if ((gpd->sbuffer_sflag & GP_STROKE_2DSPACE) && (p->v2d)) { - float x, y; - - UI_view2d_region_to_view(p->v2d, mval[0], mval[1], &x, &y); - - out[0]= x; - out[1]= y; - } - - /* 2d - on image 'canvas' (assume that p->v2d is set) */ - else if (gpd->sbuffer_sflag & GP_STROKE_2DIMAGE) { - int sizex, sizey, offsx, offsy; - - /* get stored settings - * - assume that these have been set already (there are checks that set sane 'defaults' just in case) - */ - sizex= p->im2d_settings.sizex; - sizey= p->im2d_settings.sizey; - offsx= p->im2d_settings.offsx; - offsy= p->im2d_settings.offsy; - - /* calculate new points */ - out[0]= (float)(mval[0] - offsx) / (float)sizex; - out[1]= (float)(mval[1] - offsy) / (float)sizey; - } - - /* 2d - relative to screen (viewport area) */ - else { - out[0] = (float)(mval[0]) / (float)(p->sa->winx) * 1000; - out[1] = (float)(mval[1]) / (float)(p->sa->winy) * 1000; - } -} - -/* add current stroke-point to buffer (returns whether point was successfully added) */ -static short gp_stroke_addpoint (tGPsdata *p, short mval[2], float pressure) -{ - bGPdata *gpd= p->gpd; - tGPspoint *pt; - - /* check if still room in buffer */ - if (gpd->sbuffer_size >= GP_STROKE_BUFFER_MAX) - return GP_STROKEADD_OVERFLOW; - - /* get pointer to destination point */ - pt= ((tGPspoint *)(gpd->sbuffer) + gpd->sbuffer_size); - - /* store settings */ - pt->x= mval[0]; - pt->y= mval[1]; - pt->pressure= pressure; - - /* increment counters */ - gpd->sbuffer_size++; - - /* check if another operation can still occur */ - if (gpd->sbuffer_size == GP_STROKE_BUFFER_MAX) - return GP_STROKEADD_FULL; - else - 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; - int ctrl= 0; // XXX - - /* 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); - } -} - -/* simplify a stroke (in buffer) before storing it - * - applies a reverse Chaikin filter - * - code adapted from etch-a-ton branch (editarmature_sketch.c) - */ -static void gp_stroke_simplify (tGPsdata *p) -{ - bGPdata *gpd= p->gpd; - tGPspoint *old_points= (tGPspoint *)gpd->sbuffer; - short num_points= gpd->sbuffer_size; - short flag= gpd->sbuffer_sflag; - short i, j; - int ctrl= 0; // XXX - - /* only simplify if simlification is enabled, and we're not doing a straight line */ - if (!(U.gp_settings & GP_PAINT_DOSIMPLIFY) || GP_BUFFER2STROKE_ENDPOINTS) - return; - - /* don't simplify if less than 4 points in buffer */ - if ((num_points <= 2) || (old_points == NULL)) - return; - - /* clear buffer (but don't free mem yet) so that we can write to it - * - firstly set sbuffer to NULL, so a new one is allocated - * - secondly, reset flag after, as it gets cleared auto - */ - gpd->sbuffer= NULL; - gp_session_validatebuffer(p); - gpd->sbuffer_sflag = flag; - -/* macro used in loop to get position of new point - * - used due to the mixture of datatypes in use here - */ -#define GP_SIMPLIFY_AVPOINT(offs, sfac) \ - { \ - co[0] += (float)(old_points[offs].x * sfac); \ - co[1] += (float)(old_points[offs].y * sfac); \ - pressure += old_points[offs].pressure * sfac; \ - } - - for (i = 0, j = 0; i < num_points; i++) - { - if (i - j == 3) - { - float co[2], pressure; - short mco[2]; - - /* initialise values */ - co[0]= 0; - co[1]= 0; - pressure = 0; - - /* using macro, calculate new point */ - GP_SIMPLIFY_AVPOINT(j, -0.25f); - GP_SIMPLIFY_AVPOINT(j+1, 0.75f); - GP_SIMPLIFY_AVPOINT(j+2, 0.75f); - GP_SIMPLIFY_AVPOINT(j+3, -0.25f); - - /* set values for adding */ - mco[0]= (short)co[0]; - mco[1]= (short)co[1]; - - /* ignore return values on this... assume to be ok for now */ - gp_stroke_addpoint(p, mco, pressure); - - j += 2; - } - } - - /* free old buffer */ - MEM_freeN(old_points); -} - - -/* make a new stroke from the buffer data */ -static void gp_stroke_newfrombuffer (tGPsdata *p) -{ - bGPdata *gpd= p->gpd; - bGPDstroke *gps; - bGPDspoint *pt; - tGPspoint *ptc; - int i, totelem; - int ctrl= 0; // XXX - - /* get total number of points to allocate space for: - * - in 'Draw Mode', holding the Ctrl-Modifier will only take endpoints - * - otherwise, do whole stroke - */ - if (GP_BUFFER2STROKE_ENDPOINTS) - totelem = (gpd->sbuffer_size >= 2) ? 2: gpd->sbuffer_size; - else - totelem = gpd->sbuffer_size; - - /* exit with error if no valid points from this stroke */ - if (totelem == 0) { - if (G.f & G_DEBUG) - printf("Error: No valid points in stroke buffer to convert (tot=%d) \n", gpd->sbuffer_size); - return; - } - - /* allocate memory for a new stroke */ - gps= MEM_callocN(sizeof(bGPDstroke), "gp_stroke"); - - /* allocate enough memory for a continuous array for storage points */ - pt= gps->points= MEM_callocN(sizeof(bGPDspoint)*totelem, "gp_stroke_points"); - - /* copy appropriate settings for stroke */ - gps->totpoints= totelem; - gps->thickness= p->gpl->thickness; - gps->flag= gpd->sbuffer_sflag; - - /* copy points from the buffer to the stroke */ - if (GP_BUFFER2STROKE_ENDPOINTS) { - /* 'Draw Mode' + Ctrl-Modifier - only endpoints */ - { - /* first point */ - ptc= gpd->sbuffer; - - /* convert screen-coordinates to appropriate coordinates (and store them) */ - gp_stroke_convertcoords(p, &ptc->x, &pt->x); - - /* copy pressure */ - pt->pressure= ptc->pressure; - - pt++; - } - - if (totelem == 2) { - /* last point if applicable */ - ptc= ((tGPspoint *)gpd->sbuffer) + (gpd->sbuffer_size - 1); - - /* convert screen-coordinates to appropriate coordinates (and store them) */ - gp_stroke_convertcoords(p, &ptc->x, &pt->x); - - /* copy pressure */ - pt->pressure= ptc->pressure; - } - } - else { - /* convert all points (normal behaviour) */ - for (i=0, ptc=gpd->sbuffer; i < gpd->sbuffer_size && ptc; i++, ptc++) { - /* convert screen-coordinates to appropriate coordinates (and store them) */ - gp_stroke_convertcoords(p, &ptc->x, &pt->x); - - /* copy pressure */ - pt->pressure= ptc->pressure; - - pt++; - } - } - - /* add stroke to frame */ - BLI_addtail(&p->gpf->strokes, gps); -} - -/* --- 'Eraser' for 'Paint' Tool ------ */ - -/* eraser tool - remove segment from stroke/split stroke (after lasso inside) */ -static short gp_stroke_eraser_splitdel (bGPDframe *gpf, bGPDstroke *gps, int i) -{ - bGPDspoint *pt_tmp= gps->points; - bGPDstroke *gsn = NULL; - - /* if stroke only had two points, get rid of stroke */ - if (gps->totpoints == 2) { - /* free stroke points, then stroke */ - MEM_freeN(pt_tmp); - BLI_freelinkN(&gpf->strokes, gps); - - /* nothing left in stroke, so stop */ - return 1; - } - - /* if last segment, just remove segment from the stroke */ - else if (i == gps->totpoints - 2) { - /* allocate new points array, and assign most of the old stroke there */ - gps->totpoints--; - gps->points= MEM_callocN(sizeof(bGPDspoint)*gps->totpoints, "gp_stroke_points"); - memcpy(gps->points, pt_tmp, sizeof(bGPDspoint)*gps->totpoints); - - /* free temp buffer */ - MEM_freeN(pt_tmp); - - /* nothing left in stroke, so stop */ - return 1; - } - - /* if first segment, just remove segment from the stroke */ - else if (i == 0) { - /* allocate new points array, and assign most of the old stroke there */ - gps->totpoints--; - gps->points= MEM_callocN(sizeof(bGPDspoint)*gps->totpoints, "gp_stroke_points"); - memcpy(gps->points, pt_tmp + 1, sizeof(bGPDspoint)*gps->totpoints); - - /* free temp buffer */ - MEM_freeN(pt_tmp); - - /* no break here, as there might still be stuff to remove in this stroke */ - return 0; - } - - /* segment occurs in 'middle' of stroke, so split */ - else { - /* duplicate stroke, and assign 'later' data to that stroke */ - gsn= MEM_dupallocN(gps); - gsn->prev= gsn->next= NULL; - BLI_insertlinkafter(&gpf->strokes, gps, gsn); - - gsn->totpoints= gps->totpoints - i; - gsn->points= MEM_callocN(sizeof(bGPDspoint)*gsn->totpoints, "gp_stroke_points"); - memcpy(gsn->points, pt_tmp + i, sizeof(bGPDspoint)*gsn->totpoints); - - /* adjust existing stroke */ - gps->totpoints= i; - gps->points= MEM_callocN(sizeof(bGPDspoint)*gps->totpoints, "gp_stroke_points"); - memcpy(gps->points, pt_tmp, sizeof(bGPDspoint)*i); - - /* free temp buffer */ - MEM_freeN(pt_tmp); - - /* nothing left in stroke, so stop */ - return 1; - } -} - -/* eraser tool - check if part of stroke occurs within last segment drawn by eraser */ -static short gp_stroke_eraser_strokeinside (short mval[], short mvalo[], short rad, short x0, short y0, short x1, short y1) -{ - /* simple within-radius check for now */ - if (edge_inside_circle(mval[0], mval[1], rad, x0, y0, x1, y1)) - return 1; - - /* not inside */ - return 0; -} - -/* eraser tool - evaluation per stroke */ -static void gp_stroke_eraser_dostroke (tGPsdata *p, short mval[], short mvalo[], short rad, rcti *rect, bGPDframe *gpf, bGPDstroke *gps) -{ - bGPDspoint *pt1, *pt2; - int x0=0, y0=0, x1=0, y1=0; - short xyval[2]; - int i; - - if (gps->totpoints == 0) { - /* just free stroke */ - if (gps->points) - MEM_freeN(gps->points); - BLI_freelinkN(&gpf->strokes, gps); - } - else if (gps->totpoints == 1) { - /* get coordinates */ - if (gps->flag & GP_STROKE_3DSPACE) { - project_short(p->ar, &gps->points->x, xyval); - x0= xyval[0]; - y0= xyval[1]; - } - else if (gps->flag & GP_STROKE_2DSPACE) { - UI_view2d_view_to_region(p->v2d, gps->points->x, gps->points->y, &x0, &y0); - } - else if (gps->flag & GP_STROKE_2DIMAGE) { - int offsx, offsy, sizex, sizey; - - /* get stored settings */ - sizex= p->im2d_settings.sizex; - sizey= p->im2d_settings.sizey; - offsx= p->im2d_settings.offsx; - offsy= p->im2d_settings.offsy; - - /* calculate new points */ - x0= (short)((gps->points->x * sizex) + offsx); - y0= (short)((gps->points->y * sizey) + offsy); - } - else { - x0= (short)(gps->points->x / 1000 * p->sa->winx); - y0= (short)(gps->points->y / 1000 * p->sa->winy); - } - - /* do boundbox check first */ - if (BLI_in_rcti(rect, x0, y0)) { - /* only check if point is inside */ - if ( ((x0-mval[0])*(x0-mval[0]) + (y0-mval[1])*(y0-mval[1])) <= rad*rad ) { - /* free stroke */ - MEM_freeN(gps->points); - BLI_freelinkN(&gpf->strokes, gps); - } - } - } - else { - /* loop over the points in the stroke, checking for intersections - * - an intersection will require the stroke to be split - */ - for (i=0; (i+1) < gps->totpoints; i++) { - /* get points to work with */ - pt1= gps->points + i; - pt2= gps->points + i + 1; - - /* get coordinates */ - if (gps->flag & GP_STROKE_3DSPACE) { - project_short(p->ar, &pt1->x, xyval); - x0= xyval[0]; - y0= xyval[1]; - - project_short(p->ar, &pt2->x, xyval); - x1= xyval[0]; - y1= xyval[1]; - } - else if (gps->flag & GP_STROKE_2DSPACE) { - UI_view2d_view_to_region(p->v2d, pt1->x, pt1->y, &x0, &y0); - - UI_view2d_view_to_region(p->v2d, pt2->x, pt2->y, &x1, &y1); - } - else if (gps->flag & GP_STROKE_2DIMAGE) { - int offsx, offsy, sizex, sizey; - - /* get stored settings */ - sizex= p->im2d_settings.sizex; - sizey= p->im2d_settings.sizey; - offsx= p->im2d_settings.offsx; - offsy= p->im2d_settings.offsy; - - /* calculate new points */ - x0= (short)((pt1->x * sizex) + offsx); - y0= (short)((pt1->y * sizey) + offsy); - - x1= (short)((pt2->x * sizex) + offsx); - y1= (short)((pt2->y * sizey) + offsy); - } - else { - x0= (short)(pt1->x / 1000 * p->sa->winx); - y0= (short)(pt1->y / 1000 * p->sa->winy); - x1= (short)(pt2->x / 1000 * p->sa->winx); - y1= (short)(pt2->y / 1000 * p->sa->winy); - } - - /* check that point segment of the boundbox of the eraser stroke */ - if (BLI_in_rcti(rect, x0, y0) || BLI_in_rcti(rect, x1, y1)) { - /* check if point segment of stroke had anything to do with - * eraser region (either within stroke painted, or on its lines) - * - this assumes that linewidth is irrelevant - */ - if (gp_stroke_eraser_strokeinside(mval, mvalo, rad, x0, y0, x1, y1)) { - /* if function returns true, break this loop (as no more point to check) */ - if (gp_stroke_eraser_splitdel(gpf, gps, i)) - break; - } - } - } - } -} - -/* erase strokes which fall under the eraser strokes */ -static void gp_stroke_doeraser (tGPsdata *p) -{ - bGPDframe *gpf= p->gpf; - bGPDstroke *gps, *gpn; - rcti rect; - - /* rect is rectangle of eraser */ - rect.xmin= p->mval[0] - p->radius; - rect.ymin= p->mval[1] - p->radius; - rect.xmax= p->mval[0] + p->radius; - rect.ymax= p->mval[1] + p->radius; - - /* loop over strokes, checking segments for intersections */ - for (gps= gpf->strokes.first; gps; gps= gpn) { - gpn= gps->next; - gp_stroke_eraser_dostroke(p, p->mval, p->mvalo, p->radius, &rect, gpf, gps); - } -} - -/* ---------- 'Paint' Tool ------------ */ - -/* init new painting session */ -static void gp_session_initpaint (bContext *C, tGPsdata *p) -{ - ScrArea *curarea= CTX_wm_area(C); - ARegion *ar= CTX_wm_region(C); - - /* clear previous data (note: is on stack) */ - memset(p, 0, sizeof(tGPsdata)); - - /* make sure the active view (at the starting time) is a 3d-view */ - if (curarea == NULL) { - p->status= GP_STATUS_ERROR; - if (G.f & G_DEBUG) - printf("Error: No active view for painting \n"); - return; - } - - /* pass on current scene */ - p->scene= CTX_data_scene(C); - - switch (curarea->spacetype) { - /* supported views first */ - case SPACE_VIEW3D: - { - View3D *v3d= curarea->spacedata.first; - - /* set current area */ - p->sa= curarea; - p->ar= ar; - - /* check that gpencil data is allowed to be drawn */ - if ((v3d->flag2 & V3D_DISPGP)==0) { - p->status= GP_STATUS_ERROR; - if (G.f & G_DEBUG) - printf("Error: In active view, Grease Pencil not shown \n"); - return; - } - } - break; - case SPACE_NODE: - { - SpaceNode *snode= curarea->spacedata.first; - - /* set current area */ - p->sa= curarea; - p->ar= ar; - p->v2d= &ar->v2d; - - /* check that gpencil data is allowed to be drawn */ - if ((snode->flag & SNODE_DISPGP)==0) { - p->status= GP_STATUS_ERROR; - if (G.f & G_DEBUG) - printf("Error: In active view, Grease Pencil not shown \n"); - return; - } - } - break; - case SPACE_SEQ: - { - SpaceSeq *sseq= curarea->spacedata.first; - - /* set current area */ - p->sa= curarea; - p->ar= ar; - p->v2d= &ar->v2d; - - /* check that gpencil data is allowed to be drawn */ - if (sseq->mainb == SEQ_DRAW_SEQUENCE) { - p->status= GP_STATUS_ERROR; - if (G.f & G_DEBUG) - printf("Error: In active view (sequencer), active mode doesn't support Grease Pencil \n"); - return; - } - if ((sseq->flag & SEQ_DRAW_GPENCIL)==0) { - p->status= GP_STATUS_ERROR; - if (G.f & G_DEBUG) - printf("Error: In active view, Grease Pencil not shown \n"); - return; - } - } - break; - case SPACE_IMAGE: - { - SpaceImage *sima= curarea->spacedata.first; - - /* set the current area */ - p->sa= curarea; - p->ar= ar; - p->v2d= &ar->v2d; - p->ibuf= BKE_image_get_ibuf(sima->image, &sima->iuser); - - /* check that gpencil data is allowed to be drawn */ - if ((sima->flag & SI_DISPGP)==0) { - p->status= GP_STATUS_ERROR; - if (G.f & G_DEBUG) - printf("Error: In active view, Grease Pencil not shown \n"); - return; - } - } - break; - /* unsupported views */ - default: - { - p->status= GP_STATUS_ERROR; - if (G.f & G_DEBUG) - printf("Error: Active view not appropriate for Grease Pencil drawing \n"); - return; - } - break; - } - - /* get gp-data */ - p->gpd= gpencil_data_getactive(p->sa); - if (p->gpd == NULL) { - short ok; - - p->gpd= gpencil_data_addnew(); - ok= gpencil_data_setactive(p->sa, p->gpd); - - /* most of the time, the following check isn't needed */ - if (ok == 0) { - /* free gpencil data as it can't be used */ - free_gpencil_data(p->gpd); - p->gpd= NULL; - p->status= GP_STATUS_ERROR; - if (G.f & G_DEBUG) - printf("Error: Could not assign newly created Grease Pencil data to active area \n"); - return; - } - } - - /* set edit flags */ - G.f |= G_GREASEPENCIL; - - /* clear out buffer (stored in gp-data) in case something contaminated it */ - gp_session_validatebuffer(p); - - /* set 'default' im2d_settings just in case something that uses this doesn't set it */ - p->im2d_settings.sizex= 1; - p->im2d_settings.sizey= 1; -} - -/* cleanup after a painting session */ -static void gp_session_cleanup (tGPsdata *p) -{ - bGPdata *gpd= p->gpd; - - /* error checking */ - if (gpd == NULL) - return; - - /* free stroke buffer */ - if (gpd->sbuffer) { - MEM_freeN(gpd->sbuffer); - gpd->sbuffer= NULL; - } - - /* clear flags */ - gpd->sbuffer_size= 0; - gpd->sbuffer_sflag= 0; -} - -/* init new stroke */ -static void gp_paint_initstroke (tGPsdata *p, short paintmode) -{ - /* get active layer (or add a new one if non-existent) */ - p->gpl= gpencil_layer_getactive(p->gpd); - if (p->gpl == NULL) - p->gpl= gpencil_layer_addnew(p->gpd); - if (p->gpl->flag & GP_LAYER_LOCKED) { - p->status= GP_STATUS_ERROR; - if (G.f & G_DEBUG) - printf("Error: Cannot paint on locked layer \n"); - return; - } - - /* get active frame (add a new one if not matching frame) */ - p->gpf= gpencil_layer_getframe(p->gpl, p->scene->r.cfra, 1); - if (p->gpf == NULL) { - p->status= GP_STATUS_ERROR; - if (G.f & G_DEBUG) - printf("Error: No frame created (gpencil_paint_init) \n"); - return; - } - else - p->gpf->flag |= GP_FRAME_PAINT; - - /* set 'eraser' for this stroke if using eraser */ - p->paintmode= paintmode; - if (p->paintmode == GP_PAINTMODE_ERASER) - p->gpd->sbuffer_sflag |= GP_STROKE_ERASER; - - /* check if points will need to be made in view-aligned space */ - if (p->gpd->flag & GP_DATA_VIEWALIGN) { - switch (p->sa->spacetype) { - case SPACE_VIEW3D: - { - View3D *v3d= (View3D *)p->sa->spacedata.first; - RegionView3D *rv3d= NULL; // XXX - float *fp= give_cursor(p->scene, v3d); - - initgrabz(rv3d, fp[0], fp[1], fp[2]); - - p->gpd->sbuffer_sflag |= GP_STROKE_3DSPACE; - } - break; - case SPACE_NODE: - { - p->gpd->sbuffer_sflag |= GP_STROKE_2DSPACE; - } - break; - case SPACE_SEQ: - { - SpaceSeq *sseq= (SpaceSeq *)p->sa->spacedata.first; - int rectx, recty; - float zoom, zoomx, zoomy; - - /* set draw 2d-stroke flag */ - p->gpd->sbuffer_sflag |= GP_STROKE_2DIMAGE; - - /* calculate zoom factor */ - zoom= (float)(SEQ_ZOOM_FAC(sseq->zoom)); - if (sseq->mainb == SEQ_DRAW_IMG_IMBUF) { - zoomx = zoom * ((float)p->scene->r.xasp / (float)p->scene->r.yasp); - zoomy = zoom; - } - else - zoomx = zoomy = zoom; - - /* calculate rect size to use to calculate the size of the drawing area - * - We use the size of the output image not the size of the ibuf being shown - * as it is too messy getting the ibuf (and could be too slow). This should be - * a reasonable for most cases anyway. - */ - rectx= (p->scene->r.size * p->scene->r.xsch) / 100; - recty= (p->scene->r.size * p->scene->r.ysch) / 100; - - /* set offset and scale values for opertations to use */ - p->im2d_settings.sizex= (int)(zoomx * rectx); - p->im2d_settings.sizey= (int)(zoomy * recty); - p->im2d_settings.offsx= (int)((p->sa->winx-p->im2d_settings.sizex)/2 + sseq->xof); - p->im2d_settings.offsy= (int)((p->sa->winy-p->im2d_settings.sizey)/2 + sseq->yof); - } - break; - case SPACE_IMAGE: - { - /* check if any ibuf available */ - if (p->ibuf) - p->gpd->sbuffer_sflag |= GP_STROKE_2DSPACE; - } - break; - } - } -} - -/* finish off a stroke (clears buffer, but doesn't finish the paint operation) */ -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); - - /* simplify stroke before transferring? */ - gp_stroke_simplify(p); - - /* transfer stroke to frame */ - gp_stroke_newfrombuffer(p); - } - - /* clean up buffer now */ - gp_session_validatebuffer(p); -} - -/* finish off stroke painting operation */ -static void gp_paint_cleanup (tGPsdata *p) -{ - /* finish off a stroke */ - gp_paint_strokeend(p); - - /* "unlock" frame */ - p->gpf->flag &= ~GP_FRAME_PAINT; - - /* add undo-push so stroke can be undone */ - /* FIXME: currently disabled, as it's impossible to get this working nice - * as gpenci data is on currently screen-level (which isn't saved to undo files) - */ - //BIF_undo_push("GPencil Stroke"); - - /* force redraw after drawing action */ - // XXX force_draw_plus(SPACE_ACTION, 0); -} - -/* -------- */ - -/* main call to paint a new stroke */ -// XXX will become modal(), gets event, includes all info! -short gpencil_paint (bContext *C, short paintmode) -{ - tGPsdata p; - short ok = GP_STROKEADD_NORMAL; - - /* init paint-data */ - gp_session_initpaint(C, &p); - if (p.status == GP_STATUS_ERROR) { - gp_session_cleanup(&p); - return 0; - } - gp_paint_initstroke(&p, paintmode); - if (p.status == GP_STATUS_ERROR) { - gp_session_cleanup(&p); - return 0; - } - - /* set cursor to indicate drawing */ - // XXX (cursor callbacks in regiontype) setcursor_space(p.sa->spacetype, CURSOR_VPAINT); - - /* init drawing-device settings */ - // XXX getmouseco_areawin(p.mval); - // XXX p.pressure = get_pressure(); - - p.mvalo[0]= p.mval[0]; - p.mvalo[1]= p.mval[1]; - p.opressure= p.pressure; - - /* 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) - // XXX draw_sel_circle(p.mval, NULL, p.radius, p.radius, 0); // draws frontbuffer, but sets backbuf again - - /* only allow painting of single 'dots' if: - * - pressure is not excessive (as it can be on some windows tablets) - * - draw-mode for active datablock is turned on - * - not erasing - */ - if (paintmode != GP_PAINTMODE_ERASER) { - if (!(p.pressure >= 0.99f) || (p.gpd->flag & GP_DATA_EDITPAINT)) { - gp_stroke_addpoint(&p, p.mval, p.pressure); - } - } - - /* XXX paint loop */ - if(0) { - /* get current user input */ - // XXX getmouseco_areawin(p.mval); - // XXX p.pressure = get_pressure(); - - /* only add current point to buffer if mouse moved (otherwise wait until it does) */ - if (paintmode == GP_PAINTMODE_ERASER) { - /* do 'live' erasing now */ - gp_stroke_doeraser(&p); - - // XXX draw_sel_circle(p.mval, p.mvalo, p.radius, p.radius, 0); - // XXX force_draw(0); - - 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, p.pressure); - - /* handle errors while adding point */ - if ((ok == GP_STROKEADD_FULL) || (ok == GP_STROKEADD_OVERFLOW)) { - /* finish off old stroke */ - gp_paint_strokeend(&p); - - /* start a new stroke, starting from previous point */ - 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... */ - error("Cannot paint stroke"); - p.status = GP_STATUS_ERROR; - - if (G.f & G_DEBUG) - printf("Error: Grease-Pencil Paint - Add Point Invalid \n"); - // XXX break; - } - // XXX force_draw(0); - - p.mvalo[0]= p.mval[0]; - p.mvalo[1]= p.mval[1]; - p.opressure= p.pressure; - } - - /* do mouse checking at the end, so don't check twice, and potentially - * miss a short tap - */ - } - - /* clear edit flags */ - G.f &= ~G_GREASEPENCIL; - - /* restore cursor to indicate end of drawing */ - // XXX (cursor callbacks in regiontype) setcursor_space(p.sa->spacetype, CURSOR_STD); - - /* check size of buffer before cleanup, to determine if anything happened here */ - if (paintmode == GP_PAINTMODE_ERASER) { - ok= 1; /* assume that we did something... */ - // XXX draw_sel_circle(NULL, p.mvalo, 0, p.radius, 0); - } - else - ok= p.gpd->sbuffer_size; - - /* cleanup */ - gp_paint_cleanup(&p); - gp_session_cleanup(&p); - - /* done! return if a stroke was successfully added */ - return ok; -} - - -/* All event (loops) handling checking if stroke drawing should be initiated - * should call this function. - */ -short gpencil_do_paint (bContext *C) -{ - ScrArea *sa= CTX_wm_area(C); - bGPdata *gpd = gpencil_data_getactive(sa); - short retval= 0; - int alt= 0, shift= 0, mbut= 0; // XXX - - /* check if possible to do painting */ - if (gpd == NULL) - return 0; - - /* currently, we will only 'paint' if: - * 1. draw-mode on gpd is set (for accessibility reasons) - * a) single dots are only available by this method if a single click is made - * b) a straight line is drawn if ctrl-modifier is held (check is done when stroke is converted!) - * 2. if shift-modifier is held + lmb -> 'quick paint' - * - * OR - * - * draw eraser stroke if: - * 1. using the eraser on a tablet - * 2. draw-mode on gpd is set (for accessiblity reasons) - * (eraser is mapped to right-mouse) - * 3. Alt + 'select' mouse-button - * i.e. if LMB = select: Alt-LMB - * if RMB = select: Alt-RMB - */ - if (get_activedevice() == 2) { - /* eraser on a tablet - always try to erase strokes */ - retval = gpencil_paint(C, GP_PAINTMODE_ERASER); - } - else if (gpd->flag & GP_DATA_EDITPAINT) { - /* try to paint/erase */ - if (mbut == L_MOUSE) - retval = gpencil_paint(C, GP_PAINTMODE_DRAW); - else if (mbut == R_MOUSE) - retval = gpencil_paint(C, GP_PAINTMODE_ERASER); - } - else if (!(gpd->flag & GP_DATA_LMBPLOCK)) { - /* try to paint/erase as not locked */ - if (shift && (mbut == L_MOUSE)) { - retval = gpencil_paint(C, GP_PAINTMODE_DRAW); - } - else if (alt) { - if ((U.flag & USER_LMOUSESELECT) && (mbut == L_MOUSE)) - retval = gpencil_paint(C, GP_PAINTMODE_ERASER); - else if (!(U.flag & USER_LMOUSESELECT) && (mbut == R_MOUSE)) - retval = gpencil_paint(C, GP_PAINTMODE_ERASER); - } - } - - /* return result of trying to paint */ - return retval; -} - -/* ************************************************** */ |