| 
									
										
										
										
											2011-02-23 10:52:22 +00:00
										 |  |  | /*
 | 
					
						
							| 
									
										
										
										
											2009-04-20 10:20:18 +00:00
										 |  |  |  * 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, | 
					
						
							| 
									
										
										
										
											2010-02-12 13:34:04 +00:00
										 |  |  |  * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. | 
					
						
							| 
									
										
										
										
											2009-04-20 10:20:18 +00:00
										 |  |  |  * | 
					
						
							|  |  |  |  * The Original Code is Copyright (C) 2008, Blender Foundation | 
					
						
							|  |  |  |  * This is a new part of Blender | 
					
						
							|  |  |  |  */ | 
					
						
							| 
									
										
										
										
											2011-02-27 20:40:57 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-02-18 08:08:12 +11:00
										 |  |  | /** \file
 | 
					
						
							|  |  |  |  * \ingroup bke | 
					
						
							| 
									
										
										
										
											2011-02-27 20:40:57 +00:00
										 |  |  |  */ | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2009-04-20 10:20:18 +00:00
										 |  |  | #include <stdio.h>
 | 
					
						
							|  |  |  | #include <string.h>
 | 
					
						
							|  |  |  | #include <stdlib.h>
 | 
					
						
							|  |  |  | #include <stddef.h>
 | 
					
						
							|  |  |  | #include <math.h>
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-02-01 12:44:19 +11:00
										 |  |  | #include "CLG_log.h"
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2009-04-20 10:20:18 +00:00
										 |  |  | #include "MEM_guardedalloc.h"
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | #include "BLI_blenlib.h"
 | 
					
						
							| 
									
										
										
										
											2011-01-07 18:36:47 +00:00
										 |  |  | #include "BLI_utildefines.h"
 | 
					
						
							| 
									
										
										
										
											2013-11-06 10:59:05 +00:00
										 |  |  | #include "BLI_math_vector.h"
 | 
					
						
							| 
									
										
										
										
											2017-01-16 17:33:34 +01:00
										 |  |  | #include "BLI_string_utils.h"
 | 
					
						
							| 
									
										
										
										
											2009-04-20 10:20:18 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2015-08-16 17:32:01 +10:00
										 |  |  | #include "BLT_translation.h"
 | 
					
						
							| 
									
										
										
										
											2013-03-25 08:29:06 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-07-31 10:22:19 +02:00
										 |  |  | #include "DNA_anim_types.h"
 | 
					
						
							|  |  |  | #include "DNA_meshdata_types.h"
 | 
					
						
							|  |  |  | #include "DNA_material_types.h"
 | 
					
						
							| 
									
										
										
										
											2009-04-20 10:20:18 +00:00
										 |  |  | #include "DNA_gpencil_types.h"
 | 
					
						
							| 
									
										
										
										
											2013-09-28 19:28:41 +00:00
										 |  |  | #include "DNA_userdef_types.h"
 | 
					
						
							| 
									
										
										
										
											2016-08-03 23:31:48 +02:00
										 |  |  | #include "DNA_scene_types.h"
 | 
					
						
							| 
									
										
										
										
											2018-07-31 10:22:19 +02:00
										 |  |  | #include "DNA_object_types.h"
 | 
					
						
							| 
									
										
										
										
											2009-04-20 10:20:18 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-07-31 10:22:19 +02:00
										 |  |  | #include "BKE_action.h"
 | 
					
						
							| 
									
										
											  
											
												Grease Pencil - Storyboarding Features (merge from GPencil_EditStrokes branch)
This merge-commit brings in a number of new features and workflow/UI improvements for
working with Grease Pencil. While these were originally targetted at improving
the workflow for creating 3D storyboards in Blender using the Grease Pencil,
many of these changes should also prove useful in other workflows too.
The main highlights here are:
1) It is now possible to edit Grease Pencil strokes
   - Use D Tab, or toggle the "Enable Editing" toggles in the Toolbar/Properties regions
     to enter "Stroke Edit Mode". In this mode, many common editing tools will
     operate on Grease Pencil stroke points instead.
   - Tools implemented include Select, Select All/Border/Circle/Linked/More/Less,
     Grab, Rotate, Scale, Bend, Shear, To Sphere, Mirror, Duplicate, Delete.
   - Proportional Editing works when using the transform tools
2) Grease Pencil stroke settings can now be animated
   NOTE: Currently drivers don't work, but if time allows, this may still be
         added before the release.
3) Strokes can be drawn with "filled" interiors, using a separate set of
   colour/opacity settings to the ones used for the lines themselves.
   This makes use of OpenGL filled polys, which has the limitation of only
   being able to fill convex shapes. Some artifacts may be visible on concave
   shapes (e.g. pacman's mouth will be overdrawn)
4) "Volumetric Strokes" - An alternative drawing technique for stroke drawing
   has been added which draws strokes as a series of screen-aligned discs.
   While this was originally a partial experimental technique at getting better
   quality 3D lines, the effects possible using this technique were interesting
   enough to warrant making this a dedicated feature. Best results when partial
   opacity and large stroke widths are used.
5) Improved Onion Skinning Support
   - Different colours can be selected for the before/after ghosts. To do so,
     enable the "colour wheel" toggle beside the Onion Skinning toggle, and set
     the colours accordingly.
   - Different numbers of ghosts can be shown before/after the current frame
6) Grease Pencil datablocks are now attached to the scene by default instead of
   the active object.
   - For a long time, the object-attachment has proved to be quite problematic
     for users to keep track of. Now that this is done at scene level, it is
     easier for most users to use.
   - An exception for old files (and for any addons which may benefit from object
     attachment instead), is that if the active object has a Grease Pencil datablock,
     that will be used instead.
   - It is not currently possible to choose object-attachment from the UI, but
     it is simple to do this from the console instead, by doing:
     context.active_object.grease_pencil = bpy.data.grease_pencil["blah"]
7) Various UI Cleanups
   - The layers UI has been cleaned up to use a list instead of the nested-panels
     design. Apart from saving space, this is also much nicer to look at now.
   - The UI code is now all defined in Python. To support this, it has been necessary
     to add some new context properties to make it easier to access these settings.
     e.g. "gpencil_data" for the datablock
          "active_gpencil_layer" and "active_gpencil_frame" for active data,
          "editable_gpencil_strokes" for the strokes that can be edited
   - The "stroke placement/alignment" settings (previously "Drawing Settings" at the
     bottom of the Grease Pencil panel in the Properties Region) is now located in
     the toolbar. These were more toolsettings than properties for how GPencil got drawn.
   - "Use Sketching Sessions" has been renamed "Continuous Drawing", as per a
     suggestion for an earlier discussion on developer.blender.org
   - By default, the painting operator will wait for a mouse button to be pressed
     before it starts creating the stroke. This is to make it easier to include
     this operator in various toolbars/menus/etc.   To get it immediately starting
     (as when you hold down DKEy to draw), set "wait_for_input" to False.
   - GPencil Layers can be rearranged in the "Grease Pencil" mode of the Action Editor
   - Toolbar panels have been added to all the other editors which support these.
8) Pie menus for quick-access to tools
   A set of experimental pie menus has been included for quick access to many
   tools and settings. It is not necessary to use these to get things done,
   but they have been designed to help make certain common tasks easier.
   - Ctrl-D = The main pie menu. Reveals tools in a context sensitive and
              spatially stable manner.
   - D Q    = "Quick Settings" pie. This allows quick access to the active
              layer's settings. Notably, colours, thickness, and turning
              onion skinning on/off.
											
										 
											2014-12-01 01:52:06 +13:00
										 |  |  | #include "BKE_animsys.h"
 | 
					
						
							| 
									
										
										
										
											2019-08-31 17:26:48 +02:00
										 |  |  | #include "BKE_curve.h"
 | 
					
						
							|  |  |  | #include "BKE_collection.h"
 | 
					
						
							|  |  |  | #include "BKE_colortools.h"
 | 
					
						
							| 
									
										
										
										
											2018-08-27 14:20:40 +10:00
										 |  |  | #include "BKE_deform.h"
 | 
					
						
							| 
									
										
										
										
											2009-04-20 10:20:18 +00:00
										 |  |  | #include "BKE_gpencil.h"
 | 
					
						
							| 
									
										
										
										
											2018-07-31 10:22:19 +02:00
										 |  |  | #include "BKE_icons.h"
 | 
					
						
							| 
									
										
										
										
											2009-04-20 10:20:18 +00:00
										 |  |  | #include "BKE_library.h"
 | 
					
						
							| 
									
										
										
										
											2016-07-10 14:52:00 +02:00
										 |  |  | #include "BKE_main.h"
 | 
					
						
							| 
									
										
										
										
											2018-07-31 10:22:19 +02:00
										 |  |  | #include "BKE_material.h"
 | 
					
						
							| 
									
										
										
										
											2019-08-31 17:26:48 +02:00
										 |  |  | #include "BKE_object.h"
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | #include "BLI_math_color.h"
 | 
					
						
							| 
									
										
										
										
											2009-04-20 10:20:18 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-07-31 10:22:19 +02:00
										 |  |  | #include "DEG_depsgraph.h"
 | 
					
						
							| 
									
										
										
										
											2009-04-20 10:20:18 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-02-01 12:44:19 +11:00
										 |  |  | static CLG_LogRef LOG = {"bke.gpencil"}; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2009-04-20 10:20:18 +00:00
										 |  |  | /* ************************************************** */ | 
					
						
							| 
									
										
										
										
											2018-07-31 10:22:19 +02:00
										 |  |  | /* Draw Engine */ | 
					
						
							| 
									
										
										
										
											2009-04-20 10:20:18 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-04-17 06:17:24 +02:00
										 |  |  | void (*BKE_gpencil_batch_cache_dirty_tag_cb)(bGPdata *gpd) = NULL; | 
					
						
							|  |  |  | void (*BKE_gpencil_batch_cache_free_cb)(bGPdata *gpd) = NULL; | 
					
						
							| 
									
										
										
										
											2018-07-31 10:22:19 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-08-23 10:14:29 -03:00
										 |  |  | void BKE_gpencil_batch_cache_dirty_tag(bGPdata *gpd) | 
					
						
							| 
									
										
										
										
											2018-07-31 10:22:19 +02:00
										 |  |  | { | 
					
						
							| 
									
										
										
										
											2019-04-17 06:17:24 +02:00
										 |  |  |   if (gpd) { | 
					
						
							|  |  |  |     DEG_id_tag_update(&gpd->id, ID_RECALC_GEOMETRY); | 
					
						
							|  |  |  |     BKE_gpencil_batch_cache_dirty_tag_cb(gpd); | 
					
						
							|  |  |  |   } | 
					
						
							| 
									
										
										
										
											2018-07-31 10:22:19 +02:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | void BKE_gpencil_batch_cache_free(bGPdata *gpd) | 
					
						
							|  |  |  | { | 
					
						
							| 
									
										
										
										
											2019-04-17 06:17:24 +02:00
										 |  |  |   if (gpd) { | 
					
						
							|  |  |  |     BKE_gpencil_batch_cache_free_cb(gpd); | 
					
						
							|  |  |  |   } | 
					
						
							| 
									
										
										
										
											2018-07-31 10:22:19 +02:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | /* ************************************************** */ | 
					
						
							|  |  |  | /* Memory Management */ | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | /* clean vertex groups weights */ | 
					
						
							|  |  |  | void BKE_gpencil_free_point_weights(MDeformVert *dvert) | 
					
						
							|  |  |  | { | 
					
						
							| 
									
										
										
										
											2019-04-17 06:17:24 +02:00
										 |  |  |   if (dvert == NULL) { | 
					
						
							|  |  |  |     return; | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  |   MEM_SAFE_FREE(dvert->dw); | 
					
						
							| 
									
										
										
										
											2018-07-31 10:22:19 +02:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | void BKE_gpencil_free_stroke_weights(bGPDstroke *gps) | 
					
						
							|  |  |  | { | 
					
						
							| 
									
										
										
										
											2019-04-17 06:17:24 +02:00
										 |  |  |   if (gps == NULL) { | 
					
						
							|  |  |  |     return; | 
					
						
							|  |  |  |   } | 
					
						
							| 
									
										
										
										
											2018-07-31 10:22:19 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-04-17 06:17:24 +02:00
										 |  |  |   if (gps->dvert == NULL) { | 
					
						
							|  |  |  |     return; | 
					
						
							|  |  |  |   } | 
					
						
							| 
									
										
										
										
											2018-07-31 10:22:19 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-04-17 06:17:24 +02:00
										 |  |  |   for (int i = 0; i < gps->totpoints; i++) { | 
					
						
							|  |  |  |     MDeformVert *dvert = &gps->dvert[i]; | 
					
						
							|  |  |  |     BKE_gpencil_free_point_weights(dvert); | 
					
						
							|  |  |  |   } | 
					
						
							| 
									
										
										
										
											2018-07-31 10:22:19 +02:00
										 |  |  | } | 
					
						
							| 
									
										
										
										
											2009-04-20 10:20:18 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-08-03 23:31:48 +02:00
										 |  |  | /* free stroke, doesn't unlink from any listbase */ | 
					
						
							| 
									
										
										
										
											2016-08-04 15:03:18 +02:00
										 |  |  | void BKE_gpencil_free_stroke(bGPDstroke *gps) | 
					
						
							| 
									
										
										
										
											2016-08-03 23:31:48 +02:00
										 |  |  | { | 
					
						
							| 
									
										
										
										
											2019-04-17 06:17:24 +02:00
										 |  |  |   if (gps == NULL) { | 
					
						
							|  |  |  |     return; | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  |   /* free stroke memory arrays, then stroke itself */ | 
					
						
							|  |  |  |   if (gps->points) { | 
					
						
							|  |  |  |     MEM_freeN(gps->points); | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  |   if (gps->dvert) { | 
					
						
							|  |  |  |     BKE_gpencil_free_stroke_weights(gps); | 
					
						
							|  |  |  |     MEM_freeN(gps->dvert); | 
					
						
							|  |  |  |   } | 
					
						
							| 
									
										
										
										
											2019-04-22 09:39:35 +10:00
										 |  |  |   if (gps->triangles) { | 
					
						
							| 
									
										
										
										
											2019-04-17 06:17:24 +02:00
										 |  |  |     MEM_freeN(gps->triangles); | 
					
						
							| 
									
										
										
										
											2019-04-22 09:39:35 +10:00
										 |  |  |   } | 
					
						
							| 
									
										
										
										
											2016-08-03 23:31:48 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-04-17 06:17:24 +02:00
										 |  |  |   MEM_freeN(gps); | 
					
						
							| 
									
										
										
										
											2016-08-03 23:31:48 +02:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2009-04-20 10:20:18 +00:00
										 |  |  | /* Free strokes belonging to a gp-frame */ | 
					
						
							| 
									
										
										
										
											2016-08-04 15:03:18 +02:00
										 |  |  | bool BKE_gpencil_free_strokes(bGPDframe *gpf) | 
					
						
							| 
									
										
										
										
											2009-04-20 10:20:18 +00:00
										 |  |  | { | 
					
						
							| 
									
										
										
										
											2019-04-17 06:17:24 +02:00
										 |  |  |   bGPDstroke *gps_next; | 
					
						
							|  |  |  |   bool changed = (BLI_listbase_is_empty(&gpf->strokes) == false); | 
					
						
							| 
									
										
										
										
											2013-12-03 09:22:29 +11:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-04-17 06:17:24 +02:00
										 |  |  |   /* free strokes */ | 
					
						
							|  |  |  |   for (bGPDstroke *gps = gpf->strokes.first; gps; gps = gps_next) { | 
					
						
							|  |  |  |     gps_next = gps->next; | 
					
						
							|  |  |  |     BKE_gpencil_free_stroke(gps); | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  |   BLI_listbase_clear(&gpf->strokes); | 
					
						
							| 
									
										
										
										
											2013-11-25 04:55:26 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-04-17 06:17:24 +02:00
										 |  |  |   return changed; | 
					
						
							| 
									
										
										
										
											2009-04-20 10:20:18 +00:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-07-31 10:22:19 +02:00
										 |  |  | /* Free strokes and colors belonging to a gp-frame */ | 
					
						
							| 
									
										
										
										
											2019-08-24 09:02:42 +02:00
										 |  |  | bool BKE_gpencil_free_frame_runtime_data(bGPDframe *gpf_eval) | 
					
						
							| 
									
										
										
										
											2018-07-31 10:22:19 +02:00
										 |  |  | { | 
					
						
							| 
									
										
										
										
											2019-04-17 06:17:24 +02:00
										 |  |  |   bGPDstroke *gps_next; | 
					
						
							| 
									
										
										
										
											2019-08-24 09:02:42 +02:00
										 |  |  |   if (!gpf_eval) { | 
					
						
							| 
									
										
										
										
											2019-04-17 06:17:24 +02:00
										 |  |  |     return false; | 
					
						
							|  |  |  |   } | 
					
						
							| 
									
										
										
										
											2018-07-31 10:22:19 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-04-17 06:17:24 +02:00
										 |  |  |   /* free strokes */ | 
					
						
							| 
									
										
										
										
											2019-08-24 09:02:42 +02:00
										 |  |  |   for (bGPDstroke *gps = gpf_eval->strokes.first; gps; gps = gps_next) { | 
					
						
							| 
									
										
										
										
											2019-04-17 06:17:24 +02:00
										 |  |  |     gps_next = gps->next; | 
					
						
							|  |  |  |     BKE_gpencil_free_stroke(gps); | 
					
						
							|  |  |  |   } | 
					
						
							| 
									
										
										
										
											2019-08-24 09:02:42 +02:00
										 |  |  |   BLI_listbase_clear(&gpf_eval->strokes); | 
					
						
							| 
									
										
										
										
											2018-07-31 10:22:19 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-04-17 06:17:24 +02:00
										 |  |  |   return true; | 
					
						
							| 
									
										
										
										
											2018-07-31 10:22:19 +02:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2009-04-20 10:20:18 +00:00
										 |  |  | /* Free all of a gp-layer's frames */ | 
					
						
							| 
									
										
										
										
											2016-08-04 15:03:18 +02:00
										 |  |  | void BKE_gpencil_free_frames(bGPDlayer *gpl) | 
					
						
							| 
									
										
										
										
											2009-04-20 10:20:18 +00:00
										 |  |  | { | 
					
						
							| 
									
										
										
										
											2019-04-17 06:17:24 +02:00
										 |  |  |   bGPDframe *gpf_next; | 
					
						
							| 
									
										
										
										
											2018-06-17 17:05:51 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-04-17 06:17:24 +02:00
										 |  |  |   /* error checking */ | 
					
						
							| 
									
										
										
										
											2019-04-22 09:39:35 +10:00
										 |  |  |   if (gpl == NULL) { | 
					
						
							| 
									
										
										
										
											2019-04-17 06:17:24 +02:00
										 |  |  |     return; | 
					
						
							| 
									
										
										
										
											2019-04-22 09:39:35 +10:00
										 |  |  |   } | 
					
						
							| 
									
										
										
										
											2018-06-17 17:05:51 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-04-17 06:17:24 +02:00
										 |  |  |   /* free frames */ | 
					
						
							|  |  |  |   for (bGPDframe *gpf = gpl->frames.first; gpf; gpf = gpf_next) { | 
					
						
							|  |  |  |     gpf_next = gpf->next; | 
					
						
							| 
									
										
										
										
											2018-06-17 17:05:51 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-04-17 06:17:24 +02:00
										 |  |  |     /* free strokes and their associated memory */ | 
					
						
							|  |  |  |     BKE_gpencil_free_strokes(gpf); | 
					
						
							|  |  |  |     BLI_freelinkN(&gpl->frames, gpf); | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  |   gpl->actframe = NULL; | 
					
						
							| 
									
										
										
										
											2009-04-20 10:20:18 +00:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-07-31 10:22:19 +02:00
										 |  |  | /* Free all of the gp-layers for a viewport (list should be &gpd->layers or so) */ | 
					
						
							|  |  |  | void BKE_gpencil_free_layers(ListBase *list) | 
					
						
							| 
									
										
										
										
											2016-08-03 23:31:48 +02:00
										 |  |  | { | 
					
						
							| 
									
										
										
										
											2019-04-17 06:17:24 +02:00
										 |  |  |   bGPDlayer *gpl_next; | 
					
						
							| 
									
										
										
										
											2016-08-03 23:31:48 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-04-17 06:17:24 +02:00
										 |  |  |   /* error checking */ | 
					
						
							| 
									
										
										
										
											2019-04-22 09:39:35 +10:00
										 |  |  |   if (list == NULL) { | 
					
						
							| 
									
										
										
										
											2019-04-17 06:17:24 +02:00
										 |  |  |     return; | 
					
						
							| 
									
										
										
										
											2019-04-22 09:39:35 +10:00
										 |  |  |   } | 
					
						
							| 
									
										
										
										
											2016-08-03 23:31:48 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-04-17 06:17:24 +02:00
										 |  |  |   /* delete layers */ | 
					
						
							|  |  |  |   for (bGPDlayer *gpl = list->first; gpl; gpl = gpl_next) { | 
					
						
							|  |  |  |     gpl_next = gpl->next; | 
					
						
							| 
									
										
										
										
											2016-08-03 23:31:48 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-04-17 06:17:24 +02:00
										 |  |  |     /* free layers and their data */ | 
					
						
							|  |  |  |     BKE_gpencil_free_frames(gpl); | 
					
						
							|  |  |  |     BLI_freelinkN(list, gpl); | 
					
						
							|  |  |  |   } | 
					
						
							| 
									
										
										
										
											2016-08-03 23:31:48 +02:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
											  
											
												ID-Remap - Step one: core work (cleanup and rework of generic ID datablock handling).
This commit changes a lot of how IDs are handled internally, especially the unlinking/freeing
processes. So far, this was very fuzy, to summarize cleanly deleting or replacing a datablock
was pretty much impossible, except for a few special cases.
Also, unlinking was handled by each datatype, in a rather messy and prone-to-errors way (quite
a few ID usages were missed or wrongly handled that way).
One of the main goal of id-remap branch was to cleanup this, and fatorize ID links handling
by using library_query utils to allow generic handling of those, which is now the case
(now, generic ID links handling is only "knwon" from readfile.c and library_query.c).
This commit also adds backends to allow live replacement and deletion of datablocks in Blender
(so-called 'remapping' process, where we replace all usages of a given ID pointer by a new one,
or NULL one in case of unlinking).
This will allow nice new features, like ability to easily reload or relocate libraries, real immediate
deletion of datablocks in blender, replacement of one datablock by another, etc.
Some of those are for next commits.
A word of warning: this commit is highly risky, because it affects potentially a lot in Blender core.
Though it was tested rather deeply, being totally impossible to check all possible ID usage cases,
it's likely there are some remaining issues and bugs in new code... Please report them! ;)
Review task: D2027 (https://developer.blender.org/D2027).
Reviewed by campbellbarton, thanks a bunch.
											
										 
											2016-06-22 17:29:38 +02:00
										 |  |  | /** Free (or release) any data used by this grease pencil (does not free the gpencil itself). */ | 
					
						
							| 
									
										
										
										
											2018-07-31 10:22:19 +02:00
										 |  |  | void BKE_gpencil_free(bGPdata *gpd, bool free_all) | 
					
						
							| 
									
										
										
										
											2009-04-20 10:20:18 +00:00
										 |  |  | { | 
					
						
							| 
									
										
										
										
											2019-04-17 06:17:24 +02:00
										 |  |  |   /* clear animation data */ | 
					
						
							|  |  |  |   BKE_animdata_free(&gpd->id, false); | 
					
						
							| 
									
										
											  
											
												ID-Remap - Step one: core work (cleanup and rework of generic ID datablock handling).
This commit changes a lot of how IDs are handled internally, especially the unlinking/freeing
processes. So far, this was very fuzy, to summarize cleanly deleting or replacing a datablock
was pretty much impossible, except for a few special cases.
Also, unlinking was handled by each datatype, in a rather messy and prone-to-errors way (quite
a few ID usages were missed or wrongly handled that way).
One of the main goal of id-remap branch was to cleanup this, and fatorize ID links handling
by using library_query utils to allow generic handling of those, which is now the case
(now, generic ID links handling is only "knwon" from readfile.c and library_query.c).
This commit also adds backends to allow live replacement and deletion of datablocks in Blender
(so-called 'remapping' process, where we replace all usages of a given ID pointer by a new one,
or NULL one in case of unlinking).
This will allow nice new features, like ability to easily reload or relocate libraries, real immediate
deletion of datablocks in blender, replacement of one datablock by another, etc.
Some of those are for next commits.
A word of warning: this commit is highly risky, because it affects potentially a lot in Blender core.
Though it was tested rather deeply, being totally impossible to check all possible ID usage cases,
it's likely there are some remaining issues and bugs in new code... Please report them! ;)
Review task: D2027 (https://developer.blender.org/D2027).
Reviewed by campbellbarton, thanks a bunch.
											
										 
											2016-06-22 17:29:38 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-04-17 06:17:24 +02:00
										 |  |  |   /* free layers */ | 
					
						
							|  |  |  |   BKE_gpencil_free_layers(&gpd->layers); | 
					
						
							| 
									
										
										
										
											2016-08-03 23:31:48 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-04-17 06:17:24 +02:00
										 |  |  |   /* materials */ | 
					
						
							|  |  |  |   MEM_SAFE_FREE(gpd->mat); | 
					
						
							| 
									
										
										
										
											2018-07-31 10:22:19 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-04-17 06:17:24 +02:00
										 |  |  |   /* free all data */ | 
					
						
							|  |  |  |   if (free_all) { | 
					
						
							|  |  |  |     /* clear cache */ | 
					
						
							|  |  |  |     BKE_gpencil_batch_cache_free(gpd); | 
					
						
							|  |  |  |   } | 
					
						
							| 
									
										
										
										
											2009-04-20 10:20:18 +00:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-07-31 10:22:19 +02:00
										 |  |  | /* ************************************************** */ | 
					
						
							|  |  |  | /* Container Creation */ | 
					
						
							| 
									
										
										
										
											2009-04-20 10:20:18 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  | /* add a new gp-frame to the given layer */ | 
					
						
							| 
									
										
										
										
											2016-08-04 15:03:18 +02:00
										 |  |  | bGPDframe *BKE_gpencil_frame_addnew(bGPDlayer *gpl, int cframe) | 
					
						
							| 
									
										
										
										
											2009-04-20 10:20:18 +00:00
										 |  |  | { | 
					
						
							| 
									
										
										
										
											2019-04-17 06:17:24 +02:00
										 |  |  |   bGPDframe *gpf = NULL, *gf = NULL; | 
					
						
							|  |  |  |   short state = 0; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   /* error checking */ | 
					
						
							| 
									
										
										
										
											2019-04-22 09:39:35 +10:00
										 |  |  |   if (gpl == NULL) { | 
					
						
							| 
									
										
										
										
											2019-04-17 06:17:24 +02:00
										 |  |  |     return NULL; | 
					
						
							| 
									
										
										
										
											2019-04-22 09:39:35 +10:00
										 |  |  |   } | 
					
						
							| 
									
										
										
										
											2019-04-17 06:17:24 +02:00
										 |  |  | 
 | 
					
						
							|  |  |  |   /* 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) { | 
					
						
							| 
									
										
										
										
											2019-09-03 18:58:57 +02:00
										 |  |  |     CLOG_ERROR( | 
					
						
							|  |  |  |         &LOG, "Frame (%d) existed already for this layer_active. Using existing frame", cframe); | 
					
						
							| 
									
										
										
										
											2019-04-17 06:17:24 +02:00
										 |  |  | 
 | 
					
						
							|  |  |  |     /* free the newly created one, and use the old one instead */ | 
					
						
							|  |  |  |     MEM_freeN(gpf); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     /* return existing frame instead... */ | 
					
						
							|  |  |  |     BLI_assert(gf != NULL); | 
					
						
							|  |  |  |     gpf = gf; | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  |   else if (state == 0) { | 
					
						
							|  |  |  |     /* add to end then! */ | 
					
						
							|  |  |  |     BLI_addtail(&gpl->frames, gpf); | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   /* return frame */ | 
					
						
							|  |  |  |   return gpf; | 
					
						
							| 
									
										
										
										
											2009-04-20 10:20:18 +00:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2015-12-13 21:03:13 +13:00
										 |  |  | /* add a copy of the active gp-frame to the given layer */ | 
					
						
							| 
									
										
										
										
											2016-08-04 15:03:18 +02:00
										 |  |  | bGPDframe *BKE_gpencil_frame_addcopy(bGPDlayer *gpl, int cframe) | 
					
						
							| 
									
										
										
										
											2015-12-13 21:03:13 +13:00
										 |  |  | { | 
					
						
							| 
									
										
										
										
											2019-04-17 06:17:24 +02:00
										 |  |  |   bGPDframe *new_frame; | 
					
						
							|  |  |  |   bool found = false; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   /* Error checking/handling */ | 
					
						
							|  |  |  |   if (gpl == NULL) { | 
					
						
							|  |  |  |     /* no layer */ | 
					
						
							|  |  |  |     return NULL; | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  |   else if (gpl->actframe == NULL) { | 
					
						
							|  |  |  |     /* no active frame, so just create a new one from scratch */ | 
					
						
							|  |  |  |     return BKE_gpencil_frame_addnew(gpl, cframe); | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   /* Create a copy of the frame */ | 
					
						
							|  |  |  |   new_frame = BKE_gpencil_frame_duplicate(gpl->actframe); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   /* Find frame to insert it before */ | 
					
						
							|  |  |  |   for (bGPDframe *gpf = gpl->frames.first; gpf; gpf = gpf->next) { | 
					
						
							|  |  |  |     if (gpf->framenum > cframe) { | 
					
						
							|  |  |  |       /* Add it here */ | 
					
						
							|  |  |  |       BLI_insertlinkbefore(&gpl->frames, gpf, new_frame); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |       found = true; | 
					
						
							|  |  |  |       break; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |     else if (gpf->framenum == cframe) { | 
					
						
							|  |  |  |       /* This only happens when we're editing with framelock on...
 | 
					
						
							|  |  |  |        * - Delete the new frame and don't do anything else here... | 
					
						
							|  |  |  |        */ | 
					
						
							|  |  |  |       BKE_gpencil_free_strokes(new_frame); | 
					
						
							|  |  |  |       MEM_freeN(new_frame); | 
					
						
							|  |  |  |       new_frame = NULL; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |       found = true; | 
					
						
							|  |  |  |       break; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   if (found == false) { | 
					
						
							|  |  |  |     /* Add new frame to the end */ | 
					
						
							|  |  |  |     BLI_addtail(&gpl->frames, new_frame); | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   /* Ensure that frame is set up correctly, and return it */ | 
					
						
							|  |  |  |   if (new_frame) { | 
					
						
							|  |  |  |     new_frame->framenum = cframe; | 
					
						
							|  |  |  |     gpl->actframe = new_frame; | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   return new_frame; | 
					
						
							| 
									
										
										
										
											2015-12-13 21:03:13 +13:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2009-04-20 10:20:18 +00:00
										 |  |  | /* add a new gp-layer and make it the active layer */ | 
					
						
							| 
									
										
										
										
											2016-08-04 15:03:18 +02:00
										 |  |  | bGPDlayer *BKE_gpencil_layer_addnew(bGPdata *gpd, const char *name, bool setactive) | 
					
						
							| 
									
										
										
										
											2009-04-20 10:20:18 +00:00
										 |  |  | { | 
					
						
							| 
									
										
										
										
											2019-04-17 06:17:24 +02:00
										 |  |  |   bGPDlayer *gpl = NULL; | 
					
						
							|  |  |  |   bGPDlayer *gpl_active = NULL; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   /* check that list is ok */ | 
					
						
							| 
									
										
										
										
											2019-04-22 09:39:35 +10:00
										 |  |  |   if (gpd == NULL) { | 
					
						
							| 
									
										
										
										
											2019-04-17 06:17:24 +02:00
										 |  |  |     return NULL; | 
					
						
							| 
									
										
										
										
											2019-04-22 09:39:35 +10:00
										 |  |  |   } | 
					
						
							| 
									
										
										
										
											2019-04-17 06:17:24 +02:00
										 |  |  | 
 | 
					
						
							|  |  |  |   /* allocate memory for frame and add to end of list */ | 
					
						
							|  |  |  |   gpl = MEM_callocN(sizeof(bGPDlayer), "bGPDlayer"); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   gpl_active = BKE_gpencil_layer_getactive(gpd); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   /* add to datablock */ | 
					
						
							|  |  |  |   if (gpl_active == NULL) { | 
					
						
							|  |  |  |     BLI_addtail(&gpd->layers, gpl); | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  |   else { | 
					
						
							|  |  |  |     /* if active layer, add after that layer */ | 
					
						
							|  |  |  |     BLI_insertlinkafter(&gpd->layers, gpl_active, gpl); | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   /* annotation vs GP Object behavior is slightly different */ | 
					
						
							|  |  |  |   if (gpd->flag & GP_DATA_ANNOTATIONS) { | 
					
						
							|  |  |  |     /* set default color of new strokes for this layer */ | 
					
						
							|  |  |  |     copy_v4_v4(gpl->color, U.gpencil_new_layer_col); | 
					
						
							|  |  |  |     gpl->opacity = 1.0f; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     /* set default thickness of new strokes for this layer */ | 
					
						
							|  |  |  |     gpl->thickness = 3; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     /* Onion colors */ | 
					
						
							|  |  |  |     ARRAY_SET_ITEMS(gpl->gcolor_prev, 0.302f, 0.851f, 0.302f); | 
					
						
							|  |  |  |     ARRAY_SET_ITEMS(gpl->gcolor_next, 0.250f, 0.1f, 1.0f); | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  |   else { | 
					
						
							|  |  |  |     /* thickness parameter represents "thickness change", not absolute thickness */ | 
					
						
							|  |  |  |     gpl->thickness = 0; | 
					
						
							|  |  |  |     gpl->opacity = 1.0f; | 
					
						
							|  |  |  |     /* default channel color */ | 
					
						
							|  |  |  |     ARRAY_SET_ITEMS(gpl->color, 0.2f, 0.2f, 0.2f); | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   /* auto-name */ | 
					
						
							|  |  |  |   BLI_strncpy(gpl->info, name, sizeof(gpl->info)); | 
					
						
							|  |  |  |   BLI_uniquename(&gpd->layers, | 
					
						
							|  |  |  |                  gpl, | 
					
						
							|  |  |  |                  (gpd->flag & GP_DATA_ANNOTATIONS) ? DATA_("Note") : DATA_("GP_Layer"), | 
					
						
							|  |  |  |                  '.', | 
					
						
							|  |  |  |                  offsetof(bGPDlayer, info), | 
					
						
							|  |  |  |                  sizeof(gpl->info)); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   /* make this one the active one */ | 
					
						
							| 
									
										
										
										
											2019-04-22 09:39:35 +10:00
										 |  |  |   if (setactive) { | 
					
						
							| 
									
										
										
										
											2019-04-17 06:17:24 +02:00
										 |  |  |     BKE_gpencil_layer_setactive(gpd, gpl); | 
					
						
							| 
									
										
										
										
											2019-04-22 09:39:35 +10:00
										 |  |  |   } | 
					
						
							| 
									
										
										
										
											2019-04-17 06:17:24 +02:00
										 |  |  | 
 | 
					
						
							|  |  |  |   /* return layer */ | 
					
						
							|  |  |  |   return gpl; | 
					
						
							| 
									
										
										
										
											2009-04-20 10:20:18 +00:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-07-31 10:22:19 +02:00
										 |  |  | /* add a new gp-datablock */ | 
					
						
							|  |  |  | bGPdata *BKE_gpencil_data_addnew(Main *bmain, const char name[]) | 
					
						
							| 
									
										
										
										
											2016-08-03 23:31:48 +02:00
										 |  |  | { | 
					
						
							| 
									
										
										
										
											2019-04-17 06:17:24 +02:00
										 |  |  |   bGPdata *gpd; | 
					
						
							| 
									
										
										
										
											2016-08-03 23:31:48 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-04-17 06:17:24 +02:00
										 |  |  |   /* allocate memory for a new block */ | 
					
						
							|  |  |  |   gpd = BKE_libblock_alloc(bmain, ID_GD, name, 0); | 
					
						
							| 
									
										
										
										
											2016-08-03 23:31:48 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-04-17 06:17:24 +02:00
										 |  |  |   /* initial settings */ | 
					
						
							|  |  |  |   gpd->flag = (GP_DATA_DISPINFO | GP_DATA_EXPAND); | 
					
						
							| 
									
										
										
										
											2016-08-03 23:31:48 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-04-17 06:17:24 +02:00
										 |  |  |   /* general flags */ | 
					
						
							|  |  |  |   gpd->flag |= GP_DATA_VIEWALIGN; | 
					
						
							|  |  |  |   gpd->flag |= GP_DATA_STROKE_FORCE_RECALC; | 
					
						
							| 
									
										
										
										
											2019-08-01 13:53:25 +10:00
										 |  |  |   /* always enable object onion skin switch */ | 
					
						
							| 
									
										
										
										
											2019-04-17 06:17:24 +02:00
										 |  |  |   gpd->flag |= GP_DATA_SHOW_ONIONSKINS; | 
					
						
							|  |  |  |   /* GP object specific settings */ | 
					
						
							|  |  |  |   ARRAY_SET_ITEMS(gpd->line_color, 0.6f, 0.6f, 0.6f, 0.5f); | 
					
						
							| 
									
										
										
										
											2016-08-03 23:31:48 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-04-17 06:17:24 +02:00
										 |  |  |   gpd->pixfactor = GP_DEFAULT_PIX_FACTOR; | 
					
						
							| 
									
										
										
										
											2016-08-03 23:31:48 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-04-17 06:17:24 +02:00
										 |  |  |   /* grid settings */ | 
					
						
							| 
									
										
										
										
											2019-09-27 23:15:09 +02:00
										 |  |  |   ARRAY_SET_ITEMS(gpd->grid.color, 0.5f, 0.5f, 0.5f); /* Color */ | 
					
						
							|  |  |  |   ARRAY_SET_ITEMS(gpd->grid.scale, 1.0f, 1.0f);       /* Scale */ | 
					
						
							|  |  |  |   gpd->grid.lines = GP_DEFAULT_GRID_LINES;            /* Number of lines */ | 
					
						
							| 
									
										
										
										
											2018-10-04 23:27:34 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-04-17 06:17:24 +02:00
										 |  |  |   /* onion-skinning settings (datablock level) */ | 
					
						
							|  |  |  |   gpd->onion_flag |= (GP_ONION_GHOST_PREVCOL | GP_ONION_GHOST_NEXTCOL); | 
					
						
							|  |  |  |   gpd->onion_flag |= GP_ONION_FADE; | 
					
						
							|  |  |  |   gpd->onion_mode = GP_ONION_MODE_RELATIVE; | 
					
						
							|  |  |  |   gpd->onion_factor = 0.5f; | 
					
						
							|  |  |  |   ARRAY_SET_ITEMS(gpd->gcolor_prev, 0.145098f, 0.419608f, 0.137255f); /* green */ | 
					
						
							|  |  |  |   ARRAY_SET_ITEMS(gpd->gcolor_next, 0.125490f, 0.082353f, 0.529412f); /* blue */ | 
					
						
							|  |  |  |   gpd->gstep = 1; | 
					
						
							|  |  |  |   gpd->gstep_next = 1; | 
					
						
							| 
									
										
										
										
											2016-08-03 23:31:48 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-04-17 06:17:24 +02:00
										 |  |  |   return gpd; | 
					
						
							| 
									
										
										
										
											2018-07-31 10:22:19 +02:00
										 |  |  | } | 
					
						
							| 
									
										
										
										
											2016-08-03 23:31:48 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-07-31 10:22:19 +02:00
										 |  |  | /* ************************************************** */ | 
					
						
							|  |  |  | /* Primitive Creation */ | 
					
						
							|  |  |  | /* Utilities for easier bulk-creation of geometry */ | 
					
						
							| 
									
										
										
										
											2016-08-03 23:31:48 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-07-31 10:22:19 +02:00
										 |  |  | /**
 | 
					
						
							|  |  |  |  * Populate stroke with point data from data buffers | 
					
						
							|  |  |  |  * | 
					
						
							| 
									
										
										
										
											2018-12-12 12:55:20 +11:00
										 |  |  |  * \param array: Flat array of point data values. Each entry has GP_PRIM_DATABUF_SIZE values | 
					
						
							|  |  |  |  * \param mat: 4x4 transform matrix to transform points into the right coordinate space | 
					
						
							| 
									
										
										
										
											2018-07-31 10:22:19 +02:00
										 |  |  |  */ | 
					
						
							| 
									
										
										
										
											2019-04-17 06:17:24 +02:00
										 |  |  | void BKE_gpencil_stroke_add_points(bGPDstroke *gps, | 
					
						
							|  |  |  |                                    const float *array, | 
					
						
							|  |  |  |                                    const int totpoints, | 
					
						
							|  |  |  |                                    const float mat[4][4]) | 
					
						
							| 
									
										
										
										
											2018-07-31 10:22:19 +02:00
										 |  |  | { | 
					
						
							| 
									
										
										
										
											2019-04-17 06:17:24 +02:00
										 |  |  |   for (int i = 0; i < totpoints; i++) { | 
					
						
							|  |  |  |     bGPDspoint *pt = &gps->points[i]; | 
					
						
							|  |  |  |     const int x = GP_PRIM_DATABUF_SIZE * i; | 
					
						
							| 
									
										
										
										
											2016-08-03 23:31:48 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-04-17 06:17:24 +02:00
										 |  |  |     pt->x = array[x]; | 
					
						
							|  |  |  |     pt->y = array[x + 1]; | 
					
						
							|  |  |  |     pt->z = array[x + 2]; | 
					
						
							|  |  |  |     mul_m4_v3(mat, &pt->x); | 
					
						
							| 
									
										
										
										
											2016-08-03 23:31:48 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-04-17 06:17:24 +02:00
										 |  |  |     pt->pressure = array[x + 3]; | 
					
						
							|  |  |  |     pt->strength = array[x + 4]; | 
					
						
							|  |  |  |   } | 
					
						
							| 
									
										
										
										
											2018-07-31 10:22:19 +02:00
										 |  |  | } | 
					
						
							| 
									
										
										
										
											2016-08-03 23:31:48 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-07-31 10:22:19 +02:00
										 |  |  | /* Create a new stroke, with pre-allocated data buffers */ | 
					
						
							|  |  |  | bGPDstroke *BKE_gpencil_add_stroke(bGPDframe *gpf, int mat_idx, int totpoints, short thickness) | 
					
						
							|  |  |  | { | 
					
						
							| 
									
										
										
										
											2019-04-17 06:17:24 +02:00
										 |  |  |   /* allocate memory for a new stroke */ | 
					
						
							|  |  |  |   bGPDstroke *gps = MEM_callocN(sizeof(bGPDstroke), "gp_stroke"); | 
					
						
							| 
									
										
										
										
											2016-08-03 23:31:48 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-04-17 06:17:24 +02:00
										 |  |  |   gps->thickness = thickness; | 
					
						
							|  |  |  |   gps->gradient_f = 1.0f; | 
					
						
							|  |  |  |   gps->gradient_s[0] = 1.0f; | 
					
						
							|  |  |  |   gps->gradient_s[1] = 1.0f; | 
					
						
							| 
									
										
										
										
											2019-04-15 10:32:06 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-04-17 06:17:24 +02:00
										 |  |  |   gps->inittime = 0; | 
					
						
							| 
									
										
										
										
											2016-08-03 23:31:48 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-04-17 06:17:24 +02:00
										 |  |  |   /* enable recalculation flag by default */ | 
					
						
							|  |  |  |   gps->flag = GP_STROKE_RECALC_GEOMETRY | GP_STROKE_3DSPACE; | 
					
						
							| 
									
										
										
										
											2016-08-03 23:31:48 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-04-17 06:17:24 +02:00
										 |  |  |   gps->totpoints = totpoints; | 
					
						
							|  |  |  |   gps->points = MEM_callocN(sizeof(bGPDspoint) * gps->totpoints, "gp_stroke_points"); | 
					
						
							| 
									
										
										
										
											2016-08-03 23:31:48 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-04-17 06:17:24 +02:00
										 |  |  |   /* initialize triangle memory to dummy data */ | 
					
						
							|  |  |  |   gps->triangles = MEM_callocN(sizeof(bGPDtriangle), "GP Stroke triangulation"); | 
					
						
							|  |  |  |   gps->flag |= GP_STROKE_RECALC_GEOMETRY; | 
					
						
							|  |  |  |   gps->tot_triangles = 0; | 
					
						
							| 
									
										
										
										
											2016-08-03 23:31:48 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-04-17 06:17:24 +02:00
										 |  |  |   gps->mat_nr = mat_idx; | 
					
						
							| 
									
										
										
										
											2016-08-03 23:31:48 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-04-17 06:17:24 +02:00
										 |  |  |   /* add to frame */ | 
					
						
							|  |  |  |   BLI_addtail(&gpf->strokes, gps); | 
					
						
							| 
									
										
										
										
											2016-08-03 23:31:48 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-04-17 06:17:24 +02:00
										 |  |  |   return gps; | 
					
						
							| 
									
										
										
										
											2016-08-03 23:31:48 +02:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-07-31 10:22:19 +02:00
										 |  |  | /* ************************************************** */ | 
					
						
							|  |  |  | /* Data Duplication */ | 
					
						
							| 
									
										
										
										
											2016-08-03 23:31:48 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-07-31 10:22:19 +02:00
										 |  |  | /* make a copy of a given gpencil weights */ | 
					
						
							|  |  |  | void BKE_gpencil_stroke_weights_duplicate(bGPDstroke *gps_src, bGPDstroke *gps_dst) | 
					
						
							| 
									
										
										
										
											2016-08-03 23:31:48 +02:00
										 |  |  | { | 
					
						
							| 
									
										
										
										
											2019-04-17 06:17:24 +02:00
										 |  |  |   if (gps_src == NULL) { | 
					
						
							|  |  |  |     return; | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  |   BLI_assert(gps_src->totpoints == gps_dst->totpoints); | 
					
						
							| 
									
										
										
										
											2016-08-03 23:31:48 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-04-17 06:17:24 +02:00
										 |  |  |   BKE_defvert_array_copy(gps_dst->dvert, gps_src->dvert, gps_src->totpoints); | 
					
						
							| 
									
										
										
										
											2016-08-03 23:31:48 +02:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-07-31 10:22:19 +02:00
										 |  |  | /* make a copy of a given gpencil stroke */ | 
					
						
							|  |  |  | bGPDstroke *BKE_gpencil_stroke_duplicate(bGPDstroke *gps_src) | 
					
						
							| 
									
										
										
										
											2009-04-20 10:20:18 +00:00
										 |  |  | { | 
					
						
							| 
									
										
										
										
											2019-04-17 06:17:24 +02:00
										 |  |  |   bGPDstroke *gps_dst = NULL; | 
					
						
							| 
									
										
										
										
											2018-06-17 17:05:51 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-04-17 06:17:24 +02:00
										 |  |  |   gps_dst = MEM_dupallocN(gps_src); | 
					
						
							|  |  |  |   gps_dst->prev = gps_dst->next = NULL; | 
					
						
							| 
									
										
										
										
											2018-06-17 17:05:51 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-04-17 06:17:24 +02:00
										 |  |  |   gps_dst->points = MEM_dupallocN(gps_src->points); | 
					
						
							| 
									
										
										
										
											2018-06-17 17:05:51 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-04-17 06:17:24 +02:00
										 |  |  |   if (gps_src->dvert != NULL) { | 
					
						
							|  |  |  |     gps_dst->dvert = MEM_dupallocN(gps_src->dvert); | 
					
						
							|  |  |  |     BKE_gpencil_stroke_weights_duplicate(gps_src, gps_dst); | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  |   else { | 
					
						
							|  |  |  |     gps_dst->dvert = NULL; | 
					
						
							|  |  |  |   } | 
					
						
							| 
									
										
										
										
											2018-07-31 10:22:19 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-04-17 06:17:24 +02:00
										 |  |  |   /* Don't clear triangles, so that modifier evaluation can just use
 | 
					
						
							|  |  |  |    * this without extra work first. Most places that need to force | 
					
						
							|  |  |  |    * this data to get recalculated will destroy the data anyway though. | 
					
						
							|  |  |  |    */ | 
					
						
							|  |  |  |   gps_dst->triangles = MEM_dupallocN(gps_dst->triangles); | 
					
						
							|  |  |  |   /* gps_dst->flag |= GP_STROKE_RECALC_GEOMETRY; */ | 
					
						
							| 
									
										
										
										
											2018-06-17 17:05:51 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-04-17 06:17:24 +02:00
										 |  |  |   /* return new stroke */ | 
					
						
							|  |  |  |   return gps_dst; | 
					
						
							| 
									
										
										
										
											2009-04-20 10:20:18 +00:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | /* make a copy of a given gpencil frame */ | 
					
						
							| 
									
										
										
										
											2016-08-04 15:03:18 +02:00
										 |  |  | bGPDframe *BKE_gpencil_frame_duplicate(const bGPDframe *gpf_src) | 
					
						
							| 
									
										
										
										
											2009-04-20 10:20:18 +00:00
										 |  |  | { | 
					
						
							| 
									
										
										
										
											2019-04-17 06:17:24 +02:00
										 |  |  |   bGPDstroke *gps_dst = NULL; | 
					
						
							|  |  |  |   bGPDframe *gpf_dst; | 
					
						
							| 
									
										
										
										
											2018-06-17 17:05:51 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-04-17 06:17:24 +02:00
										 |  |  |   /* error checking */ | 
					
						
							|  |  |  |   if (gpf_src == NULL) { | 
					
						
							|  |  |  |     return NULL; | 
					
						
							|  |  |  |   } | 
					
						
							| 
									
										
										
										
											2018-06-17 17:05:51 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-04-17 06:17:24 +02:00
										 |  |  |   /* make a copy of the source frame */ | 
					
						
							|  |  |  |   gpf_dst = MEM_dupallocN(gpf_src); | 
					
						
							|  |  |  |   gpf_dst->prev = gpf_dst->next = NULL; | 
					
						
							| 
									
										
										
										
											2018-06-17 17:05:51 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-04-17 06:17:24 +02:00
										 |  |  |   /* copy strokes */ | 
					
						
							|  |  |  |   BLI_listbase_clear(&gpf_dst->strokes); | 
					
						
							|  |  |  |   for (bGPDstroke *gps_src = gpf_src->strokes.first; gps_src; gps_src = gps_src->next) { | 
					
						
							|  |  |  |     /* make copy of source stroke */ | 
					
						
							|  |  |  |     gps_dst = BKE_gpencil_stroke_duplicate(gps_src); | 
					
						
							|  |  |  |     BLI_addtail(&gpf_dst->strokes, gps_dst); | 
					
						
							|  |  |  |   } | 
					
						
							| 
									
										
										
										
											2018-06-17 17:05:51 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-04-17 06:17:24 +02:00
										 |  |  |   /* return new frame */ | 
					
						
							|  |  |  |   return gpf_dst; | 
					
						
							| 
									
										
										
										
											2016-08-03 23:31:48 +02:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-07-31 10:22:19 +02:00
										 |  |  | /* make a copy of strokes between gpencil frames */ | 
					
						
							|  |  |  | void BKE_gpencil_frame_copy_strokes(bGPDframe *gpf_src, struct bGPDframe *gpf_dst) | 
					
						
							| 
									
										
										
										
											2016-08-03 23:31:48 +02:00
										 |  |  | { | 
					
						
							| 
									
										
										
										
											2019-04-17 06:17:24 +02:00
										 |  |  |   bGPDstroke *gps_dst = NULL; | 
					
						
							|  |  |  |   /* error checking */ | 
					
						
							|  |  |  |   if ((gpf_src == NULL) || (gpf_dst == NULL)) { | 
					
						
							|  |  |  |     return; | 
					
						
							|  |  |  |   } | 
					
						
							| 
									
										
										
										
											2016-08-03 23:31:48 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-04-17 06:17:24 +02:00
										 |  |  |   /* copy strokes */ | 
					
						
							|  |  |  |   BLI_listbase_clear(&gpf_dst->strokes); | 
					
						
							|  |  |  |   for (bGPDstroke *gps_src = gpf_src->strokes.first; gps_src; gps_src = gps_src->next) { | 
					
						
							|  |  |  |     /* make copy of source stroke */ | 
					
						
							|  |  |  |     gps_dst = BKE_gpencil_stroke_duplicate(gps_src); | 
					
						
							|  |  |  |     BLI_addtail(&gpf_dst->strokes, gps_dst); | 
					
						
							|  |  |  |   } | 
					
						
							| 
									
										
										
										
											2016-08-03 23:31:48 +02:00
										 |  |  | } | 
					
						
							| 
									
										
										
										
											2018-07-31 10:22:19 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2009-04-20 10:20:18 +00:00
										 |  |  | /* make a copy of a given gpencil layer */ | 
					
						
							| 
									
										
										
										
											2016-08-04 15:03:18 +02:00
										 |  |  | bGPDlayer *BKE_gpencil_layer_duplicate(const bGPDlayer *gpl_src) | 
					
						
							| 
									
										
										
										
											2009-04-20 10:20:18 +00:00
										 |  |  | { | 
					
						
							| 
									
										
										
										
											2019-04-17 06:17:24 +02:00
										 |  |  |   const bGPDframe *gpf_src; | 
					
						
							|  |  |  |   bGPDframe *gpf_dst; | 
					
						
							|  |  |  |   bGPDlayer *gpl_dst; | 
					
						
							| 
									
										
										
										
											2018-06-17 17:05:51 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-04-17 06:17:24 +02:00
										 |  |  |   /* error checking */ | 
					
						
							|  |  |  |   if (gpl_src == NULL) { | 
					
						
							|  |  |  |     return NULL; | 
					
						
							|  |  |  |   } | 
					
						
							| 
									
										
										
										
											2018-06-17 17:05:51 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-04-17 06:17:24 +02:00
										 |  |  |   /* make a copy of source layer */ | 
					
						
							|  |  |  |   gpl_dst = MEM_dupallocN(gpl_src); | 
					
						
							|  |  |  |   gpl_dst->prev = gpl_dst->next = NULL; | 
					
						
							| 
									
										
										
										
											2018-06-17 17:05:51 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-04-17 06:17:24 +02:00
										 |  |  |   /* copy frames */ | 
					
						
							|  |  |  |   BLI_listbase_clear(&gpl_dst->frames); | 
					
						
							|  |  |  |   for (gpf_src = gpl_src->frames.first; gpf_src; gpf_src = gpf_src->next) { | 
					
						
							|  |  |  |     /* make a copy of source frame */ | 
					
						
							|  |  |  |     gpf_dst = BKE_gpencil_frame_duplicate(gpf_src); | 
					
						
							|  |  |  |     BLI_addtail(&gpl_dst->frames, gpf_dst); | 
					
						
							| 
									
										
										
										
											2018-06-17 17:05:51 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-04-17 06:17:24 +02:00
										 |  |  |     /* if source frame was the current layer's 'active' frame, reassign that too */ | 
					
						
							| 
									
										
										
										
											2019-04-22 09:39:35 +10:00
										 |  |  |     if (gpf_src == gpl_dst->actframe) { | 
					
						
							| 
									
										
										
										
											2019-04-17 06:17:24 +02:00
										 |  |  |       gpl_dst->actframe = gpf_dst; | 
					
						
							| 
									
										
										
										
											2019-04-22 09:39:35 +10:00
										 |  |  |     } | 
					
						
							| 
									
										
										
										
											2019-04-17 06:17:24 +02:00
										 |  |  |   } | 
					
						
							| 
									
										
										
										
											2018-06-17 17:05:51 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-04-17 06:17:24 +02:00
										 |  |  |   /* return new layer */ | 
					
						
							|  |  |  |   return gpl_dst; | 
					
						
							| 
									
										
										
										
											2009-04-20 10:20:18 +00:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
											  
											
												Refactor ID copying (and to some extent, ID freeing).
This will allow much finer controll over how we copy data-blocks, from
full copy in Main database, to "lighter" ones (out of Main, inside an
already allocated datablock, etc.).
This commit also transfers a llot of what was previously handled by
per-ID-type custom code to generic ID handling code in BKE_library.
Hopefully will avoid in future inconsistencies and missing bits we had
all over the codebase in the past.
It also adds missing copying handling for a few types, most notably
Scene (which where using a fully customized handling previously).
Note that the type of allocation used during copying (regular in Main,
allocated but outside of Main, or not allocated by ID handling code at
all) is stored in ID's, which allows to handle them correctly when
freeing. This needs to be taken care of with caution when doing 'weird'
unusual things with ID copying and/or allocation!
As a final note, while rather noisy, this commit will hopefully not
break too much existing branches, old 'API' has been kept for the main
part, as a wrapper around new code. Cleaning it up will happen later.
Design task : T51804
Phab Diff: D2714
											
										 
											2017-08-07 16:39:55 +02:00
										 |  |  | /**
 | 
					
						
							| 
									
										
										
										
											2019-04-27 12:07:07 +10:00
										 |  |  |  * Only copy internal data of GreasePencil ID from source | 
					
						
							|  |  |  |  * to already allocated/initialized destination. | 
					
						
							|  |  |  |  * You probably never want to use that directly, | 
					
						
							|  |  |  |  * use #BKE_id_copy or #BKE_id_copy_ex for typical needs. | 
					
						
							| 
									
										
											  
											
												Refactor ID copying (and to some extent, ID freeing).
This will allow much finer controll over how we copy data-blocks, from
full copy in Main database, to "lighter" ones (out of Main, inside an
already allocated datablock, etc.).
This commit also transfers a llot of what was previously handled by
per-ID-type custom code to generic ID handling code in BKE_library.
Hopefully will avoid in future inconsistencies and missing bits we had
all over the codebase in the past.
It also adds missing copying handling for a few types, most notably
Scene (which where using a fully customized handling previously).
Note that the type of allocation used during copying (regular in Main,
allocated but outside of Main, or not allocated by ID handling code at
all) is stored in ID's, which allows to handle them correctly when
freeing. This needs to be taken care of with caution when doing 'weird'
unusual things with ID copying and/or allocation!
As a final note, while rather noisy, this commit will hopefully not
break too much existing branches, old 'API' has been kept for the main
part, as a wrapper around new code. Cleaning it up will happen later.
Design task : T51804
Phab Diff: D2714
											
										 
											2017-08-07 16:39:55 +02:00
										 |  |  |  * | 
					
						
							|  |  |  |  * WARNING! This function will not handle ID user count! | 
					
						
							|  |  |  |  * | 
					
						
							| 
									
										
										
										
											2018-12-12 12:50:58 +11:00
										 |  |  |  * \param flag: Copying options (see BKE_library.h's LIB_ID_COPY_... flags for more). | 
					
						
							| 
									
										
											  
											
												Refactor ID copying (and to some extent, ID freeing).
This will allow much finer controll over how we copy data-blocks, from
full copy in Main database, to "lighter" ones (out of Main, inside an
already allocated datablock, etc.).
This commit also transfers a llot of what was previously handled by
per-ID-type custom code to generic ID handling code in BKE_library.
Hopefully will avoid in future inconsistencies and missing bits we had
all over the codebase in the past.
It also adds missing copying handling for a few types, most notably
Scene (which where using a fully customized handling previously).
Note that the type of allocation used during copying (regular in Main,
allocated but outside of Main, or not allocated by ID handling code at
all) is stored in ID's, which allows to handle them correctly when
freeing. This needs to be taken care of with caution when doing 'weird'
unusual things with ID copying and/or allocation!
As a final note, while rather noisy, this commit will hopefully not
break too much existing branches, old 'API' has been kept for the main
part, as a wrapper around new code. Cleaning it up will happen later.
Design task : T51804
Phab Diff: D2714
											
										 
											2017-08-07 16:39:55 +02:00
										 |  |  |  */ | 
					
						
							| 
									
										
										
										
											2018-08-10 16:59:29 +02:00
										 |  |  | void BKE_gpencil_copy_data(bGPdata *gpd_dst, const bGPdata *gpd_src, const int UNUSED(flag)) | 
					
						
							| 
									
										
										
										
											2009-04-20 10:20:18 +00:00
										 |  |  | { | 
					
						
							| 
									
										
										
										
											2019-04-17 06:17:24 +02:00
										 |  |  |   /* duplicate material array */ | 
					
						
							|  |  |  |   if (gpd_src->mat) { | 
					
						
							|  |  |  |     gpd_dst->mat = MEM_dupallocN(gpd_src->mat); | 
					
						
							|  |  |  |   } | 
					
						
							| 
									
										
										
										
											2016-08-03 23:31:48 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-04-17 06:17:24 +02:00
										 |  |  |   /* copy layers */ | 
					
						
							|  |  |  |   BLI_listbase_clear(&gpd_dst->layers); | 
					
						
							|  |  |  |   for (const bGPDlayer *gpl_src = gpd_src->layers.first; gpl_src; gpl_src = gpl_src->next) { | 
					
						
							|  |  |  |     /* make a copy of source layer and its data */ | 
					
						
							| 
									
										
										
										
											2019-08-14 23:29:46 +10:00
										 |  |  | 
 | 
					
						
							|  |  |  |     /* TODO here too could add unused flags... */ | 
					
						
							|  |  |  |     bGPDlayer *gpl_dst = BKE_gpencil_layer_duplicate(gpl_src); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-04-17 06:17:24 +02:00
										 |  |  |     BLI_addtail(&gpd_dst->layers, gpl_dst); | 
					
						
							|  |  |  |   } | 
					
						
							| 
									
										
										
										
											2018-07-31 10:22:19 +02:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | /* Standard API to make a copy of GP datablock, separate from copying its data */ | 
					
						
							|  |  |  | bGPdata *BKE_gpencil_copy(Main *bmain, const bGPdata *gpd) | 
					
						
							|  |  |  | { | 
					
						
							| 
									
										
										
										
											2019-04-17 06:17:24 +02:00
										 |  |  |   bGPdata *gpd_copy; | 
					
						
							|  |  |  |   BKE_id_copy(bmain, &gpd->id, (ID **)&gpd_copy); | 
					
						
							|  |  |  |   return gpd_copy; | 
					
						
							| 
									
										
											  
											
												Refactor ID copying (and to some extent, ID freeing).
This will allow much finer controll over how we copy data-blocks, from
full copy in Main database, to "lighter" ones (out of Main, inside an
already allocated datablock, etc.).
This commit also transfers a llot of what was previously handled by
per-ID-type custom code to generic ID handling code in BKE_library.
Hopefully will avoid in future inconsistencies and missing bits we had
all over the codebase in the past.
It also adds missing copying handling for a few types, most notably
Scene (which where using a fully customized handling previously).
Note that the type of allocation used during copying (regular in Main,
allocated but outside of Main, or not allocated by ID handling code at
all) is stored in ID's, which allows to handle them correctly when
freeing. This needs to be taken care of with caution when doing 'weird'
unusual things with ID copying and/or allocation!
As a final note, while rather noisy, this commit will hopefully not
break too much existing branches, old 'API' has been kept for the main
part, as a wrapper around new code. Cleaning it up will happen later.
Design task : T51804
Phab Diff: D2714
											
										 
											2017-08-07 16:39:55 +02:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | /* make a copy of a given gpencil datablock */ | 
					
						
							| 
									
										
										
										
											2019-09-27 23:15:09 +02:00
										 |  |  | /* XXX: Should this be deprecated? */ | 
					
						
							| 
									
										
											  
											
												Refactor ID copying (and to some extent, ID freeing).
This will allow much finer controll over how we copy data-blocks, from
full copy in Main database, to "lighter" ones (out of Main, inside an
already allocated datablock, etc.).
This commit also transfers a llot of what was previously handled by
per-ID-type custom code to generic ID handling code in BKE_library.
Hopefully will avoid in future inconsistencies and missing bits we had
all over the codebase in the past.
It also adds missing copying handling for a few types, most notably
Scene (which where using a fully customized handling previously).
Note that the type of allocation used during copying (regular in Main,
allocated but outside of Main, or not allocated by ID handling code at
all) is stored in ID's, which allows to handle them correctly when
freeing. This needs to be taken care of with caution when doing 'weird'
unusual things with ID copying and/or allocation!
As a final note, while rather noisy, this commit will hopefully not
break too much existing branches, old 'API' has been kept for the main
part, as a wrapper around new code. Cleaning it up will happen later.
Design task : T51804
Phab Diff: D2714
											
										 
											2017-08-07 16:39:55 +02:00
										 |  |  | bGPdata *BKE_gpencil_data_duplicate(Main *bmain, const bGPdata *gpd_src, bool internal_copy) | 
					
						
							|  |  |  | { | 
					
						
							| 
									
										
										
										
											2019-04-17 06:17:24 +02:00
										 |  |  |   bGPdata *gpd_dst; | 
					
						
							| 
									
										
										
										
											2018-07-31 10:22:19 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-04-17 06:17:24 +02:00
										 |  |  |   /* Yuck and super-uber-hyper yuck!!!
 | 
					
						
							|  |  |  |    * Should be replaceable with a no-main copy (LIB_ID_COPY_NO_MAIN etc.), but not sure about it, | 
					
						
							|  |  |  |    * so for now keep old code for that one. */ | 
					
						
							| 
									
										
											  
											
												Refactor ID copying (and to some extent, ID freeing).
This will allow much finer controll over how we copy data-blocks, from
full copy in Main database, to "lighter" ones (out of Main, inside an
already allocated datablock, etc.).
This commit also transfers a llot of what was previously handled by
per-ID-type custom code to generic ID handling code in BKE_library.
Hopefully will avoid in future inconsistencies and missing bits we had
all over the codebase in the past.
It also adds missing copying handling for a few types, most notably
Scene (which where using a fully customized handling previously).
Note that the type of allocation used during copying (regular in Main,
allocated but outside of Main, or not allocated by ID handling code at
all) is stored in ID's, which allows to handle them correctly when
freeing. This needs to be taken care of with caution when doing 'weird'
unusual things with ID copying and/or allocation!
As a final note, while rather noisy, this commit will hopefully not
break too much existing branches, old 'API' has been kept for the main
part, as a wrapper around new code. Cleaning it up will happen later.
Design task : T51804
Phab Diff: D2714
											
										 
											2017-08-07 16:39:55 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-04-17 06:17:24 +02:00
										 |  |  |   /* error checking */ | 
					
						
							|  |  |  |   if (gpd_src == NULL) { | 
					
						
							|  |  |  |     return NULL; | 
					
						
							|  |  |  |   } | 
					
						
							| 
									
										
										
										
											2018-07-31 10:22:19 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-04-17 06:17:24 +02:00
										 |  |  |   if (internal_copy) { | 
					
						
							|  |  |  |     /* make a straight copy for undo buffers used during stroke drawing */ | 
					
						
							|  |  |  |     gpd_dst = MEM_dupallocN(gpd_src); | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  |   else { | 
					
						
							|  |  |  |     BLI_assert(bmain != NULL); | 
					
						
							|  |  |  |     BKE_id_copy(bmain, &gpd_src->id, (ID **)&gpd_dst); | 
					
						
							|  |  |  |   } | 
					
						
							| 
									
										
										
										
											2018-07-31 10:22:19 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-04-17 06:17:24 +02:00
										 |  |  |   /* Copy internal data (layers, etc.) */ | 
					
						
							|  |  |  |   BKE_gpencil_copy_data(gpd_dst, gpd_src, 0); | 
					
						
							| 
									
										
										
										
											2018-07-31 10:22:19 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-04-17 06:17:24 +02:00
										 |  |  |   /* return new */ | 
					
						
							|  |  |  |   return gpd_dst; | 
					
						
							| 
									
										
										
										
											2009-04-20 10:20:18 +00:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-07-21 16:09:08 +02:00
										 |  |  | void BKE_gpencil_make_local(Main *bmain, bGPdata *gpd, const bool lib_local) | 
					
						
							|  |  |  | { | 
					
						
							| 
									
										
										
										
											2019-04-17 06:17:24 +02:00
										 |  |  |   BKE_id_make_local_generic(bmain, &gpd->id, true, lib_local); | 
					
						
							| 
									
										
										
										
											2016-07-21 16:09:08 +02:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-07-31 10:22:19 +02:00
										 |  |  | /* ************************************************** */ | 
					
						
							|  |  |  | /* GP Stroke API */ | 
					
						
							| 
									
										
											  
											
												Grease Pencil - Storyboarding Features (merge from GPencil_EditStrokes branch)
This merge-commit brings in a number of new features and workflow/UI improvements for
working with Grease Pencil. While these were originally targetted at improving
the workflow for creating 3D storyboards in Blender using the Grease Pencil,
many of these changes should also prove useful in other workflows too.
The main highlights here are:
1) It is now possible to edit Grease Pencil strokes
   - Use D Tab, or toggle the "Enable Editing" toggles in the Toolbar/Properties regions
     to enter "Stroke Edit Mode". In this mode, many common editing tools will
     operate on Grease Pencil stroke points instead.
   - Tools implemented include Select, Select All/Border/Circle/Linked/More/Less,
     Grab, Rotate, Scale, Bend, Shear, To Sphere, Mirror, Duplicate, Delete.
   - Proportional Editing works when using the transform tools
2) Grease Pencil stroke settings can now be animated
   NOTE: Currently drivers don't work, but if time allows, this may still be
         added before the release.
3) Strokes can be drawn with "filled" interiors, using a separate set of
   colour/opacity settings to the ones used for the lines themselves.
   This makes use of OpenGL filled polys, which has the limitation of only
   being able to fill convex shapes. Some artifacts may be visible on concave
   shapes (e.g. pacman's mouth will be overdrawn)
4) "Volumetric Strokes" - An alternative drawing technique for stroke drawing
   has been added which draws strokes as a series of screen-aligned discs.
   While this was originally a partial experimental technique at getting better
   quality 3D lines, the effects possible using this technique were interesting
   enough to warrant making this a dedicated feature. Best results when partial
   opacity and large stroke widths are used.
5) Improved Onion Skinning Support
   - Different colours can be selected for the before/after ghosts. To do so,
     enable the "colour wheel" toggle beside the Onion Skinning toggle, and set
     the colours accordingly.
   - Different numbers of ghosts can be shown before/after the current frame
6) Grease Pencil datablocks are now attached to the scene by default instead of
   the active object.
   - For a long time, the object-attachment has proved to be quite problematic
     for users to keep track of. Now that this is done at scene level, it is
     easier for most users to use.
   - An exception for old files (and for any addons which may benefit from object
     attachment instead), is that if the active object has a Grease Pencil datablock,
     that will be used instead.
   - It is not currently possible to choose object-attachment from the UI, but
     it is simple to do this from the console instead, by doing:
     context.active_object.grease_pencil = bpy.data.grease_pencil["blah"]
7) Various UI Cleanups
   - The layers UI has been cleaned up to use a list instead of the nested-panels
     design. Apart from saving space, this is also much nicer to look at now.
   - The UI code is now all defined in Python. To support this, it has been necessary
     to add some new context properties to make it easier to access these settings.
     e.g. "gpencil_data" for the datablock
          "active_gpencil_layer" and "active_gpencil_frame" for active data,
          "editable_gpencil_strokes" for the strokes that can be edited
   - The "stroke placement/alignment" settings (previously "Drawing Settings" at the
     bottom of the Grease Pencil panel in the Properties Region) is now located in
     the toolbar. These were more toolsettings than properties for how GPencil got drawn.
   - "Use Sketching Sessions" has been renamed "Continuous Drawing", as per a
     suggestion for an earlier discussion on developer.blender.org
   - By default, the painting operator will wait for a mouse button to be pressed
     before it starts creating the stroke. This is to make it easier to include
     this operator in various toolbars/menus/etc.   To get it immediately starting
     (as when you hold down DKEy to draw), set "wait_for_input" to False.
   - GPencil Layers can be rearranged in the "Grease Pencil" mode of the Action Editor
   - Toolbar panels have been added to all the other editors which support these.
8) Pie menus for quick-access to tools
   A set of experimental pie menus has been included for quick access to many
   tools and settings. It is not necessary to use these to get things done,
   but they have been designed to help make certain common tasks easier.
   - Ctrl-D = The main pie menu. Reveals tools in a context sensitive and
              spatially stable manner.
   - D Q    = "Quick Settings" pie. This allows quick access to the active
              layer's settings. Notably, colours, thickness, and turning
              onion skinning on/off.
											
										 
											2014-12-01 01:52:06 +13:00
										 |  |  | 
 | 
					
						
							|  |  |  | /* ensure selection status of stroke is in sync with its points */ | 
					
						
							| 
									
										
										
										
											2016-08-04 15:03:18 +02:00
										 |  |  | void BKE_gpencil_stroke_sync_selection(bGPDstroke *gps) | 
					
						
							| 
									
										
											  
											
												Grease Pencil - Storyboarding Features (merge from GPencil_EditStrokes branch)
This merge-commit brings in a number of new features and workflow/UI improvements for
working with Grease Pencil. While these were originally targetted at improving
the workflow for creating 3D storyboards in Blender using the Grease Pencil,
many of these changes should also prove useful in other workflows too.
The main highlights here are:
1) It is now possible to edit Grease Pencil strokes
   - Use D Tab, or toggle the "Enable Editing" toggles in the Toolbar/Properties regions
     to enter "Stroke Edit Mode". In this mode, many common editing tools will
     operate on Grease Pencil stroke points instead.
   - Tools implemented include Select, Select All/Border/Circle/Linked/More/Less,
     Grab, Rotate, Scale, Bend, Shear, To Sphere, Mirror, Duplicate, Delete.
   - Proportional Editing works when using the transform tools
2) Grease Pencil stroke settings can now be animated
   NOTE: Currently drivers don't work, but if time allows, this may still be
         added before the release.
3) Strokes can be drawn with "filled" interiors, using a separate set of
   colour/opacity settings to the ones used for the lines themselves.
   This makes use of OpenGL filled polys, which has the limitation of only
   being able to fill convex shapes. Some artifacts may be visible on concave
   shapes (e.g. pacman's mouth will be overdrawn)
4) "Volumetric Strokes" - An alternative drawing technique for stroke drawing
   has been added which draws strokes as a series of screen-aligned discs.
   While this was originally a partial experimental technique at getting better
   quality 3D lines, the effects possible using this technique were interesting
   enough to warrant making this a dedicated feature. Best results when partial
   opacity and large stroke widths are used.
5) Improved Onion Skinning Support
   - Different colours can be selected for the before/after ghosts. To do so,
     enable the "colour wheel" toggle beside the Onion Skinning toggle, and set
     the colours accordingly.
   - Different numbers of ghosts can be shown before/after the current frame
6) Grease Pencil datablocks are now attached to the scene by default instead of
   the active object.
   - For a long time, the object-attachment has proved to be quite problematic
     for users to keep track of. Now that this is done at scene level, it is
     easier for most users to use.
   - An exception for old files (and for any addons which may benefit from object
     attachment instead), is that if the active object has a Grease Pencil datablock,
     that will be used instead.
   - It is not currently possible to choose object-attachment from the UI, but
     it is simple to do this from the console instead, by doing:
     context.active_object.grease_pencil = bpy.data.grease_pencil["blah"]
7) Various UI Cleanups
   - The layers UI has been cleaned up to use a list instead of the nested-panels
     design. Apart from saving space, this is also much nicer to look at now.
   - The UI code is now all defined in Python. To support this, it has been necessary
     to add some new context properties to make it easier to access these settings.
     e.g. "gpencil_data" for the datablock
          "active_gpencil_layer" and "active_gpencil_frame" for active data,
          "editable_gpencil_strokes" for the strokes that can be edited
   - The "stroke placement/alignment" settings (previously "Drawing Settings" at the
     bottom of the Grease Pencil panel in the Properties Region) is now located in
     the toolbar. These were more toolsettings than properties for how GPencil got drawn.
   - "Use Sketching Sessions" has been renamed "Continuous Drawing", as per a
     suggestion for an earlier discussion on developer.blender.org
   - By default, the painting operator will wait for a mouse button to be pressed
     before it starts creating the stroke. This is to make it easier to include
     this operator in various toolbars/menus/etc.   To get it immediately starting
     (as when you hold down DKEy to draw), set "wait_for_input" to False.
   - GPencil Layers can be rearranged in the "Grease Pencil" mode of the Action Editor
   - Toolbar panels have been added to all the other editors which support these.
8) Pie menus for quick-access to tools
   A set of experimental pie menus has been included for quick access to many
   tools and settings. It is not necessary to use these to get things done,
   but they have been designed to help make certain common tasks easier.
   - Ctrl-D = The main pie menu. Reveals tools in a context sensitive and
              spatially stable manner.
   - D Q    = "Quick Settings" pie. This allows quick access to the active
              layer's settings. Notably, colours, thickness, and turning
              onion skinning on/off.
											
										 
											2014-12-01 01:52:06 +13:00
										 |  |  | { | 
					
						
							| 
									
										
										
										
											2019-04-17 06:17:24 +02:00
										 |  |  |   bGPDspoint *pt; | 
					
						
							|  |  |  |   int i; | 
					
						
							| 
									
										
										
										
											2018-06-17 17:05:51 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-04-17 06:17:24 +02:00
										 |  |  |   /* error checking */ | 
					
						
							| 
									
										
										
										
											2019-04-22 09:39:35 +10:00
										 |  |  |   if (gps == NULL) { | 
					
						
							| 
									
										
										
										
											2019-04-17 06:17:24 +02:00
										 |  |  |     return; | 
					
						
							| 
									
										
										
										
											2019-04-22 09:39:35 +10:00
										 |  |  |   } | 
					
						
							| 
									
										
										
										
											2018-06-17 17:05:51 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-04-17 06:17:24 +02:00
										 |  |  |   /* we'll stop when we find the first selected point,
 | 
					
						
							|  |  |  |    * so initially, we must deselect | 
					
						
							|  |  |  |    */ | 
					
						
							|  |  |  |   gps->flag &= ~GP_STROKE_SELECT; | 
					
						
							| 
									
										
										
										
											2018-06-17 17:05:51 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-04-17 06:17:24 +02:00
										 |  |  |   for (i = 0, pt = gps->points; i < gps->totpoints; i++, pt++) { | 
					
						
							|  |  |  |     if (pt->flag & GP_SPOINT_SELECT) { | 
					
						
							|  |  |  |       gps->flag |= GP_STROKE_SELECT; | 
					
						
							|  |  |  |       break; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |   } | 
					
						
							| 
									
										
											  
											
												Grease Pencil - Storyboarding Features (merge from GPencil_EditStrokes branch)
This merge-commit brings in a number of new features and workflow/UI improvements for
working with Grease Pencil. While these were originally targetted at improving
the workflow for creating 3D storyboards in Blender using the Grease Pencil,
many of these changes should also prove useful in other workflows too.
The main highlights here are:
1) It is now possible to edit Grease Pencil strokes
   - Use D Tab, or toggle the "Enable Editing" toggles in the Toolbar/Properties regions
     to enter "Stroke Edit Mode". In this mode, many common editing tools will
     operate on Grease Pencil stroke points instead.
   - Tools implemented include Select, Select All/Border/Circle/Linked/More/Less,
     Grab, Rotate, Scale, Bend, Shear, To Sphere, Mirror, Duplicate, Delete.
   - Proportional Editing works when using the transform tools
2) Grease Pencil stroke settings can now be animated
   NOTE: Currently drivers don't work, but if time allows, this may still be
         added before the release.
3) Strokes can be drawn with "filled" interiors, using a separate set of
   colour/opacity settings to the ones used for the lines themselves.
   This makes use of OpenGL filled polys, which has the limitation of only
   being able to fill convex shapes. Some artifacts may be visible on concave
   shapes (e.g. pacman's mouth will be overdrawn)
4) "Volumetric Strokes" - An alternative drawing technique for stroke drawing
   has been added which draws strokes as a series of screen-aligned discs.
   While this was originally a partial experimental technique at getting better
   quality 3D lines, the effects possible using this technique were interesting
   enough to warrant making this a dedicated feature. Best results when partial
   opacity and large stroke widths are used.
5) Improved Onion Skinning Support
   - Different colours can be selected for the before/after ghosts. To do so,
     enable the "colour wheel" toggle beside the Onion Skinning toggle, and set
     the colours accordingly.
   - Different numbers of ghosts can be shown before/after the current frame
6) Grease Pencil datablocks are now attached to the scene by default instead of
   the active object.
   - For a long time, the object-attachment has proved to be quite problematic
     for users to keep track of. Now that this is done at scene level, it is
     easier for most users to use.
   - An exception for old files (and for any addons which may benefit from object
     attachment instead), is that if the active object has a Grease Pencil datablock,
     that will be used instead.
   - It is not currently possible to choose object-attachment from the UI, but
     it is simple to do this from the console instead, by doing:
     context.active_object.grease_pencil = bpy.data.grease_pencil["blah"]
7) Various UI Cleanups
   - The layers UI has been cleaned up to use a list instead of the nested-panels
     design. Apart from saving space, this is also much nicer to look at now.
   - The UI code is now all defined in Python. To support this, it has been necessary
     to add some new context properties to make it easier to access these settings.
     e.g. "gpencil_data" for the datablock
          "active_gpencil_layer" and "active_gpencil_frame" for active data,
          "editable_gpencil_strokes" for the strokes that can be edited
   - The "stroke placement/alignment" settings (previously "Drawing Settings" at the
     bottom of the Grease Pencil panel in the Properties Region) is now located in
     the toolbar. These were more toolsettings than properties for how GPencil got drawn.
   - "Use Sketching Sessions" has been renamed "Continuous Drawing", as per a
     suggestion for an earlier discussion on developer.blender.org
   - By default, the painting operator will wait for a mouse button to be pressed
     before it starts creating the stroke. This is to make it easier to include
     this operator in various toolbars/menus/etc.   To get it immediately starting
     (as when you hold down DKEy to draw), set "wait_for_input" to False.
   - GPencil Layers can be rearranged in the "Grease Pencil" mode of the Action Editor
   - Toolbar panels have been added to all the other editors which support these.
8) Pie menus for quick-access to tools
   A set of experimental pie menus has been included for quick access to many
   tools and settings. It is not necessary to use these to get things done,
   but they have been designed to help make certain common tasks easier.
   - Ctrl-D = The main pie menu. Reveals tools in a context sensitive and
              spatially stable manner.
   - D Q    = "Quick Settings" pie. This allows quick access to the active
              layer's settings. Notably, colours, thickness, and turning
              onion skinning on/off.
											
										 
											2014-12-01 01:52:06 +13:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-07-31 10:22:19 +02:00
										 |  |  | /* ************************************************** */ | 
					
						
							|  |  |  | /* GP Frame API */ | 
					
						
							| 
									
										
										
										
											2009-04-20 10:20:18 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  | /* delete the last stroke of the given frame */ | 
					
						
							| 
									
										
										
										
											2016-08-04 15:03:18 +02:00
										 |  |  | void BKE_gpencil_frame_delete_laststroke(bGPDlayer *gpl, bGPDframe *gpf) | 
					
						
							| 
									
										
										
										
											2009-04-20 10:20:18 +00:00
										 |  |  | { | 
					
						
							| 
									
										
										
										
											2019-04-17 06:17:24 +02:00
										 |  |  |   bGPDstroke *gps = (gpf) ? gpf->strokes.last : NULL; | 
					
						
							|  |  |  |   int cfra = (gpf) ? gpf->framenum : 0; /* assume that the current frame was not locked */ | 
					
						
							| 
									
										
										
										
											2018-06-17 17:05:51 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-04-17 06:17:24 +02:00
										 |  |  |   /* error checking */ | 
					
						
							| 
									
										
										
										
											2019-04-22 09:39:35 +10:00
										 |  |  |   if (ELEM(NULL, gpf, gps)) { | 
					
						
							| 
									
										
										
										
											2019-04-17 06:17:24 +02:00
										 |  |  |     return; | 
					
						
							| 
									
										
										
										
											2019-04-22 09:39:35 +10:00
										 |  |  |   } | 
					
						
							| 
									
										
										
										
											2018-06-17 17:05:51 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-04-17 06:17:24 +02:00
										 |  |  |   /* free the stroke and its data */ | 
					
						
							|  |  |  |   if (gps->points) { | 
					
						
							|  |  |  |     MEM_freeN(gps->points); | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  |   if (gps->dvert) { | 
					
						
							|  |  |  |     BKE_gpencil_free_stroke_weights(gps); | 
					
						
							|  |  |  |     MEM_freeN(gps->dvert); | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  |   MEM_freeN(gps->triangles); | 
					
						
							|  |  |  |   BLI_freelinkN(&gpf->strokes, gps); | 
					
						
							| 
									
										
										
										
											2018-06-17 17:05:51 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-04-17 06:17:24 +02:00
										 |  |  |   /* if frame has no strokes after this, delete it */ | 
					
						
							|  |  |  |   if (BLI_listbase_is_empty(&gpf->strokes)) { | 
					
						
							|  |  |  |     BKE_gpencil_layer_delframe(gpl, gpf); | 
					
						
							|  |  |  |     BKE_gpencil_layer_getframe(gpl, cfra, GP_GETFRAME_USE_PREV); | 
					
						
							|  |  |  |   } | 
					
						
							| 
									
										
										
										
											2009-04-20 10:20:18 +00:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-07-31 10:22:19 +02:00
										 |  |  | /* ************************************************** */ | 
					
						
							|  |  |  | /* GP Layer API */ | 
					
						
							| 
									
										
										
										
											2009-04-20 10:20:18 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-02-09 02:44:02 +13:00
										 |  |  | /* Check if the given layer is able to be edited or not */ | 
					
						
							|  |  |  | bool gpencil_layer_is_editable(const bGPDlayer *gpl) | 
					
						
							|  |  |  | { | 
					
						
							| 
									
										
										
										
											2019-04-17 06:17:24 +02:00
										 |  |  |   /* Sanity check */ | 
					
						
							| 
									
										
										
										
											2019-04-22 09:39:35 +10:00
										 |  |  |   if (gpl == NULL) { | 
					
						
							| 
									
										
										
										
											2019-04-17 06:17:24 +02:00
										 |  |  |     return false; | 
					
						
							| 
									
										
										
										
											2019-04-22 09:39:35 +10:00
										 |  |  |   } | 
					
						
							| 
									
										
										
										
											2018-06-17 17:05:51 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-04-17 06:17:24 +02:00
										 |  |  |   /* Layer must be: Visible + Editable */ | 
					
						
							|  |  |  |   if ((gpl->flag & (GP_LAYER_HIDE | GP_LAYER_LOCKED)) == 0) { | 
					
						
							|  |  |  |     /* Opacity must be sufficiently high that it is still "visible"
 | 
					
						
							|  |  |  |      * Otherwise, it's not really "visible" to the user, so no point editing... | 
					
						
							|  |  |  |      */ | 
					
						
							|  |  |  |     if (gpl->opacity > GPENCIL_ALPHA_OPACITY_THRESH) { | 
					
						
							|  |  |  |       return true; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |   } | 
					
						
							| 
									
										
										
										
											2018-06-17 17:05:51 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-04-17 06:17:24 +02:00
										 |  |  |   /* Something failed */ | 
					
						
							|  |  |  |   return false; | 
					
						
							| 
									
										
										
										
											2016-02-09 02:44:02 +13:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | /* Look up the gp-frame on the requested frame number, but don't add a new one */ | 
					
						
							| 
									
										
										
										
											2012-06-08 22:07:57 +00:00
										 |  |  | bGPDframe *BKE_gpencil_layer_find_frame(bGPDlayer *gpl, int cframe) | 
					
						
							|  |  |  | { | 
					
						
							| 
									
										
										
										
											2019-04-17 06:17:24 +02:00
										 |  |  |   bGPDframe *gpf; | 
					
						
							| 
									
										
										
										
											2018-06-17 17:05:51 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-04-17 06:17:24 +02:00
										 |  |  |   /* Search in reverse order, since this is often used for playback/adding,
 | 
					
						
							|  |  |  |    * where it's less likely that we're interested in the earlier frames | 
					
						
							|  |  |  |    */ | 
					
						
							|  |  |  |   for (gpf = gpl->frames.last; gpf; gpf = gpf->prev) { | 
					
						
							|  |  |  |     if (gpf->framenum == cframe) { | 
					
						
							|  |  |  |       return gpf; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |   } | 
					
						
							| 
									
										
										
										
											2018-06-17 17:05:51 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-04-17 06:17:24 +02:00
										 |  |  |   return NULL; | 
					
						
							| 
									
										
										
										
											2012-06-08 22:07:57 +00:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2009-04-20 10:20:18 +00:00
										 |  |  | /* get the appropriate gp-frame from a given layer
 | 
					
						
							| 
									
										
										
										
											2018-11-14 12:53:15 +11:00
										 |  |  |  * - 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) | 
					
						
							| 
									
										
										
										
											2009-04-20 10:20:18 +00:00
										 |  |  |  */ | 
					
						
							| 
									
										
										
										
											2016-08-04 15:03:18 +02:00
										 |  |  | bGPDframe *BKE_gpencil_layer_getframe(bGPDlayer *gpl, int cframe, eGP_GetFrame_Mode addnew) | 
					
						
							| 
									
										
										
										
											2009-04-20 10:20:18 +00:00
										 |  |  | { | 
					
						
							| 
									
										
										
										
											2019-04-17 06:17:24 +02:00
										 |  |  |   bGPDframe *gpf = NULL; | 
					
						
							| 
									
										
										
										
											2019-09-13 13:17:50 +02:00
										 |  |  |   bool found = false; | 
					
						
							| 
									
										
										
										
											2019-04-17 06:17:24 +02:00
										 |  |  | 
 | 
					
						
							|  |  |  |   /* error checking */ | 
					
						
							| 
									
										
										
										
											2019-04-22 09:39:35 +10:00
										 |  |  |   if (gpl == NULL) { | 
					
						
							| 
									
										
										
										
											2019-04-17 06:17:24 +02:00
										 |  |  |     return NULL; | 
					
						
							| 
									
										
										
										
											2019-04-22 09:39:35 +10:00
										 |  |  |   } | 
					
						
							| 
									
										
										
										
											2019-04-17 06:17:24 +02:00
										 |  |  | 
 | 
					
						
							|  |  |  |   /* 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 from changes
 | 
					
						
							|  |  |  |      * or if the layer has been set to stay on the current frame | 
					
						
							|  |  |  |      */ | 
					
						
							| 
									
										
										
										
											2019-04-22 09:39:35 +10:00
										 |  |  |     if (gpl->flag & GP_LAYER_FRAMELOCK) { | 
					
						
							| 
									
										
										
										
											2019-04-17 06:17:24 +02:00
										 |  |  |       return gpf; | 
					
						
							| 
									
										
										
										
											2019-04-22 09:39:35 +10:00
										 |  |  |     } | 
					
						
							| 
									
										
										
										
											2019-04-17 06:17:24 +02:00
										 |  |  |     /* do not allow any changes to actframe if frame has painting tag attached to it */ | 
					
						
							| 
									
										
										
										
											2019-04-22 09:39:35 +10:00
										 |  |  |     if (gpf->flag & GP_FRAME_PAINT) { | 
					
						
							| 
									
										
										
										
											2019-04-17 06:17:24 +02:00
										 |  |  |       return gpf; | 
					
						
							| 
									
										
										
										
											2019-04-22 09:39:35 +10:00
										 |  |  |     } | 
					
						
							| 
									
										
										
										
											2019-04-17 06:17:24 +02:00
										 |  |  | 
 | 
					
						
							|  |  |  |     /* try to find matching frame */ | 
					
						
							|  |  |  |     if (gpf->framenum < cframe) { | 
					
						
							|  |  |  |       for (; gpf; gpf = gpf->next) { | 
					
						
							|  |  |  |         if (gpf->framenum == cframe) { | 
					
						
							| 
									
										
										
										
											2019-09-13 13:17:50 +02:00
										 |  |  |           found = true; | 
					
						
							| 
									
										
										
										
											2019-04-17 06:17:24 +02:00
										 |  |  |           break; | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |         else if ((gpf->next) && (gpf->next->framenum > cframe)) { | 
					
						
							| 
									
										
										
										
											2019-09-13 13:17:50 +02:00
										 |  |  |           found = true; | 
					
						
							| 
									
										
										
										
											2019-04-17 06:17:24 +02:00
										 |  |  |           break; | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |       } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |       /* set the appropriate frame */ | 
					
						
							|  |  |  |       if (addnew) { | 
					
						
							| 
									
										
										
										
											2019-04-22 09:39:35 +10:00
										 |  |  |         if ((found) && (gpf->framenum == cframe)) { | 
					
						
							| 
									
										
										
										
											2019-04-17 06:17:24 +02:00
										 |  |  |           gpl->actframe = gpf; | 
					
						
							| 
									
										
										
										
											2019-04-22 09:39:35 +10:00
										 |  |  |         } | 
					
						
							|  |  |  |         else if (addnew == GP_GETFRAME_ADD_COPY) { | 
					
						
							| 
									
										
										
										
											2019-04-17 06:17:24 +02:00
										 |  |  |           gpl->actframe = BKE_gpencil_frame_addcopy(gpl, cframe); | 
					
						
							| 
									
										
										
										
											2019-04-22 09:39:35 +10:00
										 |  |  |         } | 
					
						
							|  |  |  |         else { | 
					
						
							| 
									
										
										
										
											2019-04-17 06:17:24 +02:00
										 |  |  |           gpl->actframe = BKE_gpencil_frame_addnew(gpl, cframe); | 
					
						
							| 
									
										
										
										
											2019-04-22 09:39:35 +10:00
										 |  |  |         } | 
					
						
							| 
									
										
										
										
											2019-04-17 06:17:24 +02:00
										 |  |  |       } | 
					
						
							| 
									
										
										
										
											2019-04-22 09:39:35 +10:00
										 |  |  |       else if (found) { | 
					
						
							| 
									
										
										
										
											2019-04-17 06:17:24 +02:00
										 |  |  |         gpl->actframe = gpf; | 
					
						
							| 
									
										
										
										
											2019-04-22 09:39:35 +10:00
										 |  |  |       } | 
					
						
							|  |  |  |       else { | 
					
						
							| 
									
										
										
										
											2019-04-17 06:17:24 +02:00
										 |  |  |         gpl->actframe = gpl->frames.last; | 
					
						
							| 
									
										
										
										
											2019-04-22 09:39:35 +10:00
										 |  |  |       } | 
					
						
							| 
									
										
										
										
											2019-04-17 06:17:24 +02:00
										 |  |  |     } | 
					
						
							|  |  |  |     else { | 
					
						
							|  |  |  |       for (; gpf; gpf = gpf->prev) { | 
					
						
							|  |  |  |         if (gpf->framenum <= cframe) { | 
					
						
							| 
									
										
										
										
											2019-09-13 13:17:50 +02:00
										 |  |  |           found = true; | 
					
						
							| 
									
										
										
										
											2019-04-17 06:17:24 +02:00
										 |  |  |           break; | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |       } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |       /* set the appropriate frame */ | 
					
						
							|  |  |  |       if (addnew) { | 
					
						
							| 
									
										
										
										
											2019-04-22 09:39:35 +10:00
										 |  |  |         if ((found) && (gpf->framenum == cframe)) { | 
					
						
							| 
									
										
										
										
											2019-04-17 06:17:24 +02:00
										 |  |  |           gpl->actframe = gpf; | 
					
						
							| 
									
										
										
										
											2019-04-22 09:39:35 +10:00
										 |  |  |         } | 
					
						
							|  |  |  |         else if (addnew == GP_GETFRAME_ADD_COPY) { | 
					
						
							| 
									
										
										
										
											2019-04-17 06:17:24 +02:00
										 |  |  |           gpl->actframe = BKE_gpencil_frame_addcopy(gpl, cframe); | 
					
						
							| 
									
										
										
										
											2019-04-22 09:39:35 +10:00
										 |  |  |         } | 
					
						
							|  |  |  |         else { | 
					
						
							| 
									
										
										
										
											2019-04-17 06:17:24 +02:00
										 |  |  |           gpl->actframe = BKE_gpencil_frame_addnew(gpl, cframe); | 
					
						
							| 
									
										
										
										
											2019-04-22 09:39:35 +10:00
										 |  |  |         } | 
					
						
							| 
									
										
										
										
											2019-04-17 06:17:24 +02:00
										 |  |  |       } | 
					
						
							| 
									
										
										
										
											2019-04-22 09:39:35 +10:00
										 |  |  |       else if (found) { | 
					
						
							| 
									
										
										
										
											2019-04-17 06:17:24 +02:00
										 |  |  |         gpl->actframe = gpf; | 
					
						
							| 
									
										
										
										
											2019-04-22 09:39:35 +10:00
										 |  |  |       } | 
					
						
							|  |  |  |       else { | 
					
						
							| 
									
										
										
										
											2019-04-17 06:17:24 +02:00
										 |  |  |         gpl->actframe = gpl->frames.first; | 
					
						
							| 
									
										
										
										
											2019-04-22 09:39:35 +10:00
										 |  |  |       } | 
					
						
							| 
									
										
										
										
											2019-04-17 06:17:24 +02:00
										 |  |  |     } | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  |   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) { | 
					
						
							| 
									
										
										
										
											2019-09-13 13:17:50 +02:00
										 |  |  |           found = true; | 
					
						
							| 
									
										
										
										
											2019-04-17 06:17:24 +02:00
										 |  |  |           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) { | 
					
						
							| 
									
										
										
										
											2019-09-13 13:17:50 +02:00
										 |  |  |           found = true; | 
					
						
							| 
									
										
										
										
											2019-04-17 06:17:24 +02:00
										 |  |  |           break; | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |       } | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     /* set the appropriate frame */ | 
					
						
							|  |  |  |     if (addnew) { | 
					
						
							| 
									
										
										
										
											2019-04-22 09:39:35 +10:00
										 |  |  |       if ((found) && (gpf->framenum == cframe)) { | 
					
						
							| 
									
										
										
										
											2019-04-17 06:17:24 +02:00
										 |  |  |         gpl->actframe = gpf; | 
					
						
							| 
									
										
										
										
											2019-04-22 09:39:35 +10:00
										 |  |  |       } | 
					
						
							|  |  |  |       else { | 
					
						
							| 
									
										
										
										
											2019-04-17 06:17:24 +02:00
										 |  |  |         gpl->actframe = BKE_gpencil_frame_addnew(gpl, cframe); | 
					
						
							| 
									
										
										
										
											2019-04-22 09:39:35 +10:00
										 |  |  |       } | 
					
						
							| 
									
										
										
										
											2019-04-17 06:17:24 +02:00
										 |  |  |     } | 
					
						
							| 
									
										
										
										
											2019-04-22 09:39:35 +10:00
										 |  |  |     else if (found) { | 
					
						
							| 
									
										
										
										
											2019-04-17 06:17:24 +02:00
										 |  |  |       gpl->actframe = gpf; | 
					
						
							| 
									
										
										
										
											2019-04-22 09:39:35 +10:00
										 |  |  |     } | 
					
						
							| 
									
										
										
										
											2019-04-17 06:17:24 +02:00
										 |  |  |     else { | 
					
						
							|  |  |  |       /* unresolved errogenous situation! */ | 
					
						
							|  |  |  |       CLOG_STR_ERROR(&LOG, "cannot find appropriate gp-frame"); | 
					
						
							|  |  |  |       /* gpl->actframe should still be NULL */ | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  |   else { | 
					
						
							|  |  |  |     /* currently no frames (add if allowed to) */ | 
					
						
							| 
									
										
										
										
											2019-04-22 09:39:35 +10:00
										 |  |  |     if (addnew) { | 
					
						
							| 
									
										
										
										
											2019-04-17 06:17:24 +02:00
										 |  |  |       gpl->actframe = BKE_gpencil_frame_addnew(gpl, cframe); | 
					
						
							| 
									
										
										
										
											2019-04-22 09:39:35 +10:00
										 |  |  |     } | 
					
						
							| 
									
										
										
										
											2019-04-17 06:17:24 +02:00
										 |  |  |     else { | 
					
						
							|  |  |  |       /* don't do anything... this may be when no frames yet! */ | 
					
						
							|  |  |  |       /* gpl->actframe should still be NULL */ | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   /* return */ | 
					
						
							|  |  |  |   return gpl->actframe; | 
					
						
							| 
									
										
										
										
											2009-04-20 10:20:18 +00:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | /* delete the given frame from a layer */ | 
					
						
							| 
									
										
										
										
											2016-08-04 15:03:18 +02:00
										 |  |  | bool BKE_gpencil_layer_delframe(bGPDlayer *gpl, bGPDframe *gpf) | 
					
						
							| 
									
										
										
										
											2009-04-20 10:20:18 +00:00
										 |  |  | { | 
					
						
							| 
									
										
										
										
											2019-04-17 06:17:24 +02:00
										 |  |  |   bool changed = false; | 
					
						
							| 
									
										
										
										
											2018-06-17 17:05:51 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-04-17 06:17:24 +02:00
										 |  |  |   /* error checking */ | 
					
						
							| 
									
										
										
										
											2019-04-22 09:39:35 +10:00
										 |  |  |   if (ELEM(NULL, gpl, gpf)) { | 
					
						
							| 
									
										
										
										
											2019-04-17 06:17:24 +02:00
										 |  |  |     return false; | 
					
						
							| 
									
										
										
										
											2019-04-22 09:39:35 +10:00
										 |  |  |   } | 
					
						
							| 
									
										
										
										
											2018-06-17 17:05:51 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-04-17 06:17:24 +02:00
										 |  |  |   /* if this frame was active, make the previous frame active instead
 | 
					
						
							|  |  |  |    * since it's tricky to set active frame otherwise | 
					
						
							|  |  |  |    */ | 
					
						
							| 
									
										
										
										
											2019-04-22 09:39:35 +10:00
										 |  |  |   if (gpl->actframe == gpf) { | 
					
						
							| 
									
										
										
										
											2019-04-17 06:17:24 +02:00
										 |  |  |     gpl->actframe = gpf->prev; | 
					
						
							| 
									
										
										
										
											2019-04-22 09:39:35 +10:00
										 |  |  |   } | 
					
						
							| 
									
										
										
										
											2018-06-17 17:05:51 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-04-17 06:17:24 +02:00
										 |  |  |   /* free the frame and its data */ | 
					
						
							|  |  |  |   changed = BKE_gpencil_free_strokes(gpf); | 
					
						
							|  |  |  |   BLI_freelinkN(&gpl->frames, gpf); | 
					
						
							| 
									
										
										
										
											2018-06-17 17:05:51 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-04-17 06:17:24 +02:00
										 |  |  |   return changed; | 
					
						
							| 
									
										
										
										
											2009-04-20 10:20:18 +00:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | /* get the active gp-layer for editing */ | 
					
						
							| 
									
										
										
										
											2016-08-04 15:03:18 +02:00
										 |  |  | bGPDlayer *BKE_gpencil_layer_getactive(bGPdata *gpd) | 
					
						
							| 
									
										
										
										
											2009-04-20 10:20:18 +00:00
										 |  |  | { | 
					
						
							| 
									
										
										
										
											2019-04-17 06:17:24 +02:00
										 |  |  |   bGPDlayer *gpl; | 
					
						
							| 
									
										
										
										
											2018-06-17 17:05:51 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-04-17 06:17:24 +02:00
										 |  |  |   /* error checking */ | 
					
						
							| 
									
										
										
										
											2019-04-22 09:39:35 +10:00
										 |  |  |   if (ELEM(NULL, gpd, gpd->layers.first)) { | 
					
						
							| 
									
										
										
										
											2019-04-17 06:17:24 +02:00
										 |  |  |     return NULL; | 
					
						
							| 
									
										
										
										
											2019-04-22 09:39:35 +10:00
										 |  |  |   } | 
					
						
							| 
									
										
										
										
											2018-06-17 17:05:51 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-04-17 06:17:24 +02:00
										 |  |  |   /* loop over layers until found (assume only one active) */ | 
					
						
							|  |  |  |   for (gpl = gpd->layers.first; gpl; gpl = gpl->next) { | 
					
						
							| 
									
										
										
										
											2019-04-22 09:39:35 +10:00
										 |  |  |     if (gpl->flag & GP_LAYER_ACTIVE) { | 
					
						
							| 
									
										
										
										
											2019-04-17 06:17:24 +02:00
										 |  |  |       return gpl; | 
					
						
							| 
									
										
										
										
											2019-04-22 09:39:35 +10:00
										 |  |  |     } | 
					
						
							| 
									
										
										
										
											2019-04-17 06:17:24 +02:00
										 |  |  |   } | 
					
						
							| 
									
										
										
										
											2018-06-17 17:05:51 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-04-17 06:17:24 +02:00
										 |  |  |   /* no active layer found */ | 
					
						
							|  |  |  |   return NULL; | 
					
						
							| 
									
										
										
										
											2009-04-20 10:20:18 +00:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | /* set the active gp-layer */ | 
					
						
							| 
									
										
										
										
											2016-08-04 15:03:18 +02:00
										 |  |  | void BKE_gpencil_layer_setactive(bGPdata *gpd, bGPDlayer *active) | 
					
						
							| 
									
										
										
										
											2009-04-20 10:20:18 +00:00
										 |  |  | { | 
					
						
							| 
									
										
										
										
											2019-04-17 06:17:24 +02:00
										 |  |  |   bGPDlayer *gpl; | 
					
						
							| 
									
										
										
										
											2018-06-17 17:05:51 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-04-17 06:17:24 +02:00
										 |  |  |   /* error checking */ | 
					
						
							| 
									
										
										
										
											2019-04-22 09:39:35 +10:00
										 |  |  |   if (ELEM(NULL, gpd, gpd->layers.first, active)) { | 
					
						
							| 
									
										
										
										
											2019-04-17 06:17:24 +02:00
										 |  |  |     return; | 
					
						
							| 
									
										
										
										
											2019-04-22 09:39:35 +10:00
										 |  |  |   } | 
					
						
							| 
									
										
										
										
											2018-06-17 17:05:51 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-04-17 06:17:24 +02:00
										 |  |  |   /* loop over layers deactivating all */ | 
					
						
							|  |  |  |   for (gpl = gpd->layers.first; gpl; gpl = gpl->next) { | 
					
						
							|  |  |  |     gpl->flag &= ~GP_LAYER_ACTIVE; | 
					
						
							|  |  |  |     if (gpd->flag & GP_DATA_AUTOLOCK_LAYERS) { | 
					
						
							|  |  |  |       gpl->flag |= GP_LAYER_LOCKED; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |   } | 
					
						
							| 
									
										
										
										
											2018-06-17 17:05:51 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-04-17 06:17:24 +02:00
										 |  |  |   /* set as active one */ | 
					
						
							|  |  |  |   active->flag |= GP_LAYER_ACTIVE; | 
					
						
							|  |  |  |   if (gpd->flag & GP_DATA_AUTOLOCK_LAYERS) { | 
					
						
							|  |  |  |     active->flag &= ~GP_LAYER_LOCKED; | 
					
						
							|  |  |  |   } | 
					
						
							| 
									
										
										
										
											2009-04-20 10:20:18 +00:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-09-03 18:58:57 +02:00
										 |  |  | /* Set locked layers for autolock mode. */ | 
					
						
							| 
									
										
										
										
											2019-09-06 23:33:11 +02:00
										 |  |  | void BKE_gpencil_layer_autolock_set(bGPdata *gpd, const bool unlock) | 
					
						
							| 
									
										
										
										
											2019-09-03 18:58:57 +02:00
										 |  |  | { | 
					
						
							|  |  |  |   BLI_assert(gpd != NULL); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   bGPDlayer *gpl; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   if (gpd->flag & GP_DATA_AUTOLOCK_LAYERS) { | 
					
						
							|  |  |  |     bGPDlayer *layer_active = BKE_gpencil_layer_getactive(gpd); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     /* Lock all other layers */ | 
					
						
							|  |  |  |     for (gpl = gpd->layers.first; gpl; gpl = gpl->next) { | 
					
						
							|  |  |  |       /* unlock active layer */ | 
					
						
							|  |  |  |       if (gpl == layer_active) { | 
					
						
							|  |  |  |         gpl->flag &= ~GP_LAYER_LOCKED; | 
					
						
							|  |  |  |       } | 
					
						
							|  |  |  |       else { | 
					
						
							|  |  |  |         gpl->flag |= GP_LAYER_LOCKED; | 
					
						
							|  |  |  |       } | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  |   else { | 
					
						
							|  |  |  |     /* If disable is better unlock all layers by default or it looks there is
 | 
					
						
							|  |  |  |      * a problem in the UI because the user expects all layers will be unlocked | 
					
						
							|  |  |  |      */ | 
					
						
							| 
									
										
										
										
											2019-09-06 23:33:11 +02:00
										 |  |  |     if (unlock) { | 
					
						
							|  |  |  |       for (gpl = gpd->layers.first; gpl; gpl = gpl->next) { | 
					
						
							|  |  |  |         gpl->flag &= ~GP_LAYER_LOCKED; | 
					
						
							|  |  |  |       } | 
					
						
							| 
									
										
										
										
											2019-09-03 18:58:57 +02:00
										 |  |  |     } | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2009-04-20 10:20:18 +00:00
										 |  |  | /* delete the active gp-layer */ | 
					
						
							| 
									
										
										
										
											2016-08-04 15:03:18 +02:00
										 |  |  | void BKE_gpencil_layer_delete(bGPdata *gpd, bGPDlayer *gpl) | 
					
						
							| 
									
										
										
										
											2009-04-20 10:20:18 +00:00
										 |  |  | { | 
					
						
							| 
									
										
										
										
											2019-04-17 06:17:24 +02:00
										 |  |  |   /* error checking */ | 
					
						
							| 
									
										
										
										
											2019-04-22 09:39:35 +10:00
										 |  |  |   if (ELEM(NULL, gpd, gpl)) { | 
					
						
							| 
									
										
										
										
											2019-04-17 06:17:24 +02:00
										 |  |  |     return; | 
					
						
							| 
									
										
										
										
											2019-04-22 09:39:35 +10:00
										 |  |  |   } | 
					
						
							| 
									
										
										
										
											2018-06-17 17:05:51 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-04-17 06:17:24 +02:00
										 |  |  |   /* free layer */ | 
					
						
							|  |  |  |   BKE_gpencil_free_frames(gpl); | 
					
						
							| 
									
										
										
										
											2018-07-31 10:22:19 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-04-17 06:17:24 +02:00
										 |  |  |   /* free icon providing preview of icon color */ | 
					
						
							|  |  |  |   BKE_icon_delete(gpl->runtime.icon_id); | 
					
						
							| 
									
										
										
										
											2018-07-31 10:22:19 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-04-17 06:17:24 +02:00
										 |  |  |   BLI_freelinkN(&gpd->layers, gpl); | 
					
						
							| 
									
										
										
										
											2009-04-20 10:20:18 +00:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-04-09 09:22:46 +02:00
										 |  |  | Material *BKE_gpencil_brush_material_get(Brush *brush) | 
					
						
							| 
									
										
										
										
											2016-08-03 23:31:48 +02:00
										 |  |  | { | 
					
						
							| 
									
										
										
										
											2019-04-17 06:17:24 +02:00
										 |  |  |   Material *ma = NULL; | 
					
						
							| 
									
										
										
										
											2016-08-03 23:31:48 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-04-17 06:17:24 +02:00
										 |  |  |   if ((brush != NULL) && (brush->gpencil_settings != NULL) && | 
					
						
							|  |  |  |       (brush->gpencil_settings->material != NULL)) { | 
					
						
							|  |  |  |     ma = brush->gpencil_settings->material; | 
					
						
							|  |  |  |   } | 
					
						
							| 
									
										
										
										
											2016-08-03 23:31:48 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-04-17 06:17:24 +02:00
										 |  |  |   return ma; | 
					
						
							| 
									
										
										
										
											2018-07-31 10:22:19 +02:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-04-09 09:22:46 +02:00
										 |  |  | void BKE_gpencil_brush_material_set(Brush *brush, Material *ma) | 
					
						
							| 
									
										
										
										
											2019-03-25 17:02:42 +01:00
										 |  |  | { | 
					
						
							| 
									
										
										
										
											2019-04-17 06:17:24 +02:00
										 |  |  |   BLI_assert(brush); | 
					
						
							|  |  |  |   BLI_assert(brush->gpencil_settings); | 
					
						
							|  |  |  |   if (brush->gpencil_settings->material != ma) { | 
					
						
							|  |  |  |     if (brush->gpencil_settings->material) { | 
					
						
							|  |  |  |       id_us_min(&brush->gpencil_settings->material->id); | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |     if (ma) { | 
					
						
							|  |  |  |       id_us_plus(&ma->id); | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |     brush->gpencil_settings->material = ma; | 
					
						
							|  |  |  |   } | 
					
						
							| 
									
										
										
										
											2019-03-25 17:02:42 +01:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | /* Adds the pinned material to the object if necessary. */ | 
					
						
							| 
									
										
										
										
											2019-04-09 09:22:46 +02:00
										 |  |  | Material *BKE_gpencil_object_material_ensure_from_brush(Main *bmain, Object *ob, Brush *brush) | 
					
						
							| 
									
										
										
										
											2019-03-25 17:02:42 +01:00
										 |  |  | { | 
					
						
							| 
									
										
										
										
											2019-04-17 06:17:24 +02:00
										 |  |  |   if (brush->gpencil_settings->flag & GP_BRUSH_MATERIAL_PINNED) { | 
					
						
							|  |  |  |     Material *ma = BKE_gpencil_brush_material_get(brush); | 
					
						
							| 
									
										
										
										
											2019-03-25 17:02:42 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-04-17 06:17:24 +02:00
										 |  |  |     /* check if the material is already on object material slots and add it if missing */ | 
					
						
							|  |  |  |     if (ma && BKE_gpencil_object_material_get_index(ob, ma) < 0) { | 
					
						
							|  |  |  |       BKE_object_material_slot_add(bmain, ob); | 
					
						
							|  |  |  |       assign_material(bmain, ob, ma, ob->totcol, BKE_MAT_ASSIGN_USERPREF); | 
					
						
							|  |  |  |     } | 
					
						
							| 
									
										
										
										
											2019-03-25 17:02:42 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-04-17 06:17:24 +02:00
										 |  |  |     return ma; | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  |   else { | 
					
						
							|  |  |  |     /* using active material instead */ | 
					
						
							|  |  |  |     return give_current_material(ob, ob->actcol); | 
					
						
							|  |  |  |   } | 
					
						
							| 
									
										
										
										
											2019-03-25 17:02:42 +01:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | /* Assigns the material to object (if not already present) and returns its index (mat_nr). */ | 
					
						
							| 
									
										
										
										
											2019-04-09 09:22:46 +02:00
										 |  |  | int BKE_gpencil_object_material_ensure(Main *bmain, Object *ob, Material *material) | 
					
						
							| 
									
										
										
										
											2019-03-25 17:02:42 +01:00
										 |  |  | { | 
					
						
							| 
									
										
										
										
											2019-04-17 06:17:24 +02:00
										 |  |  |   if (!material) { | 
					
						
							|  |  |  |     return -1; | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  |   int index = BKE_gpencil_object_material_get_index(ob, material); | 
					
						
							|  |  |  |   if (index < 0) { | 
					
						
							|  |  |  |     BKE_object_material_slot_add(bmain, ob); | 
					
						
							|  |  |  |     assign_material(bmain, ob, material, ob->totcol, BKE_MAT_ASSIGN_USERPREF); | 
					
						
							|  |  |  |     return ob->totcol - 1; | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  |   return index; | 
					
						
							| 
									
										
										
										
											2019-03-25 17:02:42 +01:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | /** Creates a new gpencil material and assigns it to object.
 | 
					
						
							|  |  |  |  * | 
					
						
							|  |  |  |  * \param *r_index: value is set to zero based index of the new material if r_index is not NULL | 
					
						
							|  |  |  |  */ | 
					
						
							| 
									
										
										
										
											2019-04-09 09:22:46 +02:00
										 |  |  | Material *BKE_gpencil_object_material_new(Main *bmain, Object *ob, const char *name, int *r_index) | 
					
						
							| 
									
										
										
										
											2019-03-25 17:02:42 +01:00
										 |  |  | { | 
					
						
							| 
									
										
										
										
											2019-04-17 06:17:24 +02:00
										 |  |  |   Material *ma = BKE_material_add_gpencil(bmain, name); | 
					
						
							|  |  |  |   id_us_min(&ma->id); /* no users yet */ | 
					
						
							| 
									
										
										
										
											2019-03-25 17:02:42 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-04-17 06:17:24 +02:00
										 |  |  |   BKE_object_material_slot_add(bmain, ob); | 
					
						
							|  |  |  |   assign_material(bmain, ob, ma, ob->totcol, BKE_MAT_ASSIGN_USERPREF); | 
					
						
							| 
									
										
										
										
											2019-03-25 17:02:42 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-04-17 06:17:24 +02:00
										 |  |  |   if (r_index) { | 
					
						
							|  |  |  |     *r_index = ob->actcol - 1; | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  |   return ma; | 
					
						
							| 
									
										
										
										
											2019-03-25 17:02:42 +01:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | /* Returns the material for a brush with respect to its pinned state. */ | 
					
						
							| 
									
										
										
										
											2019-04-09 09:22:46 +02:00
										 |  |  | Material *BKE_gpencil_object_material_get_from_brush(Object *ob, Brush *brush) | 
					
						
							| 
									
										
										
										
											2019-03-25 17:02:42 +01:00
										 |  |  | { | 
					
						
							| 
									
										
										
										
											2019-05-05 09:10:44 +02:00
										 |  |  |   if ((brush) && (brush->gpencil_settings) && | 
					
						
							|  |  |  |       (brush->gpencil_settings->flag & GP_BRUSH_MATERIAL_PINNED)) { | 
					
						
							| 
									
										
										
										
											2019-04-17 06:17:24 +02:00
										 |  |  |     Material *ma = BKE_gpencil_brush_material_get(brush); | 
					
						
							|  |  |  |     return ma; | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  |   else { | 
					
						
							|  |  |  |     return give_current_material(ob, ob->actcol); | 
					
						
							|  |  |  |   } | 
					
						
							| 
									
										
										
										
											2019-03-25 17:02:42 +01:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | /* Returns the material index for a brush with respect to its pinned state. */ | 
					
						
							| 
									
										
										
										
											2019-04-09 09:22:46 +02:00
										 |  |  | int BKE_gpencil_object_material_get_index_from_brush(Object *ob, Brush *brush) | 
					
						
							| 
									
										
										
										
											2019-03-25 17:02:42 +01:00
										 |  |  | { | 
					
						
							| 
									
										
										
										
											2019-04-17 06:17:24 +02:00
										 |  |  |   if ((brush) && (brush->gpencil_settings->flag & GP_BRUSH_MATERIAL_PINNED)) { | 
					
						
							|  |  |  |     return BKE_gpencil_object_material_get_index(ob, brush->gpencil_settings->material); | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  |   else { | 
					
						
							|  |  |  |     return ob->actcol - 1; | 
					
						
							|  |  |  |   } | 
					
						
							| 
									
										
										
										
											2019-03-25 17:02:42 +01:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | /* Guaranteed to return a material assigned to object. Returns never NULL. */ | 
					
						
							| 
									
										
										
										
											2019-04-17 06:17:24 +02:00
										 |  |  | Material *BKE_gpencil_object_material_ensure_from_active_input_toolsettings(Main *bmain, | 
					
						
							|  |  |  |                                                                             Object *ob, | 
					
						
							|  |  |  |                                                                             ToolSettings *ts) | 
					
						
							| 
									
										
										
										
											2019-03-25 17:02:42 +01:00
										 |  |  | { | 
					
						
							| 
									
										
										
										
											2019-04-17 06:17:24 +02:00
										 |  |  |   if (ts && ts->gp_paint && ts->gp_paint->paint.brush) { | 
					
						
							|  |  |  |     return BKE_gpencil_object_material_ensure_from_active_input_brush( | 
					
						
							|  |  |  |         bmain, ob, ts->gp_paint->paint.brush); | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  |   else { | 
					
						
							|  |  |  |     return BKE_gpencil_object_material_ensure_from_active_input_brush(bmain, ob, NULL); | 
					
						
							|  |  |  |   } | 
					
						
							| 
									
										
										
										
											2019-03-25 17:02:42 +01:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | /* Guaranteed to return a material assigned to object. Returns never NULL. */ | 
					
						
							| 
									
										
										
										
											2019-04-17 06:17:24 +02:00
										 |  |  | Material *BKE_gpencil_object_material_ensure_from_active_input_brush(Main *bmain, | 
					
						
							|  |  |  |                                                                      Object *ob, | 
					
						
							|  |  |  |                                                                      Brush *brush) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |   if (brush) { | 
					
						
							|  |  |  |     Material *ma = BKE_gpencil_object_material_ensure_from_brush(bmain, ob, brush); | 
					
						
							|  |  |  |     if (ma) { | 
					
						
							|  |  |  |       return ma; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |     else if (brush->gpencil_settings->flag & GP_BRUSH_MATERIAL_PINNED) { | 
					
						
							|  |  |  |       /* it is easier to just unpin a NULL material, instead of setting a new one */ | 
					
						
							|  |  |  |       brush->gpencil_settings->flag &= ~GP_BRUSH_MATERIAL_PINNED; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |   } | 
					
						
							| 
									
										
										
										
											2019-08-30 14:19:07 +02:00
										 |  |  |   return BKE_gpencil_object_material_ensure_from_active_input_material(ob); | 
					
						
							| 
									
										
										
										
											2019-03-25 17:02:42 +01:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-04-27 12:07:07 +10:00
										 |  |  | /**
 | 
					
						
							|  |  |  |  * Guaranteed to return a material assigned to object. Returns never NULL. | 
					
						
							|  |  |  |  * Only use this for materials unrelated to user input. | 
					
						
							|  |  |  |  */ | 
					
						
							| 
									
										
										
										
											2019-08-30 14:19:07 +02:00
										 |  |  | Material *BKE_gpencil_object_material_ensure_from_active_input_material(Object *ob) | 
					
						
							| 
									
										
										
										
											2019-03-25 17:02:42 +01:00
										 |  |  | { | 
					
						
							| 
									
										
										
										
											2019-04-17 06:17:24 +02:00
										 |  |  |   Material *ma = give_current_material(ob, ob->actcol); | 
					
						
							|  |  |  |   if (ma) { | 
					
						
							|  |  |  |     return ma; | 
					
						
							|  |  |  |   } | 
					
						
							| 
									
										
										
										
											2019-08-30 14:19:07 +02:00
										 |  |  | 
 | 
					
						
							|  |  |  |   return &defgpencil_material; | 
					
						
							| 
									
										
										
										
											2019-03-25 17:02:42 +01:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-07-31 10:22:19 +02:00
										 |  |  | /* Get active color, and add all default settings if we don't find anything */ | 
					
						
							| 
									
										
										
										
											2019-08-30 14:19:07 +02:00
										 |  |  | Material *BKE_gpencil_object_material_ensure_active(Object *ob) | 
					
						
							| 
									
										
										
										
											2018-07-31 10:22:19 +02:00
										 |  |  | { | 
					
						
							| 
									
										
										
										
											2019-04-17 06:17:24 +02:00
										 |  |  |   Material *ma = NULL; | 
					
						
							| 
									
										
										
										
											2018-07-31 10:22:19 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-04-17 06:17:24 +02:00
										 |  |  |   /* sanity checks */ | 
					
						
							| 
									
										
										
										
											2019-08-30 14:19:07 +02:00
										 |  |  |   if (ob == NULL) { | 
					
						
							| 
									
										
										
										
											2019-04-17 06:17:24 +02:00
										 |  |  |     return NULL; | 
					
						
							| 
									
										
										
										
											2019-04-22 09:39:35 +10:00
										 |  |  |   } | 
					
						
							| 
									
										
										
										
											2018-07-31 10:22:19 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-08-30 14:19:07 +02:00
										 |  |  |   ma = BKE_gpencil_object_material_ensure_from_active_input_material(ob); | 
					
						
							| 
									
										
										
										
											2019-04-17 06:17:24 +02:00
										 |  |  |   if (ma->gp_style == NULL) { | 
					
						
							|  |  |  |     BKE_material_init_gpencil_settings(ma); | 
					
						
							|  |  |  |   } | 
					
						
							| 
									
										
										
										
											2016-08-03 23:31:48 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-04-17 06:17:24 +02:00
										 |  |  |   return ma; | 
					
						
							| 
									
										
										
										
											2016-08-03 23:31:48 +02:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-07-31 10:22:19 +02:00
										 |  |  | /* ************************************************** */ | 
					
						
							|  |  |  | /* GP Object - Boundbox Support */ | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | /**
 | 
					
						
							|  |  |  |  * Get min/max coordinate bounds for single stroke | 
					
						
							|  |  |  |  * \return Returns whether we found any selected points | 
					
						
							|  |  |  |  */ | 
					
						
							| 
									
										
										
										
											2019-04-17 06:17:24 +02:00
										 |  |  | bool BKE_gpencil_stroke_minmax(const bGPDstroke *gps, | 
					
						
							|  |  |  |                                const bool use_select, | 
					
						
							|  |  |  |                                float r_min[3], | 
					
						
							|  |  |  |                                float r_max[3]) | 
					
						
							| 
									
										
										
										
											2016-08-03 23:31:48 +02:00
										 |  |  | { | 
					
						
							| 
									
										
										
										
											2019-04-17 06:17:24 +02:00
										 |  |  |   const bGPDspoint *pt; | 
					
						
							|  |  |  |   int i; | 
					
						
							|  |  |  |   bool changed = false; | 
					
						
							| 
									
										
										
										
											2016-08-03 23:31:48 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-04-22 09:39:35 +10:00
										 |  |  |   if (ELEM(NULL, gps, r_min, r_max)) { | 
					
						
							| 
									
										
										
										
											2019-04-17 06:17:24 +02:00
										 |  |  |     return false; | 
					
						
							| 
									
										
										
										
											2019-04-22 09:39:35 +10:00
										 |  |  |   } | 
					
						
							| 
									
										
										
										
											2016-08-03 23:31:48 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-04-17 06:17:24 +02:00
										 |  |  |   for (i = 0, pt = gps->points; i < gps->totpoints; i++, pt++) { | 
					
						
							|  |  |  |     if ((use_select == false) || (pt->flag & GP_SPOINT_SELECT)) { | 
					
						
							|  |  |  |       minmax_v3v3_v3(r_min, r_max, &pt->x); | 
					
						
							|  |  |  |       changed = true; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  |   return changed; | 
					
						
							| 
									
										
										
										
											2016-08-03 23:31:48 +02:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-07-31 10:22:19 +02:00
										 |  |  | /* get min/max bounds of all strokes in GP datablock */ | 
					
						
							| 
									
										
										
										
											2019-03-19 23:55:04 +11:00
										 |  |  | bool BKE_gpencil_data_minmax(const bGPdata *gpd, float r_min[3], float r_max[3]) | 
					
						
							| 
									
										
										
										
											2016-08-03 23:31:48 +02:00
										 |  |  | { | 
					
						
							| 
									
										
										
										
											2019-04-17 06:17:24 +02:00
										 |  |  |   bool changed = false; | 
					
						
							| 
									
										
										
										
											2018-10-09 16:55:13 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-04-17 06:17:24 +02:00
										 |  |  |   INIT_MINMAX(r_min, r_max); | 
					
						
							| 
									
										
										
										
											2018-07-31 10:22:19 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-04-22 09:39:35 +10:00
										 |  |  |   if (gpd == NULL) { | 
					
						
							| 
									
										
										
										
											2019-04-17 06:17:24 +02:00
										 |  |  |     return changed; | 
					
						
							| 
									
										
										
										
											2019-04-22 09:39:35 +10:00
										 |  |  |   } | 
					
						
							| 
									
										
										
										
											2016-08-03 23:31:48 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-04-17 06:17:24 +02:00
										 |  |  |   for (bGPDlayer *gpl = gpd->layers.first; gpl; gpl = gpl->next) { | 
					
						
							|  |  |  |     bGPDframe *gpf = gpl->actframe; | 
					
						
							| 
									
										
										
										
											2018-07-31 10:22:19 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-04-17 06:17:24 +02:00
										 |  |  |     if (gpf != NULL) { | 
					
						
							|  |  |  |       for (bGPDstroke *gps = gpf->strokes.first; gps; gps = gps->next) { | 
					
						
							|  |  |  |         changed = BKE_gpencil_stroke_minmax(gps, false, r_min, r_max); | 
					
						
							|  |  |  |       } | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |   } | 
					
						
							| 
									
										
										
										
											2018-10-09 16:55:13 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-04-17 06:17:24 +02:00
										 |  |  |   return changed; | 
					
						
							| 
									
										
										
										
											2018-07-31 10:22:19 +02:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-04-17 06:17:24 +02:00
										 |  |  | bool BKE_gpencil_stroke_select_check(const bGPDstroke *gps) | 
					
						
							| 
									
										
										
										
											2018-11-13 14:04:00 +11:00
										 |  |  | { | 
					
						
							| 
									
										
										
										
											2019-04-17 06:17:24 +02:00
										 |  |  |   const bGPDspoint *pt; | 
					
						
							|  |  |  |   int i; | 
					
						
							|  |  |  |   for (i = 0, pt = gps->points; i < gps->totpoints; i++, pt++) { | 
					
						
							|  |  |  |     if (pt->flag & GP_SPOINT_SELECT) { | 
					
						
							|  |  |  |       return true; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  |   return false; | 
					
						
							| 
									
										
										
										
											2018-11-13 14:04:00 +11:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-07-31 10:22:19 +02:00
										 |  |  | /* compute center of bounding box */ | 
					
						
							| 
									
										
										
										
											2018-11-13 14:24:41 +11:00
										 |  |  | void BKE_gpencil_centroid_3d(bGPdata *gpd, float r_centroid[3]) | 
					
						
							| 
									
										
										
										
											2018-07-31 10:22:19 +02:00
										 |  |  | { | 
					
						
							| 
									
										
										
										
											2019-04-17 06:17:24 +02:00
										 |  |  |   float min[3], max[3], tot[3]; | 
					
						
							| 
									
										
										
										
											2018-07-31 10:22:19 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-04-17 06:17:24 +02:00
										 |  |  |   BKE_gpencil_data_minmax(gpd, min, max); | 
					
						
							| 
									
										
										
										
											2018-07-31 10:22:19 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-04-17 06:17:24 +02:00
										 |  |  |   add_v3_v3v3(tot, min, max); | 
					
						
							|  |  |  |   mul_v3_v3fl(r_centroid, tot, 0.5f); | 
					
						
							| 
									
										
										
										
											2018-07-31 10:22:19 +02:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | /* create bounding box values */ | 
					
						
							|  |  |  | static void boundbox_gpencil(Object *ob) | 
					
						
							|  |  |  | { | 
					
						
							| 
									
										
										
										
											2019-04-17 06:17:24 +02:00
										 |  |  |   BoundBox *bb; | 
					
						
							|  |  |  |   bGPdata *gpd; | 
					
						
							|  |  |  |   float min[3], max[3]; | 
					
						
							| 
									
										
										
										
											2018-07-31 10:22:19 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-04-17 06:17:24 +02:00
										 |  |  |   if (ob->runtime.bb == NULL) { | 
					
						
							|  |  |  |     ob->runtime.bb = MEM_callocN(sizeof(BoundBox), "GPencil boundbox"); | 
					
						
							|  |  |  |   } | 
					
						
							| 
									
										
										
										
											2016-08-03 23:31:48 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-04-17 06:17:24 +02:00
										 |  |  |   bb = ob->runtime.bb; | 
					
						
							|  |  |  |   gpd = ob->data; | 
					
						
							| 
									
										
										
										
											2018-07-31 10:22:19 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-04-17 06:17:24 +02:00
										 |  |  |   if (!BKE_gpencil_data_minmax(gpd, min, max)) { | 
					
						
							|  |  |  |     min[0] = min[1] = min[2] = -1.0f; | 
					
						
							|  |  |  |     max[0] = max[1] = max[2] = 1.0f; | 
					
						
							|  |  |  |   } | 
					
						
							| 
									
										
										
										
											2019-03-19 23:49:56 +11:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-04-17 06:17:24 +02:00
										 |  |  |   BKE_boundbox_init_from_minmax(bb, min, max); | 
					
						
							| 
									
										
										
										
											2018-07-31 10:22:19 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-04-17 06:17:24 +02:00
										 |  |  |   bb->flag &= ~BOUNDBOX_DIRTY; | 
					
						
							| 
									
										
										
										
											2016-08-03 23:31:48 +02:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-07-31 10:22:19 +02:00
										 |  |  | /* get bounding box */ | 
					
						
							|  |  |  | BoundBox *BKE_gpencil_boundbox_get(Object *ob) | 
					
						
							| 
									
										
										
										
											2016-08-03 23:31:48 +02:00
										 |  |  | { | 
					
						
							| 
									
										
										
										
											2019-04-22 09:39:35 +10:00
										 |  |  |   if (ELEM(NULL, ob, ob->data)) { | 
					
						
							| 
									
										
										
										
											2019-04-17 06:17:24 +02:00
										 |  |  |     return NULL; | 
					
						
							| 
									
										
										
										
											2019-04-22 09:39:35 +10:00
										 |  |  |   } | 
					
						
							| 
									
										
										
										
											2016-08-03 23:31:48 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-04-17 06:17:24 +02:00
										 |  |  |   bGPdata *gpd = (bGPdata *)ob->data; | 
					
						
							|  |  |  |   if ((ob->runtime.bb) && ((gpd->flag & GP_DATA_CACHE_IS_DIRTY) == 0)) { | 
					
						
							|  |  |  |     return ob->runtime.bb; | 
					
						
							|  |  |  |   } | 
					
						
							| 
									
										
										
										
											2016-08-03 23:31:48 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-04-17 06:17:24 +02:00
										 |  |  |   boundbox_gpencil(ob); | 
					
						
							| 
									
										
										
										
											2018-07-31 10:22:19 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-04-17 06:17:24 +02:00
										 |  |  |   return ob->runtime.bb; | 
					
						
							| 
									
										
										
										
											2016-08-03 23:31:48 +02:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-07-31 10:22:19 +02:00
										 |  |  | /* ************************************************** */ | 
					
						
							|  |  |  | /* Apply Transforms */ | 
					
						
							| 
									
										
										
										
											2016-08-03 23:31:48 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-07-31 10:22:19 +02:00
										 |  |  | void BKE_gpencil_transform(bGPdata *gpd, float mat[4][4]) | 
					
						
							|  |  |  | { | 
					
						
							| 
									
										
										
										
											2019-04-22 09:39:35 +10:00
										 |  |  |   if (gpd == NULL) { | 
					
						
							| 
									
										
										
										
											2019-04-17 06:17:24 +02:00
										 |  |  |     return; | 
					
						
							| 
									
										
										
										
											2019-04-22 09:39:35 +10:00
										 |  |  |   } | 
					
						
							| 
									
										
										
										
											2018-07-31 10:22:19 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-07-02 19:17:38 +02:00
										 |  |  |   const float scalef = mat4_to_scale(mat); | 
					
						
							| 
									
										
										
										
											2019-04-17 06:17:24 +02:00
										 |  |  |   for (bGPDlayer *gpl = gpd->layers.first; gpl; gpl = gpl->next) { | 
					
						
							|  |  |  |     /* FIXME: For now, we just skip parented layers.
 | 
					
						
							|  |  |  |      * Otherwise, we have to update each frame to find | 
					
						
							|  |  |  |      * the current parent position/effects. | 
					
						
							|  |  |  |      */ | 
					
						
							|  |  |  |     if (gpl->parent) { | 
					
						
							|  |  |  |       continue; | 
					
						
							|  |  |  |     } | 
					
						
							| 
									
										
										
										
											2018-07-31 10:22:19 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-04-17 06:17:24 +02:00
										 |  |  |     for (bGPDframe *gpf = gpl->frames.first; gpf; gpf = gpf->next) { | 
					
						
							|  |  |  |       for (bGPDstroke *gps = gpf->strokes.first; gps; gps = gps->next) { | 
					
						
							|  |  |  |         bGPDspoint *pt; | 
					
						
							|  |  |  |         int i; | 
					
						
							| 
									
										
										
										
											2018-07-31 10:22:19 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-04-17 06:17:24 +02:00
										 |  |  |         for (pt = gps->points, i = 0; i < gps->totpoints; pt++, i++) { | 
					
						
							|  |  |  |           mul_m4_v3(mat, &pt->x); | 
					
						
							| 
									
										
										
										
											2019-07-02 19:17:38 +02:00
										 |  |  |           pt->pressure *= scalef; | 
					
						
							| 
									
										
										
										
											2019-04-17 06:17:24 +02:00
										 |  |  |         } | 
					
						
							| 
									
										
										
										
											2016-08-03 23:31:48 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-04-17 06:17:24 +02:00
										 |  |  |         /* TODO: Do we need to do this? distortion may mean we need to re-triangulate */ | 
					
						
							|  |  |  |         gps->flag |= GP_STROKE_RECALC_GEOMETRY; | 
					
						
							|  |  |  |         gps->tot_triangles = 0; | 
					
						
							|  |  |  |       } | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |   } | 
					
						
							| 
									
										
										
										
											2018-07-31 10:22:19 +02:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | /* ************************************************** */ | 
					
						
							|  |  |  | /* GP Object - Vertex Groups */ | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | /* remove a vertex group */ | 
					
						
							|  |  |  | void BKE_gpencil_vgroup_remove(Object *ob, bDeformGroup *defgroup) | 
					
						
							|  |  |  | { | 
					
						
							| 
									
										
										
										
											2019-04-17 06:17:24 +02:00
										 |  |  |   bGPdata *gpd = ob->data; | 
					
						
							|  |  |  |   MDeformVert *dvert = NULL; | 
					
						
							|  |  |  |   const int def_nr = BLI_findindex(&ob->defbase, defgroup); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   /* Remove points data */ | 
					
						
							|  |  |  |   if (gpd) { | 
					
						
							|  |  |  |     for (bGPDlayer *gpl = gpd->layers.first; gpl; gpl = gpl->next) { | 
					
						
							|  |  |  |       for (bGPDframe *gpf = gpl->frames.first; gpf; gpf = gpf->next) { | 
					
						
							|  |  |  |         for (bGPDstroke *gps = gpf->strokes.first; gps; gps = gps->next) { | 
					
						
							|  |  |  |           if (gps->dvert != NULL) { | 
					
						
							|  |  |  |             for (int i = 0; i < gps->totpoints; i++) { | 
					
						
							|  |  |  |               dvert = &gps->dvert[i]; | 
					
						
							|  |  |  |               MDeformWeight *dw = defvert_find_index(dvert, def_nr); | 
					
						
							|  |  |  |               if (dw != NULL) { | 
					
						
							|  |  |  |                 defvert_remove_group(dvert, dw); | 
					
						
							|  |  |  |               } | 
					
						
							|  |  |  |               else { | 
					
						
							|  |  |  |                 /* reorganize weights in other strokes */ | 
					
						
							|  |  |  |                 for (int g = 0; g < gps->dvert->totweight; g++) { | 
					
						
							|  |  |  |                   dw = &dvert->dw[g]; | 
					
						
							|  |  |  |                   if ((dw != NULL) && (dw->def_nr > def_nr)) { | 
					
						
							|  |  |  |                     dw->def_nr--; | 
					
						
							|  |  |  |                   } | 
					
						
							|  |  |  |                 } | 
					
						
							|  |  |  |               } | 
					
						
							|  |  |  |             } | 
					
						
							|  |  |  |           } | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |       } | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   /* Remove the group */ | 
					
						
							|  |  |  |   BLI_freelinkN(&ob->defbase, defgroup); | 
					
						
							|  |  |  |   DEG_id_tag_update(&gpd->id, ID_RECALC_TRANSFORM | ID_RECALC_GEOMETRY); | 
					
						
							| 
									
										
										
										
											2016-08-03 23:31:48 +02:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-08-26 16:39:01 +02:00
										 |  |  | void BKE_gpencil_dvert_ensure(bGPDstroke *gps) | 
					
						
							|  |  |  | { | 
					
						
							| 
									
										
										
										
											2019-04-17 06:17:24 +02:00
										 |  |  |   if (gps->dvert == NULL) { | 
					
						
							|  |  |  |     gps->dvert = MEM_callocN(sizeof(MDeformVert) * gps->totpoints, "gp_stroke_weights"); | 
					
						
							|  |  |  |   } | 
					
						
							| 
									
										
										
										
											2018-08-26 16:39:01 +02:00
										 |  |  | } | 
					
						
							| 
									
										
										
										
											2016-08-03 23:31:48 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-07-31 10:22:19 +02:00
										 |  |  | /* ************************************************** */ | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-08-08 16:12:13 +02:00
										 |  |  | static void stroke_defvert_create_nr_list(MDeformVert *dv_list, | 
					
						
							|  |  |  |                                           int count, | 
					
						
							|  |  |  |                                           ListBase *result, | 
					
						
							|  |  |  |                                           int *totweight) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |   LinkData *ld; | 
					
						
							|  |  |  |   MDeformVert *dv; | 
					
						
							|  |  |  |   MDeformWeight *dw; | 
					
						
							|  |  |  |   int i, j; | 
					
						
							|  |  |  |   int tw = 0; | 
					
						
							|  |  |  |   for (i = 0; i < count; i++) { | 
					
						
							|  |  |  |     dv = &dv_list[i]; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     /* find def_nr in list, if not exist, then create one */ | 
					
						
							|  |  |  |     for (j = 0; j < dv->totweight; j++) { | 
					
						
							| 
									
										
										
										
											2019-09-13 13:17:50 +02:00
										 |  |  |       bool found = false; | 
					
						
							| 
									
										
										
										
											2019-08-08 16:12:13 +02:00
										 |  |  |       dw = &dv->dw[j]; | 
					
						
							|  |  |  |       for (ld = result->first; ld; ld = ld->next) { | 
					
						
							| 
									
										
										
										
											2019-08-09 01:54:50 +10:00
										 |  |  |         if (ld->data == POINTER_FROM_INT(dw->def_nr)) { | 
					
						
							| 
									
										
										
										
											2019-09-13 13:17:50 +02:00
										 |  |  |           found = true; | 
					
						
							| 
									
										
										
										
											2019-08-08 16:12:13 +02:00
										 |  |  |           break; | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |       } | 
					
						
							|  |  |  |       if (!found) { | 
					
						
							|  |  |  |         ld = MEM_callocN(sizeof(LinkData), "def_nr_item"); | 
					
						
							| 
									
										
										
										
											2019-08-09 01:54:50 +10:00
										 |  |  |         ld->data = POINTER_FROM_INT(dw->def_nr); | 
					
						
							| 
									
										
										
										
											2019-08-08 16:12:13 +02:00
										 |  |  |         BLI_addtail(result, ld); | 
					
						
							|  |  |  |         tw++; | 
					
						
							|  |  |  |       } | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   *totweight = tw; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-08-09 01:54:50 +10:00
										 |  |  | static MDeformVert *stroke_defvert_new_count(int count, int totweight, ListBase *def_nr_list) | 
					
						
							| 
									
										
										
										
											2019-08-08 16:12:13 +02:00
										 |  |  | { | 
					
						
							|  |  |  |   int i, j; | 
					
						
							|  |  |  |   LinkData *ld; | 
					
						
							|  |  |  |   MDeformVert *dst = MEM_mallocN(count * sizeof(MDeformVert), "new_deformVert"); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   for (i = 0; i < count; i++) { | 
					
						
							|  |  |  |     dst[i].dw = MEM_mallocN(sizeof(MDeformWeight) * totweight, "new_deformWeight"); | 
					
						
							| 
									
										
										
										
											2019-09-13 20:59:39 +08:00
										 |  |  |     dst[i].totweight = totweight; | 
					
						
							| 
									
										
										
										
											2019-08-08 16:12:13 +02:00
										 |  |  |     j = 0; | 
					
						
							|  |  |  |     /* re-assign deform groups */ | 
					
						
							|  |  |  |     for (ld = def_nr_list->first; ld; ld = ld->next) { | 
					
						
							| 
									
										
										
										
											2019-08-09 01:54:50 +10:00
										 |  |  |       dst[i].dw[j].def_nr = POINTER_AS_INT(ld->data); | 
					
						
							| 
									
										
										
										
											2019-08-08 16:12:13 +02:00
										 |  |  |       j++; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   return dst; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static void stroke_interpolate_deform_weights( | 
					
						
							|  |  |  |     bGPDstroke *gps, int index_from, int index_to, float ratio, MDeformVert *vert) | 
					
						
							|  |  |  | { | 
					
						
							| 
									
										
										
										
											2019-08-09 02:02:32 +10:00
										 |  |  |   const MDeformVert *vl = &gps->dvert[index_from]; | 
					
						
							|  |  |  |   const MDeformVert *vr = &gps->dvert[index_to]; | 
					
						
							| 
									
										
										
										
											2019-08-08 16:12:13 +02:00
										 |  |  |   int i; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   for (i = 0; i < vert->totweight; i++) { | 
					
						
							| 
									
										
										
										
											2019-08-09 02:02:32 +10:00
										 |  |  |     float wl = defvert_find_weight(vl, vert->dw[i].def_nr); | 
					
						
							|  |  |  |     float wr = defvert_find_weight(vr, vert->dw[i].def_nr); | 
					
						
							| 
									
										
										
										
											2019-08-08 16:12:13 +02:00
										 |  |  |     vert->dw[i].weight = interpf(wr, wl, ratio); | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static int stroke_march_next_point(const bGPDstroke *gps, | 
					
						
							|  |  |  |                                    const int index_next_pt, | 
					
						
							|  |  |  |                                    const float *current, | 
					
						
							|  |  |  |                                    const float dist, | 
					
						
							|  |  |  |                                    float *result, | 
					
						
							|  |  |  |                                    float *pressure, | 
					
						
							|  |  |  |                                    float *strength, | 
					
						
							|  |  |  |                                    float *ratio_result, | 
					
						
							|  |  |  |                                    int *index_from, | 
					
						
							|  |  |  |                                    int *index_to) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |   float remaining_till_next = 0.0f; | 
					
						
							|  |  |  |   float remaining_march = dist; | 
					
						
							|  |  |  |   float step_start[3]; | 
					
						
							|  |  |  |   float point[3]; | 
					
						
							|  |  |  |   int next_point_index = index_next_pt; | 
					
						
							|  |  |  |   bGPDspoint *pt = NULL; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   if (!(next_point_index < gps->totpoints)) { | 
					
						
							|  |  |  |     return -1; | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   copy_v3_v3(step_start, current); | 
					
						
							|  |  |  |   pt = &gps->points[next_point_index]; | 
					
						
							|  |  |  |   copy_v3_v3(point, &pt->x); | 
					
						
							|  |  |  |   remaining_till_next = len_v3v3(point, step_start); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   while (remaining_till_next < remaining_march) { | 
					
						
							|  |  |  |     remaining_march -= remaining_till_next; | 
					
						
							|  |  |  |     pt = &gps->points[next_point_index]; | 
					
						
							|  |  |  |     copy_v3_v3(point, &pt->x); | 
					
						
							|  |  |  |     copy_v3_v3(step_start, point); | 
					
						
							|  |  |  |     next_point_index++; | 
					
						
							|  |  |  |     if (!(next_point_index < gps->totpoints)) { | 
					
						
							|  |  |  |       next_point_index = gps->totpoints - 1; | 
					
						
							|  |  |  |       break; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |     pt = &gps->points[next_point_index]; | 
					
						
							|  |  |  |     copy_v3_v3(point, &pt->x); | 
					
						
							|  |  |  |     remaining_till_next = len_v3v3(point, step_start); | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  |   if (remaining_till_next < remaining_march) { | 
					
						
							|  |  |  |     pt = &gps->points[next_point_index]; | 
					
						
							|  |  |  |     copy_v3_v3(result, &pt->x); | 
					
						
							|  |  |  |     *pressure = gps->points[next_point_index].pressure; | 
					
						
							|  |  |  |     *strength = gps->points[next_point_index].strength; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     *index_from = next_point_index - 1; | 
					
						
							|  |  |  |     *index_to = next_point_index; | 
					
						
							|  |  |  |     *ratio_result = 1.0f; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     return 0; | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  |   else { | 
					
						
							|  |  |  |     float ratio = remaining_march / remaining_till_next; | 
					
						
							|  |  |  |     interp_v3_v3v3(result, step_start, point, ratio); | 
					
						
							|  |  |  |     *pressure = interpf( | 
					
						
							|  |  |  |         gps->points[next_point_index].pressure, gps->points[next_point_index - 1].pressure, ratio); | 
					
						
							|  |  |  |     *strength = interpf( | 
					
						
							|  |  |  |         gps->points[next_point_index].strength, gps->points[next_point_index - 1].strength, ratio); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     *index_from = next_point_index - 1; | 
					
						
							|  |  |  |     *index_to = next_point_index; | 
					
						
							|  |  |  |     *ratio_result = ratio; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     return next_point_index; | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static int stroke_march_next_point_no_interp(const bGPDstroke *gps, | 
					
						
							|  |  |  |                                              const int index_next_pt, | 
					
						
							|  |  |  |                                              const float *current, | 
					
						
							|  |  |  |                                              const float dist, | 
					
						
							|  |  |  |                                              float *result) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |   float remaining_till_next = 0.0f; | 
					
						
							|  |  |  |   float remaining_march = dist; | 
					
						
							|  |  |  |   float step_start[3]; | 
					
						
							|  |  |  |   float point[3]; | 
					
						
							|  |  |  |   int next_point_index = index_next_pt; | 
					
						
							|  |  |  |   bGPDspoint *pt = NULL; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   if (!(next_point_index < gps->totpoints)) { | 
					
						
							|  |  |  |     return -1; | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   copy_v3_v3(step_start, current); | 
					
						
							|  |  |  |   pt = &gps->points[next_point_index]; | 
					
						
							|  |  |  |   copy_v3_v3(point, &pt->x); | 
					
						
							|  |  |  |   remaining_till_next = len_v3v3(point, step_start); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   while (remaining_till_next < remaining_march) { | 
					
						
							|  |  |  |     remaining_march -= remaining_till_next; | 
					
						
							|  |  |  |     pt = &gps->points[next_point_index]; | 
					
						
							|  |  |  |     copy_v3_v3(point, &pt->x); | 
					
						
							|  |  |  |     copy_v3_v3(step_start, point); | 
					
						
							|  |  |  |     next_point_index++; | 
					
						
							|  |  |  |     if (!(next_point_index < gps->totpoints)) { | 
					
						
							|  |  |  |       next_point_index = gps->totpoints - 1; | 
					
						
							|  |  |  |       break; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |     pt = &gps->points[next_point_index]; | 
					
						
							|  |  |  |     copy_v3_v3(point, &pt->x); | 
					
						
							|  |  |  |     remaining_till_next = len_v3v3(point, step_start); | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  |   if (remaining_till_next < remaining_march) { | 
					
						
							|  |  |  |     pt = &gps->points[next_point_index]; | 
					
						
							|  |  |  |     copy_v3_v3(result, &pt->x); | 
					
						
							|  |  |  |     return 0; | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  |   else { | 
					
						
							|  |  |  |     float ratio = remaining_march / remaining_till_next; | 
					
						
							|  |  |  |     interp_v3_v3v3(result, step_start, point, ratio); | 
					
						
							|  |  |  |     return next_point_index; | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static int stroke_march_count(const bGPDstroke *gps, const float dist) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |   int point_count = 0; | 
					
						
							|  |  |  |   float point[3]; | 
					
						
							|  |  |  |   int next_point_index = 1; | 
					
						
							|  |  |  |   bGPDspoint *pt = NULL; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   pt = &gps->points[0]; | 
					
						
							|  |  |  |   copy_v3_v3(point, &pt->x); | 
					
						
							|  |  |  |   point_count++; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   while ((next_point_index = stroke_march_next_point_no_interp( | 
					
						
							|  |  |  |               gps, next_point_index, point, dist, point)) > -1) { | 
					
						
							|  |  |  |     point_count++; | 
					
						
							|  |  |  |     if (next_point_index == 0) { | 
					
						
							|  |  |  |       break; /* last point finished */ | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  |   return point_count; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | /**
 | 
					
						
							|  |  |  |  * Resample a stroke | 
					
						
							|  |  |  |  * \param gps: Stroke to sample | 
					
						
							|  |  |  |  * \param dist: Distance of one segment | 
					
						
							|  |  |  |  */ | 
					
						
							|  |  |  | bool BKE_gpencil_sample_stroke(bGPDstroke *gps, const float dist, const bool select) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |   bGPDspoint *pt = gps->points; | 
					
						
							|  |  |  |   bGPDspoint *pt1 = NULL; | 
					
						
							|  |  |  |   bGPDspoint *pt2 = NULL; | 
					
						
							|  |  |  |   int i; | 
					
						
							|  |  |  |   LinkData *ld; | 
					
						
							|  |  |  |   ListBase def_nr_list = {0}; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   if (gps->totpoints < 2 || dist < FLT_EPSILON) { | 
					
						
							|  |  |  |     return false; | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  |   /* TODO: Implement feature point preservation. */ | 
					
						
							|  |  |  |   int count = stroke_march_count(gps, dist); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   bGPDspoint *new_pt = MEM_callocN(sizeof(bGPDspoint) * count, "gp_stroke_points_sampled"); | 
					
						
							|  |  |  |   MDeformVert *new_dv = NULL; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   int result_totweight; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   if (gps->dvert != NULL) { | 
					
						
							| 
									
										
										
										
											2019-09-13 20:59:39 +08:00
										 |  |  |     stroke_defvert_create_nr_list(gps->dvert, gps->totpoints, &def_nr_list, &result_totweight); | 
					
						
							| 
									
										
										
										
											2019-08-08 16:12:13 +02:00
										 |  |  |     new_dv = stroke_defvert_new_count(count, result_totweight, &def_nr_list); | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   int next_point_index = 1; | 
					
						
							|  |  |  |   i = 0; | 
					
						
							|  |  |  |   float pressure, strength, ratio_result; | 
					
						
							|  |  |  |   int index_from, index_to; | 
					
						
							|  |  |  |   float last_coord[3]; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   /*  1st point is always at the start */ | 
					
						
							|  |  |  |   pt1 = &gps->points[0]; | 
					
						
							|  |  |  |   copy_v3_v3(last_coord, &pt1->x); | 
					
						
							|  |  |  |   pt2 = &new_pt[i]; | 
					
						
							|  |  |  |   copy_v3_v3(&pt2->x, last_coord); | 
					
						
							|  |  |  |   new_pt[i].pressure = pt[0].pressure; | 
					
						
							|  |  |  |   new_pt[i].strength = pt[0].strength; | 
					
						
							|  |  |  |   if (select) { | 
					
						
							|  |  |  |     new_pt[i].flag |= GP_SPOINT_SELECT; | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  |   i++; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   if (new_dv) { | 
					
						
							|  |  |  |     stroke_interpolate_deform_weights(gps, 0, 0, 0, &new_dv[0]); | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   /*  the rest */ | 
					
						
							|  |  |  |   while ((next_point_index = stroke_march_next_point(gps, | 
					
						
							|  |  |  |                                                      next_point_index, | 
					
						
							|  |  |  |                                                      last_coord, | 
					
						
							|  |  |  |                                                      dist, | 
					
						
							|  |  |  |                                                      last_coord, | 
					
						
							|  |  |  |                                                      &pressure, | 
					
						
							|  |  |  |                                                      &strength, | 
					
						
							|  |  |  |                                                      &ratio_result, | 
					
						
							|  |  |  |                                                      &index_from, | 
					
						
							|  |  |  |                                                      &index_to)) > -1) { | 
					
						
							|  |  |  |     pt2 = &new_pt[i]; | 
					
						
							|  |  |  |     copy_v3_v3(&pt2->x, last_coord); | 
					
						
							|  |  |  |     new_pt[i].pressure = pressure; | 
					
						
							|  |  |  |     new_pt[i].strength = strength; | 
					
						
							|  |  |  |     if (select) { | 
					
						
							|  |  |  |       new_pt[i].flag |= GP_SPOINT_SELECT; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     if (new_dv) { | 
					
						
							|  |  |  |       stroke_interpolate_deform_weights(gps, index_from, index_to, ratio_result, &new_dv[i]); | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     i++; | 
					
						
							|  |  |  |     if (next_point_index == 0) { | 
					
						
							|  |  |  |       break; /* last point finished */ | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   gps->points = new_pt; | 
					
						
							| 
									
										
										
										
											2019-09-13 20:59:39 +08:00
										 |  |  |   /* Free original vertex list. */ | 
					
						
							|  |  |  |   MEM_freeN(pt); | 
					
						
							| 
									
										
										
										
											2019-08-08 16:12:13 +02:00
										 |  |  | 
 | 
					
						
							|  |  |  |   if (new_dv) { | 
					
						
							| 
									
										
										
										
											2019-09-13 20:59:39 +08:00
										 |  |  |     /* Free original weight data. */ | 
					
						
							| 
									
										
										
										
											2019-08-08 16:12:13 +02:00
										 |  |  |     BKE_gpencil_free_stroke_weights(gps); | 
					
						
							| 
									
										
										
										
											2019-09-13 20:59:39 +08:00
										 |  |  |     MEM_freeN(gps->dvert); | 
					
						
							| 
									
										
										
										
											2019-08-09 01:54:50 +10:00
										 |  |  |     while ((ld = BLI_pophead(&def_nr_list))) { | 
					
						
							| 
									
										
										
										
											2019-08-08 16:12:13 +02:00
										 |  |  |       MEM_freeN(ld); | 
					
						
							|  |  |  |     } | 
					
						
							| 
									
										
										
										
											2019-09-13 20:59:39 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-08-08 16:12:13 +02:00
										 |  |  |     gps->dvert = new_dv; | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-09-13 11:18:40 +02:00
										 |  |  |   gps->totpoints = i; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-08-08 16:12:13 +02:00
										 |  |  |   gps->flag |= GP_STROKE_RECALC_GEOMETRY; | 
					
						
							|  |  |  |   gps->tot_triangles = 0; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   return true; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-07-31 10:22:19 +02:00
										 |  |  | /**
 | 
					
						
							|  |  |  |  * Apply smooth to stroke point | 
					
						
							| 
									
										
										
										
											2018-12-12 12:55:20 +11:00
										 |  |  |  * \param gps: Stroke to smooth | 
					
						
							|  |  |  |  * \param i: Point index | 
					
						
							|  |  |  |  * \param inf: Amount of smoothing to apply | 
					
						
							| 
									
										
										
										
											2018-07-31 10:22:19 +02:00
										 |  |  |  */ | 
					
						
							|  |  |  | bool BKE_gpencil_smooth_stroke(bGPDstroke *gps, int i, float inf) | 
					
						
							| 
									
										
										
										
											2016-08-03 23:31:48 +02:00
										 |  |  | { | 
					
						
							| 
									
										
										
										
											2019-04-17 06:17:24 +02:00
										 |  |  |   bGPDspoint *pt = &gps->points[i]; | 
					
						
							|  |  |  |   float sco[3] = {0.0f}; | 
					
						
							| 
									
										
										
										
											2016-08-03 23:31:48 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-04-17 06:17:24 +02:00
										 |  |  |   /* Do nothing if not enough points to smooth out */ | 
					
						
							|  |  |  |   if (gps->totpoints <= 2) { | 
					
						
							|  |  |  |     return false; | 
					
						
							|  |  |  |   } | 
					
						
							| 
									
										
										
										
											2018-07-31 10:22:19 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-04-17 06:17:24 +02:00
										 |  |  |   /* Only affect endpoints by a fraction of the normal strength,
 | 
					
						
							|  |  |  |    * to prevent the stroke from shrinking too much | 
					
						
							|  |  |  |    */ | 
					
						
							|  |  |  |   if ((i == 0) || (i == gps->totpoints - 1)) { | 
					
						
							|  |  |  |     inf *= 0.1f; | 
					
						
							|  |  |  |   } | 
					
						
							| 
									
										
										
										
											2016-08-03 23:31:48 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-04-17 06:17:24 +02:00
										 |  |  |   /* Compute smoothed coordinate by taking the ones nearby */ | 
					
						
							| 
									
										
										
										
											2019-04-27 12:07:07 +10:00
										 |  |  |   /* XXX: This is potentially slow,
 | 
					
						
							|  |  |  |    *      and suffers from accumulation error as earlier points are handled before later ones. */ | 
					
						
							| 
									
										
										
										
											2019-04-17 06:17:24 +02:00
										 |  |  |   { | 
					
						
							| 
									
										
										
										
											2019-04-27 12:07:07 +10:00
										 |  |  |     /* XXX: this is hardcoded to look at 2 points on either side of the current one
 | 
					
						
							|  |  |  |      * (i.e. 5 items total). */ | 
					
						
							| 
									
										
										
										
											2019-04-17 06:17:24 +02:00
										 |  |  |     const int steps = 2; | 
					
						
							|  |  |  |     const float average_fac = 1.0f / (float)(steps * 2 + 1); | 
					
						
							|  |  |  |     int step; | 
					
						
							| 
									
										
										
										
											2018-07-31 10:22:19 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-04-17 06:17:24 +02:00
										 |  |  |     /* add the point itself */ | 
					
						
							|  |  |  |     madd_v3_v3fl(sco, &pt->x, average_fac); | 
					
						
							| 
									
										
										
										
											2018-07-31 10:22:19 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-04-17 06:17:24 +02:00
										 |  |  |     /* n-steps before/after current point */ | 
					
						
							| 
									
										
										
										
											2019-04-27 12:07:07 +10:00
										 |  |  |     /* XXX: review how the endpoints are treated by this algorithm. */ | 
					
						
							|  |  |  |     /* XXX: falloff measures should also introduce some weighting variations,
 | 
					
						
							|  |  |  |      *      so that further-out points get less weight. */ | 
					
						
							| 
									
										
										
										
											2019-04-17 06:17:24 +02:00
										 |  |  |     for (step = 1; step <= steps; step++) { | 
					
						
							|  |  |  |       bGPDspoint *pt1, *pt2; | 
					
						
							|  |  |  |       int before = i - step; | 
					
						
							|  |  |  |       int after = i + step; | 
					
						
							| 
									
										
										
										
											2018-07-31 10:22:19 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-04-17 06:17:24 +02:00
										 |  |  |       CLAMP_MIN(before, 0); | 
					
						
							|  |  |  |       CLAMP_MAX(after, gps->totpoints - 1); | 
					
						
							| 
									
										
										
										
											2018-07-31 10:22:19 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-04-17 06:17:24 +02:00
										 |  |  |       pt1 = &gps->points[before]; | 
					
						
							|  |  |  |       pt2 = &gps->points[after]; | 
					
						
							| 
									
										
										
										
											2018-07-31 10:22:19 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-04-17 06:17:24 +02:00
										 |  |  |       /* add both these points to the average-sum (s += p[i]/n) */ | 
					
						
							|  |  |  |       madd_v3_v3fl(sco, &pt1->x, average_fac); | 
					
						
							|  |  |  |       madd_v3_v3fl(sco, &pt2->x, average_fac); | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |   } | 
					
						
							| 
									
										
										
										
											2018-07-31 10:22:19 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-04-17 06:17:24 +02:00
										 |  |  |   /* Based on influence factor, blend between original and optimal smoothed coordinate */ | 
					
						
							|  |  |  |   interp_v3_v3v3(&pt->x, &pt->x, sco, inf); | 
					
						
							| 
									
										
										
										
											2016-08-03 23:31:48 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-04-17 06:17:24 +02:00
										 |  |  |   return true; | 
					
						
							| 
									
										
										
										
											2016-08-03 23:31:48 +02:00
										 |  |  | } | 
					
						
							| 
									
										
										
										
											2018-07-31 10:22:19 +02:00
										 |  |  | 
 | 
					
						
							|  |  |  | /**
 | 
					
						
							|  |  |  |  * Apply smooth for strength to stroke point */ | 
					
						
							|  |  |  | bool BKE_gpencil_smooth_stroke_strength(bGPDstroke *gps, int point_index, float influence) | 
					
						
							| 
									
										
										
										
											2016-08-03 23:31:48 +02:00
										 |  |  | { | 
					
						
							| 
									
										
										
										
											2019-04-17 06:17:24 +02:00
										 |  |  |   bGPDspoint *ptb = &gps->points[point_index]; | 
					
						
							| 
									
										
										
										
											2018-07-31 10:22:19 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-04-17 06:17:24 +02:00
										 |  |  |   /* Do nothing if not enough points */ | 
					
						
							| 
									
										
										
										
											2019-08-15 17:49:55 +02:00
										 |  |  |   if ((gps->totpoints <= 2) || (point_index < 1)) { | 
					
						
							| 
									
										
										
										
											2019-04-17 06:17:24 +02:00
										 |  |  |     return false; | 
					
						
							|  |  |  |   } | 
					
						
							| 
									
										
										
										
											2019-08-15 17:49:55 +02:00
										 |  |  |   /* Only affect endpoints by a fraction of the normal influence */ | 
					
						
							|  |  |  |   float inf = influence; | 
					
						
							|  |  |  |   if ((point_index == 0) || (point_index == gps->totpoints - 1)) { | 
					
						
							|  |  |  |     inf *= 0.01f; | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  |   /* Limit max influence to reduce pop effect. */ | 
					
						
							|  |  |  |   CLAMP_MAX(inf, 0.98f); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   float total = 0.0f; | 
					
						
							|  |  |  |   float max_strength = 0.0f; | 
					
						
							|  |  |  |   const int steps = 4; | 
					
						
							|  |  |  |   const float average_fac = 1.0f / (float)(steps * 2 + 1); | 
					
						
							|  |  |  |   int step; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   /* add the point itself */ | 
					
						
							|  |  |  |   total += ptb->strength * average_fac; | 
					
						
							|  |  |  |   max_strength = ptb->strength; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   /* n-steps before/after current point */ | 
					
						
							|  |  |  |   for (step = 1; step <= steps; step++) { | 
					
						
							|  |  |  |     bGPDspoint *pt1, *pt2; | 
					
						
							|  |  |  |     int before = point_index - step; | 
					
						
							|  |  |  |     int after = point_index + step; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     CLAMP_MIN(before, 0); | 
					
						
							|  |  |  |     CLAMP_MAX(after, gps->totpoints - 1); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     pt1 = &gps->points[before]; | 
					
						
							|  |  |  |     pt2 = &gps->points[after]; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     /* add both these points to the average-sum (s += p[i]/n) */ | 
					
						
							|  |  |  |     total += pt1->strength * average_fac; | 
					
						
							|  |  |  |     total += pt2->strength * average_fac; | 
					
						
							|  |  |  |     /* Save max value. */ | 
					
						
							|  |  |  |     if (max_strength < pt1->strength) { | 
					
						
							|  |  |  |       max_strength = pt1->strength; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |     if (max_strength < pt2->strength) { | 
					
						
							|  |  |  |       max_strength = pt2->strength; | 
					
						
							|  |  |  |     } | 
					
						
							| 
									
										
										
										
											2019-04-17 06:17:24 +02:00
										 |  |  |   } | 
					
						
							| 
									
										
										
										
											2018-07-31 10:22:19 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-08-15 17:49:55 +02:00
										 |  |  |   /* Based on influence factor, blend between original and optimal smoothed value. */ | 
					
						
							|  |  |  |   ptb->strength = interpf(ptb->strength, total, inf); | 
					
						
							|  |  |  |   /* Clamp to maximum stroke strength to avoid weird results. */ | 
					
						
							|  |  |  |   CLAMP_MAX(ptb->strength, max_strength); | 
					
						
							| 
									
										
										
										
											2018-07-31 10:22:19 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-04-17 06:17:24 +02:00
										 |  |  |   return true; | 
					
						
							| 
									
										
										
										
											2016-08-03 23:31:48 +02:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-07-31 10:22:19 +02:00
										 |  |  | /**
 | 
					
						
							|  |  |  |  * Apply smooth for thickness to stroke point (use pressure) */ | 
					
						
							|  |  |  | bool BKE_gpencil_smooth_stroke_thickness(bGPDstroke *gps, int point_index, float influence) | 
					
						
							| 
									
										
										
										
											2016-08-03 23:31:48 +02:00
										 |  |  | { | 
					
						
							| 
									
										
										
										
											2019-04-17 06:17:24 +02:00
										 |  |  |   bGPDspoint *ptb = &gps->points[point_index]; | 
					
						
							| 
									
										
										
										
											2018-06-17 17:05:51 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-04-17 06:17:24 +02:00
										 |  |  |   /* Do nothing if not enough points */ | 
					
						
							|  |  |  |   if ((gps->totpoints <= 2) || (point_index < 1)) { | 
					
						
							|  |  |  |     return false; | 
					
						
							|  |  |  |   } | 
					
						
							| 
									
										
										
										
											2019-08-15 17:49:55 +02:00
										 |  |  |   /* Only affect endpoints by a fraction of the normal influence */ | 
					
						
							|  |  |  |   float inf = influence; | 
					
						
							|  |  |  |   if ((point_index == 0) || (point_index == gps->totpoints - 1)) { | 
					
						
							|  |  |  |     inf *= 0.01f; | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  |   /* Limit max influence to reduce pop effect. */ | 
					
						
							|  |  |  |   CLAMP_MAX(inf, 0.98f); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   float total = 0.0f; | 
					
						
							|  |  |  |   float max_pressure = 0.0f; | 
					
						
							|  |  |  |   const int steps = 4; | 
					
						
							|  |  |  |   const float average_fac = 1.0f / (float)(steps * 2 + 1); | 
					
						
							|  |  |  |   int step; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   /* add the point itself */ | 
					
						
							|  |  |  |   total += ptb->pressure * average_fac; | 
					
						
							|  |  |  |   max_pressure = ptb->pressure; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   /* n-steps before/after current point */ | 
					
						
							|  |  |  |   for (step = 1; step <= steps; step++) { | 
					
						
							|  |  |  |     bGPDspoint *pt1, *pt2; | 
					
						
							|  |  |  |     int before = point_index - step; | 
					
						
							|  |  |  |     int after = point_index + step; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     CLAMP_MIN(before, 0); | 
					
						
							|  |  |  |     CLAMP_MAX(after, gps->totpoints - 1); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     pt1 = &gps->points[before]; | 
					
						
							|  |  |  |     pt2 = &gps->points[after]; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     /* add both these points to the average-sum (s += p[i]/n) */ | 
					
						
							|  |  |  |     total += pt1->pressure * average_fac; | 
					
						
							|  |  |  |     total += pt2->pressure * average_fac; | 
					
						
							|  |  |  |     /* Save max value. */ | 
					
						
							|  |  |  |     if (max_pressure < pt1->pressure) { | 
					
						
							|  |  |  |       max_pressure = pt1->pressure; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |     if (max_pressure < pt2->pressure) { | 
					
						
							|  |  |  |       max_pressure = pt2->pressure; | 
					
						
							|  |  |  |     } | 
					
						
							| 
									
										
										
										
											2019-04-17 06:17:24 +02:00
										 |  |  |   } | 
					
						
							| 
									
										
										
										
											2018-07-31 10:22:19 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-08-15 17:49:55 +02:00
										 |  |  |   /* Based on influence factor, blend between original and optimal smoothed value. */ | 
					
						
							|  |  |  |   ptb->pressure = interpf(ptb->pressure, total, inf); | 
					
						
							|  |  |  |   /* Clamp to maximum stroke thickness to avoid weird results. */ | 
					
						
							|  |  |  |   CLAMP_MAX(ptb->pressure, max_pressure); | 
					
						
							| 
									
										
										
										
											2019-04-17 06:17:24 +02:00
										 |  |  |   return true; | 
					
						
							| 
									
										
										
										
											2018-07-31 10:22:19 +02:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | /**
 | 
					
						
							| 
									
										
										
										
											2019-02-11 10:51:25 +11:00
										 |  |  |  * Apply smooth for UV rotation to stroke point (use pressure). | 
					
						
							|  |  |  |  */ | 
					
						
							| 
									
										
										
										
											2018-07-31 10:22:19 +02:00
										 |  |  | bool BKE_gpencil_smooth_stroke_uv(bGPDstroke *gps, int point_index, float influence) | 
					
						
							|  |  |  | { | 
					
						
							| 
									
										
										
										
											2019-04-17 06:17:24 +02:00
										 |  |  |   bGPDspoint *ptb = &gps->points[point_index]; | 
					
						
							| 
									
										
										
										
											2018-07-31 10:22:19 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-04-17 06:17:24 +02:00
										 |  |  |   /* Do nothing if not enough points */ | 
					
						
							|  |  |  |   if (gps->totpoints <= 2) { | 
					
						
							|  |  |  |     return false; | 
					
						
							|  |  |  |   } | 
					
						
							| 
									
										
										
										
											2018-07-31 10:22:19 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-04-17 06:17:24 +02:00
										 |  |  |   /* Compute theoretical optimal value */ | 
					
						
							|  |  |  |   bGPDspoint *pta, *ptc; | 
					
						
							|  |  |  |   int before = point_index - 1; | 
					
						
							|  |  |  |   int after = point_index + 1; | 
					
						
							| 
									
										
										
										
											2018-07-31 10:22:19 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-04-17 06:17:24 +02:00
										 |  |  |   CLAMP_MIN(before, 0); | 
					
						
							|  |  |  |   CLAMP_MAX(after, gps->totpoints - 1); | 
					
						
							| 
									
										
										
										
											2018-07-31 10:22:19 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-04-17 06:17:24 +02:00
										 |  |  |   pta = &gps->points[before]; | 
					
						
							|  |  |  |   ptc = &gps->points[after]; | 
					
						
							| 
									
										
										
										
											2018-07-31 10:22:19 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-04-17 06:17:24 +02:00
										 |  |  |   /* the optimal value is the corresponding to the interpolation of the pressure
 | 
					
						
							|  |  |  |    * at the distance of point b | 
					
						
							|  |  |  |    */ | 
					
						
							|  |  |  |   float fac = line_point_factor_v3(&ptb->x, &pta->x, &ptc->x); | 
					
						
							|  |  |  |   /* sometimes the factor can be wrong due stroke geometry, so use middle point */ | 
					
						
							|  |  |  |   if ((fac < 0.0f) || (fac > 1.0f)) { | 
					
						
							|  |  |  |     fac = 0.5f; | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  |   float optimal = interpf(ptc->uv_rot, pta->uv_rot, fac); | 
					
						
							| 
									
										
										
										
											2018-07-31 10:22:19 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-04-17 06:17:24 +02:00
										 |  |  |   /* Based on influence factor, blend between original and optimal */ | 
					
						
							|  |  |  |   ptb->uv_rot = interpf(optimal, ptb->uv_rot, influence); | 
					
						
							|  |  |  |   CLAMP(ptb->uv_rot, -M_PI_2, M_PI_2); | 
					
						
							| 
									
										
										
										
											2018-07-31 10:22:19 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-04-17 06:17:24 +02:00
										 |  |  |   return true; | 
					
						
							| 
									
										
										
										
											2018-07-31 10:22:19 +02:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | /**
 | 
					
						
							|  |  |  |  * Get range of selected frames in layer. | 
					
						
							|  |  |  |  * Always the active frame is considered as selected, so if no more selected the range | 
					
						
							|  |  |  |  * will be equal to the current active frame. | 
					
						
							| 
									
										
										
										
											2018-12-12 12:55:20 +11:00
										 |  |  |  * \param gpl: Layer | 
					
						
							|  |  |  |  * \param r_initframe: Number of first selected frame | 
					
						
							|  |  |  |  * \param r_endframe: Number of last selected frame | 
					
						
							| 
									
										
										
										
											2018-07-31 10:22:19 +02:00
										 |  |  |  */ | 
					
						
							|  |  |  | void BKE_gpencil_get_range_selected(bGPDlayer *gpl, int *r_initframe, int *r_endframe) | 
					
						
							|  |  |  | { | 
					
						
							| 
									
										
										
										
											2019-04-17 06:17:24 +02:00
										 |  |  |   *r_initframe = gpl->actframe->framenum; | 
					
						
							|  |  |  |   *r_endframe = gpl->actframe->framenum; | 
					
						
							| 
									
										
										
										
											2018-07-31 10:22:19 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-04-17 06:17:24 +02:00
										 |  |  |   for (bGPDframe *gpf = gpl->frames.first; gpf; gpf = gpf->next) { | 
					
						
							|  |  |  |     if (gpf->flag & GP_FRAME_SELECT) { | 
					
						
							|  |  |  |       if (gpf->framenum < *r_initframe) { | 
					
						
							|  |  |  |         *r_initframe = gpf->framenum; | 
					
						
							|  |  |  |       } | 
					
						
							|  |  |  |       if (gpf->framenum > *r_endframe) { | 
					
						
							|  |  |  |         *r_endframe = gpf->framenum; | 
					
						
							|  |  |  |       } | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |   } | 
					
						
							| 
									
										
										
										
											2018-07-31 10:22:19 +02:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | /**
 | 
					
						
							|  |  |  |  * Get Falloff factor base on frame range | 
					
						
							| 
									
										
										
										
											2018-12-12 12:55:20 +11:00
										 |  |  |  * \param gpf: Frame | 
					
						
							|  |  |  |  * \param actnum: Number of active frame in layer | 
					
						
							|  |  |  |  * \param f_init: Number of first selected frame | 
					
						
							|  |  |  |  * \param f_end: Number of last selected frame | 
					
						
							|  |  |  |  * \param cur_falloff: Curve with falloff factors | 
					
						
							| 
									
										
										
										
											2018-07-31 10:22:19 +02:00
										 |  |  |  */ | 
					
						
							| 
									
										
										
										
											2019-04-17 06:17:24 +02:00
										 |  |  | float BKE_gpencil_multiframe_falloff_calc( | 
					
						
							|  |  |  |     bGPDframe *gpf, int actnum, int f_init, int f_end, CurveMapping *cur_falloff) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |   float fnum = 0.5f; /* default mid curve */ | 
					
						
							|  |  |  |   float value; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   /* check curve is available */ | 
					
						
							|  |  |  |   if (cur_falloff == NULL) { | 
					
						
							|  |  |  |     return 1.0f; | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   /* frames to the right of the active frame */ | 
					
						
							|  |  |  |   if (gpf->framenum < actnum) { | 
					
						
							|  |  |  |     fnum = (float)(gpf->framenum - f_init) / (actnum - f_init); | 
					
						
							|  |  |  |     fnum *= 0.5f; | 
					
						
							| 
									
										
										
										
											2019-08-07 03:21:55 +10:00
										 |  |  |     value = BKE_curvemapping_evaluateF(cur_falloff, 0, fnum); | 
					
						
							| 
									
										
										
										
											2019-04-17 06:17:24 +02:00
										 |  |  |   } | 
					
						
							|  |  |  |   /* frames to the left of the active frame */ | 
					
						
							|  |  |  |   else if (gpf->framenum > actnum) { | 
					
						
							|  |  |  |     fnum = (float)(gpf->framenum - actnum) / (f_end - actnum); | 
					
						
							|  |  |  |     fnum *= 0.5f; | 
					
						
							| 
									
										
										
										
											2019-08-07 03:21:55 +10:00
										 |  |  |     value = BKE_curvemapping_evaluateF(cur_falloff, 0, fnum + 0.5f); | 
					
						
							| 
									
										
										
										
											2019-04-17 06:17:24 +02:00
										 |  |  |   } | 
					
						
							|  |  |  |   else { | 
					
						
							|  |  |  |     value = 1.0f; | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   return value; | 
					
						
							| 
									
										
										
										
											2016-08-03 23:31:48 +02:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-06-12 15:51:51 +02:00
										 |  |  | /* reassign strokes using a material */ | 
					
						
							|  |  |  | void BKE_gpencil_material_index_reassign(bGPdata *gpd, int totcol, int index) | 
					
						
							| 
									
										
										
										
											2016-08-03 23:31:48 +02:00
										 |  |  | { | 
					
						
							| 
									
										
										
										
											2019-04-17 06:17:24 +02:00
										 |  |  |   for (bGPDlayer *gpl = gpd->layers.first; gpl; gpl = gpl->next) { | 
					
						
							|  |  |  |     for (bGPDframe *gpf = gpl->frames.first; gpf; gpf = gpf->next) { | 
					
						
							| 
									
										
										
										
											2019-06-12 15:51:51 +02:00
										 |  |  |       for (bGPDstroke *gps = gpf->strokes.first; gps; gps = gps->next) { | 
					
						
							|  |  |  |         /* reassign strokes */ | 
					
						
							|  |  |  |         if ((gps->mat_nr > index) || (gps->mat_nr > totcol - 1)) { | 
					
						
							|  |  |  |           gps->mat_nr--; | 
					
						
							| 
									
										
										
										
											2019-08-30 14:19:07 +02:00
										 |  |  |           CLAMP_MIN(gps->mat_nr, 0); | 
					
						
							| 
									
										
										
										
											2019-04-17 06:17:24 +02:00
										 |  |  |         } | 
					
						
							|  |  |  |       } | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-07-31 12:04:52 -07:00
										 |  |  | /* remove strokes using a material */ | 
					
						
							|  |  |  | bool BKE_gpencil_material_index_used(bGPdata *gpd, int index) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |   for (bGPDlayer *gpl = gpd->layers.first; gpl; gpl = gpl->next) { | 
					
						
							|  |  |  |     for (bGPDframe *gpf = gpl->frames.first; gpf; gpf = gpf->next) { | 
					
						
							|  |  |  |       for (bGPDstroke *gps = gpf->strokes.first; gps; gps = gps->next) { | 
					
						
							|  |  |  |         if (gps->mat_nr == index) { | 
					
						
							|  |  |  |           return true; | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |       } | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   return false; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-04-17 06:17:24 +02:00
										 |  |  | void BKE_gpencil_material_remap(struct bGPdata *gpd, | 
					
						
							|  |  |  |                                 const unsigned int *remap, | 
					
						
							|  |  |  |                                 unsigned int remap_len) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |   const short remap_len_short = (short)remap_len; | 
					
						
							| 
									
										
										
										
											2018-07-31 10:22:19 +02:00
										 |  |  | 
 | 
					
						
							|  |  |  | #define MAT_NR_REMAP(n) \
 | 
					
						
							| 
									
										
										
										
											2019-04-17 06:17:24 +02:00
										 |  |  |   if (n < remap_len_short) { \ | 
					
						
							|  |  |  |     BLI_assert(n >= 0 && remap[n] < remap_len_short); \ | 
					
						
							|  |  |  |     n = remap[n]; \ | 
					
						
							|  |  |  |   } \ | 
					
						
							|  |  |  |   ((void)0) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   for (bGPDlayer *gpl = gpd->layers.first; gpl; gpl = gpl->next) { | 
					
						
							|  |  |  |     for (bGPDframe *gpf = gpl->frames.first; gpf; gpf = gpf->next) { | 
					
						
							|  |  |  |       for (bGPDstroke *gps = gpf->strokes.first; gps; gps = gps->next) { | 
					
						
							|  |  |  |         /* reassign strokes */ | 
					
						
							|  |  |  |         MAT_NR_REMAP(gps->mat_nr); | 
					
						
							|  |  |  |       } | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |   } | 
					
						
							| 
									
										
										
										
											2016-08-03 23:31:48 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-07-31 10:22:19 +02:00
										 |  |  | #undef MAT_NR_REMAP
 | 
					
						
							| 
									
										
										
										
											2016-08-03 23:31:48 +02:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-07-31 10:22:19 +02:00
										 |  |  | /* statistics functions */ | 
					
						
							|  |  |  | void BKE_gpencil_stats_update(bGPdata *gpd) | 
					
						
							| 
									
										
										
										
											2016-08-03 23:31:48 +02:00
										 |  |  | { | 
					
						
							| 
									
										
										
										
											2019-04-17 06:17:24 +02:00
										 |  |  |   gpd->totlayer = 0; | 
					
						
							|  |  |  |   gpd->totframe = 0; | 
					
						
							|  |  |  |   gpd->totstroke = 0; | 
					
						
							|  |  |  |   gpd->totpoint = 0; | 
					
						
							| 
									
										
										
										
											2016-08-03 23:31:48 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-04-17 06:17:24 +02:00
										 |  |  |   for (bGPDlayer *gpl = gpd->layers.first; gpl; gpl = gpl->next) { | 
					
						
							|  |  |  |     gpd->totlayer++; | 
					
						
							|  |  |  |     for (bGPDframe *gpf = gpl->frames.first; gpf; gpf = gpf->next) { | 
					
						
							|  |  |  |       gpd->totframe++; | 
					
						
							|  |  |  |       for (bGPDstroke *gps = gpf->strokes.first; gps; gps = gps->next) { | 
					
						
							|  |  |  |         gpd->totstroke++; | 
					
						
							|  |  |  |         gpd->totpoint += gps->totpoints; | 
					
						
							|  |  |  |       } | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |   } | 
					
						
							| 
									
										
										
										
											2016-08-03 23:31:48 +02:00
										 |  |  | } | 
					
						
							| 
									
										
										
										
											2018-08-28 08:16:30 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-03-25 17:02:42 +01:00
										 |  |  | /* get material index (0-based like mat_nr not actcol) */ | 
					
						
							| 
									
										
										
										
											2019-04-09 09:22:46 +02:00
										 |  |  | int BKE_gpencil_object_material_get_index(Object *ob, Material *ma) | 
					
						
							| 
									
										
										
										
											2018-08-28 08:16:30 +02:00
										 |  |  | { | 
					
						
							| 
									
										
										
										
											2019-04-17 06:17:24 +02:00
										 |  |  |   short *totcol = give_totcolp(ob); | 
					
						
							|  |  |  |   Material *read_ma = NULL; | 
					
						
							|  |  |  |   for (short i = 0; i < *totcol; i++) { | 
					
						
							|  |  |  |     read_ma = give_current_material(ob, i + 1); | 
					
						
							|  |  |  |     if (ma == read_ma) { | 
					
						
							|  |  |  |       return i; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |   } | 
					
						
							| 
									
										
										
										
											2018-08-28 08:16:30 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-04-17 06:17:24 +02:00
										 |  |  |   return -1; | 
					
						
							| 
									
										
										
										
											2018-08-28 08:16:30 +02:00
										 |  |  | } | 
					
						
							| 
									
										
										
										
											2018-12-25 11:10:05 +01:00
										 |  |  | 
 | 
					
						
							|  |  |  | /* Get points of stroke always flat to view not affected by camera view or view position */ | 
					
						
							| 
									
										
										
										
											2019-04-17 06:17:24 +02:00
										 |  |  | void BKE_gpencil_stroke_2d_flat(const bGPDspoint *points, | 
					
						
							|  |  |  |                                 int totpoints, | 
					
						
							|  |  |  |                                 float (*points2d)[2], | 
					
						
							|  |  |  |                                 int *r_direction) | 
					
						
							| 
									
										
										
										
											2018-12-25 11:10:05 +01:00
										 |  |  | { | 
					
						
							| 
									
										
										
										
											2019-04-17 06:17:24 +02:00
										 |  |  |   BLI_assert(totpoints >= 2); | 
					
						
							| 
									
										
										
										
											2019-01-11 19:15:23 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-04-17 06:17:24 +02:00
										 |  |  |   const bGPDspoint *pt0 = &points[0]; | 
					
						
							|  |  |  |   const bGPDspoint *pt1 = &points[1]; | 
					
						
							|  |  |  |   const bGPDspoint *pt3 = &points[(int)(totpoints * 0.75)]; | 
					
						
							| 
									
										
										
										
											2018-12-25 11:10:05 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-04-17 06:17:24 +02:00
										 |  |  |   float locx[3]; | 
					
						
							|  |  |  |   float locy[3]; | 
					
						
							|  |  |  |   float loc3[3]; | 
					
						
							|  |  |  |   float normal[3]; | 
					
						
							| 
									
										
										
										
											2018-12-25 11:10:05 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-04-17 06:17:24 +02:00
										 |  |  |   /* local X axis (p0 -> p1) */ | 
					
						
							|  |  |  |   sub_v3_v3v3(locx, &pt1->x, &pt0->x); | 
					
						
							| 
									
										
										
										
											2018-12-25 11:10:05 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-04-17 06:17:24 +02:00
										 |  |  |   /* point vector at 3/4 */ | 
					
						
							|  |  |  |   float v3[3]; | 
					
						
							|  |  |  |   if (totpoints == 2) { | 
					
						
							|  |  |  |     mul_v3_v3fl(v3, &pt3->x, 0.001f); | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  |   else { | 
					
						
							|  |  |  |     copy_v3_v3(v3, &pt3->x); | 
					
						
							|  |  |  |   } | 
					
						
							| 
									
										
										
										
											2019-01-11 19:15:23 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-04-17 06:17:24 +02:00
										 |  |  |   sub_v3_v3v3(loc3, v3, &pt0->x); | 
					
						
							| 
									
										
										
										
											2018-12-25 11:10:05 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-04-17 06:17:24 +02:00
										 |  |  |   /* vector orthogonal to polygon plane */ | 
					
						
							|  |  |  |   cross_v3_v3v3(normal, locx, loc3); | 
					
						
							| 
									
										
										
										
											2018-12-25 11:10:05 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-04-17 06:17:24 +02:00
										 |  |  |   /* local Y axis (cross to normal/x axis) */ | 
					
						
							|  |  |  |   cross_v3_v3v3(locy, normal, locx); | 
					
						
							| 
									
										
										
										
											2018-12-25 11:10:05 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-04-17 06:17:24 +02:00
										 |  |  |   /* Normalize vectors */ | 
					
						
							|  |  |  |   normalize_v3(locx); | 
					
						
							|  |  |  |   normalize_v3(locy); | 
					
						
							| 
									
										
										
										
											2018-12-25 11:10:05 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-04-17 06:17:24 +02:00
										 |  |  |   /* Get all points in local space */ | 
					
						
							|  |  |  |   for (int i = 0; i < totpoints; i++) { | 
					
						
							|  |  |  |     const bGPDspoint *pt = &points[i]; | 
					
						
							|  |  |  |     float loc[3]; | 
					
						
							| 
									
										
										
										
											2018-12-25 11:10:05 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-04-17 06:17:24 +02:00
										 |  |  |     /* Get local space using first point as origin */ | 
					
						
							|  |  |  |     sub_v3_v3v3(loc, &pt->x, &pt0->x); | 
					
						
							| 
									
										
										
										
											2018-12-25 11:10:05 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-04-17 06:17:24 +02:00
										 |  |  |     points2d[i][0] = dot_v3v3(loc, locx); | 
					
						
							|  |  |  |     points2d[i][1] = dot_v3v3(loc, locy); | 
					
						
							|  |  |  |   } | 
					
						
							| 
									
										
										
										
											2018-12-25 11:10:05 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-04-17 06:17:24 +02:00
										 |  |  |   /* Concave (-1), Convex (1), or Autodetect (0)? */ | 
					
						
							|  |  |  |   *r_direction = (int)locy[2]; | 
					
						
							| 
									
										
										
										
											2018-12-25 11:10:05 +01:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-01-11 19:15:23 +01:00
										 |  |  | /* Get points of stroke always flat to view not affected by camera view or view position
 | 
					
						
							|  |  |  |  * using another stroke as reference | 
					
						
							|  |  |  |  */ | 
					
						
							| 
									
										
										
										
											2019-04-17 06:17:24 +02:00
										 |  |  | void BKE_gpencil_stroke_2d_flat_ref(const bGPDspoint *ref_points, | 
					
						
							|  |  |  |                                     int ref_totpoints, | 
					
						
							|  |  |  |                                     const bGPDspoint *points, | 
					
						
							|  |  |  |                                     int totpoints, | 
					
						
							|  |  |  |                                     float (*points2d)[2], | 
					
						
							|  |  |  |                                     const float scale, | 
					
						
							|  |  |  |                                     int *r_direction) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |   BLI_assert(totpoints >= 2); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   const bGPDspoint *pt0 = &ref_points[0]; | 
					
						
							|  |  |  |   const bGPDspoint *pt1 = &ref_points[1]; | 
					
						
							|  |  |  |   const bGPDspoint *pt3 = &ref_points[(int)(ref_totpoints * 0.75)]; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   float locx[3]; | 
					
						
							|  |  |  |   float locy[3]; | 
					
						
							|  |  |  |   float loc3[3]; | 
					
						
							|  |  |  |   float normal[3]; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   /* local X axis (p0 -> p1) */ | 
					
						
							|  |  |  |   sub_v3_v3v3(locx, &pt1->x, &pt0->x); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   /* point vector at 3/4 */ | 
					
						
							|  |  |  |   float v3[3]; | 
					
						
							|  |  |  |   if (totpoints == 2) { | 
					
						
							|  |  |  |     mul_v3_v3fl(v3, &pt3->x, 0.001f); | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  |   else { | 
					
						
							|  |  |  |     copy_v3_v3(v3, &pt3->x); | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   sub_v3_v3v3(loc3, v3, &pt0->x); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   /* vector orthogonal to polygon plane */ | 
					
						
							|  |  |  |   cross_v3_v3v3(normal, locx, loc3); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   /* local Y axis (cross to normal/x axis) */ | 
					
						
							|  |  |  |   cross_v3_v3v3(locy, normal, locx); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   /* Normalize vectors */ | 
					
						
							|  |  |  |   normalize_v3(locx); | 
					
						
							|  |  |  |   normalize_v3(locy); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   /* Get all points in local space */ | 
					
						
							|  |  |  |   for (int i = 0; i < totpoints; i++) { | 
					
						
							|  |  |  |     const bGPDspoint *pt = &points[i]; | 
					
						
							|  |  |  |     float loc[3]; | 
					
						
							|  |  |  |     float v1[3]; | 
					
						
							|  |  |  |     float vn[3] = {0.0f, 0.0f, 0.0f}; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     /* apply scale to extremes of the stroke to get better collision detection
 | 
					
						
							|  |  |  |      * the scale is divided to get more control in the UI parameter | 
					
						
							|  |  |  |      */ | 
					
						
							|  |  |  |     /* first point */ | 
					
						
							|  |  |  |     if (i == 0) { | 
					
						
							|  |  |  |       const bGPDspoint *pt_next = &points[i + 1]; | 
					
						
							|  |  |  |       sub_v3_v3v3(vn, &pt->x, &pt_next->x); | 
					
						
							|  |  |  |       normalize_v3(vn); | 
					
						
							|  |  |  |       mul_v3_fl(vn, scale / 10.0f); | 
					
						
							|  |  |  |       add_v3_v3v3(v1, &pt->x, vn); | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |     /* last point */ | 
					
						
							|  |  |  |     else if (i == totpoints - 1) { | 
					
						
							|  |  |  |       const bGPDspoint *pt_prev = &points[i - 1]; | 
					
						
							|  |  |  |       sub_v3_v3v3(vn, &pt->x, &pt_prev->x); | 
					
						
							|  |  |  |       normalize_v3(vn); | 
					
						
							|  |  |  |       mul_v3_fl(vn, scale / 10.0f); | 
					
						
							|  |  |  |       add_v3_v3v3(v1, &pt->x, vn); | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |     else { | 
					
						
							|  |  |  |       copy_v3_v3(v1, &pt->x); | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     /* Get local space using first point as origin (ref stroke) */ | 
					
						
							|  |  |  |     sub_v3_v3v3(loc, v1, &pt0->x); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     points2d[i][0] = dot_v3v3(loc, locx); | 
					
						
							|  |  |  |     points2d[i][1] = dot_v3v3(loc, locy); | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   /* Concave (-1), Convex (1), or Autodetect (0)? */ | 
					
						
							|  |  |  |   *r_direction = (int)locy[2]; | 
					
						
							| 
									
										
										
										
											2019-01-11 19:15:23 +01:00
										 |  |  | } | 
					
						
							| 
									
										
										
										
											2019-02-26 16:04:27 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  | /**
 | 
					
						
							|  |  |  |  * Trim stroke to the first intersection or loop | 
					
						
							|  |  |  |  * \param gps: Stroke data | 
					
						
							|  |  |  |  */ | 
					
						
							|  |  |  | bool BKE_gpencil_trim_stroke(bGPDstroke *gps) | 
					
						
							|  |  |  | { | 
					
						
							| 
									
										
										
										
											2019-04-17 06:17:24 +02:00
										 |  |  |   if (gps->totpoints < 4) { | 
					
						
							|  |  |  |     return false; | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  |   bool intersect = false; | 
					
						
							|  |  |  |   int start, end; | 
					
						
							|  |  |  |   float point[3]; | 
					
						
							|  |  |  |   /* loop segments from start until we have an intersection */ | 
					
						
							|  |  |  |   for (int i = 0; i < gps->totpoints - 2; i++) { | 
					
						
							|  |  |  |     start = i; | 
					
						
							|  |  |  |     bGPDspoint *a = &gps->points[start]; | 
					
						
							|  |  |  |     bGPDspoint *b = &gps->points[start + 1]; | 
					
						
							|  |  |  |     for (int j = start + 2; j < gps->totpoints - 1; j++) { | 
					
						
							|  |  |  |       end = j + 1; | 
					
						
							|  |  |  |       bGPDspoint *c = &gps->points[j]; | 
					
						
							|  |  |  |       bGPDspoint *d = &gps->points[end]; | 
					
						
							|  |  |  |       float pointb[3]; | 
					
						
							|  |  |  |       /* get intersection */ | 
					
						
							|  |  |  |       if (isect_line_line_v3(&a->x, &b->x, &c->x, &d->x, point, pointb)) { | 
					
						
							|  |  |  |         if (len_v3(point) > 0.0f) { | 
					
						
							|  |  |  |           float closest[3]; | 
					
						
							|  |  |  |           /* check intersection is on both lines */ | 
					
						
							|  |  |  |           float lambda = closest_to_line_v3(closest, point, &a->x, &b->x); | 
					
						
							|  |  |  |           if ((lambda <= 0.0f) || (lambda >= 1.0f)) { | 
					
						
							|  |  |  |             continue; | 
					
						
							|  |  |  |           } | 
					
						
							|  |  |  |           lambda = closest_to_line_v3(closest, point, &c->x, &d->x); | 
					
						
							|  |  |  |           if ((lambda <= 0.0f) || (lambda >= 1.0f)) { | 
					
						
							|  |  |  |             continue; | 
					
						
							|  |  |  |           } | 
					
						
							|  |  |  |           else { | 
					
						
							|  |  |  |             intersect = true; | 
					
						
							|  |  |  |             break; | 
					
						
							|  |  |  |           } | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |       } | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |     if (intersect) { | 
					
						
							|  |  |  |       break; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   /* trim unwanted points */ | 
					
						
							|  |  |  |   if (intersect) { | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     /* save points */ | 
					
						
							|  |  |  |     bGPDspoint *old_points = MEM_dupallocN(gps->points); | 
					
						
							|  |  |  |     MDeformVert *old_dvert = NULL; | 
					
						
							|  |  |  |     MDeformVert *dvert_src = NULL; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     if (gps->dvert != NULL) { | 
					
						
							|  |  |  |       old_dvert = MEM_dupallocN(gps->dvert); | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     /* resize gps */ | 
					
						
							|  |  |  |     int newtot = end - start + 1; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     gps->points = MEM_recallocN(gps->points, sizeof(*gps->points) * newtot); | 
					
						
							|  |  |  |     if (gps->dvert != NULL) { | 
					
						
							|  |  |  |       gps->dvert = MEM_recallocN(gps->dvert, sizeof(*gps->dvert) * newtot); | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     for (int i = 0; i < newtot; i++) { | 
					
						
							|  |  |  |       int idx = start + i; | 
					
						
							|  |  |  |       bGPDspoint *pt_src = &old_points[idx]; | 
					
						
							|  |  |  |       bGPDspoint *pt_new = &gps->points[i]; | 
					
						
							|  |  |  |       memcpy(pt_new, pt_src, sizeof(bGPDspoint)); | 
					
						
							|  |  |  |       if (gps->dvert != NULL) { | 
					
						
							|  |  |  |         dvert_src = &old_dvert[idx]; | 
					
						
							|  |  |  |         MDeformVert *dvert = &gps->dvert[i]; | 
					
						
							|  |  |  |         memcpy(dvert, dvert_src, sizeof(MDeformVert)); | 
					
						
							|  |  |  |         if (dvert_src->dw) { | 
					
						
							|  |  |  |           memcpy(dvert->dw, dvert_src->dw, sizeof(MDeformWeight)); | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |       } | 
					
						
							|  |  |  |       if (idx == start || idx == end) { | 
					
						
							|  |  |  |         copy_v3_v3(&pt_new->x, point); | 
					
						
							|  |  |  |       } | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     gps->flag |= GP_STROKE_RECALC_GEOMETRY; | 
					
						
							|  |  |  |     gps->tot_triangles = 0; | 
					
						
							|  |  |  |     gps->totpoints = newtot; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     MEM_SAFE_FREE(old_points); | 
					
						
							|  |  |  |     MEM_SAFE_FREE(old_dvert); | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  |   return intersect; | 
					
						
							| 
									
										
										
										
											2019-02-26 16:04:27 +00:00
										 |  |  | } | 
					
						
							| 
									
										
										
										
											2019-06-30 21:03:24 +02:00
										 |  |  | 
 | 
					
						
							|  |  |  | /**
 | 
					
						
							|  |  |  |  * Close stroke | 
					
						
							|  |  |  |  * \param gps: Stroke to close | 
					
						
							|  |  |  |  */ | 
					
						
							|  |  |  | bool BKE_gpencil_close_stroke(bGPDstroke *gps) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |   bGPDspoint *pt1 = NULL; | 
					
						
							|  |  |  |   bGPDspoint *pt2 = NULL; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   /* Only can close a stroke with 3 points or more. */ | 
					
						
							|  |  |  |   if (gps->totpoints < 3) { | 
					
						
							|  |  |  |     return false; | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   /* Calc average distance between points to get same level of sampling. */ | 
					
						
							|  |  |  |   float dist_tot = 0.0f; | 
					
						
							|  |  |  |   for (int i = 0; i < gps->totpoints - 1; i++) { | 
					
						
							|  |  |  |     pt1 = &gps->points[i]; | 
					
						
							|  |  |  |     pt2 = &gps->points[i + 1]; | 
					
						
							|  |  |  |     dist_tot += len_v3v3(&pt1->x, &pt2->x); | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  |   /* Calc the average distance. */ | 
					
						
							|  |  |  |   float dist_avg = dist_tot / (gps->totpoints - 1); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   /* Calc distance between last and first point. */ | 
					
						
							|  |  |  |   pt1 = &gps->points[gps->totpoints - 1]; | 
					
						
							|  |  |  |   pt2 = &gps->points[0]; | 
					
						
							|  |  |  |   float dist_close = len_v3v3(&pt1->x, &pt2->x); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-08-10 13:15:20 +02:00
										 |  |  |   /* if the distance to close is very small, don't need add points and just enable cyclic. */ | 
					
						
							|  |  |  |   if (dist_close <= dist_avg) { | 
					
						
							|  |  |  |     gps->flag |= GP_STROKE_CYCLIC; | 
					
						
							|  |  |  |     return true; | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-06-30 21:03:24 +02:00
										 |  |  |   /* Calc number of points required using the average distance. */ | 
					
						
							|  |  |  |   int tot_newpoints = MAX2(dist_close / dist_avg, 1); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   /* Resize stroke array. */ | 
					
						
							|  |  |  |   int old_tot = gps->totpoints; | 
					
						
							|  |  |  |   gps->totpoints += tot_newpoints; | 
					
						
							|  |  |  |   gps->points = MEM_recallocN(gps->points, sizeof(*gps->points) * gps->totpoints); | 
					
						
							|  |  |  |   if (gps->dvert != NULL) { | 
					
						
							|  |  |  |     gps->dvert = MEM_recallocN(gps->dvert, sizeof(*gps->dvert) * gps->totpoints); | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   /* Generate new points */ | 
					
						
							|  |  |  |   pt1 = &gps->points[old_tot - 1]; | 
					
						
							|  |  |  |   pt2 = &gps->points[0]; | 
					
						
							|  |  |  |   bGPDspoint *pt = &gps->points[old_tot]; | 
					
						
							|  |  |  |   for (int i = 1; i < tot_newpoints + 1; i++, pt++) { | 
					
						
							| 
									
										
										
										
											2019-08-10 13:15:20 +02:00
										 |  |  |     float step = (tot_newpoints > 1) ? ((float)i / (float)tot_newpoints) : 0.99f; | 
					
						
							| 
									
										
										
										
											2019-06-30 21:03:24 +02:00
										 |  |  |     /* Clamp last point to be near, but not on top of first point. */ | 
					
						
							| 
									
										
										
										
											2019-08-10 13:15:20 +02:00
										 |  |  |     if ((tot_newpoints > 1) && (i == tot_newpoints)) { | 
					
						
							|  |  |  |       step *= 0.99f; | 
					
						
							|  |  |  |     } | 
					
						
							| 
									
										
										
										
											2019-06-30 21:03:24 +02:00
										 |  |  | 
 | 
					
						
							|  |  |  |     /* Average point. */ | 
					
						
							|  |  |  |     interp_v3_v3v3(&pt->x, &pt1->x, &pt2->x, step); | 
					
						
							|  |  |  |     pt->pressure = interpf(pt2->pressure, pt1->pressure, step); | 
					
						
							|  |  |  |     pt->strength = interpf(pt2->strength, pt1->strength, step); | 
					
						
							|  |  |  |     pt->flag = 0; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     /* Set weights. */ | 
					
						
							|  |  |  |     if (gps->dvert != NULL) { | 
					
						
							|  |  |  |       MDeformVert *dvert1 = &gps->dvert[old_tot - 1]; | 
					
						
							|  |  |  |       MDeformWeight *dw1 = defvert_verify_index(dvert1, 0); | 
					
						
							|  |  |  |       float weight_1 = dw1 ? dw1->weight : 0.0f; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |       MDeformVert *dvert2 = &gps->dvert[0]; | 
					
						
							|  |  |  |       MDeformWeight *dw2 = defvert_verify_index(dvert2, 0); | 
					
						
							|  |  |  |       float weight_2 = dw2 ? dw2->weight : 0.0f; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |       MDeformVert *dvert_final = &gps->dvert[old_tot + i - 1]; | 
					
						
							|  |  |  |       dvert_final->totweight = 0; | 
					
						
							|  |  |  |       MDeformWeight *dw = defvert_verify_index(dvert_final, 0); | 
					
						
							|  |  |  |       if (dvert_final->dw) { | 
					
						
							|  |  |  |         dw->weight = interpf(weight_2, weight_1, step); | 
					
						
							|  |  |  |       } | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   /* Enable cyclic flag. */ | 
					
						
							|  |  |  |   gps->flag |= GP_STROKE_CYCLIC; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   return true; | 
					
						
							|  |  |  | } | 
					
						
							| 
									
										
										
										
											2019-08-02 11:01:00 +02:00
										 |  |  | /* Dissolve points in stroke */ | 
					
						
							|  |  |  | void BKE_gpencil_dissolve_points(bGPDframe *gpf, bGPDstroke *gps, const short tag) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |   bGPDspoint *pt; | 
					
						
							|  |  |  |   MDeformVert *dvert = NULL; | 
					
						
							|  |  |  |   int i; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   int tot = gps->totpoints; /* number of points in new buffer */ | 
					
						
							|  |  |  |   /* first pass: count points to remove */ | 
					
						
							|  |  |  |   /* Count how many points are selected (i.e. how many to remove) */ | 
					
						
							|  |  |  |   for (i = 0, pt = gps->points; i < gps->totpoints; i++, pt++) { | 
					
						
							|  |  |  |     if (pt->flag & tag) { | 
					
						
							|  |  |  |       /* selected point - one of the points to remove */ | 
					
						
							|  |  |  |       tot--; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   /* if no points are left, we simply delete the entire stroke */ | 
					
						
							|  |  |  |   if (tot <= 0) { | 
					
						
							|  |  |  |     /* remove the entire stroke */ | 
					
						
							|  |  |  |     if (gps->points) { | 
					
						
							|  |  |  |       MEM_freeN(gps->points); | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |     if (gps->dvert) { | 
					
						
							|  |  |  |       BKE_gpencil_free_stroke_weights(gps); | 
					
						
							|  |  |  |       MEM_freeN(gps->dvert); | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |     if (gps->triangles) { | 
					
						
							|  |  |  |       MEM_freeN(gps->triangles); | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |     BLI_freelinkN(&gpf->strokes, gps); | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  |   else { | 
					
						
							|  |  |  |     /* just copy all points to keep into a smaller buffer */ | 
					
						
							|  |  |  |     bGPDspoint *new_points = MEM_callocN(sizeof(bGPDspoint) * tot, "new gp stroke points copy"); | 
					
						
							|  |  |  |     bGPDspoint *npt = new_points; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     MDeformVert *new_dvert = NULL; | 
					
						
							|  |  |  |     MDeformVert *ndvert = NULL; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     if (gps->dvert != NULL) { | 
					
						
							|  |  |  |       new_dvert = MEM_callocN(sizeof(MDeformVert) * tot, "new gp stroke weights copy"); | 
					
						
							|  |  |  |       ndvert = new_dvert; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     (gps->dvert != NULL) ? dvert = gps->dvert : NULL; | 
					
						
							|  |  |  |     for (i = 0, pt = gps->points; i < gps->totpoints; i++, pt++) { | 
					
						
							|  |  |  |       if ((pt->flag & tag) == 0) { | 
					
						
							|  |  |  |         *npt = *pt; | 
					
						
							|  |  |  |         npt++; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         if (gps->dvert != NULL) { | 
					
						
							|  |  |  |           *ndvert = *dvert; | 
					
						
							|  |  |  |           ndvert->dw = MEM_dupallocN(dvert->dw); | 
					
						
							|  |  |  |           ndvert++; | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |       } | 
					
						
							|  |  |  |       if (gps->dvert != NULL) { | 
					
						
							|  |  |  |         dvert++; | 
					
						
							|  |  |  |       } | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     /* free the old buffer */ | 
					
						
							|  |  |  |     if (gps->points) { | 
					
						
							|  |  |  |       MEM_freeN(gps->points); | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |     if (gps->dvert) { | 
					
						
							|  |  |  |       BKE_gpencil_free_stroke_weights(gps); | 
					
						
							|  |  |  |       MEM_freeN(gps->dvert); | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     /* save the new buffer */ | 
					
						
							|  |  |  |     gps->points = new_points; | 
					
						
							|  |  |  |     gps->dvert = new_dvert; | 
					
						
							|  |  |  |     gps->totpoints = tot; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     /* triangles cache needs to be recalculated */ | 
					
						
							|  |  |  |     gps->flag |= GP_STROKE_RECALC_GEOMETRY; | 
					
						
							|  |  |  |     gps->tot_triangles = 0; | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | } | 
					
						
							| 
									
										
										
										
											2019-08-08 10:23:05 +02:00
										 |  |  | 
 | 
					
						
							|  |  |  | /* Merge by distance ------------------------------------- */ | 
					
						
							|  |  |  | /* Reduce a series of points when the distance is below a threshold.
 | 
					
						
							|  |  |  |  * Special case for first and last points (both are keeped) for other points, | 
					
						
							|  |  |  |  * the merge point always is at first point. | 
					
						
							|  |  |  |  * \param gpf: Grease Pencil frame | 
					
						
							|  |  |  |  * \param gps: Grease Pencil stroke | 
					
						
							|  |  |  |  * \param threshold: Distance between points | 
					
						
							|  |  |  |  * \param use_unselected: Set to true to analyze all stroke and not only selected points | 
					
						
							|  |  |  |  */ | 
					
						
							|  |  |  | void BKE_gpencil_merge_distance_stroke(bGPDframe *gpf, | 
					
						
							|  |  |  |                                        bGPDstroke *gps, | 
					
						
							|  |  |  |                                        const float threshold, | 
					
						
							|  |  |  |                                        const bool use_unselected) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |   bGPDspoint *pt = NULL; | 
					
						
							|  |  |  |   bGPDspoint *pt_next = NULL; | 
					
						
							|  |  |  |   float tagged = false; | 
					
						
							|  |  |  |   /* Use square distance to speed up loop */ | 
					
						
							|  |  |  |   const float th_square = threshold * threshold; | 
					
						
							|  |  |  |   /* Need to have something to merge. */ | 
					
						
							|  |  |  |   if (gps->totpoints < 2) { | 
					
						
							|  |  |  |     return; | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  |   int i = 0; | 
					
						
							|  |  |  |   int step = 1; | 
					
						
							|  |  |  |   while ((i < gps->totpoints - 1) && (i + step < gps->totpoints)) { | 
					
						
							|  |  |  |     pt = &gps->points[i]; | 
					
						
							|  |  |  |     if (pt->flag & GP_SPOINT_TAG) { | 
					
						
							|  |  |  |       i++; | 
					
						
							|  |  |  |       step = 1; | 
					
						
							|  |  |  |       continue; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |     pt_next = &gps->points[i + step]; | 
					
						
							|  |  |  |     /* Do not recalc tagged points. */ | 
					
						
							|  |  |  |     if (pt_next->flag & GP_SPOINT_TAG) { | 
					
						
							|  |  |  |       step++; | 
					
						
							|  |  |  |       continue; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |     /* Check if contiguous points are selected. */ | 
					
						
							|  |  |  |     if (!use_unselected) { | 
					
						
							|  |  |  |       if (((pt->flag & GP_SPOINT_SELECT) == 0) || ((pt_next->flag & GP_SPOINT_SELECT) == 0)) { | 
					
						
							|  |  |  |         i++; | 
					
						
							|  |  |  |         step = 1; | 
					
						
							|  |  |  |         continue; | 
					
						
							|  |  |  |       } | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |     float len_square = len_squared_v3v3(&pt->x, &pt_next->x); | 
					
						
							|  |  |  |     if (len_square <= th_square) { | 
					
						
							|  |  |  |       tagged = true; | 
					
						
							|  |  |  |       if (i != gps->totpoints - 1) { | 
					
						
							|  |  |  |         /* Tag second point for delete. */ | 
					
						
							|  |  |  |         pt_next->flag |= GP_SPOINT_TAG; | 
					
						
							|  |  |  |       } | 
					
						
							|  |  |  |       else { | 
					
						
							|  |  |  |         pt->flag |= GP_SPOINT_TAG; | 
					
						
							|  |  |  |       } | 
					
						
							|  |  |  |       /* Jump to next pair of points, keeping first point segment equals.*/ | 
					
						
							|  |  |  |       step++; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |     else { | 
					
						
							|  |  |  |       /* Analyze next point. */ | 
					
						
							|  |  |  |       i++; | 
					
						
							|  |  |  |       step = 1; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   /* Always untag extremes. */ | 
					
						
							|  |  |  |   pt = &gps->points[0]; | 
					
						
							|  |  |  |   pt->flag &= ~GP_SPOINT_TAG; | 
					
						
							|  |  |  |   pt = &gps->points[gps->totpoints - 1]; | 
					
						
							|  |  |  |   pt->flag &= ~GP_SPOINT_TAG; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   /* Dissolve tagged points */ | 
					
						
							|  |  |  |   if (tagged) { | 
					
						
							|  |  |  |     BKE_gpencil_dissolve_points(gpf, gps, GP_SPOINT_TAG); | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | } | 
					
						
							| 
									
										
										
										
											2019-08-31 17:26:48 +02:00
										 |  |  | 
 | 
					
						
							|  |  |  | /* Helper: Check materials with same color. */ | 
					
						
							| 
									
										
										
										
											2019-09-01 03:35:13 +10:00
										 |  |  | static int gpencil_check_same_material_color(Object *ob_gp, float color[4], Material **r_mat) | 
					
						
							| 
									
										
										
										
											2019-08-31 17:26:48 +02:00
										 |  |  | { | 
					
						
							|  |  |  |   Material *ma = NULL; | 
					
						
							|  |  |  |   float color_cu[4]; | 
					
						
							|  |  |  |   linearrgb_to_srgb_v3_v3(color_cu, color); | 
					
						
							|  |  |  |   float hsv1[4]; | 
					
						
							|  |  |  |   rgb_to_hsv_v(color_cu, hsv1); | 
					
						
							|  |  |  |   hsv1[3] = color[3]; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   for (int i = 1; i <= ob_gp->totcol; i++) { | 
					
						
							|  |  |  |     ma = give_current_material(ob_gp, i); | 
					
						
							|  |  |  |     MaterialGPencilStyle *gp_style = ma->gp_style; | 
					
						
							|  |  |  |     /* Check color with small tolerance (better in HSV). */ | 
					
						
							|  |  |  |     float hsv2[4]; | 
					
						
							|  |  |  |     rgb_to_hsv_v(gp_style->fill_rgba, hsv2); | 
					
						
							|  |  |  |     hsv2[3] = gp_style->fill_rgba[3]; | 
					
						
							|  |  |  |     if ((gp_style->fill_style == GP_STYLE_FILL_STYLE_SOLID) && (compare_v4v4(hsv1, hsv2, 0.01f))) { | 
					
						
							| 
									
										
										
										
											2019-09-01 03:35:13 +10:00
										 |  |  |       *r_mat = ma; | 
					
						
							| 
									
										
										
										
											2019-08-31 17:26:48 +02:00
										 |  |  |       return i - 1; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-09-01 03:35:13 +10:00
										 |  |  |   *r_mat = NULL; | 
					
						
							| 
									
										
										
										
											2019-08-31 17:26:48 +02:00
										 |  |  |   return -1; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | /* Helper: Add gpencil material using curve material as base. */ | 
					
						
							|  |  |  | static Material *gpencil_add_from_curve_material(Main *bmain, | 
					
						
							|  |  |  |                                                  Object *ob_gp, | 
					
						
							| 
									
										
										
										
											2019-09-14 08:10:50 +10:00
										 |  |  |                                                  const float cu_color[4], | 
					
						
							| 
									
										
										
										
											2019-08-31 17:26:48 +02:00
										 |  |  |                                                  const bool gpencil_lines, | 
					
						
							|  |  |  |                                                  const bool fill, | 
					
						
							|  |  |  |                                                  int *r_idx) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |   Material *mat_gp = BKE_gpencil_object_material_new( | 
					
						
							|  |  |  |       bmain, ob_gp, (fill) ? "Material" : "Unassigned", r_idx); | 
					
						
							|  |  |  |   MaterialGPencilStyle *gp_style = mat_gp->gp_style; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   /* Stroke color. */ | 
					
						
							|  |  |  |   if (gpencil_lines) { | 
					
						
							|  |  |  |     ARRAY_SET_ITEMS(gp_style->stroke_rgba, 0.0f, 0.0f, 0.0f, 1.0f); | 
					
						
							|  |  |  |     gp_style->flag |= GP_STYLE_STROKE_SHOW; | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  |   else { | 
					
						
							|  |  |  |     linearrgb_to_srgb_v4(gp_style->stroke_rgba, cu_color); | 
					
						
							|  |  |  |     gp_style->flag &= ~GP_STYLE_STROKE_SHOW; | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   /* Fill color. */ | 
					
						
							|  |  |  |   linearrgb_to_srgb_v4(gp_style->fill_rgba, cu_color); | 
					
						
							|  |  |  |   /* Fill is false if the original curve hasn't material assigned, so enable it. */ | 
					
						
							|  |  |  |   if (fill) { | 
					
						
							|  |  |  |     gp_style->flag |= GP_STYLE_FILL_SHOW; | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   /* Check at least one is enabled. */ | 
					
						
							|  |  |  |   if (((gp_style->flag & GP_STYLE_STROKE_SHOW) == 0) && | 
					
						
							|  |  |  |       ((gp_style->flag & GP_STYLE_FILL_SHOW) == 0)) { | 
					
						
							|  |  |  |     gp_style->flag |= GP_STYLE_STROKE_SHOW; | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   return mat_gp; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | /* Helper: Create new stroke section. */ | 
					
						
							|  |  |  | static void gpencil_add_new_points(bGPDstroke *gps, | 
					
						
							|  |  |  |                                    float *coord_array, | 
					
						
							|  |  |  |                                    float pressure, | 
					
						
							|  |  |  |                                    int init, | 
					
						
							|  |  |  |                                    int totpoints, | 
					
						
							| 
									
										
										
										
											2019-09-14 08:10:50 +10:00
										 |  |  |                                    const float init_co[3], | 
					
						
							| 
									
										
										
										
											2019-08-31 17:26:48 +02:00
										 |  |  |                                    bool last) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |   for (int i = 0; i < totpoints; i++) { | 
					
						
							|  |  |  |     bGPDspoint *pt = &gps->points[i + init]; | 
					
						
							|  |  |  |     copy_v3_v3(&pt->x, &coord_array[3 * i]); | 
					
						
							|  |  |  |     /* Be sure the last point is not on top of the first point of the curve or
 | 
					
						
							|  |  |  |      * the close of the stroke will produce glitches. */ | 
					
						
							|  |  |  |     if ((last) && (i > 0) && (i == totpoints - 1)) { | 
					
						
							|  |  |  |       float dist = len_v3v3(init_co, &pt->x); | 
					
						
							|  |  |  |       if (dist < 0.1f) { | 
					
						
							|  |  |  |         /* Interpolate between previous point and current to back slightly. */ | 
					
						
							|  |  |  |         bGPDspoint *pt_prev = &gps->points[i + init - 1]; | 
					
						
							|  |  |  |         interp_v3_v3v3(&pt->x, &pt_prev->x, &pt->x, 0.95f); | 
					
						
							|  |  |  |       } | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     pt->pressure = pressure; | 
					
						
							|  |  |  |     pt->strength = 1.0f; | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | /* Helper: Get the first collection that includes the object. */ | 
					
						
							|  |  |  | static Collection *gpencil_get_parent_collection(Scene *scene, Object *ob) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |   Collection *mycol = NULL; | 
					
						
							|  |  |  |   FOREACH_SCENE_COLLECTION_BEGIN (scene, collection) { | 
					
						
							|  |  |  |     for (CollectionObject *cob = collection->gobject.first; cob; cob = cob->next) { | 
					
						
							|  |  |  |       if ((mycol == NULL) && (cob->ob == ob)) { | 
					
						
							|  |  |  |         mycol = collection; | 
					
						
							|  |  |  |       } | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  |   FOREACH_SCENE_COLLECTION_END; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   return mycol; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | /* Helper: Convert one spline to grease pencil stroke. */ | 
					
						
							|  |  |  | static void gpencil_convert_spline(Main *bmain, | 
					
						
							|  |  |  |                                    Object *ob_gp, | 
					
						
							|  |  |  |                                    Object *ob_cu, | 
					
						
							|  |  |  |                                    const bool gpencil_lines, | 
					
						
							|  |  |  |                                    const bool only_stroke, | 
					
						
							|  |  |  |                                    bGPDframe *gpf, | 
					
						
							|  |  |  |                                    Nurb *nu) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |   Curve *cu = (Curve *)ob_cu->data; | 
					
						
							|  |  |  |   bool cyclic = true; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   /* Create Stroke. */ | 
					
						
							|  |  |  |   bGPDstroke *gps = MEM_callocN(sizeof(bGPDstroke), "bGPDstroke"); | 
					
						
							|  |  |  |   gps->thickness = 1.0f; | 
					
						
							|  |  |  |   gps->gradient_f = 1.0f; | 
					
						
							|  |  |  |   ARRAY_SET_ITEMS(gps->gradient_s, 1.0f, 1.0f); | 
					
						
							|  |  |  |   ARRAY_SET_ITEMS(gps->caps, GP_STROKE_CAP_ROUND, GP_STROKE_CAP_ROUND); | 
					
						
							|  |  |  |   gps->inittime = 0.0f; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   /* Enable recalculation flag by default. */ | 
					
						
							|  |  |  |   gps->flag |= GP_STROKE_RECALC_GEOMETRY; | 
					
						
							|  |  |  |   gps->flag &= ~GP_STROKE_SELECT; | 
					
						
							|  |  |  |   gps->flag |= GP_STROKE_3DSPACE; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   gps->mat_nr = 0; | 
					
						
							|  |  |  |   /* Count total points
 | 
					
						
							|  |  |  |    * The total of points must consider that last point of each segment is equal to the first | 
					
						
							|  |  |  |    * point of next segment. | 
					
						
							|  |  |  |    */ | 
					
						
							|  |  |  |   int totpoints = 0; | 
					
						
							|  |  |  |   int segments = 0; | 
					
						
							|  |  |  |   int resolu = nu->resolu + 1; | 
					
						
							|  |  |  |   segments = nu->pntsu; | 
					
						
							|  |  |  |   if (((nu->flagu & CU_NURB_CYCLIC) == 0) || (nu->pntsu == 2)) { | 
					
						
							|  |  |  |     segments--; | 
					
						
							|  |  |  |     cyclic = false; | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  |   totpoints = (resolu * segments) - (segments - 1); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   /* Initialize triangle memory to dummy data. */ | 
					
						
							|  |  |  |   gps->tot_triangles = 0; | 
					
						
							|  |  |  |   gps->triangles = NULL; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   /* Materials
 | 
					
						
							|  |  |  |    * Notice: The color of the material is the color of viewport and not the final shader color. | 
					
						
							|  |  |  |    */ | 
					
						
							|  |  |  |   Material *mat_gp = NULL; | 
					
						
							|  |  |  |   bool fill = true; | 
					
						
							|  |  |  |   /* Check if grease pencil has a material with same color.*/ | 
					
						
							|  |  |  |   float color[4]; | 
					
						
							|  |  |  |   if ((cu->mat) && (*cu->mat)) { | 
					
						
							|  |  |  |     Material *mat_cu = *cu->mat; | 
					
						
							|  |  |  |     copy_v4_v4(color, &mat_cu->r); | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  |   else { | 
					
						
							|  |  |  |     /* Gray (unassigned from SVG add-on) */ | 
					
						
							|  |  |  |     zero_v4(color); | 
					
						
							|  |  |  |     add_v3_fl(color, 0.6f); | 
					
						
							|  |  |  |     color[3] = 1.0f; | 
					
						
							|  |  |  |     fill = false; | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   /* Special case: If the color was created by the SVG add-on and the name contains '_stroke' and
 | 
					
						
							|  |  |  |    * there is only one color, the stroke must not be closed, fill to false and use for | 
					
						
							|  |  |  |    * stroke the fill color. | 
					
						
							|  |  |  |    */ | 
					
						
							|  |  |  |   bool do_stroke = false; | 
					
						
							|  |  |  |   if (ob_cu->totcol == 1) { | 
					
						
							|  |  |  |     Material *ma_stroke = give_current_material(ob_cu, 1); | 
					
						
							|  |  |  |     if ((ma_stroke) && (strstr(ma_stroke->id.name, "_stroke") != NULL)) { | 
					
						
							|  |  |  |       do_stroke = true; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-09-01 03:35:13 +10:00
										 |  |  |   int r_idx = gpencil_check_same_material_color(ob_gp, color, &mat_gp); | 
					
						
							| 
									
										
										
										
											2019-08-31 17:26:48 +02:00
										 |  |  |   if ((ob_cu->totcol > 0) && (r_idx < 0)) { | 
					
						
							|  |  |  |     Material *mat_curve = give_current_material(ob_cu, 1); | 
					
						
							|  |  |  |     mat_gp = gpencil_add_from_curve_material(bmain, ob_gp, color, gpencil_lines, fill, &r_idx); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     if ((mat_curve) && (mat_curve->gp_style != NULL)) { | 
					
						
							|  |  |  |       MaterialGPencilStyle *gp_style_cur = mat_curve->gp_style; | 
					
						
							|  |  |  |       MaterialGPencilStyle *gp_style_gp = mat_gp->gp_style; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |       copy_v4_v4(gp_style_gp->mix_rgba, gp_style_cur->mix_rgba); | 
					
						
							|  |  |  |       gp_style_gp->fill_style = gp_style_cur->fill_style; | 
					
						
							|  |  |  |       gp_style_gp->mix_factor = gp_style_cur->mix_factor; | 
					
						
							|  |  |  |       gp_style_gp->gradient_angle = gp_style_cur->gradient_angle; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     /* If object has more than 1 material, use second material for stroke color. */ | 
					
						
							|  |  |  |     if ((!only_stroke) && (ob_cu->totcol > 1) && (give_current_material(ob_cu, 2))) { | 
					
						
							|  |  |  |       mat_curve = give_current_material(ob_cu, 2); | 
					
						
							| 
									
										
										
										
											2019-09-13 16:22:02 +02:00
										 |  |  |       if (mat_curve) { | 
					
						
							|  |  |  |         linearrgb_to_srgb_v3_v3(mat_gp->gp_style->stroke_rgba, &mat_curve->r); | 
					
						
							|  |  |  |         mat_gp->gp_style->stroke_rgba[3] = mat_curve->a; | 
					
						
							|  |  |  |       } | 
					
						
							| 
									
										
										
										
											2019-08-31 17:26:48 +02:00
										 |  |  |     } | 
					
						
							|  |  |  |     else if ((only_stroke) || (do_stroke)) { | 
					
						
							|  |  |  |       /* Also use the first color if the fill is none for stroke color. */ | 
					
						
							|  |  |  |       if (ob_cu->totcol > 0) { | 
					
						
							|  |  |  |         mat_curve = give_current_material(ob_cu, 1); | 
					
						
							| 
									
										
										
										
											2019-09-13 16:22:02 +02:00
										 |  |  |         if (mat_curve) { | 
					
						
							|  |  |  |           linearrgb_to_srgb_v3_v3(mat_gp->gp_style->stroke_rgba, &mat_curve->r); | 
					
						
							|  |  |  |           mat_gp->gp_style->stroke_rgba[3] = mat_curve->a; | 
					
						
							|  |  |  |           /* Set fill and stroke depending of curve type (3D or 2D). */ | 
					
						
							|  |  |  |           if ((cu->flag & CU_3D) || ((cu->flag & (CU_FRONT | CU_BACK)) == 0)) { | 
					
						
							|  |  |  |             mat_gp->gp_style->flag |= GP_STYLE_STROKE_SHOW; | 
					
						
							|  |  |  |             mat_gp->gp_style->flag &= ~GP_STYLE_FILL_SHOW; | 
					
						
							|  |  |  |           } | 
					
						
							|  |  |  |           else { | 
					
						
							|  |  |  |             mat_gp->gp_style->flag &= ~GP_STYLE_STROKE_SHOW; | 
					
						
							|  |  |  |             mat_gp->gp_style->flag |= GP_STYLE_FILL_SHOW; | 
					
						
							|  |  |  |           } | 
					
						
							| 
									
										
										
										
											2019-09-01 22:47:23 +02:00
										 |  |  |         } | 
					
						
							| 
									
										
										
										
											2019-08-31 17:26:48 +02:00
										 |  |  |       } | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  |   CLAMP_MIN(r_idx, 0); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   /* Assign material index to stroke. */ | 
					
						
							|  |  |  |   gps->mat_nr = r_idx; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   /* Add stroke to frame.*/ | 
					
						
							|  |  |  |   BLI_addtail(&gpf->strokes, gps); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   float *coord_array = NULL; | 
					
						
							|  |  |  |   float init_co[3]; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-09-02 17:23:27 +02:00
										 |  |  |   switch (nu->type) { | 
					
						
							|  |  |  |     case CU_POLY: { | 
					
						
							|  |  |  |       /* Allocate memory for storage points. */ | 
					
						
							|  |  |  |       gps->totpoints = nu->pntsu; | 
					
						
							|  |  |  |       gps->points = MEM_callocN(sizeof(bGPDspoint) * gps->totpoints, "gp_stroke_points"); | 
					
						
							|  |  |  |       /* Increase thickness for this type. */ | 
					
						
							|  |  |  |       gps->thickness = 10.0f; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |       /* Get all curve points */ | 
					
						
							|  |  |  |       for (int s = 0; s < gps->totpoints; s++) { | 
					
						
							|  |  |  |         BPoint *bp = &nu->bp[s]; | 
					
						
							|  |  |  |         bGPDspoint *pt = &gps->points[s]; | 
					
						
							|  |  |  |         copy_v3_v3(&pt->x, bp->vec); | 
					
						
							|  |  |  |         pt->pressure = bp->radius; | 
					
						
							|  |  |  |         pt->strength = 1.0f; | 
					
						
							| 
									
										
										
										
											2019-08-31 17:26:48 +02:00
										 |  |  |       } | 
					
						
							| 
									
										
										
										
											2019-09-02 17:23:27 +02:00
										 |  |  |       break; | 
					
						
							| 
									
										
										
										
											2019-08-31 17:26:48 +02:00
										 |  |  |     } | 
					
						
							| 
									
										
										
										
											2019-09-02 17:23:27 +02:00
										 |  |  |     case CU_BEZIER: { | 
					
						
							|  |  |  |       /* Allocate memory for storage points. */ | 
					
						
							|  |  |  |       gps->totpoints = totpoints; | 
					
						
							|  |  |  |       gps->points = MEM_callocN(sizeof(bGPDspoint) * gps->totpoints, "gp_stroke_points"); | 
					
						
							| 
									
										
										
										
											2019-08-31 17:26:48 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-09-02 17:23:27 +02:00
										 |  |  |       int init = 0; | 
					
						
							|  |  |  |       resolu = nu->resolu + 1; | 
					
						
							|  |  |  |       segments = nu->pntsu; | 
					
						
							|  |  |  |       if (((nu->flagu & CU_NURB_CYCLIC) == 0) || (nu->pntsu == 2)) { | 
					
						
							|  |  |  |         segments--; | 
					
						
							| 
									
										
										
										
											2019-08-31 17:26:48 +02:00
										 |  |  |       } | 
					
						
							| 
									
										
										
										
											2019-09-02 17:23:27 +02:00
										 |  |  |       /* Get all interpolated curve points of Beziert */ | 
					
						
							|  |  |  |       for (int s = 0; s < segments; s++) { | 
					
						
							|  |  |  |         int inext = (s + 1) % nu->pntsu; | 
					
						
							|  |  |  |         BezTriple *prevbezt = &nu->bezt[s]; | 
					
						
							|  |  |  |         BezTriple *bezt = &nu->bezt[inext]; | 
					
						
							|  |  |  |         bool last = (bool)(s == segments - 1); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         coord_array = MEM_callocN((size_t)3 * resolu * sizeof(float), __func__); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         for (int j = 0; j < 3; j++) { | 
					
						
							|  |  |  |           BKE_curve_forward_diff_bezier(prevbezt->vec[1][j], | 
					
						
							|  |  |  |                                         prevbezt->vec[2][j], | 
					
						
							|  |  |  |                                         bezt->vec[0][j], | 
					
						
							|  |  |  |                                         bezt->vec[1][j], | 
					
						
							|  |  |  |                                         coord_array + j, | 
					
						
							|  |  |  |                                         resolu - 1, | 
					
						
							|  |  |  |                                         3 * sizeof(float)); | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |         /* Save first point coordinates. */ | 
					
						
							|  |  |  |         if (s == 0) { | 
					
						
							|  |  |  |           copy_v3_v3(init_co, &coord_array[0]); | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |         /* Add points to the stroke */ | 
					
						
							|  |  |  |         gpencil_add_new_points(gps, coord_array, bezt->radius, init, resolu, init_co, last); | 
					
						
							|  |  |  |         /* Free memory. */ | 
					
						
							|  |  |  |         MEM_SAFE_FREE(coord_array); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         /* As the last point of segment is the first point of next segment, back one array
 | 
					
						
							|  |  |  |          * element to avoid duplicated points on the same location. | 
					
						
							|  |  |  |          */ | 
					
						
							|  |  |  |         init += resolu - 1; | 
					
						
							| 
									
										
										
										
											2019-08-31 17:26:48 +02:00
										 |  |  |       } | 
					
						
							| 
									
										
										
										
											2019-09-02 17:23:27 +02:00
										 |  |  |       break; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |     case CU_NURBS: { | 
					
						
							|  |  |  |       if (nu->pntsv == 1) { | 
					
						
							| 
									
										
										
										
											2019-08-31 17:26:48 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-09-02 17:23:27 +02:00
										 |  |  |         int nurb_points; | 
					
						
							|  |  |  |         if (nu->flagu & CU_NURB_CYCLIC) { | 
					
						
							|  |  |  |           resolu++; | 
					
						
							|  |  |  |           nurb_points = nu->pntsu * resolu; | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |         else { | 
					
						
							|  |  |  |           nurb_points = (nu->pntsu - 1) * resolu; | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |         /* Get all curve points. */ | 
					
						
							|  |  |  |         coord_array = MEM_callocN(sizeof(float[3]) * nurb_points, __func__); | 
					
						
							|  |  |  |         BKE_nurb_makeCurve(nu, coord_array, NULL, NULL, NULL, resolu, sizeof(float[3])); | 
					
						
							| 
									
										
										
										
											2019-08-31 17:26:48 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-09-02 17:23:27 +02:00
										 |  |  |         /* Allocate memory for storage points. */ | 
					
						
							|  |  |  |         gps->totpoints = nurb_points - 1; | 
					
						
							|  |  |  |         gps->points = MEM_callocN(sizeof(bGPDspoint) * gps->totpoints, "gp_stroke_points"); | 
					
						
							| 
									
										
										
										
											2019-08-31 17:26:48 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-09-02 17:23:27 +02:00
										 |  |  |         /* Add points. */ | 
					
						
							|  |  |  |         gpencil_add_new_points(gps, coord_array, 1.0f, 0, gps->totpoints, init_co, false); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         MEM_SAFE_FREE(coord_array); | 
					
						
							|  |  |  |       } | 
					
						
							|  |  |  |       break; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |     default: { | 
					
						
							|  |  |  |       break; | 
					
						
							| 
									
										
										
										
											2019-08-31 17:26:48 +02:00
										 |  |  |     } | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  |   /* Cyclic curve, close stroke. */ | 
					
						
							|  |  |  |   if ((cyclic) && (!do_stroke)) { | 
					
						
							|  |  |  |     BKE_gpencil_close_stroke(gps); | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | /* Convert a curve object to grease pencil stroke.
 | 
					
						
							|  |  |  |  * | 
					
						
							|  |  |  |  * \param bmain: Main thread pointer | 
					
						
							|  |  |  |  * \param scene: Original scene. | 
					
						
							|  |  |  |  * \param ob_gp: Grease pencil object to add strokes. | 
					
						
							|  |  |  |  * \param ob_cu: Curve to convert. | 
					
						
							|  |  |  |  * \param gpencil_lines: Use lines for strokes. | 
					
						
							|  |  |  |  * \param use_collections: Create layers using collection names. | 
					
						
							|  |  |  |  * \param only_stroke: The material must be only stroke without fill. | 
					
						
							|  |  |  |  */ | 
					
						
							|  |  |  | void BKE_gpencil_convert_curve(Main *bmain, | 
					
						
							|  |  |  |                                Scene *scene, | 
					
						
							|  |  |  |                                Object *ob_gp, | 
					
						
							|  |  |  |                                Object *ob_cu, | 
					
						
							|  |  |  |                                const bool gpencil_lines, | 
					
						
							|  |  |  |                                const bool use_collections, | 
					
						
							|  |  |  |                                const bool only_stroke) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |   if (ELEM(NULL, ob_gp, ob_cu) || (ob_gp->type != OB_GPENCIL) || (ob_gp->data == NULL)) { | 
					
						
							|  |  |  |     return; | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   Curve *cu = (Curve *)ob_cu->data; | 
					
						
							|  |  |  |   bGPdata *gpd = (bGPdata *)ob_gp->data; | 
					
						
							|  |  |  |   bGPDlayer *gpl = NULL; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   /* If the curve is empty, cancel. */ | 
					
						
							|  |  |  |   if (cu->nurb.first == NULL) { | 
					
						
							|  |  |  |     return; | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   /* Check if there is an active layer. */ | 
					
						
							|  |  |  |   if (use_collections) { | 
					
						
							|  |  |  |     Collection *collection = gpencil_get_parent_collection(scene, ob_cu); | 
					
						
							|  |  |  |     if (collection != NULL) { | 
					
						
							|  |  |  |       gpl = BLI_findstring(&gpd->layers, collection->id.name + 2, offsetof(bGPDlayer, info)); | 
					
						
							|  |  |  |       if (gpl == NULL) { | 
					
						
							|  |  |  |         gpl = BKE_gpencil_layer_addnew(gpd, collection->id.name + 2, true); | 
					
						
							|  |  |  |       } | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   if (gpl == NULL) { | 
					
						
							|  |  |  |     gpl = BKE_gpencil_layer_getactive(gpd); | 
					
						
							|  |  |  |     if (gpl == NULL) { | 
					
						
							|  |  |  |       gpl = BKE_gpencil_layer_addnew(gpd, DATA_("GP_Layer"), true); | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   /* Check if there is an active frame and add if needed. */ | 
					
						
							|  |  |  |   bGPDframe *gpf = BKE_gpencil_layer_getframe(gpl, CFRA, GP_GETFRAME_ADD_COPY); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   /* Read all splines of the curve and create a stroke for each. */ | 
					
						
							|  |  |  |   for (Nurb *nu = cu->nurb.first; nu; nu = nu->next) { | 
					
						
							| 
									
										
										
										
											2019-09-01 03:35:13 +10:00
										 |  |  |     gpencil_convert_spline(bmain, ob_gp, ob_cu, gpencil_lines, only_stroke, gpf, nu); | 
					
						
							| 
									
										
										
										
											2019-08-31 17:26:48 +02:00
										 |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   /* Tag for recalculation */ | 
					
						
							|  |  |  |   DEG_id_tag_update(&gpd->id, ID_RECALC_GEOMETRY | ID_RECALC_COPY_ON_WRITE); | 
					
						
							|  |  |  | } |