Project1

标题: 如何平滑地画出贝塞尔曲线 [打印本页]

作者: lotsofone    时间: 2013-1-3 21:27
标题: 如何平滑地画出贝塞尔曲线
本帖最后由 lotsofone 于 2013-1-3 21:36 编辑

下面是贝塞尔曲线的代码
  1. class Point
  2.   attr_accessor :x,:y
  3.   def initialize(x,y)
  4.     @x = x ; @y = y
  5.   end
  6.   def +(point)
  7.     return Point.new(@x+point.x,@y+point.y)
  8.   end
  9.   def *(value)
  10.     return Point.new(@x*value,@y*value)
  11.   end
  12.   def -(point)
  13.     return Point.new(@x-point.x,@y-point.y)
  14.   end
  15.   def distance_to(point)
  16.     return Math.sqrt((@x-point.x)**2+(@y-point.y)**2)
  17.   end
  18.   def round
  19.     return Point.new(@x.round, @y.round)
  20.   end
  21.   def to_i
  22.     return Point.new(@x.to_i, @y.to_i)
  23.   end
  24. end

  25. class Bitmap
  26.   def simple_draw_line(x1_input, y1_input, x2_input, y2_input,
  27.     color = Color.new(255, 255, 255))
  28.     x1 = x1_input.to_i ; y1 = y1_input.to_i
  29.     x2 = x2_input.to_i ; y2 = y2_input.to_i
  30.     if (x1 - x2).abs >= (y1 - y2).abs #横轴比纵轴长,此时以x为循环变量画线。
  31.       if x1 > x2#保证x1在左边
  32.         c = x1 ; x1 = x2 ; x2 = c ; c = y1 ; y1 = y2 ; y2 = c
  33.       end
  34.       for x in x1..x2#作图
  35.         y = y2*(x - x1).to_f/(x2 - x1) + y1*(x2 - x).to_f/(x2 -x1)
  36.         self.set_pixel(x, y, color)
  37.       end
  38.     else #纵轴比横轴长,此时以y为循环变量画线。
  39.       if y1 > y2 #保证y1在上面
  40.         c = x1 ; x1 = x2 ; x2 = c ; c = y1 ; y1 = y2 ; y2 = c
  41.       end
  42.       for y in y1..y2#作图
  43.         x = x2*(y - y1).to_f/(y2 - y1) + x1*(y2 - y).to_f/(y2 - y1)
  44.         self.set_pixel(x, y, color)
  45.       end
  46.     end
  47.   end
  48. end
复制代码
  1. class Bezier
  2.   attr_accessor :p1, :p2, :p3, :p4
  3.   def initialize(p1,p2,p3,p4)#p1,p4为端点,p2,p3为控制点
  4.     @p1 = p1 ; @p2 = p2 ; @p3 = p3 ; @p4 = p4
  5.   end
  6.   def show_bezier_sprite(viewport, width = 1, color = Color.new(255, 255, 255))#返回一个精灵显示曲线。
  7.     rx = [p1.x,p2.x,p3.x,p4.x].min
  8.     ry = [p1.y,p2.y,p3.y,p4.y].min
  9.     rw = [p1.x,p2.x,p3.x,p4.x].max
  10.     rh = [p1.y,p2.y,p3.y,p4.y].max
  11.     rh -= ry ; rw -= rx
  12.     w = width/2.0 + 3
  13.     rx -= w ; ry -= w ; rh += 2*w ; rw += 2*w
  14.     rx = rx.to_i ; ry = ry.to_i ; rh = rh.to_i ; rw = rw.to_i
  15.     #整个图形必定限制在矩形rx,ry,rw,rh中
  16.     sprite = Sprite.new(viewport)
  17.     sprite.bitmap = Bitmap.new(rw, rh)
  18.     sprite.x = rx ; sprite.y = ry
  19.     cp1 = @p1 - Point.new(rx, ry)
  20.     cp2 = @p2 - Point.new(rx, ry)
  21.     cp3 = @p3 - Point.new(rx, ry)
  22.     cp4 = @p4 - Point.new(rx, ry)
  23.     #p为以窗口左上角为0点的点,cp为以bitmap左上角为原点的点
  24.       coefficien_a = cp4-cp3*3+cp2*3-cp1
  25.       coefficien_b = cp3*3-cp2*6+cp1*3
  26.       coefficien_c = cp2*3-cp1*3
  27.       coefficien_d = cp1
  28.       lastp = cp1.to_i
  29.       sprite.bitmap.set_pixel(lastp.x, lastp.y, color)
  30.       for n in 1..100#这个数据可更改,数据越大,精度越高。若更改,下面的100.0也要改。
  31.         t = n/100.0
  32.         thisp = (coefficien_a*t**3+coefficien_b*t**2+coefficien_c*t+coefficien_d).to_i
  33.         if thisp.distance_to(lastp) > 1.42 then
  34.           sprite.bitmap.simple_draw_line(lastp.x, lastp.y, thisp.x, thisp.y, color)
  35.         else
  36.           sprite.bitmap.set_pixel(thisp.x, thisp.y, color)
  37.         end
  38.         lastp = thisp
  39.       end
  40.     return sprite
  41.   end
  42. end
复制代码
运行下列代码,就可以得到一个画上曲线的精灵了。
  1. viewport = Viewport.new(0, 0, 544, 416)
  2. p1 = Point.new(50, 50)
  3. p2 = Point.new(50, 400)
  4. p3 = Point.new(500, 400)
  5. p4 = Point.new(500, 50)
  6. bezier = Bezier.new(p1, p2, p3, p4)
  7. sb = bezier.show_bezier_sprite(viewport, 1, Color.new(0, 255, 0))
复制代码
下面两张图是效果图,数字1 2 3 4和控制点是我自己加的。


可是打开人家coreldraw的曲线一看

那么平滑,我这么粗糙的线怕是拿不出手啊。
如何才能让曲线更平滑呢?
作者: yangff    时间: 2013-1-4 17:41
本帖最后由 yangff 于 2013-1-4 17:52 编辑

用插值,把中间点插出来。
另外可能要解方程而不是直接画。。
如果要简单的做法的话就放大10倍画,然后插值缩放。插值缩放的代码论坛有。
作者: lotsofone    时间: 2013-1-12 16:57
yangff 发表于 2013-1-4 17:41
用插值,把中间点插出来。
另外可能要解方程而不是直接画。。
如果要简单的做法的话就放大10倍画,然后插值 ...

放大10倍,但线粗没有放大十倍,然后再插值,导致颜色很浅很浅。
解方程要怎么解?
我试过,通过逐点判断到曲线的距离,按距离决定透明度,不过这要求一个六次函数的最小值,以我目前的数学水平解不出。
作者: 浪迹天涯    时间: 2013-1-12 17:57
能扫个盲不?什么是贝塞尔曲线啊。
作者: lotsofone    时间: 2013-1-12 18:09
浪迹天涯 发表于 2013-1-12 17:57
能扫个盲不?什么是贝塞尔曲线啊。

ps里的钢笔工具用的就是贝塞尔曲线
作者: yangff    时间: 2013-1-13 14:11
lotsofone 发表于 2013-1-12 16:57
放大10倍,但线粗没有放大十倍,然后再插值,导致颜色很浅很浅。
解方程要怎么解?
我试过,通过逐点判断 ...

求导然后乱搞,挺麻烦的……你可以百度下吧。。
作者: 吐槽砖家    时间: 2013-1-13 20:20
看起来很奇妙啊
作者: devilg    时间: 2013-1-26 04:34
在点阵纯黑点的话会那么简单就平滑么?没有周围淡色调整真的可以么?
放大缩小听起来相当靠谱。
其实我的画图计算器的锯齿就跟这个差不多。
作者: lotsofone    时间: 2013-1-26 14:02
yangff 发表于 2013-1-13 14:11
求导然后乱搞,挺麻烦的……你可以百度下吧。。

6次求导了5次方程解不出。
也许只能通过画逼近的折线的方法了。




欢迎光临 Project1 (https://rpg.blue/) Powered by Discuz! X3.1