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
path: root/source
diff options
context:
space:
mode:
authorTon Roosendaal <ton@blender.org>2009-01-14 19:13:50 +0300
committerTon Roosendaal <ton@blender.org>2009-01-14 19:13:50 +0300
commit9aec7aaf6c560ddc0c2dd1c553c809f9b1e706d5 (patch)
tree53e851c0a106b6bb135ab0ecf37b5fa9c3055349 /source
parent3e756ca0a616a467d64521d7c82bb8229a38447c (diff)
2.5
GPencil code back! Joshua will probably take this further. I've added XXX notes where fixes are required. Also note that ED_gpencil.h has all old exports, which is probably not needed, most can go to gpencil_intern.h Also added Makefile fixes for sculpt
Diffstat (limited to 'source')
-rw-r--r--source/Makefile1
-rw-r--r--source/blender/editors/Makefile2
-rw-r--r--source/blender/editors/SConscript2
-rw-r--r--source/blender/editors/gpencil/drawgpencil.c1061
-rw-r--r--source/blender/editors/gpencil/editaction_gpencil.c739
-rw-r--r--source/blender/editors/gpencil/gpencil.c2162
-rw-r--r--source/blender/editors/gpencil/gpencil_intern.h81
-rw-r--r--source/blender/editors/include/ED_armature.h3
-rw-r--r--source/blender/editors/include/ED_gpencil.h101
-rw-r--r--source/blender/editors/include/ED_sequencer.h31
-rw-r--r--source/blender/editors/include/ED_view3d.h2
-rw-r--r--source/blender/editors/sculpt/Makefile57
-rw-r--r--source/blender/editors/space_sequencer/sequencer_draw.c1
-rw-r--r--source/blender/editors/space_sequencer/sequencer_intern.h2
-rw-r--r--source/blender/editors/space_view3d/view3d_header.c6
15 files changed, 4244 insertions, 7 deletions
diff --git a/source/Makefile b/source/Makefile
index 818aa07218e..3c127544505 100644
--- a/source/Makefile
+++ b/source/Makefile
@@ -241,6 +241,7 @@ PULIB += $(OCGDIR)/blender/ed_object/libed_object.a
PULIB += $(OCGDIR)/blender/ed_curve/libed_curve.a
PULIB += $(OCGDIR)/blender/ed_armature/libed_armature.a
PULIB += $(OCGDIR)/blender/ed_mesh/libed_mesh.a
+PULIB += $(OCGDIR)/blender/ed_sculpt/libed_sculpt.a
PULIB += $(OCGDIR)/blender/ed_animation/libed_animation.a
PULIB += $(OCGDIR)/blender/ed_transform/libed_transform.a
PULIB += $(OCGDIR)/blender/ed_util/libed_util.a
diff --git a/source/blender/editors/Makefile b/source/blender/editors/Makefile
index ed59a82d2b2..b7b2096fea9 100644
--- a/source/blender/editors/Makefile
+++ b/source/blender/editors/Makefile
@@ -29,6 +29,6 @@
# Bounces make to subdirectories.
SOURCEDIR = source/blender/editors
-DIRS = armature mesh animation object datafiles transform screen curve gpencil physics preview space_outliner space_time space_view3d interface util space_api space_ipo space_image space_node space_buttons space_info space_file space_sound space_action space_nla space_script space_text space_sequencer
+DIRS = armature mesh animation object sculpt datafiles transform screen curve gpencil physics preview space_outliner space_time space_view3d interface util space_api space_ipo space_image space_node space_buttons space_info space_file space_sound space_action space_nla space_script space_text space_sequencer
include nan_subdirs.mk
diff --git a/source/blender/editors/SConscript b/source/blender/editors/SConscript
index dba6044ea66..9c77d44945a 100644
--- a/source/blender/editors/SConscript
+++ b/source/blender/editors/SConscript
@@ -11,7 +11,7 @@ SConscript(['datafiles/SConscript',
'mesh/SConscript',
'object/SConscript',
'curve/SConscript',
- #'gpencil/SConscript', enable this when there is actually code in gpencil/
+ 'gpencil/SConscript',
#'physics/SConscript', enable this when there is actually code in physics/
#'preview/SConscript', enable this when there is actually code in preview/
'space_buttons/SConscript',
diff --git a/source/blender/editors/gpencil/drawgpencil.c b/source/blender/editors/gpencil/drawgpencil.c
new file mode 100644
index 00000000000..b467450673d
--- /dev/null
+++ b/source/blender/editors/gpencil/drawgpencil.c
@@ -0,0 +1,1061 @@
+/**
+ * $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 <float.h>
+
+#include "MEM_guardedalloc.h"
+
+#include "IMB_imbuf.h"
+#include "IMB_imbuf_types.h"
+
+#include "BMF_Api.h"
+
+#include "BLI_arithb.h"
+#include "BLI_blenlib.h"
+
+#include "DNA_gpencil_types.h"
+#include "DNA_listBase.h"
+#include "DNA_scene_types.h"
+#include "DNA_screen_types.h"
+#include "DNA_space_types.h"
+#include "DNA_userdef_types.h"
+#include "DNA_view3d_types.h"
+
+#include "BKE_blender.h"
+#include "BKE_context.h"
+#include "BKE_global.h"
+#include "BKE_sequence.h"
+#include "BKE_utildefines.h"
+
+#include "PIL_time.h"
+
+#include "WM_api.h"
+#include "WM_types.h"
+
+#include "BIF_gl.h"
+#include "BIF_glutil.h"
+
+#include "ED_gpencil.h"
+#include "ED_sequencer.h"
+#include "ED_util.h"
+
+#include "UI_interface.h"
+#include "UI_resources.h"
+#include "UI_view2d.h"
+
+#include "gpencil_intern.h"
+
+/* ************************************************** */
+/* GREASE PENCIL PANEL-UI DRAWING */
+
+/* Every space which implements Grease-Pencil functionality should have a panel
+ * for the settings. All of the space-dependent parts should be coded in the panel
+ * code for that space, but the rest is all handled by generic panel here.
+ */
+
+/* ------- Callbacks ----------- */
+/* These are just 'dummy wrappers' around gpencil api calls */
+
+#if 0
+// XXX
+/* make layer active one after being clicked on */
+void gp_ui_activelayer_cb (void *gpd, void *gpl)
+{
+ gpencil_layer_setactive(gpd, gpl);
+
+ scrarea_queue_winredraw(curarea);
+ allqueue(REDRAWACTION, 0);
+}
+
+/* rename layer and set active */
+void gp_ui_renamelayer_cb (void *gpd_arg, void *gpl_arg)
+{
+ bGPdata *gpd= (bGPdata *)gpd_arg;
+ bGPDlayer *gpl= (bGPDlayer *)gpl_arg;
+
+ BLI_uniquename(&gpd->layers, gpl, "GP_Layer", offsetof(bGPDlayer, info[0]), 128);
+ gpencil_layer_setactive(gpd, gpl);
+
+ scrarea_queue_winredraw(curarea);
+ allqueue(REDRAWACTION, 0);
+}
+
+/* add a new layer */
+void gp_ui_addlayer_cb (void *gpd, void *dummy)
+{
+ gpencil_layer_addnew(gpd);
+
+ scrarea_queue_winredraw(curarea);
+ allqueue(REDRAWACTION, 0);
+}
+
+/* delete active layer */
+void gp_ui_dellayer_cb (void *gpd, void *dummy)
+{
+ gpencil_layer_delactive(gpd);
+
+ scrarea_queue_winredraw(curarea);
+ allqueue(REDRAWACTION, 0);
+}
+
+/* delete last stroke of active layer */
+void gp_ui_delstroke_cb (void *gpd, void *gpl)
+{
+ bGPDframe *gpf= gpencil_layer_getframe(gpl, CFRA, 0);
+
+ gpencil_layer_setactive(gpd, gpl);
+ gpencil_frame_delete_laststroke(gpf);
+
+ scrarea_queue_winredraw(curarea);
+}
+
+/* delete active frame of active layer */
+void gp_ui_delframe_cb (void *gpd, void *gpl)
+{
+ bGPDframe *gpf= gpencil_layer_getframe(gpl, CFRA, 0);
+
+ gpencil_layer_setactive(gpd, gpl);
+ gpencil_layer_delframe(gpl, gpf);
+
+ scrarea_queue_winredraw(curarea);
+ allqueue(REDRAWACTION, 0);
+}
+
+/* convert the active layer to geometry */
+void gp_ui_convertlayer_cb (void *gpd, void *gpl)
+{
+ gpencil_layer_setactive(gpd, gpl);
+ gpencil_convert_menu();
+
+ scrarea_queue_winredraw(curarea);
+}
+#endif
+
+/* ------- Drawing Code ------- */
+
+#if 0
+/* XXX */
+/* draw the controls for a given layer */
+static void gp_drawui_layer (uiBlock *block, bGPdata *gpd, bGPDlayer *gpl, short *xco, short *yco)
+{
+ uiBut *but;
+ short active= (gpl->flag & GP_LAYER_ACTIVE);
+ short width= 314;
+ short height;
+ int rb_col;
+
+ /* unless button has own callback, it adds this callback to button */
+ uiBlockSetFunc(block, gp_ui_activelayer_cb, gpd, gpl);
+
+ /* draw header */
+ {
+ uiBlockSetEmboss(block, UI_EMBOSSN);
+
+ /* rounded header */
+ if (active) uiBlockSetCol(block, TH_BUT_ACTION);
+ rb_col= (active)?-20:20;
+ uiDefBut(block, ROUNDBOX, B_REDR, "", *xco-8, *yco-2, width, 24, NULL, 5.0, 0.0, 15.0, (float)(rb_col-20), "");
+ if (active) uiBlockSetCol(block, TH_AUTO);
+
+ /* lock toggle */
+ uiDefIconButBitI(block, ICONTOG, GP_LAYER_LOCKED, B_REDR, ICON_UNLOCKED, *xco-7, *yco-1, 20, 20, &gpl->flag, 0.0, 0.0, 0, 0, "Layer cannot be modified");
+ }
+
+ /* when layer is locked or hidden, only draw header */
+ if (gpl->flag & (GP_LAYER_LOCKED|GP_LAYER_HIDE)) {
+ char name[256]; /* gpl->info is 128, but we need space for 'locked/hidden' as well */
+
+ height= 0;
+
+ /* visibility button (only if hidden but not locked!) */
+ if ((gpl->flag & GP_LAYER_HIDE) && !(gpl->flag & GP_LAYER_LOCKED))
+ uiDefIconButBitI(block, ICONTOG, GP_LAYER_HIDE, B_REDR, ICON_RESTRICT_VIEW_OFF, *xco+12, *yco-1, 20, 20, &gpl->flag, 0.0, 0.0, 0, 0, "Visibility of layer");
+
+ /* name */
+ if (gpl->flag & GP_LAYER_HIDE)
+ sprintf(name, "%s (Hidden)", gpl->info);
+ else
+ sprintf(name, "%s (Locked)", gpl->info);
+ uiDefBut(block, LABEL, 1, name, *xco+35, *yco, 240, 20, NULL, 0.0, 0.0, 0, 0, "Short description of what this layer is for (optional)");
+
+ /* delete button (only if hidden but not locked!) */
+ if ((gpl->flag & GP_LAYER_HIDE) & !(gpl->flag & GP_LAYER_LOCKED)) {
+ but= uiDefIconBut(block, BUT, B_REDR, ICON_X, *xco+(width-30), *yco, 19, 19, NULL, 0.0, 0.0, 0.0, 0.0, "Delete layer");
+ uiButSetFunc(but, gp_ui_dellayer_cb, gpd, NULL);
+ }
+ uiBlockSetEmboss(block, UI_EMBOSS);
+ }
+ else {
+ height= 97;
+
+ /* draw rest of header */
+ {
+ /* visibility button */
+ uiDefIconButBitI(block, ICONTOG, GP_LAYER_HIDE, B_REDR, ICON_RESTRICT_VIEW_OFF, *xco+12, *yco-1, 20, 20, &gpl->flag, 0.0, 0.0, 0, 0, "Visibility of layer");
+
+ uiBlockSetEmboss(block, UI_EMBOSS);
+
+ /* name */
+ but= uiDefButC(block, TEX, B_REDR, "Info:", *xco+36, *yco, 240, 19, gpl->info, 0, 127, 0, 0, "Short description of what this layer is for (optional)");
+ uiButSetFunc(but, gp_ui_renamelayer_cb, gpd, gpl);
+
+ /* delete 'button' */
+ uiBlockSetEmboss(block, UI_EMBOSSN);
+
+ but= uiDefIconBut(block, BUT, B_REDR, ICON_X, *xco+(width-30), *yco, 19, 19, NULL, 0.0, 0.0, 0.0, 0.0, "Delete layer");
+ uiButSetFunc(but, gp_ui_dellayer_cb, gpd, NULL);
+
+ uiBlockSetEmboss(block, UI_EMBOSS);
+ }
+
+ /* draw backdrop */
+ if (active) uiBlockSetCol(block, TH_BUT_ACTION);
+ uiDefBut(block, ROUNDBOX, B_DIFF, "", *xco-8, *yco-height, width, height-1, NULL, 5.0, 0.0, 12.0, (float)rb_col, "");
+ if (active) uiBlockSetCol(block, TH_AUTO);
+
+ /* draw settings */
+ {
+ /* color */
+ uiBlockBeginAlign(block);
+ uiDefButF(block, COL, B_REDR, "", *xco, *yco-26, 150, 19, gpl->color, 0, 0, 0, 0, "Color to use for all strokes on this Grease Pencil Layer");
+ uiDefButF(block, NUMSLI, B_REDR, "Opacity: ", *xco,*yco-45,150,19, &gpl->color[3], 0.3f, 1.0f, 0, 0, "Visibility of stroke (0.3 to 1.0)");
+ uiBlockEndAlign(block);
+
+ /* stroke thickness */
+ uiDefButS(block, NUMSLI, B_REDR, "Thickness:", *xco, *yco-75, 150, 20, &gpl->thickness, 1, 10, 0, 0, "Thickness of strokes (in pixels)");
+
+ /* debugging options */
+ if (G.f & G_DEBUG) {
+ uiDefButBitI(block, TOG, GP_LAYER_DRAWDEBUG, B_REDR, "Show Points", *xco, *yco-95, 150, 20, &gpl->flag, 0, 0, 0, 0, "Show points which form the strokes");
+ }
+
+ /* onion-skinning */
+ uiBlockBeginAlign(block);
+ uiDefButBitI(block, TOG, GP_LAYER_ONIONSKIN, B_REDR, "Onion-Skin", *xco+160, *yco-26, 140, 20, &gpl->flag, 0, 0, 0, 0, "Ghost frames on either side of frame");
+ uiDefButS(block, NUMSLI, B_REDR, "GStep:", *xco+160, *yco-46, 140, 20, &gpl->gstep, 0, 120, 0, 0, "Max number of frames on either side of active frame to show (0 = just 'first' available sketch on either side)");
+ uiBlockEndAlign(block);
+
+ /* options */
+ uiBlockBeginAlign(block);
+ if (curarea->spacetype == SPACE_VIEW3D) {
+ but= uiDefBut(block, BUT, B_REDR, "Convert to...", *xco+160, *yco-75, 140, 20, NULL, 0, 0, 0, 0, "Converts this layer's strokes to geometry (Hotkey = Alt-Shift-C)");
+ uiButSetFunc(but, gp_ui_convertlayer_cb, gpd, gpl);
+ }
+ else {
+ but= uiDefBut(block, BUT, B_REDR, "Del Active Frame", *xco+160, *yco-75, 140, 20, NULL, 0, 0, 0, 0, "Erases the the active frame for this layer (Hotkey = Alt-XKEY/DEL)");
+ uiButSetFunc(but, gp_ui_delframe_cb, gpd, gpl);
+ }
+
+ but= uiDefBut(block, BUT, B_REDR, "Del Last Stroke", *xco+160, *yco-95, 140, 20, NULL, 0, 0, 0, 0, "Erases the last stroke from the active frame (Hotkey = Alt-XKEY/DEL)");
+ uiButSetFunc(but, gp_ui_delstroke_cb, gpd, gpl);
+ uiBlockEndAlign(block);
+ }
+ }
+
+ /* adjust height for new to start */
+ (*yco) -= (height + 27);
+}
+#endif
+/* Draw the contents for a grease-pencil panel. This assumes several things:
+ * - that panel has been created, is 318 x 204. max yco is 225
+ * - that a toggle for turning on/off gpencil drawing is 150 x 20, starting from (10,225)
+ * which is basically the top left-hand corner
+ * It will return the amount of extra space to extend the panel by
+ */
+short draw_gpencil_panel (uiBlock *block, bGPdata *gpd, ScrArea *sa)
+{
+#if 0
+ uiBut *but;
+ bGPDlayer *gpl;
+ short xco= 10, yco= 170;
+
+ /* draw gpd settings first */
+ {
+ /* add new layer buttons */
+ but= uiDefBut(block, BUT, B_REDR, "Add New Layer", 10,205,150,20, 0, 0, 0, 0, 0, "Adds a new Grease Pencil Layer");
+ uiButSetFunc(but, gp_ui_addlayer_cb, gpd, NULL);
+
+
+ /* show override lmb-clicks button + painting lock */
+ uiBlockBeginAlign(block);
+ if ((gpd->flag & GP_DATA_EDITPAINT)==0) {
+ uiDefButBitI(block, TOG, GP_DATA_EDITPAINT, B_REDR, "Draw Mode", 170, 225, 130, 20, &gpd->flag, 0, 0, 0, 0, "Interpret click-drag as new strokes");
+
+ uiBlockSetCol(block, TH_BUT_SETTING);
+ uiDefIconButBitI(block, ICONTOG, GP_DATA_LMBPLOCK, B_REDR, ICON_UNLOCKED, 300, 225, 20, 20, &gpd->flag, 0.0, 0.0, 0, 0, "Painting cannot occur with Shift-LMB (when making selections)");
+ uiBlockSetCol(block, TH_AUTO);
+ }
+ else
+ uiDefButBitI(block, TOG, GP_DATA_EDITPAINT, B_REDR, "Draw Mode", 170, 225, 150, 20, &gpd->flag, 0, 0, 0, 0, "Interpret click-drag as new strokes");
+ uiBlockEndAlign(block);
+
+ /* 'view align' button (naming depends on context) */
+ if (sa->spacetype == SPACE_VIEW3D)
+ uiDefButBitI(block, TOG, GP_DATA_VIEWALIGN, B_REDR, "Sketch in 3D", 170, 205, 150, 20, &gpd->flag, 0, 0, 0, 0, "New strokes are added in 3D-space");
+ else
+ uiDefButBitI(block, TOG, GP_DATA_VIEWALIGN, B_REDR, "Stick to View", 170, 205, 150, 20, &gpd->flag, 0, 0, 0, 0, "New strokes are added on 2d-canvas");
+ }
+
+ /* draw for each layer */
+ for (gpl= gpd->layers.first; gpl; gpl= gpl->next) {
+ gp_drawui_layer(block, gpd, gpl, &xco, &yco);
+ }
+
+ /* return new height if necessary */
+ return (yco < 0) ? (204 - yco) : 204;
+#endif
+ return 0;
+}
+
+/* ************************************************** */
+/* GREASE PENCIL DRAWING */
+
+/* ----- General Defines ------ */
+
+/* flags for sflag */
+enum {
+ GP_DRAWDATA_NOSTATUS = (1<<0), /* don't draw status info */
+ GP_DRAWDATA_ONLY3D = (1<<1), /* only draw 3d-strokes */
+ GP_DRAWDATA_ONLYV2D = (1<<2), /* only draw 'canvas' strokes */
+ GP_DRAWDATA_ONLYI2D = (1<<3), /* only draw 'image' strokes */
+};
+
+/* thickness above which we should use special drawing */
+#define GP_DRAWTHICKNESS_SPECIAL 3
+
+/* ----- Tool Buffer Drawing ------ */
+
+/* draw stroke defined in buffer (simple ogl lines/points for now, as dotted lines) */
+static void gp_draw_stroke_buffer (tGPspoint *points, int totpoints, short thickness, short dflag, short sflag)
+{
+ tGPspoint *pt;
+ int i;
+
+ /* error checking */
+ if ((points == NULL) || (totpoints <= 0))
+ return;
+
+ /* check if buffer can be drawn */
+ if (dflag & (GP_DRAWDATA_ONLY3D|GP_DRAWDATA_ONLYV2D))
+ return;
+
+ /* if drawing a single point, draw it larger */
+ if (totpoints == 1) {
+ /* draw point */
+ glBegin(GL_POINTS);
+ glVertex2f(points->x, points->y);
+ glEnd();
+ }
+ else if (sflag & GP_STROKE_ERASER) {
+ /* don't draw stroke at all! */
+ }
+ else {
+ float oldpressure = 0.0f;
+
+ /* draw stroke curve */
+ if (G.f & G_DEBUG) setlinestyle(2);
+
+ glBegin(GL_LINE_STRIP);
+ for (i=0, pt=points; i < totpoints && pt; i++, pt++) {
+ if (fabs(pt->pressure - oldpressure) > 0.2f) {
+ glEnd();
+ glLineWidth(pt->pressure * thickness);
+ glBegin(GL_LINE_STRIP);
+
+ glVertex2f(pt->x, pt->y);
+
+ oldpressure = pt->pressure;
+ }
+ else
+ glVertex2f(pt->x, pt->y);
+ }
+ glEnd();
+
+ if (G.f & G_DEBUG) setlinestyle(0);
+ }
+}
+
+/* ----- Existing Strokes Drawing (3D and Point) ------ */
+
+/* draw a given stroke - just a single dot (only one point) */
+static void gp_draw_stroke_point (bGPDspoint *points, short thickness, short sflag, int offsx, int offsy, int winx, int winy)
+{
+ /* draw point */
+ if (sflag & GP_STROKE_3DSPACE) {
+ glBegin(GL_POINTS);
+ glVertex3f(points->x, points->y, points->z);
+ glEnd();
+ }
+ else {
+ int spacetype= 0; // XXX make local gpencil state var?
+ float co[2];
+
+ /* get coordinates of point */
+ if (sflag & GP_STROKE_2DSPACE) {
+ co[0]= points->x;
+ co[1]= points->y;
+ }
+ else if (sflag & GP_STROKE_2DIMAGE) {
+ co[0]= (points->x * winx) + offsx;
+ co[1]= (points->y * winy) + offsy;
+ }
+ else {
+ co[0]= (points->x / 1000 * winx);
+ co[1]= (points->y / 1000 * winy);
+ }
+
+ /* if thickness is less than GP_DRAWTHICKNESS_SPECIAL, simple dot looks ok
+ * - also mandatory in if Image Editor 'image-based' dot
+ */
+ if ( (thickness < GP_DRAWTHICKNESS_SPECIAL) ||
+ ((spacetype==SPACE_IMAGE) && (sflag & GP_STROKE_2DSPACE)) )
+ {
+ glBegin(GL_POINTS);
+ glVertex2fv(co);
+ glEnd();
+ }
+ else
+ {
+ /* draw filled circle as is done in circf (but without the matrix push/pops which screwed things up) */
+ GLUquadricObj *qobj = gluNewQuadric();
+
+ gluQuadricDrawStyle(qobj, GLU_FILL);
+
+ /* need to translate drawing position, but must reset after too! */
+ glTranslatef(co[0], co[1], 0.);
+ gluDisk(qobj, 0.0, thickness, 32, 1);
+ glTranslatef(-co[0], -co[1], 0.);
+
+ gluDeleteQuadric(qobj);
+ }
+ }
+}
+
+/* draw a given stroke in 3d (i.e. in 3d-space), using simple ogl lines */
+static void gp_draw_stroke_3d (bGPDspoint *points, int totpoints, short thickness, short dflag, short sflag, short debug, int winx, int winy)
+{
+ bGPDspoint *pt;
+ float oldpressure = 0.0f;
+ int i;
+
+ /* draw stroke curve */
+ glBegin(GL_LINE_STRIP);
+ for (i=0, pt=points; i < totpoints && pt; i++, pt++) {
+ if (fabs(pt->pressure - oldpressure) > 0.2f) {
+ glEnd();
+ glLineWidth(pt->pressure * thickness);
+ glBegin(GL_LINE_STRIP);
+
+ glVertex3f(pt->x, pt->y, pt->z);
+
+ oldpressure = pt->pressure;
+ }
+ else
+ glVertex3f(pt->x, pt->y, pt->z);
+ }
+ glEnd();
+
+ /* draw debug points of curve on top? */
+ if (debug) {
+ glBegin(GL_POINTS);
+ for (i=0, pt=points; i < totpoints && pt; i++, pt++)
+ glVertex3f(pt->x, pt->y, pt->z);
+ glEnd();
+ }
+}
+
+/* ----- Fancy 2D-Stroke Drawing ------ */
+
+/* draw a given stroke in 2d */
+static void gp_draw_stroke (bGPDspoint *points, int totpoints, short thickness, short dflag, short sflag,
+ short debug, int offsx, int offsy, int winx, int winy)
+{
+ int spacetype= 0; // XXX make local gpencil state var?
+
+ /* if thickness is less than GP_DRAWTHICKNESS_SPECIAL, 'smooth' opengl lines look better
+ * - 'smooth' opengl lines are also required if Image Editor 'image-based' stroke
+ */
+ if ( (thickness < GP_DRAWTHICKNESS_SPECIAL) ||
+ ((spacetype==SPACE_IMAGE) && (dflag & GP_DRAWDATA_ONLYV2D)) )
+ {
+ bGPDspoint *pt;
+ int i;
+
+ glBegin(GL_LINE_STRIP);
+ for (i=0, pt=points; i < totpoints && pt; i++, pt++) {
+ if (sflag & GP_STROKE_2DSPACE) {
+ glVertex2f(pt->x, pt->y);
+ }
+ else if (sflag & GP_STROKE_2DIMAGE) {
+ const float x= (pt->x * winx) + offsx;
+ const float y= (pt->y * winy) + offsy;
+
+ glVertex2f(x, y);
+ }
+ else {
+ const float x= (pt->x / 1000 * winx);
+ const float y= (pt->y / 1000 * winy);
+
+ glVertex2f(x, y);
+ }
+ }
+ glEnd();
+ }
+
+ /* tesselation code - draw stroke as series of connected quads with connection
+ * edges rotated to minimise shrinking artifacts, and rounded endcaps
+ */
+ else
+ {
+ bGPDspoint *pt1, *pt2;
+ float pm[2];
+ int i;
+
+ glShadeModel(GL_FLAT);
+ glBegin(GL_QUADS);
+
+ for (i=0, pt1=points, pt2=points+1; i < (totpoints-1); i++, pt1++, pt2++) {
+ float s0[2], s1[2]; /* segment 'center' points */
+ float t0[2], t1[2]; /* tesselated coordinates */
+ float m1[2], m2[2]; /* gradient and normal */
+ float mt[2], sc[2]; /* gradient for thickness, point for end-cap */
+ float pthick; /* thickness at segment point */
+
+ /* get x and y coordinates from points */
+ if (sflag & GP_STROKE_2DSPACE) {
+ s0[0]= pt1->x; s0[1]= pt1->y;
+ s1[0]= pt2->x; s1[1]= pt2->y;
+ }
+ else if (sflag & GP_STROKE_2DIMAGE) {
+ s0[0]= (pt1->x * winx) + offsx;
+ s0[1]= (pt1->y * winy) + offsy;
+ s1[0]= (pt2->x * winx) + offsx;
+ s1[1]= (pt2->y * winy) + offsy;
+ }
+ else {
+ s0[0]= (pt1->x / 1000 * winx);
+ s0[1]= (pt1->y / 1000 * winy);
+ s1[0]= (pt2->x / 1000 * winx);
+ s1[1]= (pt2->y / 1000 * winy);
+ }
+
+ /* calculate gradient and normal - 'angle'=(ny/nx) */
+ m1[1]= s1[1] - s0[1];
+ m1[0]= s1[0] - s0[0];
+ Normalize2(m1);
+ m2[1]= -m1[0];
+ m2[0]= m1[1];
+
+ /* always use pressure from first point here */
+ pthick= (pt1->pressure * thickness);
+
+ /* if the first segment, start of segment is segment's normal */
+ if (i == 0) {
+ /* draw start cap first
+ * - make points slightly closer to center (about halfway across)
+ */
+ mt[0]= m2[0] * pthick * 0.5f;
+ mt[1]= m2[1] * pthick * 0.5f;
+ sc[0]= s0[0] - (m1[0] * pthick * 0.75f);
+ sc[1]= s0[1] - (m1[1] * pthick * 0.75f);
+
+ t0[0]= sc[0] - mt[0];
+ t0[1]= sc[1] - mt[1];
+ t1[0]= sc[0] + mt[0];
+ t1[1]= sc[1] + mt[1];
+
+ glVertex2fv(t0);
+ glVertex2fv(t1);
+
+ /* calculate points for start of segment */
+ mt[0]= m2[0] * pthick;
+ mt[1]= m2[1] * pthick;
+
+ t0[0]= s0[0] - mt[0];
+ t0[1]= s0[1] - mt[1];
+ t1[0]= s0[0] + mt[0];
+ t1[1]= s0[1] + mt[1];
+
+ /* draw this line twice (first to finish off start cap, then for stroke) */
+ glVertex2fv(t1);
+ glVertex2fv(t0);
+ glVertex2fv(t0);
+ glVertex2fv(t1);
+ }
+ /* if not the first segment, use bisector of angle between segments */
+ else {
+ float mb[2]; /* bisector normal */
+ float athick, dfac; /* actual thickness, difference between thicknesses */
+
+ /* calculate gradient of bisector (as average of normals) */
+ mb[0]= (pm[0] + m2[0]) / 2;
+ mb[1]= (pm[1] + m2[1]) / 2;
+ Normalize2(mb);
+
+ /* calculate gradient to apply
+ * - as basis, use just pthick * bisector gradient
+ * - if cross-section not as thick as it should be, add extra padding to fix it
+ */
+ mt[0]= mb[0] * pthick;
+ mt[1]= mb[1] * pthick;
+ athick= Vec2Length(mt);
+ dfac= pthick - (athick * 2);
+ if ( ((athick * 2) < pthick) && (IS_EQ(athick, pthick)==0) )
+ {
+ mt[0] += (mb[0] * dfac);
+ mt[1] += (mb[1] * dfac);
+ }
+
+ /* calculate points for start of segment */
+ t0[0]= s0[0] - mt[0];
+ t0[1]= s0[1] - mt[1];
+ t1[0]= s0[0] + mt[0];
+ t1[1]= s0[1] + mt[1];
+
+ /* draw this line twice (once for end of current segment, and once for start of next) */
+ glVertex2fv(t1);
+ glVertex2fv(t0);
+ glVertex2fv(t0);
+ glVertex2fv(t1);
+ }
+
+ /* if last segment, also draw end of segment (defined as segment's normal) */
+ if (i == totpoints-2) {
+ /* for once, we use second point's pressure (otherwise it won't be drawn) */
+ pthick= (pt2->pressure * thickness);
+
+ /* calculate points for end of segment */
+ mt[0]= m2[0] * pthick;
+ mt[1]= m2[1] * pthick;
+
+ t0[0]= s1[0] - mt[0];
+ t0[1]= s1[1] - mt[1];
+ t1[0]= s1[0] + mt[0];
+ t1[1]= s1[1] + mt[1];
+
+ /* draw this line twice (once for end of stroke, and once for endcap)*/
+ glVertex2fv(t1);
+ glVertex2fv(t0);
+ glVertex2fv(t0);
+ glVertex2fv(t1);
+
+
+ /* draw end cap as last step
+ * - make points slightly closer to center (about halfway across)
+ */
+ mt[0]= m2[0] * pthick * 0.5f;
+ mt[1]= m2[1] * pthick * 0.5f;
+ sc[0]= s1[0] + (m1[0] * pthick * 0.75f);
+ sc[1]= s1[1] + (m1[1] * pthick * 0.75f);
+
+ t0[0]= sc[0] - mt[0];
+ t0[1]= sc[1] - mt[1];
+ t1[0]= sc[0] + mt[0];
+ t1[1]= sc[1] + mt[1];
+
+ glVertex2fv(t1);
+ glVertex2fv(t0);
+ }
+
+ /* store stroke's 'natural' normal for next stroke to use */
+ Vec2Copyf(pm, m2);
+ }
+
+ glEnd();
+ }
+
+ /* draw debug points of curve on top? (original stroke points) */
+ if (debug) {
+ bGPDspoint *pt;
+ int i;
+
+ glBegin(GL_POINTS);
+ for (i=0, pt=points; i < totpoints && pt; i++, pt++) {
+ if (sflag & GP_STROKE_2DSPACE) {
+ glVertex2f(pt->x, pt->y);
+ }
+ else if (sflag & GP_STROKE_2DIMAGE) {
+ const float x= (float)((pt->x * winx) + offsx);
+ const float y= (float)((pt->y * winy) + offsy);
+
+ glVertex2f(x, y);
+ }
+ else {
+ const float x= (float)(pt->x / 1000 * winx);
+ const float y= (float)(pt->y / 1000 * winy);
+
+ glVertex2f(x, y);
+ }
+ }
+ glEnd();
+ }
+}
+
+/* ----- General Drawing ------ */
+
+/* draw a set of strokes */
+static void gp_draw_strokes (bGPDframe *gpf, int offsx, int offsy, int winx, int winy, int dflag,
+ short debug, short lthick, float color[4])
+{
+ bGPDstroke *gps;
+
+ /* set color first (may need to reset it again later too) */
+ glColor4f(color[0], color[1], color[2], color[3]);
+
+ for (gps= gpf->strokes.first; gps; gps= gps->next) {
+ /* check if stroke can be drawn - checks here generally fall into pairs */
+ if ((dflag & GP_DRAWDATA_ONLY3D) && !(gps->flag & GP_STROKE_3DSPACE))
+ continue;
+ if (!(dflag & GP_DRAWDATA_ONLY3D) && (gps->flag & GP_STROKE_3DSPACE))
+ continue;
+ if ((dflag & GP_DRAWDATA_ONLYV2D) && !(gps->flag & GP_STROKE_2DSPACE))
+ continue;
+ if (!(dflag & GP_DRAWDATA_ONLYV2D) && (gps->flag & GP_STROKE_2DSPACE))
+ continue;
+ if ((dflag & GP_DRAWDATA_ONLYI2D) && !(gps->flag & GP_STROKE_2DIMAGE))
+ continue;
+ if (!(dflag & GP_DRAWDATA_ONLYI2D) && (gps->flag & GP_STROKE_2DIMAGE))
+ continue;
+ if ((gps->points == 0) || (gps->totpoints < 1))
+ continue;
+
+ /* check which stroke-drawer to use */
+ if (gps->totpoints == 1)
+ gp_draw_stroke_point(gps->points, lthick, gps->flag, offsx, offsy, winx, winy);
+ else if (dflag & GP_DRAWDATA_ONLY3D)
+ gp_draw_stroke_3d(gps->points, gps->totpoints, lthick, dflag, gps->flag, debug, winx, winy);
+ else if (gps->totpoints > 1)
+ gp_draw_stroke(gps->points, gps->totpoints, lthick, dflag, gps->flag, debug, offsx, offsy, winx, winy);
+ }
+}
+
+/* draw grease-pencil datablock */
+static void gp_draw_data (bGPdata *gpd, int offsx, int offsy, int winx, int winy, int cfra, int dflag)
+{
+ bGPDlayer *gpl, *actlay=NULL;
+
+ /* turn on smooth lines (i.e. anti-aliasing) */
+ glEnable(GL_LINE_SMOOTH);
+
+ /* turn on alpha-blending */
+ glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
+ glEnable(GL_BLEND);
+
+ /* loop over layers, drawing them */
+ for (gpl= gpd->layers.first; gpl; gpl= gpl->next) {
+ bGPDframe *gpf;
+
+ short debug = (gpl->flag & GP_LAYER_DRAWDEBUG) ? 1 : 0;
+ short lthick= gpl->thickness;
+ float color[4], tcolor[4];
+
+ /* don't draw layer if hidden */
+ if (gpl->flag & GP_LAYER_HIDE)
+ continue;
+
+ /* if layer is active one, store pointer to it */
+ if (gpl->flag & GP_LAYER_ACTIVE)
+ actlay= gpl;
+
+ /* get frame to draw */
+ gpf= gpencil_layer_getframe(gpl, cfra, 0);
+ if (gpf == NULL)
+ continue;
+
+ /* set color, stroke thickness, and point size */
+ glLineWidth(lthick);
+ QUATCOPY(color, gpl->color); // just for copying 4 array elements
+ QUATCOPY(tcolor, gpl->color); // additional copy of color (for ghosting)
+ glColor4f(color[0], color[1], color[2], color[3]);
+ glPointSize((float)(gpl->thickness + 2));
+
+ /* draw 'onionskins' (frame left + right) */
+ if (gpl->flag & GP_LAYER_ONIONSKIN) {
+ /* drawing method - only immediately surrounding (gstep = 0), or within a frame range on either side (gstep > 0)*/
+ if (gpl->gstep) {
+ bGPDframe *gf;
+ float fac;
+
+ /* draw previous frames first */
+ for (gf=gpf->prev; gf; gf=gf->prev) {
+ /* check if frame is drawable */
+ if ((gpf->framenum - gf->framenum) <= gpl->gstep) {
+ /* alpha decreases with distance from curframe index */
+ fac= (float)(gpf->framenum - gf->framenum) / (float)gpl->gstep;
+ tcolor[3] = color[3] - fac;
+ gp_draw_strokes(gf, offsx, offsy, winx, winy, dflag, debug, lthick, tcolor);
+ }
+ else
+ break;
+ }
+
+ /* now draw next frames */
+ for (gf= gpf->next; gf; gf=gf->next) {
+ /* check if frame is drawable */
+ if ((gf->framenum - gpf->framenum) <= gpl->gstep) {
+ /* alpha decreases with distance from curframe index */
+ fac= (float)(gf->framenum - gpf->framenum) / (float)gpl->gstep;
+ tcolor[3] = color[3] - fac;
+ gp_draw_strokes(gf, offsx, offsy, winx, winy, dflag, debug, lthick, tcolor);
+ }
+ else
+ break;
+ }
+
+ /* restore alpha */
+ glColor4f(color[0], color[1], color[2], color[3]);
+ }
+ else {
+ /* draw the strokes for the ghost frames (at half of the alpha set by user) */
+ if (gpf->prev) {
+ tcolor[3] = (color[3] / 7);
+ gp_draw_strokes(gpf->prev, offsx, offsy, winx, winy, dflag, debug, lthick, tcolor);
+ }
+
+ if (gpf->next) {
+ tcolor[3] = (color[3] / 4);
+ gp_draw_strokes(gpf->next, offsx, offsy, winx, winy, dflag, debug, lthick, tcolor);
+ }
+
+ /* restore alpha */
+ glColor4f(color[0], color[1], color[2], color[3]);
+ }
+ }
+
+ /* draw the strokes already in active frame */
+ tcolor[3]= color[3];
+ gp_draw_strokes(gpf, offsx, offsy, winx, winy, dflag, debug, lthick, tcolor);
+
+ /* Check if may need to draw the active stroke cache, only if this layer is the active layer
+ * that is being edited. (Stroke buffer is currently stored in gp-data)
+ */
+ if ((G.f & G_GREASEPENCIL) && (gpl->flag & GP_LAYER_ACTIVE) &&
+ (gpf->flag & GP_FRAME_PAINT))
+ {
+ /* Buffer stroke needs to be drawn with a different linestyle to help differentiate them from normal strokes. */
+ gp_draw_stroke_buffer(gpd->sbuffer, gpd->sbuffer_size, lthick, dflag, gpd->sbuffer_sflag);
+ }
+ }
+
+ /* turn off alpha blending, then smooth lines */
+ glDisable(GL_BLEND); // alpha blending
+ glDisable(GL_LINE_SMOOTH); // smooth lines
+
+ /* show info for debugging the status of gpencil */
+ if ( ((dflag & GP_DRAWDATA_NOSTATUS)==0) && (gpd->flag & GP_DATA_DISPINFO) ) {
+ char printable[256];
+ short xmax;
+
+ /* get text to display */
+ if (actlay) {
+ if (gpd->flag & GP_DATA_EDITPAINT)
+ UI_ThemeColor(TH_BONE_POSE); // should be blue-ish
+ else if (actlay->actframe == NULL)
+ UI_ThemeColor(TH_REDALERT);
+ else if (actlay->actframe->framenum == cfra)
+ UI_ThemeColor(TH_VERTEX_SELECT); // should be yellow
+ else
+ UI_ThemeColor(TH_TEXT_HI);
+
+ if (actlay->actframe) {
+ sprintf(printable, "GPencil: Layer ('%s'), Frame (%d)%s",
+ actlay->info, actlay->actframe->framenum,
+ ((gpd->flag & GP_DATA_EDITPAINT)?" , Draw Mode On":"") );
+ }
+ else {
+ sprintf(printable, "GPencil: Layer ('%s'), Frame <None>%s",
+ actlay->info, ((gpd->flag & GP_DATA_EDITPAINT)?" , Draw Mode On":"") );
+ }
+ }
+ else {
+ UI_ThemeColor(TH_REDALERT);
+ sprintf(printable, "GPencil: Layer <None>");
+ }
+ xmax= GetButStringLength(printable);
+
+ /* only draw it if view is wide enough (assume padding of 20 is enough for now) */
+ if (winx > (xmax + 20)) {
+ glRasterPos2i(winx-xmax, winy-20);
+ BMF_DrawString(G.fonts, printable);
+ }
+ }
+
+ /* restore initial gl conditions */
+ glLineWidth(1.0);
+ glPointSize(1.0);
+ glColor4f(0, 0, 0, 1);
+}
+
+/* ----- Grease Pencil Sketches Drawing API ------ */
+
+/* draw grease-pencil sketches to specified 2d-view that uses ibuf corrections */
+void draw_gpencil_2dimage (bContext *C, ImBuf *ibuf)
+{
+ ScrArea *sa= CTX_wm_area(C);
+ ARegion *ar= CTX_wm_region(C);
+ Scene *scene= CTX_data_scene(C);
+ bGPdata *gpd;
+ int offsx, offsy, sizex, sizey;
+ int dflag = GP_DRAWDATA_NOSTATUS;
+
+ /* check that we have grease-pencil stuff to draw */
+ if (ELEM(NULL, sa, ibuf)) return;
+ gpd= gpencil_data_getactive(sa);
+ if (gpd == NULL) return;
+
+ /* calculate rect */
+ switch (sa->spacetype) {
+ case SPACE_IMAGE: /* image */
+ {
+
+ /* just draw using standard scaling (settings here are currently ignored anyways) */
+ // FIXME: the opengl poly-strokes don't draw at right thickness when done this way, so disabled
+ offsx= 0;
+ offsy= 0;
+ sizex= ar->winx;
+ sizey= ar->winy;
+
+ wmOrtho2(ar->v2d.cur.xmin, ar->v2d.cur.xmax, ar->v2d.cur.ymin, ar->v2d.cur.ymax);
+
+ dflag |= GP_DRAWDATA_ONLYV2D;
+ }
+ break;
+
+ case SPACE_SEQ: /* sequence */
+ {
+ SpaceSeq *sseq= (SpaceSeq *)sa->spacedata.first;
+ float zoom, zoomx, zoomy;
+
+ /* calculate accessory values */
+ zoom= (float)(SEQ_ZOOM_FAC(sseq->zoom));
+ if (sseq->mainb == SEQ_DRAW_IMG_IMBUF) {
+ /* XXX sequencer zoom should store it? */
+ zoomx = zoom; // * ((float)G.scene->r.xasp / (float)G.scene->r.yasp);
+ zoomy = zoom;
+ }
+ else
+ zoomx = zoomy = zoom;
+
+ /* calculate transforms (Note: we use ibuf here, as we have it) */
+ sizex= (int)(zoomx * ibuf->x);
+ sizey= (int)(zoomy * ibuf->y);
+ offsx= (int)( (ar->winx-sizex)/2 + sseq->xof );
+ offsy= (int)( (ar->winy-sizey)/2 + sseq->yof );
+
+ dflag |= GP_DRAWDATA_ONLYI2D;
+ }
+ break;
+
+ default: /* for spacetype not yet handled */
+ offsx= 0;
+ offsy= 0;
+ sizex= ar->winx;
+ sizey= ar->winy;
+
+ dflag |= GP_DRAWDATA_ONLYI2D;
+ break;
+ }
+
+
+ /* draw it! */
+ gp_draw_data(gpd, offsx, offsy, sizex, sizey, CFRA, dflag);
+}
+
+/* draw grease-pencil sketches to specified 2d-view assuming that matrices are already set correctly
+ * Note: this gets called twice - first time with onlyv2d=1 to draw 'canvas' strokes, second time with onlyv2d=0 for screen-aligned strokes
+ */
+void draw_gpencil_2dview (bContext *C, short onlyv2d)
+{
+ ScrArea *sa= CTX_wm_area(C);
+ ARegion *ar= CTX_wm_region(C);
+ Scene *scene= CTX_data_scene(C);
+ bGPdata *gpd;
+ int dflag = 0;
+
+ /* check that we have grease-pencil stuff to draw */
+ if (sa == NULL) return;
+ gpd= gpencil_data_getactive(sa);
+ if (gpd == NULL) return;
+
+ /* draw it! */
+ if (onlyv2d) dflag |= (GP_DRAWDATA_ONLYV2D|GP_DRAWDATA_NOSTATUS);
+ gp_draw_data(gpd, 0, 0, ar->winx, ar->winy, CFRA, dflag);
+}
+
+/* draw grease-pencil sketches to specified 3d-view assuming that matrices are already set correctly
+ * Note: this gets called twice - first time with only3d=1 to draw 3d-strokes, second time with only3d=0 for screen-aligned strokes
+ */
+void draw_gpencil_3dview (bContext *C, short only3d)
+{
+ ScrArea *sa= CTX_wm_area(C);
+ ARegion *ar= CTX_wm_region(C);
+ Scene *scene= CTX_data_scene(C);
+ bGPdata *gpd;
+ int dflag = 0;
+
+ /* check that we have grease-pencil stuff to draw */
+ gpd= gpencil_data_getactive(sa);
+ if (gpd == NULL) return;
+
+ /* draw it! */
+ if (only3d) dflag |= (GP_DRAWDATA_ONLY3D|GP_DRAWDATA_NOSTATUS);
+ gp_draw_data(gpd, 0, 0, ar->winx, ar->winy, CFRA, dflag);
+}
+
+/* draw grease-pencil sketches to opengl render window assuming that matrices are already set correctly */
+// XXX porting note, ogl render will probably be a window with one 3d region
+void draw_gpencil_oglrender (bContext *C)
+{
+ ScrArea *sa= CTX_wm_area(C);
+ View3D *v3d= (View3D *)sa->spacedata.first;
+ ARegion *ar= CTX_wm_region(C);
+ Scene *scene= CTX_data_scene(C);
+ bGPdata *gpd;
+
+ /* assume gpencil data comes from v3d */
+ if (v3d == NULL) return;
+ gpd= v3d->gpd;
+ if (gpd == NULL) return;
+
+ /* pass 1: draw 3d-strokes ------------ > */
+ gp_draw_data(gpd, 0, 0, ar->winx, ar->winy, CFRA, (GP_DRAWDATA_NOSTATUS|GP_DRAWDATA_ONLY3D));
+
+ /* pass 2: draw 2d-strokes ------------ > */
+ /* adjust view matrices */
+ wmOrtho2(-0.375f, (float)(ar->winx)-0.375f, -0.375f, (float)(ar->winy)-0.375f);
+ glLoadIdentity();
+
+ /* draw it! */
+ gp_draw_data(gpd, 0, 0, ar->winx, ar->winy, CFRA, GP_DRAWDATA_NOSTATUS);
+}
+
+/* ************************************************** */
diff --git a/source/blender/editors/gpencil/editaction_gpencil.c b/source/blender/editors/gpencil/editaction_gpencil.c
new file mode 100644
index 00000000000..79057b95fd1
--- /dev/null
+++ b/source/blender/editors/gpencil/editaction_gpencil.c
@@ -0,0 +1,739 @@
+/**
+ * $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 "BLI_arithb.h"
+#include "BLI_blenlib.h"
+
+#include "DNA_listBase.h"
+#include "DNA_action_types.h"
+#include "DNA_gpencil_types.h"
+#include "DNA_scene_types.h"
+#include "DNA_screen_types.h"
+#include "DNA_space_types.h"
+#include "DNA_userdef_types.h"
+#include "DNA_view3d_types.h"
+
+#include "BKE_global.h"
+#include "BKE_utildefines.h"
+#include "BKE_blender.h"
+#include "BKE_ipo.h"
+
+#include "BIF_gl.h"
+#include "BIF_glutil.h"
+
+#include "PIL_time.h"
+
+#include "ED_anim_api.h"
+#include "ED_keyframes_edit.h"
+#include "ED_keyframes_draw.h"
+#include "ED_gpencil.h"
+#include "ED_util.h"
+#include "ED_types.h"
+
+#include "gpencil_intern.h"
+
+/* XXX */
+static void actdata_filter() {}
+static void BIF_undo_push() {}
+static void error() {}
+static void *get_action_context() {return NULL;}
+static int find_nearest_marker_time() {return 1;}
+/* XXX */
+
+
+/* ***************************************** */
+/* NOTE ABOUT THIS FILE:
+ * This file contains code for editing Grease Pencil data in the Action Editor
+ * as a 'keyframes', so that a user can adjust the timing of Grease Pencil drawings.
+ * Therefore, this file mostly contains functions for selecting Grease-Pencil frames.
+ */
+/* ***************************************** */
+/* Generics - Loopers */
+
+/* Loops over the gp-frames for a gp-layer, and applies the given callback */
+short gplayer_frames_looper (bGPDlayer *gpl, Scene *scene, short (*gpf_cb)(bGPDframe *, Scene *))
+{
+ bGPDframe *gpf;
+
+ /* error checker */
+ if (gpl == NULL)
+ return 0;
+
+ /* do loop */
+ for (gpf= gpl->frames.first; gpf; gpf= gpf->next) {
+ /* execute callback */
+ if (gpf_cb(gpf, scene))
+ return 1;
+ }
+
+ /* nothing to return */
+ return 0;
+}
+
+/* ****************************************** */
+/* Data Conversion Tools */
+
+/* make a listing all the gp-frames in a layer as cfraelems */
+void gplayer_make_cfra_list (bGPDlayer *gpl, ListBase *elems, short onlysel)
+{
+ bGPDframe *gpf;
+ CfraElem *ce;
+
+ /* error checking */
+ if (ELEM(NULL, gpl, elems))
+ return;
+
+ /* loop through gp-frames, adding */
+ for (gpf= gpl->frames.first; gpf; gpf= gpf->next) {
+ if ((onlysel == 0) || (gpf->flag & GP_FRAME_SELECT)) {
+ ce= MEM_callocN(sizeof(CfraElem), "CfraElem");
+
+ ce->cfra= (float)gpf->framenum;
+ ce->sel= (gpf->flag & GP_FRAME_SELECT) ? 1 : 0;
+
+ BLI_addtail(elems, ce);
+ }
+ }
+}
+
+/* ***************************************** */
+/* Selection Tools */
+
+/* check if one of the frames in this layer is selected */
+short is_gplayer_frame_selected (bGPDlayer *gpl)
+{
+ bGPDframe *gpf;
+
+ /* error checking */
+ if (gpl == NULL)
+ return 0;
+
+ /* stop at the first one found */
+ for (gpf= gpl->frames.first; gpf; gpf= gpf->next) {
+ if (gpf->flag & GP_FRAME_SELECT)
+ return 1;
+ }
+
+ /* not found */
+ return 0;
+}
+
+/* helper function - select gp-frame based on SELECT_* mode */
+static void gpframe_select (bGPDframe *gpf, short select_mode)
+{
+ switch (select_mode) {
+ case SELECT_ADD:
+ gpf->flag |= GP_FRAME_SELECT;
+ break;
+ case SELECT_SUBTRACT:
+ gpf->flag &= ~GP_FRAME_SELECT;
+ break;
+ case SELECT_INVERT:
+ gpf->flag ^= GP_FRAME_SELECT;
+ break;
+ }
+}
+
+/* set all/none/invert select (like above, but with SELECT_* modes) */
+void select_gpencil_frames (bGPDlayer *gpl, short select_mode)
+{
+ bGPDframe *gpf;
+
+ /* error checking */
+ if (gpl == NULL)
+ return;
+
+ /* handle according to mode */
+ for (gpf= gpl->frames.first; gpf; gpf= gpf->next) {
+ gpframe_select(gpf, select_mode);
+ }
+}
+
+/* set all/none/invert select */
+void set_gplayer_frame_selection (bGPDlayer *gpl, short mode)
+{
+ /* error checking */
+ if (gpl == NULL)
+ return;
+
+ /* convert mode to select_mode */
+ switch (mode) {
+ case 2:
+ mode= SELECT_INVERT;
+ break;
+ case 1:
+ mode= SELECT_ADD;
+ break;
+ case 0:
+ mode= SELECT_SUBTRACT;
+ break;
+ default:
+ return;
+ }
+
+ /* now call the standard function */
+ select_gpencil_frames (gpl, mode);
+}
+
+/* select the frame in this layer that occurs on this frame (there should only be one at most) */
+void select_gpencil_frame (bGPDlayer *gpl, int selx, short select_mode)
+{
+ bGPDframe *gpf;
+
+ /* search through frames for a match */
+ for (gpf= gpl->frames.first; gpf; gpf= gpf->next) {
+ /* there should only be one frame with this frame-number */
+ if (gpf->framenum == selx) {
+ gpframe_select(gpf, select_mode);
+ break;
+ }
+ }
+}
+
+/* select the frames in this layer that occur within the bounds specified */
+void borderselect_gplayer_frames (bGPDlayer *gpl, float min, float max, short select_mode)
+{
+ bGPDframe *gpf;
+
+ /* only select those frames which are in bounds */
+ for (gpf= gpl->frames.first; gpf; gpf= gpf->next) {
+ if (IN_RANGE(gpf->framenum, min, max))
+ gpframe_select(gpf, select_mode);
+ }
+}
+
+
+/* De-selects or inverts the selection of Layers for a grease-pencil block
+ * mode: 0 = default behaviour (select all), 1 = test if (de)select all, 2 = invert all
+ */
+void deselect_gpencil_layers (void *data, short mode)
+{
+ ListBase act_data = {NULL, NULL};
+ bActListElem *ale;
+ int filter, sel=1;
+
+ /* filter data */
+ filter= ACTFILTER_VISIBLE;
+ actdata_filter(&act_data, filter, data, ACTCONT_GPENCIL);
+
+ /* See if we should be selecting or deselecting */
+ if (mode == 1) {
+ for (ale= act_data.first; ale; ale= ale->next) {
+ if (sel == 0)
+ break;
+
+ if (ale->flag & GP_LAYER_SELECT)
+ sel= 0;
+ }
+ }
+ else
+ sel= 0;
+
+ /* Now set the flags */
+ for (ale= act_data.first; ale; ale= ale->next) {
+ bGPDlayer *gpl= (bGPDlayer *)ale->data;
+
+ if (mode == 2)
+ gpl->flag ^= GP_LAYER_SELECT;
+ else if (sel)
+ gpl->flag |= GP_LAYER_SELECT;
+ else
+ gpl->flag &= ~GP_LAYER_SELECT;
+
+ gpl->flag &= ~GP_LAYER_ACTIVE;
+ }
+
+ /* Cleanup */
+ BLI_freelistN(&act_data);
+}
+
+/* ***************************************** */
+/* Frame Editing Tools */
+
+/* Delete selected grease-pencil layers */
+void delete_gpencil_layers (void)
+{
+ ListBase act_data = {NULL, NULL};
+ bActListElem *ale, *next;
+ void *data;
+ short datatype;
+ int filter;
+
+ /* determine what type of data we are operating on */
+ data = get_action_context(&datatype);
+ if (data == NULL) return;
+ if (datatype != ACTCONT_GPENCIL) return;
+
+ /* filter data */
+ filter= (ACTFILTER_VISIBLE | ACTFILTER_FOREDIT | ACTFILTER_CHANNELS | ACTFILTER_SEL);
+ actdata_filter(&act_data, filter, data, datatype);
+
+ /* clean up grease-pencil layers */
+ for (ale= act_data.first; ale; ale= next) {
+ bGPdata *gpd= (bGPdata *)ale->owner;
+ bGPDlayer *gpl= (bGPDlayer *)ale->data;
+ next= ale->next;
+
+ /* free layer and its data */
+ if (SEL_GPL(gpl)) {
+ free_gpencil_frames(gpl);
+ BLI_freelinkN(&gpd->layers, gpl);
+ }
+
+ /* free temp memory */
+ BLI_freelinkN(&act_data, ale);
+ }
+
+ BIF_undo_push("Delete GPencil Layers");
+}
+
+/* Delete selected frames */
+void delete_gplayer_frames (bGPDlayer *gpl)
+{
+ bGPDframe *gpf, *gpfn;
+
+ /* error checking */
+ if (gpl == NULL)
+ return;
+
+ /* check for frames to delete */
+ for (gpf= gpl->frames.first; gpf; gpf= gpfn) {
+ gpfn= gpf->next;
+
+ if (gpf->flag & GP_FRAME_SELECT)
+ gpencil_layer_delframe(gpl, gpf);
+ }
+}
+
+/* Duplicate selected frames from given gp-layer */
+void duplicate_gplayer_frames (bGPDlayer *gpl)
+{
+ bGPDframe *gpf, *gpfn;
+
+ /* error checking */
+ if (gpl == NULL)
+ return;
+
+ /* duplicate selected frames */
+ for (gpf= gpl->frames.first; gpf; gpf= gpfn) {
+ gpfn= gpf->next;
+
+ /* duplicate this frame */
+ if (gpf->flag & GP_FRAME_SELECT) {
+ bGPDframe *gpfd;
+
+ /* duplicate frame, and deselect self */
+ gpfd= gpencil_frame_duplicate(gpf);
+ gpf->flag &= ~GP_FRAME_SELECT;
+
+ BLI_insertlinkafter(&gpl->frames, gpf, gpfd);
+ }
+ }
+}
+
+/* -------------------------------------- */
+/* Copy and Paste Tools */
+/* - The copy/paste buffer currently stores a set of GP_Layers, with temporary
+ * GP_Frames with the necessary strokes
+ * - Unless there is only one element in the buffer, names are also tested to check for compatability.
+ * - All pasted frames are offset by the same amount. This is calculated as the difference in the times of
+ * the current frame and the 'first keyframe' (i.e. the earliest one in all channels).
+ * - The earliest frame is calculated per copy operation.
+ */
+
+/* globals for copy/paste data (like for other copy/paste buffers) */
+ListBase gpcopybuf = {NULL, NULL};
+static int gpcopy_firstframe= 999999999;
+
+/* This function frees any MEM_calloc'ed copy/paste buffer data */
+void free_gpcopybuf ()
+{
+ free_gpencil_layers(&gpcopybuf);
+
+ gpcopybuf.first= gpcopybuf.last= NULL;
+ gpcopy_firstframe= 999999999;
+}
+
+/* This function adds data to the copy/paste buffer, freeing existing data first
+ * Only the selected GP-layers get their selected keyframes copied.
+ */
+void copy_gpdata ()
+{
+ ListBase act_data = {NULL, NULL};
+ bActListElem *ale;
+ int filter;
+ void *data;
+ short datatype;
+
+ /* clear buffer first */
+ free_gpcopybuf();
+
+ /* get data */
+ data= get_action_context(&datatype);
+ if (data == NULL) return;
+ if (datatype != ACTCONT_GPENCIL) return;
+
+ /* filter data */
+ filter= (ACTFILTER_VISIBLE | ACTFILTER_SEL);
+ actdata_filter(&act_data, filter, data, datatype);
+
+ /* assume that each of these is an ipo-block */
+ for (ale= act_data.first; ale; ale= ale->next) {
+ bGPDlayer *gpls, *gpln;
+ bGPDframe *gpf, *gpfn;
+
+ /* get new layer to put into buffer */
+ gpls= (bGPDlayer *)ale->data;
+ gpln= MEM_callocN(sizeof(bGPDlayer), "GPCopyPasteLayer");
+
+ gpln->frames.first= gpln->frames.last= NULL;
+ strcpy(gpln->info, gpls->info);
+
+ BLI_addtail(&gpcopybuf, gpln);
+
+ /* loop over frames, and copy only selected frames */
+ for (gpf= gpls->frames.first; gpf; gpf= gpf->next) {
+ /* if frame is selected, make duplicate it and its strokes */
+ if (gpf->flag & GP_FRAME_SELECT) {
+ /* add frame to buffer */
+ gpfn= gpencil_frame_duplicate(gpf);
+ BLI_addtail(&gpln->frames, gpfn);
+
+ /* check if this is the earliest frame encountered so far */
+ if (gpf->framenum < gpcopy_firstframe)
+ gpcopy_firstframe= gpf->framenum;
+ }
+ }
+ }
+
+ /* check if anything ended up in the buffer */
+ if (ELEM(NULL, gpcopybuf.first, gpcopybuf.last))
+ error("Nothing copied to buffer");
+
+ /* free temp memory */
+ BLI_freelistN(&act_data);
+}
+
+void paste_gpdata (Scene *scene)
+{
+ ListBase act_data = {NULL, NULL};
+ bActListElem *ale;
+ int filter;
+ void *data;
+ short datatype;
+
+ const int offset = (CFRA - gpcopy_firstframe);
+ short no_name= 0;
+
+ /* check if buffer is empty */
+ if (ELEM(NULL, gpcopybuf.first, gpcopybuf.last)) {
+ error("No data in buffer to paste");
+ return;
+ }
+ /* check if single channel in buffer (disregard names if so) */
+ if (gpcopybuf.first == gpcopybuf.last)
+ no_name= 1;
+
+ /* get data */
+ data= get_action_context(&datatype);
+ if (data == NULL) return;
+ if (datatype != ACTCONT_GPENCIL) return;
+
+ /* filter data */
+ filter= (ACTFILTER_VISIBLE | ACTFILTER_SEL | ACTFILTER_FOREDIT);
+ actdata_filter(&act_data, filter, data, datatype);
+
+ /* from selected channels */
+ for (ale= act_data.first; ale; ale= ale->next) {
+ bGPDlayer *gpld= (bGPDlayer *)ale->data;
+ bGPDlayer *gpls= NULL;
+ bGPDframe *gpfs, *gpf;
+
+ /* find suitable layer from buffer to use to paste from */
+ for (gpls= gpcopybuf.first; gpls; gpls= gpls->next) {
+ /* check if layer name matches */
+ if ((no_name) || (strcmp(gpls->info, gpld->info)==0))
+ break;
+ }
+
+ /* this situation might occur! */
+ if (gpls == NULL)
+ continue;
+
+ /* add frames from buffer */
+ for (gpfs= gpls->frames.first; gpfs; gpfs= gpfs->next) {
+ /* temporarily apply offset to buffer-frame while copying */
+ gpfs->framenum += offset;
+
+ /* get frame to copy data into (if no frame returned, then just ignore) */
+ gpf= gpencil_layer_getframe(gpld, gpfs->framenum, 1);
+ if (gpf) {
+ bGPDstroke *gps, *gpsn;
+ ScrArea *sa;
+
+ /* get area that gp-data comes from */
+ sa= gpencil_data_findowner((bGPdata *)ale->owner);
+
+ /* this should be the right frame... as it may be a pre-existing frame,
+ * must make sure that only compatible stroke types get copied over
+ * - we cannot just add a duplicate frame, as that would cause errors
+ * - need to check for compatible types to minimise memory usage (copying 'junk' over)
+ */
+ for (gps= gpfs->strokes.first; gps; gps= gps->next) {
+ short stroke_ok;
+
+ /* if there's an area, check that it supports this type of stroke */
+ if (sa) {
+ stroke_ok= 0;
+
+ /* check if spacetype supports this type of stroke
+ * - NOTE: must sync this with gp_paint_initstroke() in gpencil.c
+ */
+ switch (sa->spacetype) {
+ case SPACE_VIEW3D: /* 3D-View: either screen-aligned or 3d-space */
+ if ((gps->flag == 0) || (gps->flag & GP_STROKE_3DSPACE))
+ stroke_ok= 1;
+ break;
+
+ case SPACE_NODE: /* Nodes Editor: either screen-aligned or view-aligned */
+ case SPACE_IMAGE: /* Image Editor: either screen-aligned or view\image-aligned */
+ if ((gps->flag == 0) || (gps->flag & GP_STROKE_2DSPACE))
+ stroke_ok= 1;
+ break;
+
+ case SPACE_SEQ: /* Sequence Editor: either screen-aligned or view-aligned */
+ if ((gps->flag == 0) || (gps->flag & GP_STROKE_2DIMAGE))
+ stroke_ok= 1;
+ break;
+ }
+ }
+ else
+ stroke_ok= 1;
+
+ /* if stroke is ok, we make a copy of this stroke and add to frame */
+ if (stroke_ok) {
+ /* make a copy of stroke, then of its points array */
+ gpsn= MEM_dupallocN(gps);
+ gpsn->points= MEM_dupallocN(gps->points);
+
+ /* append stroke to frame */
+ BLI_addtail(&gpf->strokes, gpsn);
+ }
+ }
+
+ /* if no strokes (i.e. new frame) added, free gpf */
+ if (gpf->strokes.first == NULL)
+ gpencil_layer_delframe(gpld, gpf);
+ }
+
+ /* unapply offset from buffer-frame */
+ gpfs->framenum -= offset;
+ }
+ }
+
+ /* free temp memory */
+ BLI_freelistN(&act_data);
+
+ /* undo and redraw stuff */
+ BIF_undo_push("Paste Grease Pencil Frames");
+}
+
+/* -------------------------------------- */
+/* Snap Tools */
+
+static short snap_gpf_nearest (bGPDframe *gpf, Scene *scene)
+{
+ if (gpf->flag & GP_FRAME_SELECT)
+ gpf->framenum= (int)(floor(gpf->framenum+0.5));
+ return 0;
+}
+
+static short snap_gpf_nearestsec (bGPDframe *gpf, Scene *scene)
+{
+ float secf = (float)FPS;
+ if (gpf->flag & GP_FRAME_SELECT)
+ gpf->framenum= (int)(floor(gpf->framenum/secf + 0.5f) * secf);
+ return 0;
+}
+
+static short snap_gpf_cframe (bGPDframe *gpf, Scene *scene)
+{
+ if (gpf->flag & GP_FRAME_SELECT)
+ gpf->framenum= (int)CFRA;
+ return 0;
+}
+
+static short snap_gpf_nearmarker (bGPDframe *gpf, Scene *scene)
+{
+ if (gpf->flag & GP_FRAME_SELECT)
+ gpf->framenum= (int)find_nearest_marker_time((float)gpf->framenum);
+ return 0;
+}
+
+
+/* snap selected frames to ... */
+void snap_gplayer_frames (bGPDlayer *gpl, Scene *scene, short mode)
+{
+ switch (mode) {
+ case 1: /* snap to nearest frame */
+ gplayer_frames_looper(gpl, scene, snap_gpf_nearest);
+ break;
+ case 2: /* snap to current frame */
+ gplayer_frames_looper(gpl, scene, snap_gpf_cframe);
+ break;
+ case 3: /* snap to nearest marker */
+ gplayer_frames_looper(gpl, scene, snap_gpf_nearmarker);
+ break;
+ case 4: /* snap to nearest second */
+ gplayer_frames_looper(gpl, scene, snap_gpf_nearestsec);
+ break;
+ default: /* just in case */
+ gplayer_frames_looper(gpl, scene, snap_gpf_nearest);
+ break;
+ }
+}
+
+/* -------------------------------------- */
+/* Mirror Tools */
+
+static short mirror_gpf_cframe (bGPDframe *gpf, Scene *scene)
+{
+ int diff;
+
+ if (gpf->flag & GP_FRAME_SELECT) {
+ diff= CFRA - gpf->framenum;
+ gpf->framenum= CFRA;
+ }
+
+ return 0;
+}
+
+static short mirror_gpf_yaxis (bGPDframe *gpf, Scene *scene)
+{
+ int diff;
+
+ if (gpf->flag & GP_FRAME_SELECT) {
+ diff= -gpf->framenum;
+ gpf->framenum= diff;
+ }
+
+ return 0;
+}
+
+static short mirror_gpf_xaxis (bGPDframe *gpf, Scene *scene)
+{
+ int diff;
+
+ if (gpf->flag & GP_FRAME_SELECT) {
+ diff= -gpf->framenum;
+ gpf->framenum= diff;
+ }
+
+ return 0;
+}
+
+static short mirror_gpf_marker (bGPDframe *gpf, Scene *scene)
+{
+ static TimeMarker *marker;
+ static short initialised = 0;
+ int diff;
+
+ /* In order for this mirror function to work without
+ * any extra arguments being added, we use the case
+ * of bezt==NULL to denote that we should find the
+ * marker to mirror over. The static pointer is safe
+ * to use this way, as it will be set to null after
+ * each cycle in which this is called.
+ */
+
+ if (gpf) {
+ /* mirroring time */
+ if ((gpf->flag & GP_FRAME_SELECT) && (marker)) {
+ diff= (marker->frame - gpf->framenum);
+ gpf->framenum= (marker->frame + diff);
+ }
+ }
+ else {
+ /* initialisation time */
+ if (initialised) {
+ /* reset everything for safety */
+ marker = NULL;
+ initialised = 0;
+ }
+ else {
+ /* try to find a marker */
+ for (marker= scene->markers.first; marker; marker=marker->next) {
+ if (marker->flag & SELECT) {
+ initialised = 1;
+ break;
+ }
+ }
+
+ if (initialised == 0)
+ marker = NULL;
+ }
+ }
+
+ return 0;
+}
+
+
+/* mirror selected gp-frames on... */
+void mirror_gplayer_frames (bGPDlayer *gpl, Scene *scene, short mode)
+{
+ switch (mode) {
+ case 1: /* mirror over current frame */
+ gplayer_frames_looper(gpl, scene, mirror_gpf_cframe);
+ break;
+ case 2: /* mirror over frame 0 */
+ gplayer_frames_looper(gpl, scene, mirror_gpf_yaxis);
+ break;
+ case 3: /* mirror over value 0 */
+ gplayer_frames_looper(gpl, scene, mirror_gpf_xaxis);
+ break;
+ case 4: /* mirror over marker */
+ mirror_gpf_marker(NULL, NULL);
+ gplayer_frames_looper(gpl, scene, mirror_gpf_marker);
+ mirror_gpf_marker(NULL, NULL);
+ break;
+ default: /* just in case */
+ gplayer_frames_looper(gpl, scene, mirror_gpf_yaxis);
+ break;
+ }
+}
+
+/* ***************************************** */
diff --git a/source/blender/editors/gpencil/gpencil.c b/source/blender/editors/gpencil/gpencil.c
new file mode 100644
index 00000000000..799c9db6d94
--- /dev/null
+++ b/source/blender/editors/gpencil/gpencil.c
@@ -0,0 +1,2162 @@
+/**
+ * $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)
+ 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) {
+ /* 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
+ View3D *v3d= NULL; // XXX
+
+ if (gps->flag & GP_STROKE_3DSPACE) {
+ /* directly use 3d-coordinates */
+ VecCopyf(p3d, &pt->x);
+ }
+ else {
+ short mval[2], 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, mval, mval+1);
+ }
+ else {
+ // XXX
+ // mval[0]= (short)(pt->x / 1000 * curarea->winx);
+ // mval[1]= (short)(pt->y / 1000 * curarea->winy);
+ }
+ mx= mval[0];
+ my= mval[1];
+
+ /* convert screen coordinate to 3d coordinates
+ * - method taken from editview.c - mouse_cursor()
+ */
+ project_short_noclip(ar, v3d, fp, mval);
+ window_to_3d(ar, v3d, 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);
+
+ 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
+ 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(v3d, 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[])
+{
+ View3D *v3d= NULL; // XXX
+ 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, v3d, fp, mval);
+ window_to_3d(p->ar, v3d, 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;
+ View3D *v3d= NULL;
+ short 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, v3d, &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, v3d, &pt1->x, xyval);
+ x0= xyval[0];
+ y0= xyval[1];
+
+ project_short(p->ar, v3d, &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 == 0) {
+ 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;
+ float *fp= give_cursor(p->scene, v3d);
+ initgrabz(v3d, 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;
+}
+
+/* ************************************************** */
diff --git a/source/blender/editors/gpencil/gpencil_intern.h b/source/blender/editors/gpencil/gpencil_intern.h
new file mode 100644
index 00000000000..38cee1e559c
--- /dev/null
+++ b/source/blender/editors/gpencil/gpencil_intern.h
@@ -0,0 +1,81 @@
+/**
+ * $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) 2009 Blender Foundation.
+ * All rights reserved.
+ *
+ *
+ * Contributor(s): Blender Foundation
+ *
+ * ***** END GPL LICENSE BLOCK *****
+ */
+#ifndef ED_GPENCIL_INTERN_H
+#define ED_GPENCIL_INTERN_H
+
+/* internal exports only */
+/******************************************************* */
+/* FILTERED ACTION DATA - TYPES */
+
+/* XXX */
+/* This struct defines a structure used for quick access */
+typedef struct bActListElem {
+ struct bActListElem *next, *prev;
+
+ void *data; /* source data this elem represents */
+ int type; /* one of the ACTTYPE_* values */
+ int flag; /* copy of elem's flags for quick access */
+ int index; /* copy of adrcode where applicable */
+
+ void *key_data; /* motion data - ipo or ipo-curve */
+ short datatype; /* type of motion data to expect */
+
+ struct bActionGroup *grp; /* action group that owns the channel */
+
+ void *owner; /* will either be an action channel or fake ipo-channel (for keys) */
+ short ownertype; /* type of owner */
+} bActListElem;
+
+/******************************************************* */
+/* FILTER ACTION DATA - METHODS/TYPES */
+
+/* filtering flags - under what circumstances should a channel be added */
+typedef enum ACTFILTER_FLAGS {
+ ACTFILTER_VISIBLE = (1<<0), /* should channels be visible */
+ ACTFILTER_SEL = (1<<1), /* should channels be selected */
+ ACTFILTER_FOREDIT = (1<<2), /* does editable status matter */
+ ACTFILTER_CHANNELS = (1<<3), /* do we only care that it is a channel */
+ ACTFILTER_IPOKEYS = (1<<4), /* only channels referencing ipo's */
+ ACTFILTER_ONLYICU = (1<<5), /* only reference ipo-curves */
+ ACTFILTER_FORDRAWING = (1<<6), /* make list for interface drawing */
+ ACTFILTER_ACTGROUPED = (1<<7) /* belongs to the active group */
+} ACTFILTER_FLAGS;
+
+/* Action Editor - Main Data types */
+typedef enum ACTCONT_TYPES {
+ ACTCONT_NONE = 0,
+ ACTCONT_ACTION,
+ ACTCONT_SHAPEKEY,
+ ACTCONT_GPENCIL
+} ACTCONT_TYPES;
+
+
+
+
+#endif /* ED_GPENCIL_INTERN_H */
+
diff --git a/source/blender/editors/include/ED_armature.h b/source/blender/editors/include/ED_armature.h
index 33e8c87e6ca..c731c9ed740 100644
--- a/source/blender/editors/include/ED_armature.h
+++ b/source/blender/editors/include/ED_armature.h
@@ -99,6 +99,9 @@ void clear_armature(struct Scene *scene, struct Object *ob, char mode);
void create_vgroups_from_armature(struct Scene *scene, struct Object *ob, struct Object *par);
void docenter_armature (struct Scene *scene, struct View3D *v3d, struct Object *ob, int centermode);
+void auto_align_armature(struct Scene *scene, struct View3D *v3d, short mode);
+void unique_editbone_name (ListBase *edbo, char *name);
+
/* poseobject.c */
void ED_armature_exit_posemode(struct Base *base);
void ED_armature_enter_posemode(struct Base *base);
diff --git a/source/blender/editors/include/ED_gpencil.h b/source/blender/editors/include/ED_gpencil.h
new file mode 100644
index 00000000000..f3f054c2b6f
--- /dev/null
+++ b/source/blender/editors/include/ED_gpencil.h
@@ -0,0 +1,101 @@
+/**
+ * $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 *****
+ */
+
+#ifndef ED_GPENCIL_H
+#define ED_GPENCIL_H
+
+struct ListBase;
+struct bScreen;
+struct ScrArea;
+struct ARegion;
+struct View3D;
+struct SpaceNode;
+struct SpaceSeq;
+struct bGPdata;
+struct bGPDlayer;
+struct bGPDframe;
+struct bGPdata;
+struct uiBlock;
+struct ImBuf;
+
+
+
+/* ------------- Grease-Pencil Helpers -------------- */
+
+/* Temporary 'Stroke Point' data */
+typedef struct tGPspoint {
+ short x, y; /* x and y coordinates of cursor (in relative to area) */
+ float pressure; /* pressure of tablet at this point */
+} tGPspoint;
+
+/* ------------ Grease-Pencil API ------------------ */
+
+void free_gpencil_strokes(struct bGPDframe *gpf);
+void free_gpencil_frames(struct bGPDlayer *gpl);
+void free_gpencil_layers(struct ListBase *list);
+void free_gpencil_data(struct bGPdata *gpd);
+
+struct bGPDframe *gpencil_frame_addnew(struct bGPDlayer *gpl, int cframe);
+struct bGPDlayer *gpencil_layer_addnew(struct bGPdata *gpd);
+struct bGPdata *gpencil_data_addnew(void);
+
+struct bGPDframe *gpencil_frame_duplicate(struct bGPDframe *src);
+struct bGPDlayer *gpencil_layer_duplicate(struct bGPDlayer *src);
+struct bGPdata *gpencil_data_duplicate(struct bGPdata *gpd);
+
+struct bGPdata *gpencil_data_getactive(struct ScrArea *sa);
+short gpencil_data_setactive(struct ScrArea *sa, struct bGPdata *gpd);
+struct ScrArea *gpencil_data_findowner(struct bGPdata *gpd);
+
+void gpencil_frame_delete_laststroke(struct bGPDframe *gpf);
+
+struct bGPDframe *gpencil_layer_getframe(struct bGPDlayer *gpl, int cframe, short addnew);
+void gpencil_layer_delframe(struct bGPDlayer *gpl, struct bGPDframe *gpf);
+struct bGPDlayer *gpencil_layer_getactive(struct bGPdata *gpd);
+void gpencil_layer_setactive(struct bGPdata *gpd, struct bGPDlayer *active);
+void gpencil_layer_delactive(struct bGPdata *gpd);
+
+void gpencil_delete_actframe(struct bGPdata *gpd, int cfra);
+void gpencil_delete_laststroke(struct bGPdata *gpd, int cfra);
+
+void gpencil_delete_operation(int cfra, short mode);
+void gpencil_delete_menu(void);
+
+void gpencil_convert_operation(short mode);
+void gpencil_convert_menu(void);
+
+short gpencil_do_paint(struct bContext *C);
+
+/* drawgpencil.c */
+
+void draw_gpencil_2dimage(struct bContext *C, struct ImBuf *ibuf);
+void draw_gpencil_2dview(struct bContext *C, short onlyv2d);
+void draw_gpencil_3dview(struct bContext *C, short only3d);
+void draw_gpencil_oglrender(struct bContext *C);
+
+
+#endif /* ED_GPENCIL_H */
diff --git a/source/blender/editors/include/ED_sequencer.h b/source/blender/editors/include/ED_sequencer.h
new file mode 100644
index 00000000000..2a3b5445dfe
--- /dev/null
+++ b/source/blender/editors/include/ED_sequencer.h
@@ -0,0 +1,31 @@
+/**
+ * $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) 2009, Blender Foundation
+ *
+ * ***** END GPL LICENSE BLOCK *****
+ */
+
+#ifndef ED_SEQUENCER_H
+#define ED_SEQUENCER_H
+
+#define SEQ_ZOOM_FAC(szoom) (szoom > 0)? (szoom) : (szoom == 0)? (1.0) : (-1.0/szoom)
+
+
+#endif /* ED_SEQUENCER_H */
diff --git a/source/blender/editors/include/ED_view3d.h b/source/blender/editors/include/ED_view3d.h
index f9ea8053b72..00fb017d752 100644
--- a/source/blender/editors/include/ED_view3d.h
+++ b/source/blender/editors/include/ED_view3d.h
@@ -94,6 +94,8 @@ unsigned int view3d_sample_backbuf(struct ViewContext *vc, int x, int y);
#define MAXPICKBUF 10000
short view3d_opengl_select(struct ViewContext *vc, unsigned int *buffer, unsigned int bufsize, rcti *input);
void view3d_set_viewcontext(struct bContext *C, struct ViewContext *vc);
+/* XXX should move to arithb.c */
+int edge_inside_circle(short centx, short centy, short rad, short x1, short y1, short x2, short y2);
/* modes */
void ED_view3d_exit_paint_modes(struct bContext *C);
diff --git a/source/blender/editors/sculpt/Makefile b/source/blender/editors/sculpt/Makefile
new file mode 100644
index 00000000000..6b5c8baf27a
--- /dev/null
+++ b/source/blender/editors/sculpt/Makefile
@@ -0,0 +1,57 @@
+#
+# $Id: Makefile 14 2002-10-13 15:57:19Z hans $
+#
+# ***** 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) 2007 Blender Foundation
+# All rights reserved.
+#
+# The Original Code is: all of this file.
+#
+# Contributor(s): none yet.
+#
+# ***** END GPL LICENSE BLOCK *****
+#
+# Makes module object directory and bounces make to subdirectories.
+
+LIBNAME = ed_sculpt
+DIR = $(OCGDIR)/blender/$(LIBNAME)
+
+include nan_compile.mk
+
+CFLAGS += $(LEVEL_1_C_WARNINGS)
+
+CPPFLAGS += -I$(NAN_GLEW)/include
+CPPFLAGS += -I$(OPENGL_HEADERS)
+
+CPPFLAGS += -I$(NAN_BMFONT)/include
+CPPFLAGS += -I$(NAN_GUARDEDALLOC)/include
+CPPFLAGS += -I$(NAN_ELBEEM)/include
+
+CPPFLAGS += -I../../windowmanager
+CPPFLAGS += -I../../blenkernel
+CPPFLAGS += -I../../blenloader
+CPPFLAGS += -I../../blenlib
+CPPFLAGS += -I../../makesdna
+CPPFLAGS += -I../../makesrna
+CPPFLAGS += -I../../imbuf
+CPPFLAGS += -I../../gpu
+CPPFLAGS += -I../../render/extern/include
+
+# own include
+
+CPPFLAGS += -I../include
diff --git a/source/blender/editors/space_sequencer/sequencer_draw.c b/source/blender/editors/space_sequencer/sequencer_draw.c
index 9c91d95fc27..267d1213fc5 100644
--- a/source/blender/editors/space_sequencer/sequencer_draw.c
+++ b/source/blender/editors/space_sequencer/sequencer_draw.c
@@ -60,6 +60,7 @@
#include "ED_anim_api.h"
#include "ED_space_api.h"
+#include "ED_sequencer.h"
#include "ED_types.h"
#include "UI_interface.h"
diff --git a/source/blender/editors/space_sequencer/sequencer_intern.h b/source/blender/editors/space_sequencer/sequencer_intern.h
index 47d2ae6ee9b..bc6824015d9 100644
--- a/source/blender/editors/space_sequencer/sequencer_intern.h
+++ b/source/blender/editors/space_sequencer/sequencer_intern.h
@@ -37,8 +37,6 @@ struct SpaceSeq;
struct ARegion;
struct Scene;
-#define SEQ_ZOOM_FAC(szoom) (szoom > 0)? (szoom) : (szoom == 0)? (1.0) : (-1.0/szoom)
-
/* sequencer_header.c */
void sequencer_header_buttons(const struct bContext *C, struct ARegion *ar);
diff --git a/source/blender/editors/space_view3d/view3d_header.c b/source/blender/editors/space_view3d/view3d_header.c
index c862562dcff..f7662913bf6 100644
--- a/source/blender/editors/space_view3d/view3d_header.c
+++ b/source/blender/editors/space_view3d/view3d_header.c
@@ -3298,15 +3298,15 @@ static void do_view3d_edit_curve_controlpointsmenu(bContext *C, void *arg, int e
clear_tilt();
break;
case 2: /* Free */
- sethandlesNurb(3);
+ sethandlesNurb(editnurb, 3);
DAG_object_flush_update(scene, obedit, OB_RECALC_DATA);
break;
case 3: /* vector */
- sethandlesNurb(2);
+ sethandlesNurb(editnurb, 2);
DAG_object_flush_update(scene, obedit, OB_RECALC_DATA);
break;
case 4: /* smooth */
- sethandlesNurb(1);
+ sethandlesNurb(editnurb, 1);
DAG_object_flush_update(scene, obedit, OB_RECALC_DATA);
break;
case 5: /* make vertex parent */