用Lua做了个绘制二元等式或不等式的图像的软件

输入任意的包含x和y两个变量的Lua表达式就可以绘制出对应的图像。Lua支持的运算符如下:

算术+ - * / % ^
比较== ~= < > <= >=
逻辑and or not

其中%是求余,^是乘方;需要注意的是相等用==,写=则报语法错误。 Lua预定义的数学函数参见Reference Manual的mathlib。软件界面如下:

绘图的算法很简单,遍历每一个像素点,如果对应的坐标区域满足表达式给出的条件就给这个像素着色。

需要特别说明的是Pixel Split的含义,每个像素点并不是对应一对坐标值,而是对应一个矩形区域,因此对于某些函数需要做细分才能画准确一点。比如输入8表示细分为8x8=64个网格点,只要有一个格点满足表达式给出的条件就给像素着色。细分的数字越大所需的计算量也就越大,建议取1到50之间的数字。

 

在表达式中使用逻辑运算符可以将一些图形组合起来显示,比如:

 

画布的大小和一些参数的默认值可以在配置文件中修改,配置文件中还包括了收藏夹的定义,预置一些比较有趣的表达式,文件名是Favorites.lua,可以用记事本编辑,然后重新打开程序生效。

 

做这个软件的想法是看了《几个令人惊叹的函数图像》一文,文中提到作图软件GrafEq比较古老,很多系统不兼容了。另外最近正在学Lua,正好实践一下。而且用Lua很适合,提供了以下几个便利:

1. 可以把用户输入的表达式文本转化为一个函数:

     local  expr  =  iup.GetDialogChild(dlg,  " exprtext " ).value
    
local  func, err  =   loadstring ( " return function (x,y) return  " .. expr.. "  end " )
    
if  func == nil   then  iup.Message( " Error " " Expression has syntax errors:\r\n " ..err)  return   end
    equation 
=  func()

2. 一句话就能载入配置文件:

dofile ( " Favorites.lua " )

3. 配置文件本身是一个合法的Lua程序,因而用户可以在里面自定义一些函数。

比如这个图用到了自定义的belongto函数:

function  between(x, minValue, maxValue)
    
return  x  >=  minValue  and  x  <=  maxValue
end

function  belongto(x, ranges)
   
for  _,range  in   ipairs (ranges)  do
        
if  between(x, range[ 1 ], range[ 2 ])  then   return   true   end
   
end
   
return   false
end

表达式为:belongto(sin(sqrt((x+5)^2+y^2))*cos(8*atan(y/(x+5)))*sin(sqrt((x-5)^2+(y-5)^2))*cos(8*atan((y-5)/(x-5)))*sin(sqrt(x^2+(y+5)^2))*cos(8*atan((y+5)/x)), {{-0.1,0},{0.2,math.huge}})

这个软件有两种玩法:一是输入表达式观察对应的图形,二是预想一个图案,然后设计表达式生成想要的图形。

就实用性而言,一是对学数学的中学生会很有用,二是生成的图案可以用于其他的平面或3D绘图软件。

可以设定画布的背景色和画笔的颜色,可以保存图片到文件中。

下面是程序的源代码,只有204行:

View Code
  1  --  GraphPlot.wlua
  2  --  Plot any Lua expression in two variables x and y
  3  --  Junfeng Liu @ 2011-06-27
  4 
  5  require " cdlua "
  6  require " iuplua "
  7  require " iupluacd "
  8 
  9  setmetatable ( _G , { __index  =  math })
 10  =  exp( 1 )
 11 
 12  --  Load Config and Favorites
 13  dofile ( " Favorites.lua " )
 14  pixels  =  {}
 15  for  r = 1 ,Config.Height  do
 16    pixels[r]  =  {}
 17     for  c = 1 ,Config.Width  do
 18          pixels[r][c]  =   false
 19     end
 20  end
 21 
 22  cnv  =  iup.canvas{ rastersize = string.format ( " %dx%d " , Config.Width, Config.Height) }
 23  treeFavorite  =  iup.tree{ size  =   " 100x100 " }
 24 
 25  canvasColor  =  cd.EncodeColor( 255 , 255 , 255 )
 26  penColor  =  cd.EncodeColor( 0 , 0 , 0 )
 27 
 28  function  selectColor(self)
 29    r,g,b  =  iup.GetColor(iup.CENTER,iup.CENTER)
 30   self.bgcolor  =   string.format ( " %d %d %d " ,r,g,b)
 31    if  self.name  ==   " btnPen "   then  penColor  =  cd.EncodeColor(r,g,b)
 32    else  canvasColor  =  cd.EncodeColor(r,g,b)  end
 33  end
 34 
 35  dlg  =  iup.dialog
 36  {
 37      title = " Plot graphs of equations and inequalities in two vairables " ,
 38      resize = " NO " ,
 39      minbox = " NO " ,
 40      iup.hbox
 41      {
 42          iup.vbox
 43          {
 44             iup.frame
 45             {
 46                 title  =   " Color " ,
 47                 iup.hbox
 48                 {
 49                      iup.label{title  =   "  Canvas:  " },
 50                      iup.button{ name  =   " btnCanvas " , size  =   " 22x10 " , bgcolor = " 255 255 255 " , flat = " YES " , action  =  selectColor},
 51                      iup.label{title  =   "  Pen:  " },
 52                      iup.button{ name  =   " btnPen " , size  =   " 22x10 " , bgcolor = " 0 0 0 " , flat = " YES " , action  =  selectColor},
 53                      alignment  =   " ACENTER "
 54                 }
 55             },
 56             iup.frame
 57             {
 58                 title  =   " Range " ,
 59                 iup.vbox
 60                 {
 61                      iup.hbox
 62                      {
 63                          iup.label{title  =   " X:  " },
 64                          iup.text{ name  =   " txtXMin " , size  =   " 38x10 " , value  =  Config.Xmin},
 65                          iup.label{title  =   "  ~  " },
 66                          iup.text{ name  =   " txtXMax " , size  =   " 38x10 " , value  =  Config.Xmax}
 67                      },
 68                      iup.hbox
 69                      {
 70                          iup.label{title  =   " Y:  " },
 71                          iup.text{ name  =   " txtYMin " , size  =   " 38x10 " , value  =  Config.Ymin},
 72                          iup.label{title  =   "  ~  " },
 73                          iup.text{ name  =   " txtYMax " , size  =   " 38x10 " , value  =  Config.Ymax}
 74                      }
 75                 }
 76             },
 77             iup.hbox
 78             {
 79                  iup.label{ title  =   " Pixel Split:  "  },
 80                  iup.text{ name  =   " txtSplit " , value  =  Config.Split},
 81                  alignment  =   " ACENTER "
 82             },
 83             treeFavorite,
 84             margin  =   " 2x2 "
 85          },
 86          iup.vbox
 87          {
 88              iup.hbox
 89              {
 90                  iup.label{title  =   " Expression:  " },
 91                  iup.text{ name  =   " exprtext " , rastersize  =  (Config.Width - 120 ).. " x22 " , value  =   " abs(x - y) % 20 < 0.0001 " },
 92                  iup.button{title  =   " Plot " , size  =   " 34x12 " , action  =   function () plotExpression()  end },
 93                  alignment  =   " ACENTER "
 94              },
 95              cnv,
 96              margin  =   " 2x2 "
 97          }
 98      }
 99  }
100 
101  function  getNumber(name)
102       return   tonumber (iup.GetDialogChild(dlg, name).value)
103  end
104 
105  function  setTextValue(name, value)
106      iup.GetDialogChild(dlg, name).value  =  value
107  end
108 
109  function  plotExpression()
110       local  expr  =  iup.GetDialogChild(dlg,  " exprtext " ).value
111       local  func, err  =   loadstring ( " return function (x,y) return  " .. expr.. "  end " )
112       if  func == nil   then  iup.Message( " Error " " Expression has syntax errors:\r\n " ..err)  return   end
113      equation  =  func()
114      drawing  =   true
115      iup.Redraw(cnv,  0 )
116  end
117 
118  function  cnv:map_cb()
119    canvas  =  cd.CreateCanvas(cd.IUP, self)
120  end
121 
122  drawing  =   false
123  function  cnv:action()
124    canvas:Activate()
125    canvas:Background(canvasColor)
126    canvas:Clear()
127     local  width  =  Config.Width
128     local  height  =  Config.Height
129     if  drawing  ==   false   then
130       for  r = height, 1 , - 1   do
131         for  c = 1 ,width  do
132             if  pixels[r][c]  then
133              canvas:Pixel(c, r, penColor)
134             end
135         end
136       end
137       return
138     end
139 
140    dlg.active  =   " NO "
141     local  xMin  =  getNumber( " txtXMin " )
142     local  xMax  =  getNumber( " txtXMax " )
143     local  yMin  =  getNumber( " txtYMin " )
144     local  yMax  =  getNumber( " txtYMax " )
145     local  split  =  getNumber( " txtSplit " )
146     local  DX  =  (xMax  -  xMin) / width
147     local  DY  =  (yMax  -  yMin) / height
148     local  dx  =  DX  /  split
149     local  dy  =  DY  /  split
150     local  x  =  xMin
151     local  y  =  yMax
152     for  r = height, 1 , - 1   do
153         for  c = 1 ,width  do
154           local  fill  =  isSolution(x,y,dx,dy,split)
155          pixels[r][c]  =  fill
156           if  fill  then
157              canvas:Pixel(c, r, penColor)
158           end
159          x  =  x  +  DX
160         end
161        x  =  xMin
162        y  =  y  -  DY
163     end
164    drawing  =   false
165    dlg.active  =   " YES "
166  end
167 
168  function  isSolution(x0,y0,dx,dy,split)
169       local  x  =  x0
170       local  y  =  y0
171       for  i = 1 ,split  do
172           for  j = 1 ,split  do
173               if  equation(x, y)  then   return   true   end
174              x  =  x  +  dx
175           end
176          x  =  x0
177          y  =  y -  dy
178       end
179       return   false
180  end
181 
182  function  treeFavorite:selection_cb(id, status)
183       if  status  ==   1   then
184          node  =  Favorites[id]
185          setTextValue( " exprtext " , node.tip)
186           if  node.config  ~=   nil   then
187              setTextValue( " txtXMin " , node.config.Xmin)
188              setTextValue( " txtXMax " , node.config.Xmax)
189              setTextValue( " txtYMin " , node.config.Ymin)
190              setTextValue( " txtYMax " , node.config.Ymax)
191              setTextValue( " txtSplit " , node.config.Split)
192           end
193       end
194  end
195 
196  function  dlg:close_cb()
197    canvas:Kill()
198    self:destroy()
199     return  iup.IGNORE
200  end
201 
202  dlg:show()
203  iup.TreeAddNodes(treeFavorite, Favorites)
204  iup.MainLoop()

 

最后程序用wsrlua工具做了打包,不需要安装Lua环境就能运行,由于是GUI程序需要msvcr100.dll,没有的话另外下载。

下载地址1

下载地址2

转载于:https://www.cnblogs.com/rufi/archive/2011/06/28/GraphPlot.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值