上星期,为了画出过定点的贝塞尔曲线,纠结了很长时间,最终还是在网上找到了相应的算法,呵呵…

严格意义上来说,这个日志不是我原创的…

谢谢一下两篇日志…

http://blog.csdn.net/microchenhong/article/details/6316332

http://liyiwen.iteye.com/blog/705489

以下代码是skia的绘制代码…

大致思路就是 先算出相邻原始点的中点,在把相邻中点连成的线段,将线段的中点平移到对应的原始点。再以平移后的中点作为控制点,相邻原始点为起始点画贝塞尔曲线,这样就保证了连接处的光滑。而贝塞尔曲线本身是光滑的,所以就把这些原始点用光滑曲线连起来了。

  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
void Draw(SkCanvas *canvas)
{
    SkPoint points[4];
    points[0].fX = 50;
    points[0].fY = 50;
    points[1].fX = 75;
    points[1].fY = 25;
    points[2].fX = 125;
    points[2].fY = 25;
    points[3].fX = 150;
    points[3].fY = 50;
 
    SkPaint p3;
    p3.setColor(SK_ColorBLUE);
    p3.setStyle(SkPaint::kStroke_Style);
 
    SkPath path3;
    path3.moveTo(points[0]);
    path3.cubicTo(points[1], points[2], points[3]);
    canvas->drawPath(path3, p3);
 
    SkPaint p4;
    p4.setColor(SK_ColorRED);
    p4.setStyle(SkPaint::kStroke_Style);
 
    SkPath path4;
    path4.moveTo(points[0]);
    path4.addPoly(points, 4, false);
    canvas->drawPath(path4, p4);
 
    vector<SkPoint> originPt;
    for (int index = 0; index < 4; ++index)
    {
        SkPoint pt;
        pt.fX = points[index].fX;
        pt.fY = points[index].fY;
 
        originPt.push_back(pt);
    }
 
    vector<SkPoint> extrapoints;
    createCurve(originPt, extrapoints);
 
    SkPaint p5;
    p5.setColor(SK_ColorGREEN);
    p5.setStyle(SkPaint::kStroke_Style);
 
    SkPath path5;
    for (int index = 0; index < extrapoints.size(); index += 2)
    {
        path5.moveTo(extrapoints.at(index));
        path5.lineTo(extrapoints.at((index + 1) % extrapoints.size()) );
    }
    canvas->drawPath(path5, p5);
 
    SkPaint p6;
    p6.setColor(SK_ColorYELLOW);
    p6.setStyle(SkPaint::kStroke_Style);
 
    SkPath path6;
    path6.moveTo(originPt.at(0));
    for (size_t index = 0; index < originPt.size() - 1; ++index)
    {
        path6.cubicTo(extrapoints.at(index * 2 + 1), extrapoints.at(index * 2 + 2), originPt.at(index + 1));
    }
    canvas->drawPath(path6, p6);
 
    canvas->save();
    canvas->translate(0, 50);
    SkPaint p7;
    p7.setColor(SK_ColorBLACK);
    p7.setStyle(SkPaint::kStroke_Style);
 
    vector<SkPoint> curvePoint;
    createBezierCurve(originPt, curvePoint);
 
    SkPath path7;
    path7.moveTo(curvePoint.at(0));
    for (size_t index = 1; index < curvePoint.size(); ++index)
    {
        path7.lineTo(curvePoint.at(index));
    }
    canvas->drawPath(path6, p6);
}
 
void createCurve(const vector<SkPoint> &originPoint, vector<SkPoint> &extrapoints)
{
    //控制点收缩系数 ,经调试0.6较好,CvPoint是opencv的,可自行定义结构体(x,y)
    SkScalar scale = 0.6;
    vector<SkPoint> midpoints;
    midpoints.resize(originPoint.size());
    //生成中点
    for(int i = 0 ; i < originPoint.size() ; i++)
    {
        int nexti = (i + 1) % originPoint.size();
        midpoints[i].fX = (originPoint[i].fX + originPoint[nexti].fX) / 2.0;
        midpoints[i].fY = (originPoint[i].fY + originPoint[nexti].fY) / 2.0;
    }
 
    //平移中点
    extrapoints.resize(2 * originPoint.size());
    for(int i = 0 ; i < originPoint.size() ; i++)
    {
        // 求出当前点的前驱和后继
        int nexti = (i + 1) % originPoint.size();
        int backi = (i + originPoint.size() - 1) % originPoint.size();
 
        // 求出当前点的前驱和后继连线的中点
        SkPoint midinmid;
        midinmid.fX = (midpoints[i].fX + midpoints[backi].fX) / 2.0;
        midinmid.fY = (midpoints[i].fY + midpoints[backi].fY) / 2.0;
 
        // 前驱和后继连线的中点移动到当前点的偏移量
        int offsetx = originPoint[i].fX - midinmid.fX;
        int offsety = originPoint[i].fY - midinmid.fY;
 
        // 求出偏移后的前驱和后继坐标(缩放相应的缩放系数)
        int extraindex = 2 * i;
        extrapoints[extraindex].fX = originPoint[i].fX + (midpoints[backi].fX + offsetx - originPoint[i].fX) * scale;
        extrapoints[extraindex].fY = originPoint[i].fY + (midpoints[backi].fY + offsety - originPoint[i].fY) * scale;
        int extranexti = (extraindex + 1) % (2 * originPoint.size());
        extrapoints[extranexti].fX = originPoint[i].fX + (midpoints[i].fX + offsetx - originPoint[i].fX) * scale;
        extrapoints[extranexti].fY = originPoint[i].fY + (midpoints[i].fY + offsety - originPoint[i].fY) * scale;
    }
}
 
void createBezierCurve(const vector<SkPoint> &originPoint, vector<SkPoint> &curvePoint)
{
    vector<SkPoint> extrapoints;
    createCurve(originPoint, extrapoints);
 
    SkPoint controlPoint[4];
    //生成4控制点,产生贝塞尔曲线
    for(int i = 0 ; i < originPoint.size() ; i++)
    {
        controlPoint[0] = originPoint[i];
        int extraindex = 2 * i;
        controlPoint[1] = extrapoints[extraindex + 1];
        int extranexti = (extraindex + 2) % (2 * originPoint.size());
        controlPoint[2] = extrapoints[extranexti];
        int nexti = (i + 1) % originPoint.size();
        controlPoint[3] = originPoint[nexti];
        SkScalar u = 1;
        while(u >= 0)
        {
            SkScalar px = bezier3funcX(u, controlPoint);
            SkScalar py = bezier3funcY(u, controlPoint);
            //u的步长决定曲线的疏密
            u -= 0.005;
            SkPoint tempP;
            tempP.fX = px;
            tempP.fY = py;
            //存入曲线点
            curvePoint.push_back(tempP);
        }
    }
}
 
//三次贝塞尔曲线
SkScalar bezier3funcX(SkScalar uu, SkPoint *controlP)
{
    SkScalar part0 = controlP[0].fX * uu * uu * uu;
    SkScalar part1 = 3 * controlP[1].fX * uu * uu * (1 - uu);
    SkScalar part2 = 3 * controlP[2].fX * uu * (1 - uu) * (1 - uu);
    SkScalar part3 = controlP[3].fX * (1 - uu) * (1 - uu) * (1 - uu);
    return part0 + part1 + part2 + part3;
}
 
SkScalar bezier3funcY(SkScalar uu, SkPoint *controlP)
{
    SkScalar part0 = controlP[0].fY * uu * uu * uu;
    SkScalar part1 = 3 * controlP[1].fY * uu * uu * (1 - uu);
    SkScalar part2 = 3 * controlP[2].fY * uu * (1 - uu) * (1 - uu);
    SkScalar part3 = controlP[3].fY * (1 - uu) * (1 - uu) * (1 - uu);
    return part0 + part1 + part2 + part3;
}

过定点贝塞尔

因为skia本身有贝塞尔的相应画法,所以createCurve只用算出新增的两个控制点的坐标即可

而从坐标向下平移50个单位后的贝塞尔曲线可以看出…如果自己通过三次贝塞尔插值createBezierCurve其实画得也不差嘛,哈哈…