[Bf-blender-cvs] [85898fb122d] greasepencil-object: GPencil: Add option to export SVG in constant thickness lines

Antonio Vazquez noreply at git.blender.org
Mon Jul 27 23:01:16 CEST 2020


Commit: 85898fb122d2e2c160a18a3dd9c9ac3a5af44edb
Author: Antonio Vazquez
Date:   Mon Jul 27 22:57:29 2020 +0200
Branches: greasepencil-object
https://developer.blender.org/rB85898fb122d2e2c160a18a3dd9c9ac3a5af44edb

GPencil: Add option to export SVG in constant thickness lines

===================================================================

M	source/blender/editors/io/io_gpencil.c
M	source/blender/io/gpencil/gpencil_io_exporter.h
M	source/blender/io/gpencil/intern/gpencil_io_base.cc
M	source/blender/io/gpencil/intern/gpencil_io_base.h
M	source/blender/io/gpencil/intern/gpencil_io_svg.cc
M	source/blender/io/gpencil/intern/gpencil_io_svg.h

===================================================================

diff --git a/source/blender/editors/io/io_gpencil.c b/source/blender/editors/io/io_gpencil.c
index 9783697575c..6d21f6f6161 100644
--- a/source/blender/editors/io/io_gpencil.c
+++ b/source/blender/editors/io/io_gpencil.c
@@ -148,10 +148,12 @@ static int wm_gpencil_export_exec(bContext *C, wmOperator *op)
 
   const bool only_active_frame = RNA_boolean_get(op->ptr, "only_active_frame");
   const bool use_fill = RNA_boolean_get(op->ptr, "use_fill");
+  const bool use_norm_thickness = RNA_boolean_get(op->ptr, "use_normalized_thickness");
 
   /* Set flags. */
   int flag = 0;
   SET_FLAG_FROM_TEST(flag, use_fill, GP_EXPORT_FILL);
+  SET_FLAG_FROM_TEST(flag, use_norm_thickness, GP_EXPORT_NORM_THICKNESS);
 
   struct GpencilExportParams params = {
       .C = C,
@@ -238,6 +240,7 @@ static void ui_gpencil_export_settings(uiLayout *layout, PointerRNA *imfptr)
 
   sub = uiLayoutColumn(col, true);
   uiItemR(sub, imfptr, "use_fill", 0, NULL, ICON_NONE);
+  uiItemR(sub, imfptr, "use_normalized_thickness", 0, NULL, ICON_NONE);
 }
 
 static void wm_gpencil_export_draw(bContext *C, wmOperator *op)
@@ -339,6 +342,11 @@ void WM_OT_gpencil_export(wmOperatorType *ot)
 
   RNA_def_boolean(ot->srna, "only_active_frame", true, "Active Frame", "Export only active frame");
   RNA_def_boolean(ot->srna, "use_fill", true, "Fill", "Export filled areas");
+  RNA_def_boolean(ot->srna,
+                  "use_normalized_thickness",
+                  false,
+                  "Normalize",
+                  "Export strokes with constant thickness along the stroke");
 
   /* This dummy prop is used to check whether we need to init the start and
    * end frame values to that of the scene's, otherwise they are reset at
diff --git a/source/blender/io/gpencil/gpencil_io_exporter.h b/source/blender/io/gpencil/gpencil_io_exporter.h
index eb779112fe4..1d30b4b45de 100644
--- a/source/blender/io/gpencil/gpencil_io_exporter.h
+++ b/source/blender/io/gpencil/gpencil_io_exporter.h
@@ -53,6 +53,8 @@ struct GpencilExportParams {
 typedef enum eGpencilExportParams_Flag {
   /* Export Filled strokes. */
   GP_EXPORT_FILL = (1 << 0),
+  /* Export normalized thickness. */
+  GP_EXPORT_NORM_THICKNESS = (1 << 1),
 } eGpencilExportParams_Flag;
 
 bool gpencil_io_export(const struct GpencilExportParams *params);
diff --git a/source/blender/io/gpencil/intern/gpencil_io_base.cc b/source/blender/io/gpencil/intern/gpencil_io_base.cc
index e3917e304df..773329e80af 100644
--- a/source/blender/io/gpencil/intern/gpencil_io_base.cc
+++ b/source/blender/io/gpencil/intern/gpencil_io_base.cc
@@ -25,6 +25,7 @@
 
 #include "BKE_context.h"
 #include "BKE_gpencil.h"
+#include "BKE_gpencil_geom.h"
 #include "BKE_main.h"
 
 #include "BLI_blenlib.h"
@@ -35,6 +36,7 @@
 
 #include "DNA_gpencil_types.h"
 #include "DNA_object_types.h"
+#include "DNA_screen_types.h"
 
 #ifdef WIN32
 #  include "utfconv.h"
@@ -92,6 +94,29 @@ bool GpencilExporter::gpencil_3d_point_to_screen_space(struct ARegion *region,
   return false;
 }
 
+/**
+ * Get average pressure
+ * \param gps: Pointer to stroke
+ * \retun value
+ */
+float GpencilExporter::stroke_average_pressure(struct bGPDstroke *gps)
+{
+  bGPDspoint *pt = NULL;
+
+  if (gps->totpoints == 1) {
+    pt = &gps->points[0];
+    return pt->pressure;
+  }
+
+  float tot = 0.0f;
+  for (int i = 0; i < gps->totpoints; i++) {
+    pt = &gps->points[i];
+    tot += pt->pressure;
+  }
+
+  return tot / (float)gps->totpoints;
+}
+
 /**
  * Check if the thickness of the stroke is constant
  * \param gps: Pointer to stroke
@@ -116,6 +141,35 @@ bool GpencilExporter::is_stroke_thickness_constant(struct bGPDstroke *gps)
   return true;
 }
 
+float GpencilExporter::point_radius(const struct bGPDlayer *gpl,
+                                    struct bGPDstroke *gps,
+                                    float diff_mat[4][4])
+{
+  RegionView3D *rv3d = (RegionView3D *)params.region->regiondata;
+  bGPDspoint *pt = NULL;
+  float v1[2], screen_co[2], screen_ex[2];
+
+  pt = &gps->points[0];
+  gpencil_3d_point_to_screen_space(params.region, diff_mat, &pt->x, screen_co);
+  /* Invert Y axis. */
+  screen_co[1] = params.region->winy - screen_co[1];
+
+  /* Radius. */
+  bGPDstroke *gps_perimeter = BKE_gpencil_stroke_perimeter_from_view(
+      rv3d, gpd, gpl, gps, 3, diff_mat);
+
+  pt = &gps_perimeter->points[0];
+  gpencil_3d_point_to_screen_space(params.region, diff_mat, &pt->x, screen_ex);
+  /* Invert Y axis. */
+  screen_ex[1] = params.region->winy - screen_ex[1];
+
+  sub_v2_v2v2(v1, screen_co, screen_ex);
+  float radius = len_v2(v1);
+  BKE_gpencil_free_stroke(gps_perimeter);
+
+  return radius;
+}
+
 /**
  * Convert a color to Hex value (#FFFFFF)
  * \param color: Original RGB color
diff --git a/source/blender/io/gpencil/intern/gpencil_io_base.h b/source/blender/io/gpencil/intern/gpencil_io_base.h
index 1dfce8b4f36..31a31c2080d 100644
--- a/source/blender/io/gpencil/intern/gpencil_io_base.h
+++ b/source/blender/io/gpencil/intern/gpencil_io_base.h
@@ -31,6 +31,9 @@
 struct Main;
 struct ARegion;
 
+struct bGPDlayer;
+struct bGPDstroke;
+
 namespace blender {
 namespace io {
 namespace gpencil {
@@ -48,6 +51,8 @@ class GpencilExporter {
                                         float r_co[2]);
 
   bool is_stroke_thickness_constant(struct bGPDstroke *gps);
+  float stroke_average_pressure(struct bGPDstroke *gps);
+  float point_radius(const struct bGPDlayer *gpl, struct bGPDstroke *gps, float diff_mat[4][4]);
 
   std::string rgb_to_hex(float color[3]);
   std::string to_lower_string(char *input_text);
diff --git a/source/blender/io/gpencil/intern/gpencil_io_svg.cc b/source/blender/io/gpencil/intern/gpencil_io_svg.cc
index f70c2311af3..0afa8165e39 100644
--- a/source/blender/io/gpencil/intern/gpencil_io_svg.cc
+++ b/source/blender/io/gpencil/intern/gpencil_io_svg.cc
@@ -20,6 +20,8 @@
 #include <iostream>
 #include <string>
 
+#include "MEM_guardedalloc.h"
+
 #include "BKE_context.h"
 #include "BKE_gpencil.h"
 #include "BKE_gpencil_geom.h"
@@ -232,23 +234,35 @@ void GpencilExporterSVG::export_layers(void)
         export_point(gpl_node, gpl, gps, diff_mat);
       }
       else {
+        bool is_normalized = ((params.flag & GP_EXPORT_NORM_THICKNESS) != 0);
+
         /* Fill. */
         if ((is_fill) && (params.flag & GP_EXPORT_FILL)) {
-          export_stroke_path(gpl_node, gps, diff_mat, true);
+          if (is_normalized) {
+            export_stroke_polyline(gpl_node, gpl, gps, gp_style, diff_mat, true);
+          }
+          else {
+            export_stroke_path(gpl_node, gps, diff_mat, true);
+          }
         }
 
         /* Stroke. */
         if (is_stroke) {
-          bGPDstroke *gps_perimeter = BKE_gpencil_stroke_perimeter_from_view(
-              rv3d, gpd, gpl, gps, 3, diff_mat);
+          if (is_normalized) {
+            export_stroke_polyline(gpl_node, gpl, gps, gp_style, diff_mat, false);
+          }
+          else {
+            bGPDstroke *gps_perimeter = BKE_gpencil_stroke_perimeter_from_view(
+                rv3d, gpd, gpl, gps, 3, diff_mat);
 
-          /* Reproject and sample stroke. */
-          // ED_gpencil_project_stroke_to_view(params.C, gpl, gps_perimeter);
-          BKE_gpencil_stroke_sample(gps_perimeter, 0.03f, false);
+            /* Reproject and sample stroke. */
+            // ED_gpencil_project_stroke_to_view(params.C, gpl, gps_perimeter);
+            BKE_gpencil_stroke_sample(gps_perimeter, 0.03f, false);
 
-          export_stroke_path(gpl_node, gps_perimeter, diff_mat, false);
+            export_stroke_path(gpl_node, gps_perimeter, diff_mat, false);
 
-          BKE_gpencil_free_stroke(gps_perimeter);
+            BKE_gpencil_free_stroke(gps_perimeter);
+          }
         }
       }
     }
@@ -340,6 +354,77 @@ void GpencilExporterSVG::export_stroke_path(pugi::xml_node gpl_node,
   gps_node.append_attribute("d").set_value(txt.c_str());
 }
 
+/**
+ * Export a stroke using polyline or polygon
+ * \param gpl_node: Node of the layer.
+ * \param gps: Stroke to export.
+ * \param diff_mat: Transformation matrix.
+ * \param is_fill: True if the stroke is only fill
+ */
+void GpencilExporterSVG::export_stroke_polyline(pugi::xml_node gpl_node,
+                                                struct bGPDlayer *gpl,
+                                                struct bGPDstroke *gps,
+                                                struct MaterialGPencilStyle *gp_style,
+                                                float diff_mat[4][4],
+                                                const bool is_fill)
+{
+  const bool is_thickness_const = is_stroke_thickness_constant(gps);
+  const bool cyclic = ((gps->flag & GP_STROKE_CYCLIC) != 0);
+
+  bGPDspoint *pt = &gps->points[0];
+  float avg_pressure = pt->pressure;
+  if (!is_thickness_const) {
+    avg_pressure = stroke_average_pressure(gps);
+  }
+
+  /* Get the thickness in pixels using a simple 1 point stroke. */
+  bGPDstroke *gps_temp = BKE_gpencil_stroke_duplicate(gps, false);
+  gps_temp->totpoints = 1;
+  gps_temp->points = (bGPDspoint *)MEM_callocN(sizeof(bGPDspoint), "gp_stroke_points");
+  bGPDspoint *pt_src = &gps->points[0];
+  bGPDspoint *pt_dst = &gps_temp->points[0];
+  copy_v3_v3(&pt_dst->x, &pt_src->x);
+  pt_dst->pressure = avg_pressure;
+
+  float radius = point_radius(gpl, gps_temp, diff_mat);
+
+  BKE_gpencil_free_stroke(gps_temp);
+
+  pugi::xml_node gps_node = gpl_node.append_child(is_fill || cyclic ? "polygon" : "polyline");
+
+  float col[3];
+  if (is_fill) {
+    linearrgb_to_srgb_v3_v3(col, gp_style->fill_rgba);
+    std::string stroke_hex = rgb_to_hex(col);
+    gps_node.append_attribute("fill").set_value(stroke_hex.c_str());
+    gps_node.append_attribute("stroke").set_value("none");
+  }
+  else {
+    linearrgb_to_srgb_v3_v3(col, gp_style->stroke_rgba);
+    std::string stroke_hex = rgb_to_hex(col);
+    gps_node.append_attribute("fill").set_value("none");
+    gps_node.append_attribute("stroke").set_value(stroke_hex.c_str());
+  }
+
+  float thickness = is_fill ? 1.0f : radius;
+  gps_node.append_attribute("stroke-width").set_value(thickness);
+
+  std::string txt;
+  for (int i = 0; i < gps->totpoints; i++) {
+    if (i > 0) {
+      txt.append("

@@ Diff output truncated at 10240 characters. @@



More information about the Bf-blender-cvs mailing list