This repository has been archived on 2023-10-09. You can view files and clone it, but cannot push or open issues or pull requests.
Files
blender-archive/source/blender/editors/gpencil/gpencil_primitive.c

801 lines
22 KiB
C
Raw Normal View History

/*
* ***** 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
* The Original Code is Copyright (C) 2017, Blender Foundation
* This is a new part of Blender
*
* Contributor(s): Antonio Vazquez
*
* ***** END GPL LICENSE BLOCK *****
*
* Operators for creating new Grease Pencil primitives (boxes, circles, ...)
*/
/** \file blender/editors/gpencil/gpencil_primitive.c
* \ingroup edgpencil
*/
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <stddef.h>
#include <math.h>
#include "MEM_guardedalloc.h"
#include "BLI_blenlib.h"
#include "BLI_utildefines.h"
#include "BLI_math.h"
#include "BLT_translation.h"
#include "DNA_brush_types.h"
#include "DNA_gpencil_types.h"
#include "DNA_meshdata_types.h"
#include "DNA_object_types.h"
#include "DNA_scene_types.h"
#include "DNA_screen_types.h"
#include "DNA_space_types.h"
#include "DNA_view3d_types.h"
#include "BKE_brush.h"
#include "BKE_context.h"
#include "BKE_deform.h"
#include "BKE_global.h"
#include "BKE_gpencil.h"
#include "BKE_main.h"
#include "BKE_material.h"
#include "BKE_paint.h"
#include "BKE_report.h"
#include "UI_interface.h"
#include "WM_api.h"
#include "WM_types.h"
#include "RNA_access.h"
#include "RNA_define.h"
#include "RNA_enum_types.h"
#include "ED_gpencil.h"
#include "ED_object.h"
#include "ED_screen.h"
#include "ED_view3d.h"
#include "ED_space_api.h"
#include "DEG_depsgraph.h"
#include "DEG_depsgraph_query.h"
#include "gpencil_intern.h"
#define MIN_EDGES 2
#define MAX_EDGES 100
#define IDLE 0
#define IN_PROGRESS 1
/* ************************************************ */
/* Core/Shared Utilities */
/* Poll callback for primitive operators */
static bool gpencil_primitive_add_poll(bContext *C)
{
/* only 3D view */
ScrArea *sa = CTX_wm_area(C);
if (sa && sa->spacetype != SPACE_VIEW3D) {
return 0;
}
/* need data to create primitive */
bGPdata *gpd = CTX_data_gpencil_data(C);
if (gpd == NULL) {
return 0;
}
/* only in edit and paint modes
* - paint as it's the "drawing/creation mode"
* - edit as this is more of an atomic editing operation
* (similar to copy/paste), and also for consistency
*/
if ((gpd->flag & (GP_DATA_STROKE_PAINTMODE | GP_DATA_STROKE_EDITMODE)) == 0) {
CTX_wm_operator_poll_msg_set(C, "Primitives can only be added in Draw or Edit modes");
return 0;
}
/* don't allow operator to function if the active layer is locked/hidden
* (BUT, if there isn't an active layer, we are free to add new layer when the time comes)
*/
bGPDlayer *gpl = BKE_gpencil_layer_getactive(gpd);
if ((gpl) && (gpl->flag & (GP_LAYER_LOCKED | GP_LAYER_HIDE))) {
CTX_wm_operator_poll_msg_set(C, "Primitives cannot be added as active layer is locked or hidden");
return 0;
}
return 1;
}
/* ****************** Primitive Interactive *********************** */
/* Helper: Create internal strokes primitives data */
static void gp_primitive_set_initdata(bContext *C, tGPDprimitive *tgpi)
{
ToolSettings *ts = CTX_data_tool_settings(C);
Depsgraph *depsgraph = CTX_data_depsgraph(C);
int cfra_eval = (int)DEG_get_ctime(depsgraph);
bGPDlayer *gpl = CTX_data_active_gpencil_layer(C);
/* if brush doesn't exist, create a new one */
Paint *paint = &ts->gp_paint->paint;
/* if not exist, create a new one */
if (paint->brush == NULL) {
/* create new brushes */
BKE_brush_gpencil_presets(C);
}
tgpi->brush = paint->brush;
/* if layer doesn't exist, create a new one */
if (gpl == NULL) {
gpl = BKE_gpencil_layer_addnew(tgpi->gpd, DATA_("Primitives"), true);
}
tgpi->gpl = gpl;
/* create a new temporary frame */
tgpi->gpf = MEM_callocN(sizeof(bGPDframe), "Temp bGPDframe");
tgpi->gpf->framenum = tgpi->cframe = cfra_eval;
/* create new temp stroke */
bGPDstroke *gps = MEM_callocN(sizeof(bGPDstroke), "Temp bGPDstroke");
gps->thickness = 2.0f;
gps->inittime = 0.0f;
/* enable recalculation flag by default */
gps->flag |= GP_STROKE_RECALC_CACHES;
/* the polygon must be closed, so enabled cyclic */
if (tgpi->type != GP_STROKE_LINE) {
gps->flag |= GP_STROKE_CYCLIC;
}
else {
gps->flag &= ~GP_STROKE_CYCLIC;
}
gps->flag |= GP_STROKE_3DSPACE;
gps->mat_nr = BKE_gpencil_get_material_index(tgpi->ob, tgpi->mat) - 1;
/* allocate memory for storage points, but keep empty */
gps->totpoints = 0;
gps->points = MEM_callocN(sizeof(bGPDspoint), "gp_stroke_points");
/* initialize triangle memory to dummy data */
gps->tot_triangles = 0;
gps->triangles = NULL;
gps->flag |= GP_STROKE_RECALC_CACHES;
/* add to strokes */
BLI_addtail(&tgpi->gpf->strokes, gps);
}
/* ----------------------- */
/* Drawing Callbacks */
/* Drawing callback for modal operator in 3d mode */
static void gpencil_primitive_draw_3d(const bContext *C, ARegion *UNUSED(ar), void *arg)
{
tGPDprimitive *tgpi = (tGPDprimitive *)arg;
ED_gp_draw_primitives(C, tgpi, REGION_DRAW_POST_VIEW);
}
/* ----------------------- */
/* Helper: Draw status message while the user is running the operator */
static void gpencil_primitive_status_indicators(bContext *C, tGPDprimitive *tgpi)
{
Scene *scene = tgpi->scene;
char status_str[UI_MAX_DRAW_STR];
char msg_str[UI_MAX_DRAW_STR];
if (tgpi->type == GP_STROKE_BOX) {
BLI_strncpy(msg_str, IFACE_("Rectangle: ESC/RMB to cancel, LMB set origin, Enter/LMB to confirm, Shift to square, Alt to center"), UI_MAX_DRAW_STR);
}
else if (tgpi->type == GP_STROKE_LINE) {
BLI_strncpy(msg_str, IFACE_("Line: ESC/RMB to cancel, LMB set origin, Enter/LMB to confirm, Alt to center"), UI_MAX_DRAW_STR);
}
else {
BLI_strncpy(msg_str, IFACE_("Circle: ESC/RMB to cancel, Enter/LMB to confirm, WHEEL to adjust edge number, Shift to square, Alt to center"), UI_MAX_DRAW_STR);
}
if (tgpi->type == GP_STROKE_CIRCLE) {
if (hasNumInput(&tgpi->num)) {
char str_offs[NUM_STR_REP_LEN];
outputNumInput(&tgpi->num, str_offs, &scene->unit);
BLI_snprintf(status_str, sizeof(status_str), "%s: %s", msg_str, str_offs);
}
else {
if (tgpi->flag == IN_PROGRESS) {
2018-07-31 20:11:55 +10:00
BLI_snprintf(
status_str, sizeof(status_str), "%s: %d (%d, %d) (%d, %d)", msg_str, (int)tgpi->tot_edges,
tgpi->top[0], tgpi->top[1], tgpi->bottom[0], tgpi->bottom[1]);
}
else {
2018-07-31 20:11:55 +10:00
BLI_snprintf(
status_str, sizeof(status_str), "%s: %d (%d, %d)", msg_str, (int)tgpi->tot_edges,
tgpi->bottom[0], tgpi->bottom[1]);
}
}
}
else {
if (tgpi->flag == IN_PROGRESS) {
2018-07-31 20:11:55 +10:00
BLI_snprintf(
status_str, sizeof(status_str), "%s: (%d, %d) (%d, %d)", msg_str,
tgpi->top[0], tgpi->top[1], tgpi->bottom[0], tgpi->bottom[1]);
}
else {
2018-07-31 20:11:55 +10:00
BLI_snprintf(
status_str, sizeof(status_str), "%s: (%d, %d)", msg_str,
tgpi->bottom[0], tgpi->bottom[1]);
}
}
ED_workspace_status_text(C, status_str);
}
/* ----------------------- */
/* create a rectangle */
static void gp_primitive_rectangle(tGPDprimitive *tgpi, tGPspoint *points2D)
{
BLI_assert(tgpi->tot_edges == 4);
points2D[0].x = tgpi->top[0];
points2D[0].y = tgpi->top[1];
points2D[1].x = tgpi->bottom[0];
points2D[1].y = tgpi->top[1];
points2D[2].x = tgpi->bottom[0];
points2D[2].y = tgpi->bottom[1];
points2D[3].x = tgpi->top[0];
points2D[3].y = tgpi->bottom[1];
}
/* create a line */
static void gp_primitive_line(tGPDprimitive *tgpi, tGPspoint *points2D)
{
BLI_assert(tgpi->tot_edges == 2);
points2D[0].x = tgpi->top[0];
points2D[0].y = tgpi->top[1];
points2D[1].x = tgpi->bottom[0];
points2D[1].y = tgpi->bottom[1];
}
/* create a circle */
static void gp_primitive_circle(tGPDprimitive *tgpi, tGPspoint *points2D)
{
const int totpoints = tgpi->tot_edges;
const float step = (2.0f * M_PI) / (float)(totpoints);
float center[2];
float radius[2];
float a = 0.0f;
/* TODO: Use math-lib functions for these? */
center[0] = tgpi->top[0] + ((tgpi->bottom[0] - tgpi->top[0]) / 2.0f);
center[1] = tgpi->top[1] + ((tgpi->bottom[1] - tgpi->top[1]) / 2.0f);
radius[0] = fabsf(((tgpi->bottom[0] - tgpi->top[0]) / 2.0f));
radius[1] = fabsf(((tgpi->bottom[1] - tgpi->top[1]) / 2.0f));
for (int i = 0; i < totpoints; i++) {
tGPspoint *p2d = &points2D[i];
p2d->x = (int)(center[0] + cosf(a) * radius[0]);
p2d->y = (int)(center[1] + sinf(a) * radius[1]);
a += step;
}
}
/* Helper: Update shape of the stroke */
static void gp_primitive_update_strokes(bContext *C, tGPDprimitive *tgpi)
{
ToolSettings *ts = tgpi->scene->toolsettings;
bGPdata *gpd = tgpi->gpd;
bGPDstroke *gps = tgpi->gpf->strokes.first;
/* realloc points to new size */
/* TODO: only do this if the size has changed? */
gps->points = MEM_reallocN(gps->points, sizeof(bGPDspoint) * tgpi->tot_edges);
if (gps->dvert != NULL) {
gps->dvert = MEM_reallocN(gps->dvert, sizeof(MDeformVert) * tgpi->tot_edges);
}
gps->totpoints = tgpi->tot_edges;
/* compute screen-space coordinates for points */
tGPspoint *points2D = MEM_callocN(sizeof(tGPspoint) * tgpi->tot_edges, "gp primitive points2D");
switch (tgpi->type) {
case GP_STROKE_BOX:
gp_primitive_rectangle(tgpi, points2D);
break;
case GP_STROKE_LINE:
gp_primitive_line(tgpi, points2D);
break;
case GP_STROKE_CIRCLE:
gp_primitive_circle(tgpi, points2D);
break;
default:
break;
}
/* convert screen-coordinates to 3D coordinates */
for (int i = 0; i < gps->totpoints; i++) {
bGPDspoint *pt = &gps->points[i];
tGPspoint *p2d = &points2D[i];
/* convert screen-coordinates to 3D coordinates */
gp_stroke_convertcoords_tpoint(tgpi->scene, tgpi->ar, tgpi->ob, tgpi->gpl, p2d, NULL, &pt->x);
pt->pressure = 1.0f;
pt->strength = tgpi->brush->gpencil_settings->draw_strength;
pt->time = 0.0f;
if (gps->dvert != NULL) {
MDeformVert *dvert = &gps->dvert[i];
dvert->totweight = 0;
dvert->dw = NULL;
}
}
/* if axis locked, reproject to plane locked */
if (tgpi->lock_axis > GP_LOCKAXIS_VIEW) {
bGPDspoint *tpt = gps->points;
float origin[3];
ED_gp_get_drawing_reference(tgpi->scene, tgpi->ob, tgpi->gpl,
ts->gpencil_v3d_align, origin);
for (int i = 0; i < gps->totpoints; i++, tpt++) {
ED_gp_project_point_to_plane(tgpi->ob, tgpi->rv3d, origin,
ts->gp_sculpt.lock_axis - 1,
tpt);
}
}
/* if parented change position relative to parent object */
for (int i = 0; i < gps->totpoints; i++) {
bGPDspoint *pt = &gps->points[i];
gp_apply_parent_point(tgpi->depsgraph, tgpi->ob, tgpi->gpd, tgpi->gpl, pt);
}
/* force fill recalc */
gps->flag |= GP_STROKE_RECALC_CACHES;
/* free temp data */
MEM_SAFE_FREE(points2D);
DEG_id_tag_update(&gpd->id, DEG_TAG_COPY_ON_WRITE);
DEG_id_tag_update(&gpd->id, OB_RECALC_OB | OB_RECALC_DATA);
WM_event_add_notifier(C, NC_GPENCIL | NA_EDITED, NULL);
}
/* Update screen and stroke */
static void gpencil_primitive_update(bContext *C, wmOperator *op, tGPDprimitive *tgpi)
{
/* update indicator in header */
gpencil_primitive_status_indicators(C, tgpi);
/* apply... */
tgpi->type = RNA_enum_get(op->ptr, "type");
tgpi->tot_edges = RNA_int_get(op->ptr, "edges");
/* update points position */
gp_primitive_update_strokes(C, tgpi);
}
/* ----------------------- */
static void gpencil_primitive_interaction_begin(tGPDprimitive *tgpi, const wmEvent *event)
{
tgpi->origin[0] = event->mval[0];
tgpi->origin[1] = event->mval[1];
tgpi->top[0] = event->mval[0];
tgpi->top[1] = event->mval[1];
tgpi->bottom[0] = event->mval[0];
tgpi->bottom[1] = event->mval[1];
}
/* Exit and free memory */
static void gpencil_primitive_exit(bContext *C, wmOperator *op)
{
tGPDprimitive *tgpi = op->customdata;
bGPdata *gpd = tgpi->gpd;
/* don't assume that operator data exists at all */
if (tgpi) {
/* remove drawing handler */
if (tgpi->draw_handle_3d) {
ED_region_draw_cb_exit(tgpi->ar->type, tgpi->draw_handle_3d);
}
/* clear status message area */
ED_workspace_status_text(C, NULL);
/* finally, free memory used by temp data */
BKE_gpencil_free_strokes(tgpi->gpf);
MEM_freeN(tgpi->gpf);
MEM_freeN(tgpi);
}
DEG_id_tag_update(&gpd->id, OB_RECALC_OB | OB_RECALC_DATA);
WM_event_add_notifier(C, NC_GPENCIL | NA_EDITED, NULL);
/* clear pointer */
op->customdata = NULL;
}
/* Init new temporary primitive data */
static void gpencil_primitive_init(bContext *C, wmOperator *op)
{
ToolSettings *ts = CTX_data_tool_settings(C);
bGPdata *gpd = CTX_data_gpencil_data(C);
Main *bmain = CTX_data_main(C);
Scene *scene = CTX_data_scene(C);
Depsgraph *depsgraph = CTX_data_depsgraph(C);
int cfra_eval = (int)DEG_get_ctime(depsgraph);
/* create temporary operator data */
tGPDprimitive *tgpi = MEM_callocN(sizeof(tGPDprimitive), "GPencil Primitive Data");
op->customdata = tgpi;
/* set current scene and window info */
tgpi->scene = scene;
tgpi->ob = CTX_data_active_object(C);
tgpi->sa = CTX_wm_area(C);
tgpi->ar = CTX_wm_region(C);
tgpi->rv3d = tgpi->ar->regiondata;
tgpi->v3d = tgpi->sa->spacedata.first;
tgpi->depsgraph = CTX_data_depsgraph(C);
tgpi->win = CTX_wm_window(C);
/* set current frame number */
tgpi->cframe = cfra_eval;
/* set GP datablock */
tgpi->gpd = gpd;
/* getcolor info */
tgpi->mat = BKE_gpencil_material_ensure(bmain, tgpi->ob);
/* set parameters */
tgpi->type = RNA_enum_get(op->ptr, "type");
/* if circle set default to 32 */
if (tgpi->type == GP_STROKE_CIRCLE) {
RNA_int_set(op->ptr, "edges", 32);
}
2018-07-31 20:11:55 +10:00
else if (tgpi->type == GP_STROKE_BOX) {
RNA_int_set(op->ptr, "edges", 4);
}
else { /* LINE */
RNA_int_set(op->ptr, "edges", 2);
}
tgpi->tot_edges = RNA_int_get(op->ptr, "edges");
tgpi->flag = IDLE;
tgpi->lock_axis = ts->gp_sculpt.lock_axis;
/* set temp layer, frame and stroke */
gp_primitive_set_initdata(C, tgpi);
}
/* ----------------------- */
/* Invoke handler: Initialize the operator */
static int gpencil_primitive_invoke(bContext *C, wmOperator *op, const wmEvent *event)
{
wmWindow *win = CTX_wm_window(C);
bGPdata *gpd = CTX_data_gpencil_data(C);
tGPDprimitive *tgpi = NULL;
/* initialize operator runtime data */
gpencil_primitive_init(C, op);
tgpi = op->customdata;
const bool is_modal = RNA_boolean_get(op->ptr, "wait_for_input");
if (!is_modal) {
tgpi->flag = IN_PROGRESS;
gpencil_primitive_interaction_begin(tgpi, event);
}
/* if in tools region, wait till we get to the main (3d-space)
* region before allowing drawing to take place.
*/
op->flag |= OP_IS_MODAL_CURSOR_REGION;
/* Enable custom drawing handlers */
tgpi->draw_handle_3d = ED_region_draw_cb_activate(tgpi->ar->type, gpencil_primitive_draw_3d, tgpi, REGION_DRAW_POST_VIEW);
/* set cursor to indicate modal */
WM_cursor_modal_set(win, BC_CROSSCURSOR);
/* update sindicator in header */
gpencil_primitive_status_indicators(C, tgpi);
DEG_id_tag_update(&gpd->id, OB_RECALC_OB | OB_RECALC_DATA);
WM_event_add_notifier(C, NC_GPENCIL | NA_EDITED, NULL);
/* add a modal handler for this operator */
WM_event_add_modal_handler(C, op);
return OPERATOR_RUNNING_MODAL;
}
/* Helper to complete a primitive */
static void gpencil_primitive_interaction_end(bContext *C, wmOperator *op, wmWindow *win, tGPDprimitive *tgpi)
{
bGPDframe *gpf;
bGPDstroke *gps;
ToolSettings *ts = tgpi->scene->toolsettings;
const int def_nr = tgpi->ob->actdef - 1;
const bool have_weight = (bool)BLI_findlink(&tgpi->ob->defbase, def_nr);
/* return to normal cursor and header status */
ED_workspace_status_text(C, NULL);
WM_cursor_modal_restore(win);
/* insert keyframes as required... */
gpf = BKE_gpencil_layer_getframe(tgpi->gpl, tgpi->cframe, GP_GETFRAME_ADD_NEW);
2018-09-27 15:49:59 +02:00
/* prepare stroke to get transferred */
gps = tgpi->gpf->strokes.first;
if (gps) {
gps->thickness = tgpi->brush->size;
gps->flag |= GP_STROKE_RECALC_CACHES;
gps->tot_triangles = 0;
}
/* transfer stroke from temporary buffer to the actual frame */
BLI_movelisttolist(&gpf->strokes, &tgpi->gpf->strokes);
BLI_assert(BLI_listbase_is_empty(&tgpi->gpf->strokes));
/* add weights if required */
if ((ts->gpencil_flags & GP_TOOL_FLAG_CREATE_WEIGHTS) && (have_weight)) {
BKE_gpencil_dvert_ensure(gps);
for (int i = 0; i < gps->totpoints; i++) {
MDeformVert *ve = &gps->dvert[i];
MDeformWeight *dw = defvert_verify_index(ve, def_nr);
if (dw) {
dw->weight = ts->vgroup_weight;
}
}
}
DEG_id_tag_update(&tgpi->gpd->id, DEG_TAG_COPY_ON_WRITE);
DEG_id_tag_update(&tgpi->gpd->id, OB_RECALC_OB | OB_RECALC_DATA);
/* clean up temp data */
gpencil_primitive_exit(C, op);
}
/* Helper to square a primitive */
static void gpencil_primitive_to_square(tGPDprimitive *tgpi, const int x, const int y) {
int w = abs(x);
int h = abs(y);
if ((x > 0 && y > 0) || (x < 0 && y < 0)) {
if (w > h)
tgpi->bottom[1] = tgpi->origin[1] + x;
else
tgpi->bottom[0] = tgpi->origin[0] + y;
}
else {
if (w > h)
tgpi->bottom[1] = tgpi->origin[1] - x;
else
tgpi->bottom[0] = tgpi->origin[0] - y;
}
}
/* Modal handler: Events handling during interactive part */
static int gpencil_primitive_modal(bContext *C, wmOperator *op, const wmEvent *event)
{
tGPDprimitive *tgpi = op->customdata;
wmWindow *win = CTX_wm_window(C);
const bool has_numinput = hasNumInput(&tgpi->num);
switch (event->type) {
case LEFTMOUSE:
if ((event->val == KM_PRESS) && (tgpi->flag == IDLE)) {
/* start drawing primitive */
/* TODO: Ignore if not in main region yet */
tgpi->flag = IN_PROGRESS;
gpencil_primitive_interaction_begin(tgpi, event);
}
else if ((event->val == KM_RELEASE) && (tgpi->flag == IN_PROGRESS)) {
/* stop drawing primitive */
tgpi->flag = IDLE;
gpencil_primitive_interaction_end(C, op, win, tgpi);
/* done! */
return OPERATOR_FINISHED;
}
else {
if (G.debug & G_DEBUG) {
printf("GP Add Primitive Modal: LEFTMOUSE %d, Status = %d\n", event->val, tgpi->flag);
}
}
break;
case RETKEY: /* confirm */
{
tgpi->flag = IDLE;
gpencil_primitive_interaction_end(C, op, win, tgpi);
/* done! */
return OPERATOR_FINISHED;
}
case ESCKEY: /* cancel */
case RIGHTMOUSE:
{
/* return to normal cursor and header status */
ED_workspace_status_text(C, NULL);
WM_cursor_modal_restore(win);
/* clean up temp data */
gpencil_primitive_exit(C, op);
/* canceled! */
return OPERATOR_CANCELLED;
}
case WHEELUPMOUSE:
{
if (tgpi->type == GP_STROKE_CIRCLE) {
tgpi->tot_edges = tgpi->tot_edges + 1;
CLAMP(tgpi->tot_edges, MIN_EDGES, MAX_EDGES);
RNA_int_set(op->ptr, "edges", tgpi->tot_edges);
/* update screen */
gpencil_primitive_update(C, op, tgpi);
}
break;
}
case WHEELDOWNMOUSE:
{
if (tgpi->type == GP_STROKE_CIRCLE) {
tgpi->tot_edges = tgpi->tot_edges - 1;
CLAMP(tgpi->tot_edges, MIN_EDGES, MAX_EDGES);
RNA_int_set(op->ptr, "edges", tgpi->tot_edges);
/* update screen */
gpencil_primitive_update(C, op, tgpi);
}
break;
}
case MOUSEMOVE: /* calculate new position */
{
/* only handle mousemove if not doing numinput */
if (has_numinput == false) {
/* update position of mouse */
tgpi->bottom[0] = event->mval[0];
tgpi->bottom[1] = event->mval[1];
tgpi->top[0] = tgpi->origin[0];
tgpi->top[1] = tgpi->origin[1];
if (tgpi->flag == IDLE) {
tgpi->origin[0] = event->mval[0];
tgpi->origin[1] = event->mval[1];
}
/* Keep square if shift key */
if (event->shift) {
int x = tgpi->bottom[0] - tgpi->origin[0];
int y = tgpi->bottom[1] - tgpi->origin[1];
if (tgpi->type == GP_STROKE_LINE) {
float angle = fabsf(atan2f((float)y, (float)x));
if (angle < 0.4f || angle > (M_PI - 0.4f)) {
tgpi->bottom[1] = tgpi->origin[1];
}
else if (angle > (M_PI_2 - 0.4f) && angle < (M_PI_2 + 0.4f)) {
tgpi->bottom[0] = tgpi->origin[0];
}
else {
gpencil_primitive_to_square(tgpi, x, y);
}
}
else {
gpencil_primitive_to_square(tgpi, x, y);
}
}
/* Center primitive if alt key */
if (event->alt) {
tgpi->top[0] = tgpi->origin[0] - (tgpi->bottom[0] - tgpi->origin[0]);
tgpi->top[1] = tgpi->origin[1] - (tgpi->bottom[1] - tgpi->origin[1]);
}
/* update screen */
gpencil_primitive_update(C, op, tgpi);
}
break;
}
default:
{
if ((event->val == KM_PRESS) && handleNumInput(C, &tgpi->num, event)) {
float value;
/* Grab data from numeric input, and store this new value (the user see an int) */
value = tgpi->tot_edges;
applyNumInput(&tgpi->num, &value);
tgpi->tot_edges = value;
CLAMP(tgpi->tot_edges, MIN_EDGES, MAX_EDGES);
RNA_int_set(op->ptr, "edges", tgpi->tot_edges);
/* update screen */
gpencil_primitive_update(C, op, tgpi);
break;
}
else {
/* unhandled event - allow to pass through */
return OPERATOR_RUNNING_MODAL | OPERATOR_PASS_THROUGH;
}
}
}
/* still running... */
return OPERATOR_RUNNING_MODAL;
}
/* Cancel handler */
static void gpencil_primitive_cancel(bContext *C, wmOperator *op)
{
/* this is just a wrapper around exit() */
gpencil_primitive_exit(C, op);
}
void GPENCIL_OT_primitive(wmOperatorType *ot)
{
static EnumPropertyItem primitive_type[] = {
2018-11-10 09:24:23 +11:00
{GP_STROKE_BOX, "BOX", 0, "Box", ""},
{GP_STROKE_LINE, "LINE", 0, "Line", ""},
{GP_STROKE_CIRCLE, "CIRCLE", 0, "Circle", ""},
{0, NULL, 0, NULL, NULL}
};
/* identifiers */
ot->name = "Grease Pencil Shapes";
ot->idname = "GPENCIL_OT_primitive";
ot->description = "Create predefined grease pencil stroke shapes";
/* callbacks */
ot->invoke = gpencil_primitive_invoke;
ot->modal = gpencil_primitive_modal;
ot->cancel = gpencil_primitive_cancel;
ot->poll = gpencil_primitive_add_poll;
/* flags */
ot->flag = OPTYPE_UNDO | OPTYPE_BLOCKING;
/* properties */
PropertyRNA *prop;
RNA_def_int(ot->srna, "edges", 4, MIN_EDGES, MAX_EDGES, "Edges", "Number of polygon edges", MIN_EDGES, MAX_EDGES);
RNA_def_enum(ot->srna, "type", primitive_type, GP_STROKE_BOX, "Type", "Type of shape");
prop = RNA_def_boolean(ot->srna, "wait_for_input", true, "Wait for Input", "");
RNA_def_property_flag(prop, PROP_HIDDEN | PROP_SKIP_SAVE);
}
/* *************************************************************** */