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. @@ _______________________________________________ Bf-blender-cvs mailing list Bf-blender-cvs@blender.org https://lists.blender.org/mailman/listinfo/bf-blender-cvs