[Bf-blender-cvs] [249cafebf86] greasepencil-object: GPencil: Integrate perimter functions

Antonio Vazquez noreply at git.blender.org
Sat Jul 25 20:38:41 CEST 2020


Commit: 249cafebf86056f079d1aab6029ff010e54c78bf
Author: Antonio Vazquez
Date:   Sat Jul 25 20:38:33 2020 +0200
Branches: greasepencil-object
https://developer.blender.org/rB249cafebf86056f079d1aab6029ff010e54c78bf

GPencil: Integrate perimter functions

This is a modified version of the functions included in D6637

https://developer.blender.org/D6637

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

M	source/blender/blenkernel/BKE_gpencil_geom.h
M	source/blender/blenkernel/intern/gpencil_geom.c
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

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

diff --git a/source/blender/blenkernel/BKE_gpencil_geom.h b/source/blender/blenkernel/BKE_gpencil_geom.h
index 964764d99e7..610b113c5d0 100644
--- a/source/blender/blenkernel/BKE_gpencil_geom.h
+++ b/source/blender/blenkernel/BKE_gpencil_geom.h
@@ -28,10 +28,12 @@
 extern "C" {
 #endif
 
+struct bContext;
 struct BoundBox;
 struct Depsgraph;
 struct Main;
 struct Object;
+struct RegionView3D;
 struct Scene;
 struct bGPDframe;
 struct bGPDlayer;
@@ -128,6 +130,18 @@ void BKE_gpencil_convert_mesh(struct Main *bmain,
                               const bool use_faces,
                               const bool simple_material);
 
+void BKE_gpencil_stroke_to_view_space(struct RegionView3D *rv3d,
+                                      struct bGPDstroke *gps,
+                                      float diff_mat[4][4]);
+void BKE_gpencil_stroke_from_view_space(struct RegionView3D *rv3d,
+                                        struct bGPDstroke *gps,
+                                        float diff_mat[4][4]);
+struct bGPDstroke *BKE_gpencil_stroke_perimeter_from_view(struct RegionView3D *rv3d,
+                                                          const struct bGPdata *gpd,
+                                                          const struct bGPDlayer *gpl,
+                                                          struct bGPDstroke *gps,
+                                                          int subdivisions,
+                                                          float diff_mat[4][4]);
 #ifdef __cplusplus
 }
 #endif
diff --git a/source/blender/blenkernel/intern/gpencil_geom.c b/source/blender/blenkernel/intern/gpencil_geom.c
index 426b14e3e66..5afe05ed168 100644
--- a/source/blender/blenkernel/intern/gpencil_geom.c
+++ b/source/blender/blenkernel/intern/gpencil_geom.c
@@ -44,7 +44,9 @@
 #include "DNA_mesh_types.h"
 #include "DNA_meshdata_types.h"
 #include "DNA_scene_types.h"
+#include "DNA_screen_types.h"
 
+#include "BKE_context.h"
 #include "BKE_deform.h"
 #include "BKE_gpencil.h"
 #include "BKE_gpencil_geom.h"
@@ -2594,4 +2596,517 @@ void BKE_gpencil_stroke_set_random_color(bGPDstroke *gps)
     copy_v4_v4(pt->vert_color, color);
   }
 }
+
+/**
+ * Stroke to view space
+ * Transforms a stroke to view space. This allows for manipulations in 2D but also easy conversion
+ * back to 3D.
+ * Note: also takes care of parent space transform
+ */
+void BKE_gpencil_stroke_to_view_space(RegionView3D *rv3d, bGPDstroke *gps, float diff_mat[4][4])
+{
+  float tmp[3];
+
+  for (int i = 0; i < gps->totpoints; i++) {
+    bGPDspoint *pt = &gps->points[i];
+    /* point to parent space */
+    mul_v3_m4v3(tmp, diff_mat, &pt->x);
+    /* point to view space */
+    mul_m4_v3(rv3d->viewmat, tmp);
+    copy_v3_v3(&pt->x, tmp);
+  }
+}
+
+/**
+ * Stroke from view space
+ * Transforms a stroke from view space back to world space. Inverse of
+ * BKE_gpencil_stroke_to_view_space
+ * Note: also takes care of parent space transform
+ */
+void BKE_gpencil_stroke_from_view_space(RegionView3D *rv3d, bGPDstroke *gps, float diff_mat[4][4])
+{
+  float tmp[3];
+  float inverse_diff_mat[4][4];
+  invert_m4_m4(inverse_diff_mat, diff_mat);
+
+  for (int i = 0; i < gps->totpoints; i++) {
+    bGPDspoint *pt = &gps->points[i];
+    mul_v3_m4v3(tmp, rv3d->viewinv, &pt->x);
+    mul_m4_v3(inverse_diff_mat, tmp);
+    copy_v3_v3(&pt->x, tmp);
+  }
+}
+
+/* ----------------------------------------------------------------------------- */
+/* Stroke to perimeter */
+
+typedef struct tPerimeterPoint {
+  struct tPerimeterPoint *next, *prev;
+  float x, y, z;
+  bool is_left;
+} tPerimeterPoint;
+
+static tPerimeterPoint *new_perimeter_point(const float pt[3], bool is_left)
+{
+  tPerimeterPoint *new_pt = MEM_callocN(sizeof(tPerimeterPoint), __func__);
+  copy_v3_v3(&new_pt->x, pt);
+  new_pt->is_left = is_left;
+  return new_pt;
+}
+
+static int generate_arc_from_point_to_point(ListBase *list,
+                                            tPerimeterPoint *from,
+                                            tPerimeterPoint *to,
+                                            float center_pt[3],
+                                            int subdivisions,
+                                            bool clockwise,
+                                            bool is_left)
+{
+  float vec_from[2];
+  float vec_to[2];
+  sub_v2_v2v2(vec_from, &from->x, center_pt);
+  sub_v2_v2v2(vec_to, &to->x, center_pt);
+  if (is_zero_v2(vec_from) || is_zero_v2(vec_to)) {
+    return 0;
+  }
+
+  float dot = dot_v2v2(vec_from, vec_to);
+  float det = cross_v2v2(vec_from, vec_to);
+  float angle = clockwise ? M_PI - atan2f(-det, -dot) : atan2f(-det, -dot) + M_PI;
+
+  /* number of points is 2^(n+1) + 1 on half a circle (n=subdivisions)
+   * so we multiply by (angle / pi) to get the right amount of
+   * points to insert */
+  int num_points = (int)(((1 << (subdivisions + 1)) - 1) * (angle / M_PI));
+  if (num_points > 0) {
+    float angle_incr = angle / (float)num_points;
+
+    float vec_p[3];
+    float vec_t[3];
+    float tmp_angle;
+    tPerimeterPoint *last_point;
+    if (clockwise) {
+      last_point = to;
+      copy_v3_v3(vec_t, vec_to);
+    }
+    else {
+      last_point = from;
+      copy_v3_v3(vec_t, vec_from);
+    }
+
+    for (int i = 0; i < num_points - 1; i++) {
+      tmp_angle = (i + 1) * angle_incr;
+
+      rotate_v2_v2fl(vec_p, vec_t, tmp_angle);
+      add_v2_v2(vec_p, center_pt);
+      vec_p[2] = center_pt[2];
+
+      tPerimeterPoint *new_point = new_perimeter_point(vec_p, is_left);
+      if (clockwise) {
+        BLI_insertlinkbefore(list, last_point, new_point);
+      }
+      else {
+        BLI_insertlinkafter(list, last_point, new_point);
+      }
+
+      last_point = new_point;
+    }
+
+    return num_points - 1;
+  }
+
+  return 0;
+}
+
+static int generate_semi_circle_from_point_to_point(
+    ListBase *list, tPerimeterPoint *from, tPerimeterPoint *to, int subdivisions, bool is_left)
+{
+  int num_points = (1 << (subdivisions + 1)) + 1;
+  float center_pt[3];
+  interp_v3_v3v3(center_pt, &from->x, &to->x, 0.5f);
+
+  float vec_center[2];
+  sub_v2_v2v2(vec_center, &from->x, center_pt);
+  if (is_zero_v2(vec_center)) {
+    return 0;
+  }
+
+  float vec_p[3];  // temp vector to do the vector math
+  float angle_incr = M_PI / ((float)num_points - 1);
+
+  tPerimeterPoint *last_point = from;
+  for (int i = 1; i < num_points; i++) {
+    float angle = i * angle_incr;
+
+    /* rotate vector around point to get perimeter points */
+    rotate_v2_v2fl(vec_p, vec_center, angle);
+    add_v2_v2(vec_p, center_pt);
+    vec_p[2] = center_pt[2];
+
+    tPerimeterPoint *new_point = new_perimeter_point(vec_p, is_left);
+    BLI_insertlinkafter(list, last_point, new_point);
+
+    last_point = new_point;
+  }
+
+  return num_points - 1;
+}
+
+static int generate_perimeter_cap(const float point[4],
+                                  const float other_point[4],
+                                  float radius,
+                                  ListBase *list,
+                                  int subdivisions,
+                                  short cap_type,
+                                  bool is_left)
+{
+  float cap_vec[2];
+  sub_v2_v2v2(cap_vec, other_point, point);
+  normalize_v2(cap_vec);
+
+  float cap_nvec[2];
+  if (is_zero_v2(cap_vec)) {
+    cap_nvec[0] = 0;
+    cap_nvec[1] = radius;
+  }
+  else {
+    cap_nvec[0] = -cap_vec[1];
+    cap_nvec[1] = cap_vec[0];
+    mul_v2_fl(cap_nvec, radius);
+  }
+  float cap_nvec_inv[2];
+  negate_v2_v2(cap_nvec_inv, cap_nvec);
+
+  float vec_perimeter[3];
+  copy_v3_v3(vec_perimeter, point);
+  add_v2_v2(vec_perimeter, cap_nvec);
+
+  float vec_perimeter_inv[3];
+  copy_v3_v3(vec_perimeter_inv, point);
+  add_v2_v2(vec_perimeter_inv, cap_nvec_inv);
+
+  tPerimeterPoint *p_pt = new_perimeter_point(vec_perimeter, is_left);
+  tPerimeterPoint *p_pt_inv = new_perimeter_point(vec_perimeter_inv, is_left);
+
+  BLI_addtail(list, p_pt);
+  BLI_addtail(list, p_pt_inv);
+
+  int num_points = 0;
+  if (cap_type == GP_STROKE_CAP_ROUND) {
+    num_points += generate_semi_circle_from_point_to_point(
+        list, p_pt, p_pt_inv, subdivisions, is_left);
+  }
+
+  return num_points + 2;
+}
+
+/**
+ * Calculate the perimeter (outline) of a stroke as list of tPerimeterPoint.
+ * \param subdivisions: Number of subdivions for the start and end caps
+ * \return: list of tPerimeterPoint
+ */
+static ListBase *gpencil_stroke_perimeter_ex(const bGPdata *gpd,
+                                             const bGPDlayer *gpl,
+                                             const bGPDstroke *gps,
+                                             int subdivisions,
+                                             int *r_num_perimeter_points)
+{
+  /* sanity check */
+  if (gps->totpoints < 1) {
+    return NULL;
+  }
+
+  float defaultpixsize = 1000.0f / gpd->pixfactor;
+  float stroke_radius = ((gps->thickness + gpl->line_change) / defaultpixsize) / 2.0f;
+
+  ListBase *perimeter_right_side = MEM_callocN(sizeof(ListBase), __func__);
+  ListBase *perimeter_left_side = MEM_callocN(sizeof(ListBase), __func__);
+  int num_perimeter_points = 0;
+
+  bGPDspoint *first = &gps->points[0];
+  bGPDspoint *last = &gps->points[gps->totpoints - 1];
+
+  float first_radius = stroke_radius * first->pressure;
+  float last_radius = stroke_radius * last->pressure;
+
+  bGPDspoint *first_next;
+  bGPDspoint *last_prev;
+  if (gps->totpoints > 1) {
+    first_next = &gps->points[1];
+    last_prev = &gps->points[gps->totpoints - 2];
+  }
+  else {
+    first_next = first;
+    last_prev = last;
+  }
+
+  float first_pt[3];
+  float last_pt[3];
+  float first_next_pt[3];
+  float last_prev_pt[3];
+  copy_v3_v3(first_pt, &first->x);
+  copy_v3_v3(last_pt, &last->x);
+  copy_v3_v3(first_next_pt, &first_next->x);
+  copy_v3_v3(last_prev_pt, &last_prev->x);
+
+  /* edgecase if single point */
+  if (gps->totpoints == 1) {
+    first_next_pt[0] += 1.0f;
+    last_prev_pt[0] -= 1.0f;
+  }
+
+  /* generate points for start cap */
+  num_perimeter_points += generate_perimeter_cap(first_pt,
+                                                 first_next_pt,
+           

@@ Diff output truncated at 10240 characters. @@



More information about the Bf-blender-cvs mailing list