离线
里面有 Makefile 可以直接用 make 编译,
也可以用以下命令行编译:
gcc -o bspline bspline.c bspline_constr.c bspline_ctrl.c `pkg-config cairo --cflags --libs` -lm
运行:
./bspline
离线
这是生成的b样条曲线文件。
离线
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)
离线
最近刚好用python控制cairo画了这个曲线,用来绘制Plot。感谢你的C code!可能会用C改写一遍
不用客气,别人写的,我只是借花献佛而已。
离线
改了几行代码, 把点都标出来 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;
}
代码参考: https://www.cairographics.org/samples/
最近编辑记录 红白机 (2020-01-03 14:21:53)
离线
离线