Иногда возникает потребность построить график какой-нибудь функции (даже не важно, написана она самим пользователем или же стандартная функция языка программирования), и вот тогда возникают некоторые сомнения и трудности. Такая задача легко разрешима в Icon.
Для построения графиков в Icon сначала определим окно размерами 500×500 пикселей:
link graphics procedure main() local W W:=WOpen("label=graph","size=500,500") WDone() end
Как известно, в Icon начало координат — левый верхний угол (точка с координатами (0, 0) пикселей), координата Х растет по направлению к правому верхнему углу, а координата Y — по направлению к нижнему левому углу (т.е. представляете себе, как не совсем удобно расположены координатные оси). Увы, нам такое расположение осей очень неудобно.
Однако, есть способ как решить проблему — для этого необходимо переместить точку начала координат в какое-нибудь другое место и считать координаты относительно этого места. В качестве такого места лучше всего использовать центр окна, который в нашем случае имеет координаты (250,250), от него мы и будем отсчитывать координаты.
Для следующего шага нам потребуется координатная сетка с неким дискретным шагом, поскольку размеры окна в пикселях достаточно велики, то удобнее всего брать шаг сетки, соизмеримый с одним из делителей 500 (ну или лучше всего 250).
Я взял шаг сетки равный 10 пикселям и получил следующую процедуру построения координатной сетки:
procedure grid_coord() local i Fg("lightgray") every i:=0 to 500 by 10 do { DrawLine(i,0,i,500) DrawLine(0,i,500,i) } Fg("black") DrawLine(0,250,500,250) DrawLine(250,0,250,500) end
В результате получим сетку светло-серого цвета, при этом оси идущие из начала координат (не забываем, что оно теперь имеет координаты [250,250] ) будут черного цвета:
Теперь необходимо сделать процедуру для построения самого графика, да и с учетом нового начала координат.
Разумнее всего, строить график по точкам (т.е. используя процедуру DrawPoint), используя для этого цикл внутри которого помещается команда вычисления функции, используя счетчик цикла как меняющийся параметр, и команда построения точки, которая принимает два аргумента — координата X точки (с учетом нового начала координат) и координата Y (вычисленное значение функции с учетом нового начала координат).
Однако, как сделать так, чтобы можно было подставлять практически любую из описанных функций, не описывая в цикле саму схему ее вычисления ? Да очень просто — нужно объявить функцию, график которой мы будем строить как invocable и дополнить процедуру построения графика функции еще одним параметром — строкой с именем функции!
В итоге получаем следующую процедуру:
procedure func_plot(a,b,func,n,col) local i /n:=0.01 /col:="black" i:=a while i <= b { Fg(col) DrawPoint(i+250,250-100*func(i/20.0)) i+:=n } end
где a — начальная точка для построения (по координате Х), b — конечная точка построения (по координате Х), func — имя функции для построения, n — «разрешающая способность» (шаг приращения по координате Х), col — цвет графика (строковая переменная, описывающая цвет).
Но и это не всё — само выражение 250-100*func(i/20.0) содержит еще 2 параметра (но не в явном виде, разумеется), в частности в выражении 100*func(i/20.0) и присутствуют оба параметра: число 100 перед функцией (обозначает во сколько раз увеличен масштаб по оси Y) и число 20.0 внутри самой функции (обозначает во сколько раз увеличен (растянут) масштаб по оси X), разумеется, параметры можно менять под свои нужды.
И ещё одно — если вы думаете, что процедура func_plot принимает 5 аргументов, то вы ошибаетесь! Применяя эту процедуру, вы можете опустить аргументы n и сol, тогда процедура примет значения для этих аргументов по умолчанию (n = 0.01 и col= «black», т.е. приращение по Х 0.01 и черный цвет графика), например:
func_plot(0,100,"sin")
(при условии,что sin объявлен как invocable)
или так:
func_plot(0,100,"sin",,"red")
Ну, а теперь пара интересных функций.
sinc(x) — синус кардинальный:
procedure sinc(x)
if x=0 then return 1 else return sin(&pi*x)/(&pi*x)
end
График:
func_plot(-250,250,"sinc",,"green")
si(x) — синус интегральный (потребуется библиотека factors):
procedure si(x) local i,g,s,k,v i:=0 s:=0 while i<20 do { k:=(2*i)+1 g:=(-1)^i v:=(g*(x^k))/(factorial(k)*k) s+:=v i+:=1 } return s end
График:
func_plot(-250,250,"si",,"orange")
сi(x) — косинус интегральный (тоже нужна factors):
procedure ci(x) local i,g,s,k g:=0.5772156649 s:=g+log(x,&e) i:=1 while i<20 do { k:=(((-1.0)^i)*(x^(2.0*i)))/(factorial(2.0*i)*2.0*i) s+:=k i+:=1 } return s end
График:
func_plot(2,250,"ci",,"blue")
Также графики можно совмещать:
func_plot(-250,250,"sinc",,"green")
func_plot(2,250,"ci",,"blue")
func_plot(1,250,"log",,"violet")
func_plot(0,250,"cos",,"cyan")
В общем, большой простор для экспериментов…