文章目录

  • 一、为什么我的cairo画出的直线不同角度宽度不同???
  • 二、再看Cairo官方API介绍
    • cairo_set_line_width ()
  • 三、修改code实现不修改CTM情况下的坐标变换

一、为什么我的cairo画出的直线不同角度宽度不同???

前一段时间研究通感一体化,需要使用Cairo绘制波形图,设置了统一的线宽,但是绘制出的图形线段的宽度却不一样,原始code如下:

/****************************************************/
/***********  gtk_line_chart_custom.c   *************//* 定义 Properties */
enum {PROP_0,PROP_AXIS_X_RANGE, // X轴的范围PROP_AXIS_Y_RANGE, // Y轴的范围PROP_ORIGINAL_X,   // 原点X的坐标PROP_ORIGINAL_Y,   // 原点Y的坐标PROP_MAX};static GParamSpec *line_chart_props[PROP_MAX]={NULL};// 客制化控件,用于绘制折线图
#define GTK_TYPE_LINE_CHART (gtk_line_chart_get_type ())
G_DECLARE_FINAL_TYPE (GtkLineChart, gtk_line_chart, GTK, LINE_CHART, GObject)struct _GtkLineChart{GObject parent_instance;
};typedef struct {/*坐标轴范围,及原点坐标*/double axis_x_range;double axis_y_range;double original_x;double original_y;
}GtkLineChartPrivate;/***************************** 实现GdkPaintable 接口***********************/
void
gtk_line_chart_snapshot (GdkPaintable *paintable,GdkSnapshot  *snapshot,double        width,double        height)
{cairo_t *cr;GtkLineChart *lineChart = GTK_LINE_CHART(paintable);GtkLineChartPrivate *priv = gtk_line_chart_get_instance_private(lineChart);gtk_snapshot_append_color (snapshot,&priv->bg_color,&GRAPHENE_RECT_INIT (0, 0, width, height));cr = gtk_snapshot_append_cairo (snapshot,&GRAPHENE_RECT_INIT (0,0,width, height));// 变换CTM将坐标轴缩放在 // X:0 ~ priv->axis_x_range// Y:0 ~ priv->axis_y_rangecairo_scale(cr,width/priv->axis_x_range,height/priv->axis_y_range);// 变换CTM ,将坐标的原点移动到(priv->original_x,priv->original_y)cairo_translate(cr,priv->original_x,priv->original_y);//设置线宽cairo_set_line_width(cr,80);// 水平绘制一条直线cairo_move_to(cr,-priv->original_x,0.);cairo_line_to(cr,priv->axis_x_range-priv->original_x,0.);cairo_stroke(cr);// 对角线绘制一条直线cairo_move_to(cr,-priv->original_x,priv->axis_y_range-priv->original_y);cairo_line_to(cr,priv->axis_x_range-priv->original_x,-priv->original_y);cairo_stroke(cr);cairo_destroy (cr);
}static GdkPaintable *
gtk_line_chart_rt_get_current_image (GdkPaintable *paintable)
{GtkLineChart *lineChart = GTK_LINE_CHART(paintable);GtkLineChartPrivate *priv = gtk_line_chart_get_instance_private(lineChart);return gtk_line_chart_new (priv->axis_x_range,priv->axis_y_range);
}static void
gtk_line_chart_paintable_init (GdkPaintableInterface *iface)
{iface->snapshot = gtk_line_chart_snapshot;iface->get_current_image = gtk_line_chart_rt_get_current_image;
}
/***************************** 实现客制化控件***********************/
G_DEFINE_TYPE_WITH_CODE (GtkLineChart , gtk_line_chart, G_TYPE_OBJECT,{G_IMPLEMENT_INTERFACE (GDK_TYPE_PAINTABLE,gtk_line_chart_paintable_init)G_ADD_PRIVATE (GtkLineChart)})static void
gtk_line_chart_set_property (GObject         *object,guint            prop_id,const GValue    *value,GParamSpec      *pspec)
{GtkLineChart *lineChart = GTK_LINE_CHART(object);GtkLineChartPrivate *priv = gtk_line_chart_get_instance_private(lineChart);switch (prop_id){case PROP_AXIS_X_RANGE:priv->axis_x_range = g_value_get_double(value);break;case PROP_AXIS_Y_RANGE:priv->axis_y_range = g_value_get_double(value);break;case PROP_ORIGINAL_X:priv->original_x = g_value_get_double(value);g_return_if_fail(priv->original_x <= priv->axis_x_range);break;case PROP_ORIGINAL_Y:priv->original_y = g_value_get_double(value);g_return_if_fail(priv->original_y <= priv->axis_y_range);break;default:G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);break;}gdk_paintable_invalidate_contents(GDK_PAINTABLE(lineChart));
}static void
gtk_line_chart_get_property (GObject         *object,guint            prop_id,GValue          *value,GParamSpec      *pspec)
{GtkLineChart *lineChart = GTK_LINE_CHART(object);GtkLineChartPrivate *priv = gtk_line_chart_get_instance_private(lineChart);switch (prop_id){case PROP_AXIS_X_RANGE:g_value_set_double (value, priv->axis_x_range);break;case PROP_AXIS_Y_RANGE:g_value_set_double (value, priv->axis_y_range);break;case PROP_ORIGINAL_X:g_value_set_double (value, priv->original_x);break;case PROP_ORIGINAL_Y:g_value_set_double (value, priv->original_y);break;default:G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);break;}
}static void
gtk_line_chart_class_init (GtkLineChartClass *klass)
{GObjectClass *gobject_class = G_OBJECT_CLASS (klass);gobject_class->finalize = gtk_line_chart_finalize;gobject_class->set_property = gtk_line_chart_set_property;gobject_class->get_property = gtk_line_chart_get_property;line_chart_props[PROP_AXIS_X_RANGE] =g_param_spec_double("x-range","Axis X range value","Axis X range value",0,G_MAXDOUBLE,1,G_PARAM_READWRITE);line_chart_props[PROP_AXIS_Y_RANGE] =g_param_spec_double("y-range","Axis Y range value","Axis Y range value",0,G_MAXDOUBLE,1,G_PARAM_READWRITE);line_chart_props[PROP_ORIGINAL_X] =g_param_spec_double("original-x","original-x","original-x",0,G_MAXDOUBLE,0,G_PARAM_READWRITE);line_chart_props[PROP_ORIGINAL_Y] =g_param_spec_double("original-y","original-y","original-y",0,G_MAXDOUBLE,0,G_PARAM_READWRITE); g_object_class_install_properties(gobject_class,PROP_MAX,line_chart_props);
}static void
gtk_line_chart_init (GtkLineChart *lineChart)
{GtkLineChartPrivate *priv = gtk_line_chart_get_instance_private(lineChart);priv->original_x = 0.;priv->original_y = 0.;
}GdkPaintable *
gtk_line_chart_new (double axis_x_range,double axis_y_range)
{GtkLineChart *lineChart;lineChart = g_object_new (GTK_TYPE_LINE_CHART,"x-range",axis_x_range,"y-range",axis_y_range,NULL);return GDK_PAINTABLE (lineChart);
}

绘制效果如下图:

大家肯定会发现设置一样宽度的直线线宽(cairo_set_line_width(cr,80);)但是为什么绘制的直线宽度却不一样???

二、再看Cairo官方API介绍

cairo_set_line_width ()

Sets the current line width within the cairo context. The line width value specifies the diameter of a pen that is circular in user space, (though device-space pen may be an ellipse in general due to scaling/shear/rotation of the CTM).

在cairo上下文中设置当前的线宽度。线宽值指定用户空间中圆形笔的直径(尽管由于CTM的缩放/剪切/旋转,设备空间笔通常可能是椭圆形)。

那问题很清晰了,主要是因为我们对于CTM的变换,导致设备空间的画笔变成了椭圆形,从而导致不同角度绘制的直线宽度不一致的问题。

三、修改code实现不修改CTM情况下的坐标变换

我们这里不使用Cairo提供的CTM变换函数,实现逻辑坐标系到物理坐标系的映射过程,例如如下图:

蓝色部分是我们Gtk应用的窗口,我们绘图的时候Cairo都会将用户空间的坐标转换成设备空间的坐标,默认情况下(不修改CTM)设备空间和用户空间的画笔都是圆形,所以我们这边自定义一个函数,实现用户自定义的坐标空间到应用窗口空间坐标的变换,函数实现如下图:

/* 用户自定义的坐标空间到应用窗口空间坐标的变换函数 */
static void
gtk_line_chart_user_to_device(GtkLineChart *lineChart,double width,double height,double user_x,double user_y,double *device_x , double *device_y)
{g_return_if_fail(device_x != NULL);g_return_if_fail(device_y != NULL);GtkLineChartPrivate *priv = gtk_line_chart_get_instance_private(lineChart);double x_step = width/priv->axis_x_range;double y_step = height/priv->axis_y_range;*device_x = (priv->original_x+user_x)*x_step;*device_y = (priv->original_y-user_y)*y_step;
}/* 应用窗口空间坐标到用户自定义的坐标空间的变换函数 */
static void
gtk_line_chart_device_to_user(GtkLineChart *lineChart,double width,double height,double device_x,double device_y,double *user_x , double *user_y)
{g_return_if_fail(user_x != NULL);g_return_if_fail(user_y != NULL);GtkLineChartPrivate *priv = gtk_line_chart_get_instance_private(lineChart);double x_step = priv->axis_x_range/width;double y_step = priv->axis_y_range/height;*user_x = (device_x*x_step)-priv->original_x;*user_y = priv->original_y-(device_y*y_step);
}

修改上述code,实现不同角度下画出的线条长度都一致,

......void
gtk_line_chart_snapshot (GdkPaintable *paintable,GdkSnapshot  *snapshot,double        width,double        height)
{cairo_t *cr;double device_x=0.;double device_y=0.;double user_x=0.;double user_y=0.;GtkLineChart *lineChart = GTK_LINE_CHART(paintable);GtkLineChartPrivate *priv = gtk_line_chart_get_instance_private(lineChart);gtk_snapshot_append_color (snapshot,&priv->bg_color,&GRAPHENE_RECT_INIT (0, 0, width, height));cr = gtk_snapshot_append_cairo (snapshot,&GRAPHENE_RECT_INIT (0,0,width, height));cairo_set_line_width(cr,20);gtk_line_chart_user_to_device(lineChart, width, height,-priv->original_x,0.,&device_x , &device_y);cairo_move_to(cr,device_x,device_y);gtk_line_chart_user_to_device(lineChart, width, height,priv->axis_x_range-priv->original_x,0.,&device_x , &device_y);cairo_line_to(cr,device_x,device_y);cairo_stroke(cr);gtk_line_chart_user_to_device(lineChart, width, height,-priv->original_x,priv->original_y-priv->axis_y_range,&device_x , &device_y);cairo_move_to(cr,device_x,device_y);gtk_line_chart_user_to_device(lineChart, width, height,priv->axis_x_range-priv->original_x,priv->original_y,&device_x , &device_y);cairo_line_to(cr,device_x,device_y);cairo_stroke(cr);cairo_destroy (cr);
}
......

效果图如下(没有对锯齿进行优化):


为什么我的cairo画出的直线不同角度宽度不同???相关推荐

  1. Linux ubuntu18.04.4下如何利用CPU利用率画出50%直线、正弦曲线、心形曲线

    Linux ubuntu18.04.4下如何利用CPU利用率画出50%直线.正弦曲线.心形曲线 记录一下,经过了各种失败以后,本菜鸡在一步步的尝试中终于成功画出了曲线.整理一下,我经历的最主要问题如下 ...

  2. Unity使用LineRenderer组件画出菱形,长度,角度可任意调节

    首先说下我的思路: 开始想的比较简单,就是先建两个空物体分别挂上脚本,一个在X轴画直线,一个与X轴成angle夹角,通过计算得出坐标,这样就能画出一个菱形的角度,下面是实例图: 最后再写个脚本通过循环 ...

  3. 【非线性光纤光学】,第四章第4题,我的解答,画出KDP晶体的角度调谐曲线

    <非线性光纤光学>第四章第4题,我的解答 作者:最甜甜甜甜的小草莓莓 题目:画出KDP晶体的角度调谐曲线 先声明,不保证就是正确答案!!!! (但是某J大佬觉得ok,小声) (1) 前辈有 ...

  4. 手绘线条一直画不直_手绘板画出的斜直线不直,总是波浪线。

    展开全部 一  要不要练习板绘线条勾线?32313133353236313431303231363533e59b9ee7ad9431333431366239可以不练这个小技术吗? 勾线是板绘的基础,学 ...

  5. 用python的turtle画圆 转角度前进_使用python实现画出一段给定角度的近似圆弧

    原博文 2017-08-27 13:24 − import turtleimport timeimport mathdef circle(bob,r,angle): n = 720 jiaoDu = ...

  6. java旋转图片并画出_java实现图片角度旋转并获得图片信息

    本文实例为大家分享了java实现图片角度旋转并获得图片信息的具体代码,供大家参考,具体内容如下 public class demo { /** * 调整图片角度 * make by dongxh 20 ...

  7. 【多媒体课件制作软件】Focusky教程 | 怎样画出水平线、垂直线?

    (Focusky动画演示大师简称"FS软件")在"图形"工具下找到"\",点击,按住shift键并按下鼠标向右拖动即可在画布上画出水平直线: ...

  8. [html] 你能否画出一个0.5px的直线?

    [html] 你能否画出一个0.5px的直线? 通过scale(0.5)来实现 个人简介 我是歌谣,欢迎和大家一起交流前后端知识.放弃很容易, 但坚持一定很酷.欢迎大家一起讨论 主目录 与歌谣一起通关 ...

  9. c语言用graphics画直线 带箭头直线_动漫人体比例怎么画?教你画出萌系少女!...

    这里需要注意的是,原来想要画人物的全身画时需要参考很多的人物素描以及人体的构图等方面的资料,但是我认为,萌系的画是一种虽然很像现实中的人物但是却存在一定差异的东西,所以这里我们这个部落格将以其他东西的 ...

最新文章

  1. easyui-menu 解决disableItem不能禁用绑定事件的方法
  2. javascript解析json
  3. HTML5跳转页面并传值以及localStorage的用法
  4. 按下电源后的几秒钟,CPU在干嘛?
  5. python3 if else 简洁写法 三元运行
  6. DirectX 开发环境配置
  7. mysql error 1236_解决MySQL数据库同步1236错误
  8. 在IOS XR上配置BFD
  9. java中用jdom 如何创建xml文档/将数据写入XML中
  10. leetcode 59. 螺旋矩阵 II(递归)
  11. android笔试添加自定义服务,Android之Listview(item为单选题)自定义adapter,像考试时前面的10几道单选题的实现...
  12. 在linux下搭建git环境,在linux下搭建git github开发环境
  13. java登录验证技术,login_required -- 登录验证
  14. PowerShell 中执行 dir /b /s
  15. 2018想要薪资翻倍?你需要掌握这个技能
  16. 求职 IT 少年李文星之死:请务必学会保护自己!
  17. EDU 61 F. Clear the String 区间dp
  18. AWS SageMaker 基于Random Cut Forest 的异常值检测 随机森林
  19. 计算机无法映射网络驱动器,win7系统无法映射网络驱动器的解决方法
  20. c2000 电阻采样_常用超低阻值采样电阻阻值一览表

热门文章

  1. H5营销海报如何制作,在线制作平台分享
  2. 计算机硬件被网游封了是怎么回事,电脑硬件市场没落的真正原因:网络游戏对电脑硬件的要求太低了...
  3. linux最新bbr加速,CentOS 7.8 安装BBR加速
  4. 【顺序、分支、循环、子程序设计】—— 微机原理实验
  5. pfv和php,【Retina China 2019】工欲善其事必先利其器——影像学在眼底病中的应用与发展...
  6. 爬虫类Chrome去除前端无限debugger反调试(轻松分析算法)
  7. VMware Workstation 不可恢复错误: (vmx)Exception 0xc0000006 (disk error while paging) has occurred.
  8. 法国 计算机金融 大学,法国金融硕士专业大学排名(2020年QS)_快飞留学
  9. STM8/STM32 SPI模式的MAX7456代码
  10. 关闭笔记本电脑自带键盘