Построение графиков функций в Icon

Иногда возникает потребность построить график какой-нибудь функции (даже не важно, написана она самим пользователем или же стандартная функция языка программирования), и вот тогда возникают некоторые сомнения и трудности. Такая задача легко разрешима в 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")

или так:

func_plot(0,100,"sin",,"red")
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")
plot4_0_o

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")
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(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")
Совмещенные графики

В общем, большой простор для экспериментов…

aquaratixc

Программист-самоучка и программист-любитель

Добавить комментарий