Программирование
On-line приложения
Почитать
Web-сервер Apache
Печать и форматирование
MySQL
Разные рецепты
Сборка/установка
Справки
Философия
Мой опыт
Скачать
Программы на Tcl/Tk (GUI)
Программы на Python/Tk (GUI)
Программы (CLI)
Help
Хобби
Фракталы
on-line
Язык для рисования фракталов
Гиперкуб
Теория относительности
Ампуллярии
Преподавание
Студенту/абитуриенту
Мой опыт
Автора!

Fact на примерах

Здесь приведено неформальное описание основных возможностей языка Fact на примерах. В дистрибутиве вместе с программой содержится несколько примеров. Здесь я рассматриваю и комментирую некоторые из них.

Первый рисунок

Запустите программу example1 (выберете пункт меню «Open» (или просто нажмите клавишу «O») и откройте файл example1).

# элементарное рисование
#
bgcolor 1 1 1 # цвет фона белый
width .05     # толщина линии
jump .25 .25  # смещаемся
color 0 0 0   # выбираем чёрный цвет
draw .5 0     # линия вправо
color 1 0 0   # красный
draw  0 .5    # линия вверх
color 0 1 0   # зелёный
draw  -.5 0   # линия влево
color 0 0 1   # синий
draw  0 -.5   # линия вниз

В окне будет сделан рисунок:

Как видите, программа состоит из слов, разделённых пробелами. Если в строке обнаружен символ #, то он и весь остаток строки игнорируется. Пробельные символы (пробелы, табуляции, символы конца строки) интерпретатором не различаются. Следующие записи эквивалентны и корректны:

color 1 0 0 # устанавливаем красный цвет

color # устанавливаем цвет
    1 # доля красного
    0 # доля зелёного
    0 # доля синего

Операции установки цвета (bgcolor и color) требуют трёх аргументов задающих насыщенность красной, зелёной и синей компонент. Насыщенность задаётся числом от 0 до 1. Если цвет больше 1, то используется 1, если меньше 0, то 0.

Операции, задающие размеры работают относительно текущей системы координат. По умолчанию система координат такова: ось x совпадает с нижней границей области отрисовки, ось y — с левой. Длина стороны равна единице.

Толщина линии width задаётся в единицах системы координат.

Инструкция jump сдвигает систему координат на заданные расстояния вдоль оси x и y, соответственно.

Инструкция draw сдвигает систему координат и одновременно вычерчивает линию.

Масштабирования, повороты и повторения операций

Пример example2 иллюстрирует ещё три возможности. Они отмечены знаком «+» в комментариях:

# повторение операций
#
bgcolor 1 1 1 # цвет фона белый
width .05     # толщина линии
color 0 0 0   # выбираем чёрный цвет
scale .1      # + уменьшаем единицу измерения в 10 раз
jump  5 8.5   # смещаемся
right 126     # + поворачиваем вправо на 126 градусов
repeat 5      # + повторяем пять раз
 draw  0 4    # линия вдоль оси x
 right 72     # + поворачиваем вправо
end           # + конец блока

Команда scale увеличивает единицу измерения в заданное число раз. В этом примере мы уменьшили единицу измерения в 10 раз. То есть после выполнения операции scale сторона области отрисовки становится равна десяти единицам системы координат.

Операция right поворачивает систему координат вправо (по часовой стрелке) на заданный угол (в градусах). Операция left аналогична, но поворачивает в другую сторону. Значение угла может быть отрицательным.

Со слова repeat начинается блок команд, который следует повторить несколько раз. Блок заканчивается словом end.

Результат работы кода таков:

Использование переменных

Третий пример является лишь модифицированным вторым примером. В нём критичные параметры задаются с помощью переменных:

# использование переменных
#
set n 6       # + устанавливаем переменную n
set a 120     # + начальный поворот 90+180/n
set b 60      # + поворот в каждой вершине 360/n
set l 3.4     # + длина грани
bgcolor 1 1 1 # цвет фона белый
width .05     # толщина линии
color 0 0 0   # выбираем чёрный цвет
scale .1      # уменьшаем единицу измерения в 10 раз
jump  5 8.5   # смещаемся
right a       # + поворачиваем вправо на 126 градусов
repeat n      # + повторяем пять раз
 draw  0 l    # + линия вдоль оси x
 right b      # + поворачиваем вправо
end           # конец блока

Для присвоения переменным значений используется инструкция set. Имена переменных могут быть любые: не только названия операций (пример color), но даже числа (хотя это может сильно запутать программу).

Однажды определённую переменную можно потом использовать везде, где требуются аргументы. В данном случае результат будет таким:

Но среди примеров, содержащихся в дистрибутиве, есть несколько вариаций этого примера, где строятся звёзды и подкрашенные звёзды.

Подпрограммы

Четвёртый пример (example4) иллюстрирует работу подпрограмм (которые наверно правильнее было бы назвать макросами).

# подпрограммы
#
def sq        # + определении процедуры рисования
              #   квадрата от его центра
  jump -.5 .5 # смещаемся из цента в левый-верхний угол
  repeat 4    # четыре раза повторяем следующие операции
    draw 1 0  # рисуем вдоль оси х
    right 90  # поворачиваем вправо
  end         # конец повторяемого блока
end           # + конец определения процедуры

scale .25     # масштабируем в четыре раза
jump 2 1      # смещаемся
width .02     # устанавливаем толщину линии
bgcolor 0 0 0 # устанавливаем цвет фона

color 1 1 1   # белый цвет
call sq       # + нарисовать квадрат
color 1 0 0   # красный цвет
call sq       # + нарисовать квадрат

Подпрограмма это последовательность операций, заключённая между директивами def и end. Как видите, в этом примере создана подпрограмм sq, которая вычерчивает квадрат со стороной равной 1 вокруг текущей точки.

Вызывать подпрограмму можно в любом месте программы инструкцией call.

Результат работы этой программы будет таков:

Обратите внимание, что подпрограмма начинает рисовать квадрат из центра, а заканчивает рисование в левом-верхнем углу квадрата. Поэтому красный квадрат получился нарисованным вокруг левой-верхней вершины белого.

Часто бывает нежелательным, чтобы процедура вносила изменения в текущий контекст. Чтобы этого добиться существуют средства локализации контекста.

Локализация контекста

Пятый пример является модернизацией четвёртого и иллюстрирует локализацию.

# локализация контекста
#
def sq
  local # локализация контекста
    jump -.5 .5
    repeat 4
      draw 1 0
      right 90
    end
  end   # конец локальной области
end

scale .25
jump 1 1
width .02
bgcolor 0 0 0

color 1 1 1
call sq
color 1 0 0
jump 2 0 # смещаемся вправо
         # иначе квадраты наложатся
         # друг на друга
call sq

Все инструкции между директивами local и end выполняются в локальном контексте. То есть все сдвиги и изменения переменных произведённые внутри блока local-end теряют свою силу за пределами блока.

Локализуются только переменные и смещения. Однако процедуры, определённые внутри блока local-end сохраняются и за пределами блока. Точно также в силе остаются изменения цвета и толщины линии.

Если вам требуется локализовать цвет и толщину, то используйте переменные в директивах color и widht.

Существуют аналогичные инструкции, локализующие только работу с системой коориднат или только переменные. Подробнее смотри полное описание Fact.

Условные операции и рекурсия

Простые итерации можно получить при помощи оператора repeat. следующий пример вам должен быть полностью понятен, в нём нет ничего нового.

# условные операции
#
def sq
  local
    jump -.5 .5
    repeat 4
      draw 1 0
      right 90
    end
  end
end

scale .2
jump 2.5 1
width .02
bgcolor 0 0 0

color 1 1 1
repeat 10
  call sq
  jump 0 .5        # мы на верхней грани куба
  right 45         # поворот на 45 градусов
  scale 0.70710678 # масштабирование в "корень из двух" раз
  jump 0 1
end

Так как при каждом построении мы масштабируем систему координат, то в результате у нас получится последовательность уменьшающихся квадратов.

Однако рекурсия позволяет получить ветвящиеся деревья. Вот пример программы, реализующей рекурсию:

# условные операции и итерации
#
def sq
  local
    jump -.5 .5
    repeat 4
      draw 1 0
      right 90
    end
  end
end

def sq_seq
  call sq
  if level lt 7 # + проверка условия
    local
      add level 1
      jump 0 .5
      scale 0.70710678
      local
        right 45
        jump 0 1
        call sq_seq
      end
      local
        left 45
        jump 0 1
        call sq_seq
      end
    end
  end
end

jump .5 .2
scale .18
width .02
bgcolor 0 0 0

color 1 1 1
set level 0
call sq_seq

Подпрограмма sq_seq реализует рекурсию. перед её вызовом, мы присваиваем переменной level значение 0.

Что делает подпрограмма sq_seq?

Первым делом она отрисовывает квадрат (call sq). За тем она проверяет значение переменной level. Если переменная меньше семи, то выполняется целый рад действий, все из которых вам уже знакомы: происходит локализация контекста (теперь все изменения переменной level не затронут «оригинал»), переменная level увеличивается на единицу (add level 1), производятся уже знакомые нам из предыдущего примера преобразования системы координат и функция sq_seq вызывает сама себя дважды. Так начинается отрисовка левой и правой ветки. Оба вызова снова дважды вызывают сами себя и начинается отрисовка левой и правой подветок у каждой ветки и так далее, пока level не достигнет 7 (на каждом шаге level увеличивается на единицу).

В этом примере новой для нас является проверка условий. В общем виде условие может проверяться так:

if <переменная или значение> <операция> <переменная или значение>
 ...последовательность операций...
end

или

if <переменная или значение> <операция> <переменная или значение>
 ...последовательность операций...
else
 ...альтернативная последовательность операций...
end

Операции (<операция>) может иметь два значения:

  • lt — меньше
  • gt — больше

Если выражение идущее после if истинно, то код, следующий за if выполняется. Если это выражение ложно, то выполняется код, следующий за else (если имеется).

Блок if так же как и другие заканчивается словом end.

Приведу несколько примеров:

if 1 lt 2
 color 1 0 0 # красный
end

Будет установлен красный цвет, так как 1 действительно меньше, чем 2.

set one 1
set who 2
if one gt two
 color 1 0 0 # красный
else
 color 0 1 0 # зелёный
end

Будет установлен зелёный цвет, так как выражение 1>2 ложно.

Кроме проверки условий здесь ещё используется операция add. Она прибавляет к значению указанной переменной заданную величину. Подробнее математические операции описаны в описании Fact.

Можно ли сделать так, чтобы ветки не накладывались друг на друга? Да. Среди примеров, поставляемых с программой есть вот такой:

Я не буду подробно разбирать его (ничего принципиально нового там нет). Идея же очень проста: на каждом шаге мы считаем не только уровень вложенности (level), но и количество сделанных правых и левых поворотов. Если оказывается, что мы находимся в крайней ветке (все повороты имеют одно и тоже направление), то рост идёт неограниченно (ограничивается только предельным уровнем вложенности). Если же оказывается, что мы находимся на внутренней ветке (имеются и правые и левые повороты), то рост должен быть остановлен.

Окраска отображает значения счётчиков правых и левых поворотов: чем больше зелёного, тем больше правых, чем больше красного, тем больше левых.

Вы можете сами «поиграться» с этим примером.

В этой заметке не были освещены ещё несколько функций, облегчающих отладку и управляющих отрисовкой. За более подробной информацией обратитесь к описанию Fact.

© 1999 − 2008 Мичурин Алексей — http://www.michurin.com.ru/