您尚未登录。

楼主 #1 2020-01-03 11:28:27

红白机
会员
注册时间: 2020-01-02
已发帖子: 133
积分: 133

cairo 2D 画图引擎生成b样条曲线 (b-spline curve)

离线

楼主 #2 2020-01-03 11:31:27

红白机
会员
注册时间: 2020-01-02
已发帖子: 133
积分: 133

Re: cairo 2D 画图引擎生成b样条曲线 (b-spline curve)

里面有 Makefile 可以直接用 make 编译,

也可以用以下命令行编译:

gcc -o bspline  bspline.c bspline_constr.c bspline_ctrl.c  `pkg-config cairo --cflags --libs` -lm

运行:

./bspline

离线

楼主 #3 2020-01-03 11:32:20

红白机
会员
注册时间: 2020-01-02
已发帖子: 133
积分: 133

Re: cairo 2D 画图引擎生成b样条曲线 (b-spline curve)

bspline.png

这是生成的b样条曲线文件。

离线

楼主 #4 2020-01-03 11:34:49

红白机
会员
注册时间: 2020-01-02
已发帖子: 133
积分: 133

Re: cairo 2D 画图引擎生成b样条曲线 (b-spline curve)

bspline.c

/*! \file bspline.c
 * This file contains the functions to calculate control points from a list of
 * points for drawing bezier curves.
 *
 * License: This is free software. Take it, use it, modify it!
 *
 *  @author Bernhard R. Fischer
 *  @version 2015/11/30
 *
 */

#include <stdio.h>
#include <cairo.h>
#include <math.h>

#include "bspline.h"


int main(int argc, char **argv)
{
   // Cairo surface and context.
   cairo_surface_t *dst;
   cairo_t *ctx;
   // Loop variable and variable for number of Points.
   int cnt;
   // Array of points.
   point_t pt[] = {{50, 400}, {500, 400}, {650, 220}, {480,90}};
   // Alternative array of points.
   //point_t pt[] = {{160, 190}, {100, 400}, {470, 600}, {300, 200}, {400, 150},
   //   {470, 250}, {575, 400}, {630, 390}, {650, 300}, {670, 210}};

  /* This parameter defines if the points shall be connected in a loop (start
    * = 0) or as open line (start = 1). */
   int start = 1;

   // Number of points in array.
   cnt = sizeof(pt) / sizeof(*pt);

//#define WITH_RAND
#ifdef WITH_RAND
   srand(time(NULL));
   for (int i = 0; i < cnt; i++)
      pt[i].x = rand() % 800, pt[i].y = rand() % 800;
#endif

   // Create and init Cairo drawing context.
   dst = cairo_image_surface_create(CAIRO_FORMAT_ARGB32, 800, 800);
   ctx = cairo_create(dst);
   COL_BG(ctx);
   cairo_paint(ctx);

   // Draw curve between the points pt.
   draw_curve(ctx, pt, cnt, start);

   // Save image and destroy Cairo context.
   cairo_destroy(ctx);
   cairo_surface_write_to_png(dst, "bspline.png");
   cairo_surface_destroy(dst);

   return 0;
}

bspline_constr.c

#ifdef CONSTRUCTION

#include <stdio.h>
#include <cairo.h>
#include <math.h>

#include "bspline.h"


static void dp(cairo_t *ctx, double x, double y)
{
   cairo_rectangle(ctx, x - R, y - R, R * 2, R * 2);
   cairo_fill(ctx);
}


static void show_angle(cairo_t *ctx, const line_t *l)
{
   char buf[20];
   double a;

   a = angle(l);
   snprintf(buf, sizeof(buf), "%.1f°", a * 180 / M_PI);
   cairo_save(ctx);
   cairo_move_to(ctx, (l->A.x + l->B.x) / 2, (l->A.y + l->B.y) / 2 - 5);
   if (a < -M_PI_2 || a > M_PI_2)
      a += M_PI;
   cairo_rotate(ctx, a);
   cairo_show_text(ctx, buf);
   cairo_restore(ctx);
}


void draw_lines(cairo_t *ctx, const point_t *pt, int cnt)
{
   char buf[20];
   int i;

   cairo_save(ctx);
   cairo_set_line_width(ctx, W_LINE);
   COL_LINE(ctx);
   cairo_new_path(ctx);
   for (i = 0; i < cnt; i++)
      cairo_line_to(ctx, pt[i].x, pt[i].y);
   cairo_stroke(ctx);

   cairo_select_font_face(ctx, "sans-serif", CAIRO_FONT_SLANT_NORMAL, CAIRO_FONT_WEIGHT_BOLD);
   cairo_set_font_size(ctx, F_SIZE);
   for (i = 0; i < cnt; i++)
   {
      snprintf(buf, sizeof(buf), "P%d", i);
      cairo_move_to(ctx, pt[i].x - 5, pt[i].y + 14);
      cairo_show_text(ctx, buf);
      if (i < cnt - 1)
         show_angle(ctx, &((line_t) {pt[i], pt[(i + 1) % cnt]}));
   }
   cairo_restore(ctx);
}


void draw_construction(cairo_t *ctx, const point_t *pt, int cnt, int start)
{
   point_t c1, c2, prev_p;
   line_t g, l;
   double dash[] = {10, 5};
   char buf[20];
   int i;

   cairo_save(ctx);
   cairo_select_font_face(ctx, "sans-serif", CAIRO_FONT_SLANT_NORMAL, CAIRO_FONT_WEIGHT_BOLD);
   cairo_set_font_size(ctx, F_SIZE);
   for (i = start; i < cnt; i++)
   {
      g.A = pt[(i + cnt - 2) % cnt];
      g.B = pt[(i + cnt - 1) % cnt];
      l.A = pt[(i + cnt + 0) % cnt];
      l.B = pt[(i + cnt + 1) % cnt];

      control_points(&g, &l, &c1, &c2);
      if (start && i == 1) c1 = g.B;
      if (start && i == cnt - 1) c2 = l.A;

      COL_C1(ctx);
      dp(ctx, c1.x, c1.y);
      snprintf(buf, sizeof(buf), "C1[%d] (%d/%d)", i, (int) c1.x, (int) c1.y);
      cairo_move_to(ctx, c1.x + 5, c1.y);
      cairo_show_text(ctx, buf);

      COL_C2(ctx);
      dp(ctx, c2.x, c2.y);
      snprintf(buf, sizeof(buf), "C2[%d] (%d/%d)", i, (int) c2.x, (int) c2.y);
      cairo_move_to(ctx, c2.x + 5, c2.y);
      cairo_show_text(ctx, buf);

      if (i > start)
      {
         cairo_save(ctx);
         COL_TLINE(ctx);
         cairo_set_line_width(ctx, 1);
         cairo_set_dash(ctx, dash, 2, 0);
         cairo_move_to(ctx, prev_p.x, prev_p.y);
         cairo_line_to(ctx, c1.x, c1.y);
         cairo_stroke(ctx);
         cairo_restore(ctx);
         }
      prev_p = c2;
   }
   cairo_restore(ctx);
}

#endif

bspline_ctrl.c

/*! \file bspline_ctrl.c
 * This file contains the functions to calculate control points from a list of
 * points for drawing bezier curves.
 *
 * License: This is free software. Take it, use it, modify it!
 *
 *  @author Bernhard R. Fischer
 *  @version 2015/11/30
 *
 */

#include <cairo.h>
#include <math.h>

#include "bspline.h"

// This factor defines the "curviness". Play with it!
#define CURVE_F 0.25
// This defines the method of using isosceles triangles. Undefine it to see the
// method of equal distances.
#define ISOSCELES_TRIANGLE


/*! This function calculates the angle of line in respect to the coordinate
 * system.
 * @param g Pointer to a line.
 * @return Returns the angle in radians.
 */
double angle(const line_t *g)
{
   return atan2(g->B.y - g->A.y, g->B.x - g->A.x);
}


/*! This function calculates the control points. It takes two lines g and l as
 * arguments but it takes three lines into account for calculation. This is
 * line g (P0/P1), line h (P1/P2), and line l (P2/P3). The control points being
 * calculated are actually those for the middle line h, this is from P1 to P2.
 * Line g is the predecessor and line l the successor of line h.
 * @param g Pointer to first line.
 * @param l Pointer to third line (the second line is connecting g and l).
 * @param p1 Pointer to memory of first control point. This will receive the
 * coordinates.
 * @param p2 Pointer to memory of second control point.
 */
void control_points(const line_t *g, const line_t *l, point_t *p1, point_t *p2)
{
   line_t h;
   double f = CURVE_F;
   double lgt, a;

   // calculate length of line (P1/P2)
   lgt = sqrt(pow(g->B.x - l->A.x, 2) + pow(g->B.y - l->A.y, 2));

#ifdef ISOSCELES_TRIANGLE
   // end point of 1st tangent
   h.B = l->A;
   // start point of tangent at same distance as end point along 'g'
   h.A.x = g->B.x - lgt * cos(angle(g));
   h.A.y = g->B.y - lgt * sin(angle(g));
#else
   h.A = g->A;
   h.B = l->A;
#endif

   // angle of tangent
   a = angle(&h);
   // 1st control point on tangent at distance 'lgt * f'
   p1->x = g->B.x + lgt * cos(a) * f;
   p1->y = g->B.y + lgt * sin(a) * f;

#ifdef ISOSCELES_TRIANGLE
   // start point of 2nd tangent
   h.A = g->B;
   // end point of tangent at same distance as start point along 'l'
   h.B.x = l->A.x + lgt * cos(angle(l));
   h.B.y = l->A.y + lgt * sin(angle(l));
#else
   h.A = g->B;
   h.B = l->B;
#endif

   // angle of tangent
   a = angle(&h);
   // 2nd control point on tangent at distance 'lgt * f'
   p2->x = l->A.x - lgt * cos(a) * f;
   p2->y = l->A.y - lgt * sin(a) * f;
}


void draw_curve(cairo_t *ctx, const point_t *pt, int cnt, int start)
{
   // Helping variables for lines.
   line_t g, l;
   // Variables for control points.
   point_t c1, c2;

#ifdef CONSTRUCTION
   // Draw direct lines between points.
   draw_lines(ctx, pt, cnt);
#endif

   // Draw bezier curve through all points.
   COL_CURVE(ctx);
   cairo_set_line_width(ctx, W_CURVE);
   cairo_move_to(ctx, pt[(start - 1 + cnt) % cnt].x, pt[(start - 1 + cnt) % cnt].y);
   for (int i = start; i < cnt; i++)
   {
      g.A = pt[(i + cnt - 2) % cnt];
      g.B = pt[(i + cnt - 1) % cnt];
      l.A = pt[(i + cnt + 0) % cnt];
      l.B = pt[(i + cnt + 1) % cnt];

      // Calculate controls points for points pt[i-1] and pt[i].
      control_points(&g, &l, &c1, &c2);

      // Handle special case if points are not connected in a loop.
      if (start && i == 1) c1 = g.B;
      if (start && i == cnt - 1) c2 = l.A;

      // Create Cairo curve path.
      cairo_curve_to(ctx, c1.x, c1.y, c2.x, c2.y, pt[i].x, pt[i].y);
   }
   // Actually draw curve.
   cairo_stroke(ctx);

#ifdef CONSTRUCTION
   // Draw construction lines.
   draw_construction(ctx, pt, cnt, start);
#endif
}


bspline.h

#ifndef BSPLINE_H
#define BSPLINE_H

#include <cairo.h>


#define COL_BG(x) cairo_set_source_rgb(x, 1, 1, 1)
#define COL_CURVE(x) cairo_set_source_rgb(x, 0, 0, 1)
#define COL_LINE(x) cairo_set_source_rgb(ctx, 0, 0, 0)
#define COL_C1(x) cairo_set_source_rgb(ctx, 1, 0.2, 0.2)
#define COL_C2(x) cairo_set_source_rgb(ctx, 0.2, 0.8, 0.2)
#define COL_TLINE(x) cairo_set_source_rgb(ctx, 1, 0.2, 0.2)
#define COL_HLINE(x) cairo_set_source_rgb(ctx, 0, 0, 0)

#define W_CURVE 5
#define W_LINE 2
#define F_SIZE 10
#define R 3

typedef struct point
{
   double x, y;
} point_t;

typedef struct line
{
   point_t A, B;
} line_t;


#ifdef CONSTRUCTION
void draw_lines(cairo_t *, const point_t *, int );
void draw_construction(cairo_t *, const point_t *, int , int );
#endif

double angle(const line_t *);
void control_points(const line_t *, const line_t *, point_t *, point_t *);
void draw_curve(cairo_t *, const point_t *, int , int );

#endif

最近编辑记录 红白机 (2020-01-03 13:54:11)

离线

楼主 #6 2020-01-03 13:41:19

红白机
会员
注册时间: 2020-01-02
已发帖子: 133
积分: 133

Re: cairo 2D 画图引擎生成b样条曲线 (b-spline curve)

达克罗德 说:

最近刚好用python控制cairo画了这个曲线,用来绘制Plot。感谢你的C code!可能会用C改写一遍

不用客气,别人写的,我只是借花献佛而已。

离线

楼主 #7 2020-01-03 13:51:56

红白机
会员
注册时间: 2020-01-02
已发帖子: 133
积分: 133

Re: cairo 2D 画图引擎生成b样条曲线 (b-spline curve)

改了几行代码, 把点都标出来 bspline.c

/*! \file bspline.c
 * This file contains the functions to calculate control points from a list of
 * points for drawing bezier curves.
 *
 * License: This is free software. Take it, use it, modify it!
 *
 *  @author Bernhard R. Fischer
 *  @version 2015/11/30
 *
 */

#include <stdio.h>
#include <cairo.h>
#include <math.h>

#include "bspline.h"

void draw_dot(cairo_t *ctx, int x, int y)
{
//int   x=50.0;
//int y=400.0;
cairo_move_to (ctx, x,y);

/* draw helping lines */
cairo_set_source_rgba (ctx, 1, 0.2, 0.2, 0.6);
cairo_set_line_width (ctx, 6.0);
cairo_arc (ctx, x, y, 10.0, 0, 2*M_PI);
cairo_fill (ctx);
}


int main(int argc, char **argv)
{
   // Cairo surface and context.
   cairo_surface_t *dst;
   cairo_t *ctx;
   // Loop variable and variable for number of Points.
   int cnt;
   // Array of points.
   point_t pt[] = {{50, 400}, {500, 400}, {650, 220}, {480,90}, {430, 100}, {400, 110}, {300, 190}, };
   // Alternative array of points.
   //point_t pt[] = {{160, 190}, {100, 400}, {470, 600}, {300, 200}, {400, 150},
   //   {470, 250}, {575, 400}, {630, 390}, {650, 300}, {670, 210}};

  /* This parameter defines if the points shall be connected in a loop (start
    * = 0) or as open line (start = 1). */
   int start = 1;

   // Number of points in array.
   cnt = sizeof(pt) / sizeof(*pt);

//#define WITH_RAND
#ifdef WITH_RAND
   srand(time(NULL));
   for (int i = 0; i < cnt; i++)
      pt[i].x = rand() % 800, pt[i].y = rand() % 800;
#endif

   // Create and init Cairo drawing context.
   dst = cairo_image_surface_create(CAIRO_FORMAT_ARGB32, 800, 800);
   ctx = cairo_create(dst);
   COL_BG(ctx);
   cairo_paint(ctx);

   for(int i=0;i<sizeof(pt)/sizeof(pt[0]);i++)
   {
        draw_dot(ctx, pt[i].x, pt[i].y);
   }

#if 0
   draw_dot(ctx, pt[0].x, pt[0].y);
   draw_dot(ctx, pt[1].x, pt[1].y);
   draw_dot(ctx, pt[2].x, pt[2].y);
   draw_dot(ctx, pt[3].x, pt[3].y);
#endif
   // Draw curve between the points pt.
   draw_curve(ctx, pt, cnt, start);

   // Save image and destroy Cairo context.
   cairo_destroy(ctx);
   cairo_surface_write_to_png(dst, "bspline.png");
   cairo_surface_destroy(dst);

   return 0;
}

bspline2.png

代码参考: https://www.cairographics.org/samples/

最近编辑记录 红白机 (2020-01-03 14:21:53)

离线

楼主 #8 2020-01-04 09:42:16

红白机
会员
注册时间: 2020-01-02
已发帖子: 133
积分: 133

离线

页脚

工信部备案:粤ICP备20025096号 Powered by FluxBB

感谢为中文互联网持续输出优质内容的各位老铁们。 QQ: 516333132, 微信(wechat): whycan_cn (哇酷网/挖坑网/填坑网) service@whycan.cn