Учебная работа. Курсовая работа: Программирование математических объектов
СОДЕРЖАНИЕ
ВСТУПЛЕНИЕ
1. Математические объекты
1.1 Группы
1.2 Графы
2. Справка по работе с программкой
2.1 Предназначение программного продукта
2.2 Обучение работе с программным продуктом
2.3 Ограничения внедрения 10
3. Нереализованные способности
4. Основная форма
5. Способы сотворения программки
5.1 Матричные преобразования
5.2 Создание одноцветного треугольника
6. программка
ЗАКЛЮЧЕНИЕ
СПИСОК ЛИТЕРАТУРЫ
ВСТУПЛЕНИЕ
Любой вещественный объект, имеющий форму, является большим, как следует, его положение в пространстве можно задать при помощи трёх координат X, Y, Z. Итог хоть какого вещественного производства, дома, авто, станки, можно представить как 3-х мерную модель и показать эту модель на мониторе компа при помощи соответственной программки.
Нет необходимости создавать раздельно программку для сотворения моделей раздельно домов, раздельно станков и их устройств, раздельно для каров и т. д. Еще разумней сделать одну программку, которая может учесть специфику хоть какой отрасли и создавать, фактически любые 3-х мерные модели.
Программирование уже издавна не стало быть уделом энтузиастов. Современный программер — не писатель либо ученый, а квалифицированный рабочий. Прошли те времена, когда, удавалось «удовлетворять свой Энтузиазм за счет страны«: месяцами изучить начальный код какой-либо совсем никчемной в практическом плане утилиты, забираться в недра начальных текстов оригинально изготовленных компонент.
естественно, маленькому проценту разрабов по долгу службы необходимы глубочайшие специальные познания, но от подавляющего большинства программистов сейчас требуется, до этого всего, умение писать программки очень стремительно и без ошибок. К примеру программка, представленная в этом проекте просит малое количество интеллектуальных издержек и времени юзера либо программера высочайшего уровня для заслуги результата. Программер высочайшего уровня — это программер, составляющий свои программки на базе разработок остальных программистов. Под разработками остальных программистов будем осознавать разработанные ими средства программирования: (язык программирования, функции, процедуры, утилиты, библиотеки, модули, составляющие, драйвера и т. д.
Хоть какой природный процесс, которым человек пробует управлять(повлиять на него с прогнозируемыми последствиями), должен быть поначалу представлен в виде модели той либо другой степени трудности. Спецам в области компьютерных технологий приходится брать такие модели либо их фрагменты для следующего объединения из определенных предметных областей, общих познаний и представлений о внешнем мире и создавать свою модель для решения поставленной задачки. Опосля этого, беря во внимание индивидуальности работы вычислительного устройства (компа) и избранного языка программирования – разрабатывать метод реализации модели, программировать(кодировать) этот метод. Не считая реализации главный функции, нужно помыслить о вспомогательных функциях(контроль входных данных, проверка адекватности результата, человека) имеют дискретную природу (дискретный
(лат. diskretus) – разбитый, прерывающийся). нрав (алфавит машинного языка двухсимвольный, огромное количество состояний микропроцессора конечное), потому неважно какая модель, реализованная в виде машинного кода, дискретна. Потому для реализации рассматриваемой программки нужно соприкоснуться с некими областями дискретной арифметики.
1. Математические объекты
1.1 Группы
Группа — оперативное огромное количество, в каком действует процедура умножения и которое подчинено последующим условиям:
1) замкнутости: для каждой пары g1g2=g3, при этом g3 должен принадлежать группе G: g1g2 ÎG; g1g2 = g3 ÎG;
2) наличия тождественного элемента e: посреди огромного количества частей группы G справедливы равенство eg = g; ge = g; e,gÎG;
3) наличие оборотных частей: для всякого g из G должен отыскаться единственный ему оборотный элемент g, принадлежащий G, при умножении на который вышел тождественный элемент e. e = q×q; e,g,gÎG;
4) ассоциативности: для всех трёх частей g1, g2, g3 из G справедливо равенство: g1(g2g3) = (g1g2)g3. Условия ассоциативности производится для квадратных матриц.
Линейное преобразование A вектора x в вектор c осуществляется при помощи квадратной матрицы y = Ax.
Если есть прямое преобразование, то обязано быть и оборотное, при условии, что A имеет A. y = Ax;
x = Tx ;y = Tx ; Как следует Ty = ATx; Умножим с лева на T , получим y = TATx;
Отсюда следует формула прямого преобразования подобия.
Формулу оборотного преобразования подобия можно получить, умножив на T с права:
Если Ax = lx; то ненулевой вектор x является своим вектором. Для нахождения собственных векторов употребляют формулу
*
где E — единичная матрица.
Корешки многочлена P(l) = det(A — lE) = 0 подставляют в формулу
и получают собственные векторы x.
Группы, лишившись собственной предметной области, не нужны в истинное время. Хотя ранее, приблизительно 40 лет вспять, теория групп была всераспространена из-за того, что группы тесновато увязывалась с базовыми областями естествознания — физикой простых частиц, квантовой механикой, физикой твёрдого тела и кристаллографией.
В данной программке все объёмные фигуры состоят из треугольных граней, любая из которых содержит три верхушки. Задав положение объёмного тела в пространстве, мы задаём положение всех вершин. Любая верхушка представляется как вектор.
Чтоб конвертировать объект,(повернуть, удалить, приблизить, сжать, растянуть и т. п.) нужно любой вектор каждой грани объекта помножить на матрицу преобразования, к примеру на матрицу поворота вокруг оси Y.
Ах так эта матрица будет смотреться:
1.2 Графы
Хоть какое объемное тело, как уже было сказано выше, можно выстроить при помощи треугольных граней, любая из которых имеет хотя бы одну общую верхушку с примыкающей гранью. Схематично каждую грань можно изобразить как совокупа вершин, соединенную контурными линиями. Контурные полосы — это полосы описывающие контур. Контур — это замкнутый путь. Таковым образом, грань, содержащая верхушки упорядоченно соединённые рёбрами представляет собой направленный граф либо Орграф.
Граф G
как математический объект – это совокупа 2-ух множеств: непустого огромного количества вершин V
и огромного количества ребер E
, элементы которого представляет собой неупорядоченные (для нацеленного графа – упорядоченные) пары частей из огромного количества V
.
G (V,E) = áV; Eñ, n(V) > 0, E Ì V ´ V,
где для неориентированного графа E
=
E
–1
(бинарное отношение E
симметрично).
Малый граф состоит из одной верхушки.
Любому неориентированному графу можно поставить в соответствие направленный граф, в каком каждое ребро заменено 2-мя обратно нацеленными ребрами, инцидентными этим же верхушкам.
Пусть v
1
и v
2
– верхушки, e
1
= (v
1
, v
2
) – соединяющее их ребро.
Тогда верхушка v
1
и ребро e
1
инцидентны, верхушка v
2
и ребро e
1
также инцидентны. Два ребра, инцидентные одной верхушке, именуются смежными; две верхушки, инцидентные одному ребру, также именуются смежными.
Обычно граф изображают на плоскости в виде диаграммы: верхушки – точками, ребра – линиями, соединяющими инцидентные верхушки.
Огромное количество вершин и огромное количество рёбер для конечных графов задаются, как правило, перечислением. Может быть задание графа описанием дела инцидентности.
1 Отношение инцидентности задано матрицей смежности:
– столбцы и строчки матрицы – верхушки графа;
– для смежных вершин элемент матрицы равен1, для других – 0;
– для неориентированного графа эта матрица постоянно симметрична;
– число рёбер равно числу единиц выше либо ниже главной диагонали матрицы ( включая элементы на диагонали).
2 Отношение инцидентности задано матрицей инцидентности:
– столбцы матрицы соответствуют верхушкам графа, а строчки – рёбрам;
– если ребро ei
инцидентно верхушке vj
, то элемент матрицы eij
=1, в неприятном случае – eij
= 0.
Таковым образом, в каждой строке одна либо две единицы, другие нули (для петли две единицы).
Для нацеленного графа при заполнении матрицы:
eij
= –1,если vj
– начало ребра;
eij
=1,если vj
–конец ребра;
eij
= a (где a – хоть какое число, не считая –1,1,0),если ребро – петля в верхушке vj
;
в других вариантах eij
= 0.
3 Граф задан перечнем ребер.
ei
vi,
vj
1
a, b
2
b, d
…
Примечание. тут ei
–ребро, vi
,
vj
– пара вершин, соединяемых сиим ребром.
Граф связан, если неважно какая пара его вершин связана ребром.
Граф без кратных ребер именуют полным, если любая пара вершин соединена ребром.
Граф H
именуют частью графа G
, если огромное количество вершин графа H
принадлежит огромному количеству вершин графа G
и огромное количество рёбер графа H
принадлежит огромному количеству рёбер графа G
, т.е.:
V(H) ÌV(G); E(H) ÌE(G).
часть графа H
именуется суграфом, если она содержит все верхушки графа G
.
Суграф H
для неориентированного графа G
именуется покрывающим суграфом, если неважно какая верхушка крайнего инцидентна хотя бы одному ребру из H
.
Подграф G
(
U
)
графа G
на огромном количестве вершин U
(
U
Ì
V
)
– это часть графа, которой принадлежат все ребра с обоими концами из U
.
Звёздный граф для верхушки v
(
v
Î
G
)
состоит из всех рёбер с началом и концом в верхушке v
. Огромное количество вершин звёздного графа состоит из верхушки v
и остальных смежных с ней вершин.
Маршрутом в единичном связном графе G
именуется таковая конечная последовательность ребер (e
1,
e
2….
en
), в какой любые два примыкающих ребра имеют общую инцидентную верхушку.
Верхушка v
о
, инцидентная ребру e
1
и не инцидентная ребру e
2
, именуется началом маршрута в графе G
.
Верхушка vn
, инцидентная ребру en
и не инцидентная ребру en
-1
, именуется концом маршрута.
Число ребер маршрута именуется его длиной.
Если верхушки v
о
и vn
совпадают, то маршрут именуется повторяющимся (либо просто циклом).
Отрезок конечного либо нескончаемого маршрута сам является маршрутом.
Маршрут в графе G
именуется цепью
, если все ребра в последовательности различны, и обычной цепью
, если все верхушки, через которые проходит маршрут (а означает и ребра) различны.
Иными словами, в цепи ребро может повстречаться не наиболее 1-го раза, а в обычной цепи верхушка – не наиболее 1-го раза.
Молвят, что две верхушки в графе соединены, если существует соединяющая их цепь. Граф, в каком все верхушки соединены, именуется связным
.
Расстоянием
меж 2-мя верхушками графа именуется малая длина обычной цепи, связывающей эти верхушки (обозначение d(v¢,v²)).
Протяженностью
меж 2-мя верхушками графа именуется наибольшая длина обычной цепи, связывающей эти верхушки (обозначение g(v¢,v²)).
В личном случае расстояние и протяженность меж верхушками могут быть схожими.
2. Справка по работе с программкой
2.1 Предназначение программного продукта
Данный программный продукт предназначен для построения всех больших фигур с следующей их демонстрацией на дисплее монитора. Необходимо подчеркнуть, что фигуры могут быть как выпуклыми, так и вогнутыми, но непременно не обязано нарушаться условие целостности. Это означает, что ни одна часть объекта не обязана существовать раздельно, а обязана быть хоть каким-либо своим элементом присоедененной к объекту. Другими словами два огромного количества: вершин и рёбер, которые получаются, если пройти по контуру, от первой верхушки к крайней, представляют собой, согласно определению, направленный граф и имеют маршрут. Сделанный объект можно сохранить на диске, также загрузить с него, чтоб поглядеть либо отредактировать итог работы. Для построения трехмерных объектов можно применять некие доступные примитивы, а конкретно грань и куб. Любая грань, в создаваемом объекте, имеет собственный цвет, избираемый программкой случайным образом.
2.2 Обучение работе с программным продукт
ом
Опосля загрузки приложения покажется окно опции, в каком нужно установить разрешение цвета, которое непременно обязано совпадать с разрешением цвета текущего графического режима, к примеру 16 бит (рис. 1).
В неприятном случае на форме будет наблюдаться некоторое аномальное графическое изображение, которое будет некорректно отражать итог работы, что, делает всякую работу неосуществимой. Дальше необходимо установить разрешение текущего графического режима в пикселях. Это необходимо для полноэкранного режима просмотра. Опосля установления графического режима покажется рабочее окно, разделённое на четыре части — четверти. Любая четверть является плоскостью проекций, размещение которых соответствует принципам начертательной геометрии: верхняя левая — передная, правая верхняя — вертикальная, нижняя левая — горизонтальная. Нижняя — правая четверть не является плоскостью проекций, по этому там отрисовывать недозволено. Точки задаются нажатиями левой клавиши мыши. Если точек больше одной, то они соединяются линией, если точек три, то они образуют треугольник, где любая точка является верхушкой.
Следует держать в голове, что мы строим трёхмерное изображение не по точкам либо по линиям, а по треугольным граням, и проецируем не точки, а грани, по этому если точка поставлена в одной плоскости проекций, то и другие две точки данной грани должны быть поставлены в данной нам же плоскости. Для построения хоть какой объёмной фигуры довольно любые две плоскости. 3-я
Набросок 1
Набросок 2
Опосля того, как объект спроецирован на плоскости проекций необходимо надавить клавишу «Просмотр», находящуюся на форме (рис. 2), чтоб поглядеть на большой объект в перспективе, и получить возможность сохранить документ. Если объект сотворен, но небыла нажата кнопка «Просмотр», то опосля выбора функции «Сохранить как» информация не будет сохранена. Опосля нажатия клавиши «Просмотр» покажется однотонное окно чёрного цвета в каком будет находиться трёхмерный объект, на который можно поглядеть со всех сторон, вращая вокруг осей X,Y,Z нажатиями соответственных кнопок «X», «Y», «Z», при этом не имеет значения большая кнопка либо нет. Просмотрев итог сотворения объекта, можно его достроить, нажав на форме клавишу «Добавить». Опосля нажатия данной нам клавиши опять покажется окно с 3-мя плоскостями проекций, в каком будут находиться проекции объекта и к которым можно добавить новейшие проекции. Следует увидеть, что вращение трёхмерного объекта в перспективе практически не приводит к изменению его положения в пространстве. Другими словами если опосля вращения объекта перейти к плоскостям проекций, картина не поменяется, и опосля перехода назад к перспективе объект будет находиться в начальном положении: его положение в перспективе будет соответствовать его проекциям на плоскостях проекций.
Добавление примитива: «Куб», осуществляется путём чертежа полосы на одной из плоскости проекций, причём, если на уровне мыслей представить проекции данной нам полосы на абсциссу и ординату, то стороной куба будет наименьшая из проекций.
Если опосля сотворения 1-го трёхмерного объекта загрузить иной объект с диска, то 1-ый объект уничтожится, а на его месте покажется 2-ой, который загружен с диска. Это разъясняется тем, что загруженный объект не добавляется к уже имеющемуся, зато можно напротив, как уже было описано выше, к загруженному объекту добавить примитивы (грани, кубы).
В программном продукте действительны такие многофункциональные клавиши:
Esc — выход из программки в операционную систему;
F1 — справка о программном продукте;
F9 — режим просмотра ;
X — вращение объекта вокруг оси X;
Y — вращение объекта вокруг оси Y;
Z — вращение объекта вокруг оси Z;
В программном продукте есть такие пункты меню «файл«, «Вид», «Помощь».
В пт меню «файл«, содержатся функции для работы с файлами. Выбрав опцию «Сделать», создаётся новенькая форма для сотворения объекта. Если поменять длину либо ширину формы и избрать опцию «Сделать», то рабочее окно воспримет размеры, надлежащие новеньким размерам формы. Причём длина и ширина будут в любом случае схожи.
При помощи функции «Открыть» можно открыть, файл, содержащий трёхмерный объект. Функция «Сохранить как» нужна для того, чтоб записать трёхмерный объект в файл.
Для загрузки и сохранения файлов в данном программном продукте, нужно и довольно указать имя файла без разширения, либо с разширением, состоящим из всех непременно трёх знаков.
Функция «Выход» создана для выхода из программки в операционную систему.
В пт меню «Вид» можно задать полноэкранный режим формы. Пункт меню «Помощь» содержит опцию «Справка», выбор которой сопровождается открытием документа, содержащего подробную справку о программном продукте.
Так же пункт меню «Помощь» содержит опцию «О программке», в какой можно выяснить короткую информацию о программном продукте и его разрабе.
2.3 Ограничения внедрения
В программке лучше применять 16 битный режим, причём разрешение графического режима, выраженное в пикселях, не имеет значения. Допускаются лишь 8 битные, 16 битные и 32 битные графические режимы. Во всех других режимах, к примеру, 2 битных, 4 битных, 24 битных программка работать не будет.
При использовании различных графических режимов рабочие окна, места на форме, где задаются грани, будут иметь разный вид (различные цвета линий разметки и линий проекций). Это соединено с тем, что цвета в программном продукте не имеют значения. Принципиально чтоб проекции на плоскости проекций были видны, и соответствовали изображаемому объекту. цвета трёхмерного объекта, совершенно выбираются случайным образом, по этому тут важен не цвет, а форма объекта.
При загрузке трёхмерного объекта из файла не непременно указывать разширение файла, либо если уж оно обозначено, то обязано содержать не наименее трёх знаков опосля точки. Это разъясняется тем, что программка сохраняет объект в 2-ух файлах. Один с разширением «Res», а иной «Dat». При всем этом не принципиально какое разширение укажет юзер, оно всё равно отбрасывается программкой.
При открытии файла программка отбрасывает обозначенное юзером разширение, если оно есть, и добавляет к имени файла своё. Когда программка отбрасывает разрешение, она отбрасывает три знака, находящиеся опосля точки. Потому если юзер указал разширение, принципиально, чтоб оно состояло не наименее чем из трёх знаков, по другому при открытии файла произойдёт ошибка.
Проектировать грани необходимо при помощи задания трёх вершин, причём если задание проекций трёх вершин начато в одной плоскости проекций, то все эти три проекции должны быть заданы в данной нам плоскости, а не раздельно: одна проекция точки на передней, иная на горизонтальной, 3-я на профильной. Все три проекции точки должны быть в одной плоскости проекций. Это соединено, как уже говорилось, с тем что проецируются не точки либо полосы, а грани.
Рабочее окно, которое выводит графическую информацию, быть может лишь квадратным, по этому изменяя длину либо ширину рабочего окна, необходимо учесть, что программка уравняет эти оба параметра.
3. Нереализованные способности
Самый основной элемент, который нужно воплотить — это полное отсутствие ошибок. На данном шаге разработки ошибок не выявлено, но гарантии их отсутствия не существует.
Потом следовало бы прирастить количество примитивов: добавить к грани и кубу, возможность сотворения призмы, многоугольника, цилиндра, сферы, диска. наличие этих фигур обеспечило бы лёгкость сотворения всех объёмных тел. Поэтому что, к примеру призма, в базе которой верный восьмиугольник содержит 32 треугольной грани. естественно юзеру вручную весьма проблематично сделать эту фигуру, потому её наличие в коллекции примитивов значительно облегчило бы ему работу.
Так же можно было бы создать способы наложения различных текстур на трёхмерные объекты. Текстуры — это двухмерные графические изображения, которые, наложив как фотографию на грань объекта, превращают обычный куб в телек, дом с окнами, стиральную машинку и остальные кубические объекты.
Текстуры бывают: аффинные, четкие (перспективно — корректные), параболические. Текстуры можно накладывать на все трёхмерные объекты.
Можно было бы воплотить прозрачность трёхмерных объектов. к примеру, если необходимо было бы сделать объёмное тело из стекла. В этом случае нужно было бы применять прозрачность. Трёхмерные объекты, как понятно, создаются двухмерными точками. Потому чтоб сделать прозрачность нужно перед выводом каждой точки объекта в определённом месте получать цвет уже стоящей на этом месте точки, если она есть, потом вывести точку цвет которой будет средним меж цветом точки, которая ставится и которая уже стоит. Таковым образом, за выводимым трёхмерным объектом будет видно размещение другого объёмного тела. Это и есть прозрачность.
4. Основная форма
Основная форма содержит такие составляющие как:
Main
Menu
— Предназначен для прибавления к главной программке головного меню. Является компонентом Standart. Компонент MainMenu не зрительный компонент. Редактор меню вызывается опосля выбора функции Items. В редакторе можно создавать пункты меню проименовывая их с помощь характеристики Caption. Переходя от одной компоненте к иной можно при помощи такого же характеристики Caption отредактировать пункты меню. Чтоб вставить линию разделитель необходимо в свойстве Caption первой позицией указать знак «-» (дефис). Опосля окончания сотворения пт меню редактор меню нужно закрыть.
BitBtn
— Этот компонент предназначен для сотворения клавиши с картинкой. В системе имеется набор готовых шаблонов.
Опосля размещения объекта на форме изображение, помещаемое на клавишу, задаётся в свойстве Glyph (Значок). При всем этом вызывается редактор, при помощи которого выбирается подходящая картина (в формате BMP). Любая таковая картина может состоять из 4 частей, равных по ширине. 1-ая часть — изображение клавиши в обыкновенном состоянии, 2-ая — изображение «отключённой» клавиши, 3-я — изображение клавиши опосля щелчка мыши, изображение на нажатой кнопочке. Число составных частей задаётся в свойстве NumGlyph (от 1 до 4). Расстояние от рисунки до границ клавиши (в пикселях) можно указать в свойстве Margin. В свойстве Kind задаётся реакция клавиши на щелчок.
Button
— Компонент предназначен для сотворения клавиш на форме и обработки действия нажатия клавиши. Размещен на панели Standart. имя клавиши указывается в поле Name, а выводимый текст на кнопочке в свойстве Caption.
Combo
Box
— Компонент Поле со перечнем. Представляет собой вариант перечня, с присоединённым доп полем, в каком отображается избранный элемент перечня. Это поле может употребляться для ввода новейших частей либо для резвого поиска частей по исходным символам. Если на дисплее отображается лишь присоединённое поле («раскрывающийся перечень»), то для раскрытия перечня можно применять клавиатурную комбинацию Alt+Вниз.
Open
Dialog
— Компонент предназначен для выбора файла с целью следующего открытия; характеристики класса TOpenDialog приведены в табл. 1
Таблица 1 — характеристики класса TOpenDialog
Параметров
о
Назначен
и
е
DefaultExt
расширение имени, применяемое по дефлоту. Добавляется в конец выбраного юзером имени файла, если расширение не обозначено очевидно.
FileName
Выбранное юзером имя файла вкупе с полным путём поиска
Files
Перечень избранных имён файлов. В свойстве Options должен быть включён флаг ofAllowMultiSelect
Filter
Набор масок, в согласовании с которыми отбираются названия файлов для отображения в диалоговом окне. Любая маска состоит из 2-ух частей: наименования и шаблона, — разделённых эмблемой |. Одному наименованию могут соответствовать несколько шаблонов. Маски отделяются друг от друга эмблемой |
FilterIndex
Номер текущей маски. Нумерация начинается с 1
HistoryList
Перечень ранее избранных файлов (тип Strings)
InitialDir
Текущий каталог, содержимое которого отображается при первом открытии диалогового окна
Options
Набор флагов, определяющих окна выбора файлов.
Title
Заголовок диалогового окна
Save
Dialog
— Этот компонент фактически ничем не различается от компонента OpenDialog кроме нескольких опций, специфичных для процесса сохранения файла.
Label
— Компонент находится на панэле Standart. Предназначен для вывода текста на форме при помощи характеристики Text.
TDXTimer
— Компонент находится на панели DelphiX. В программках, выполняющих деяния, связанные с моделированием либо обработкой графики, созданных для общения с юзером в настоящем режиме времени либо выполняющих длительные вычисления, нужна компонента TDXTimer.
При помощи компонента TDXTimer можно включить генерацию сообщений, поступающих от системного таймера Windows с данной периодичностью (в миллисекундах) и делать определённую часть действий конкретно в обработчике этого действия. Это одна из способностей многозадачности приложений Windows.
TDXDraw
— Этот компонент находится на вкладке DelphiX. Он предназначен для вывода двухмерной и трёхмерной графики, используя DirectDraw и DirectX. Разрешение выводимой графики задаётся в свойстве Display. Вид курсора задаётся в свойстве Cursor. способ DXDraw.Surface.Fill(Color) — заполняет видеостраницу цветом Color. При помощи характеристики DXDraw.Surface.pixels[x, y]:=Color — ставится точка на виртуальной видеостранице. При помощи способа DXDraw.Flip — невидимая видеостраница делается видимой. Схема обрисовки изображения на 2-ух видеостраницах: видимой и не видимой делает вероятной сделать анимацию без мигания изображения.
5. способы сотворения программки
Для сотворения трёхмерных объектов нужно уметь преобразовывать трёхмерную графику в двухмерную. Ведь процедура вывода точки имеет лишь две координаты, задающие положение точки (x, y), как следует, реально имеется дело с двухмерной графикой. Для начала следует принять систему трёхмерных координат:
Набросок 3
тут знаками x, y, z обозначены положительные направления осей Ox, Oy и Oz соответственно. Также предполагается, что камера недвижна и находится в точке с координатами (0,0,-dist), ось зрения камеры ориентирована по оси Oz, а конкретно в точку (0,0,0) (т.е. camera target = (0,0,0)), ось Ox исходя из убеждений камеры ориентирована слева вправо, ось Oy — снизу ввысь, ось Oz — вглубь экрана. Размер экрана — xSize на ySize пикселей.
тут и дальше употребляются обозначения:
sx, sy
координаты проекции точки на дисплее
x, y, z
3D координаты точки,
Dist
расстояние от камеры (она находится в точке (0,0,-dist)) до начала координат,
Для созданя трёхмерных объектов нужны примитивы. Таковыми примитивами являются простые трёхмерные объекты — треугольные грани. У каждой треугольной грани есть три верхушки — три точки, имеющие трёхмерные координаты. Чтоб спроецировать их на плоскость, т. е. из трёхмерных координат x,y,z получить двухмерные sx, sy нужно воспользоваться формулой:
sx = xSize/2+x*dist/(z+dist); sy = ySize/2-y*dist/(z+dist);
5.1 Матричные преобразования
В данной нам главе коротко представлены простые понятия о матричных преобразованиях, являющихся составной частью линейной алгебры, и дискретной арифметики (см. раздел Группы).
Введем несколько определений. n-мерный вектор, он же вектор размерности n, он же вектор размера n: упорядоченный набор n реальных чисел. Матрица размера m на n (будет обозначаться как m*n, mxn): таблица размера m на n, в каждой клеточке которой — действительное число.
Вот вам наглядный пример матрицы 3×3:
[ 15 y*z 0.6 ]
[ 7 -3 91 ]
[ sin(x) 0.123 exp(t) ]
Вектор будем записывать в столбик и разглядывать его как матрицу размера n*1.
Операция скалярного произведения векторов: определена для 2-ух векторов схожих размеров. Итог есть число, равное сумме произведений соответственных частей векторов. Пример:
[ 1 ] [ 4 ]
[ 2 ] * [ 5 ] = 1*4 + 2*5 + 3*6 = 32
[ 3 ] [ 6 ]
Операция векторного произведения: определена для (n-1) вектора схожего размера n. Итог — вектор, при этом, перпендикулярный всем множителям.
Замечание:
итог изменяется от перестановки мест множителей!
Формально определяется как определитель матрицы, 1-ая строчка которой все есть базовые вектора, а все следующие — надлежащие координаты всех множителей. Так как она нужна лишь для 3D места, мы определим векторное произведение 2-ух 3D векторов очевидно:
[ Ax ] [ Bx ] | i j k | [ Ay*Bz-Az*By ]
AxB = [ Ay ] x [ By ] = | Ax Ay Az | = [ Az*Bx-Ax*Bz ]
[ Az ] [ Bz ] | Bx By Bz | [ Ax*By-Ay*Bx ]
Операция сложения 2-ух матриц: определена для матриц схожих размеров. Любой элемент суммы (другими словами, каждое число в таблице) приравнивается сумме соответственных частей слагаемых-матриц. Пример:
[ 1 x 500 ] [ 8 a 3 ] [ 9 a+x 503 ]
[ 2 y 600 ] + [ 9 b 2 ] = [ 11 b+y 602 ]
[ 3 z 700 ] [ 10 c 1 ] [ 13 c+z 701 ]
Операция умножения матрицы на число: определена для хоть какой матрицы и хоть какого числа; любой элемент результата приравнивается произведению соответственного элемента матрицы-множителя и числа-множителя.
Операция умножения 2-ух матриц: определена для 2-ух матриц таковых размеров a*b и c*d, что b = c. к примеру, если b = c, но a ¹ d, то при перестановке множителей операция будет совершенно не определена. Результатом умножения матрицы A размером a*b на матрицу B размером b*d будет матрица C размером a*d, в какой элемент, стоящий в строке i и столбце j, равен произведению строчки i матрицы A на столбец j матрицы B. Произведение строчки на столбец определяется как сумма произведений соответственных частей строчки и столбца. к примеру, умножение строчки на столбец (они должны быть равной длины, потому и такие ограничения на размеры матриц):
[ 4 ]
[ 1 2 3 ] * [ 5 ] = 1*4 + 2*5 + 3*6 = 32
[ 6 ]
А чтоб перемножить две матрицы, нужно эту операцию сделать для всякого элемента. Вот вам наглядный пример:
[ 1 2 3 ] [ 0 3 ] [ 1*0+2*1+3*2 1*3+2*4+3*5 ]
[ 4 5 6 ] * [ 1 4 ] = [ 4*0+5*1+6*2 4*3+5*4+6*5 ] = …
[ 7 8 9 ] [ 2 5 ] [ 7*0+8*1+9*2 7*3+8*4+9*5 ]
Умножение и сложение матриц владеют практически этим же набором параметров, что и обыденные числа, хотя некие обычные характеристики не производятся (к примеру, A*B ¹ B*A); по сути требуется знать, что произведение вида A*B*C*D*… не зависит от того, как расставить скобки. Либо, что
A*(B*C) = (A*B)*C.
Хоть какое движение (другими словами преобразование места, сохраняющее расстояние меж точками) в трехмерном пространстве, согласно аксиоме Шаля, быть может представлено в виде суперпозиции поворота и параллельного переноса, другими словами поочередного выполнения поворота и параллельного переноса. Потому основная часть информация о поведении объекта — это его смещение, ось поворота и угол поворота. Потому нам довольно знать, как создать два преобразования — перенос и поворот.
Перенос точки (точки будут также рассматриваться как вектора с началом сначала координат и концом в фактически точке) с координатами (x,y,z) на вектор (dx,dy,dz) делается обычным сложением всех координат. Другими словами итог — это (x+dx,y+dy,z+dz). Подобно сложению вектора-точки с вектором-переносом.
Разглядим для примера поворот точки (x,y,z) относительно оси z. В этом случае z не изменяется, а (x,y) изменяются так же, как и при 2D повороте относительно начала координат.
Покажем координаты точки A’ — результата поворота A(x,y) на угол alpha (рис. 4).
Набросок 4
Пусть r = sqrt(x*x+y*y). Пусть угол AOx равен phi, тогда из рисунка видно, что cos(phi) = x/r, sin(phi) = y/r. Угол A’OA равен по условию alpha. Отсюда
x’ = r*cos(alpha+phi) = r*(cos(alpha)*cos(phi)-sin(alpha)*sin(phi)) =
= (r*cos(phi))*cos(alpha)-(r*sin(phi))*sin(alpha) =
= x*cos(alpha)-y*sin(alpha)
y’ = r*sin(alpha+phi) = r*(cos(alpha)*sin(phi)+sin(alpha)*cos(phi)) =
= (r*cos(phi))*sin(alpha)+(r*sin(phi))*cos(alpha) =
= x*sin(alpha)+y*cos(alpha)
Для трехмерного варианта, таковым образом
x’ = x*cos(alpha)-y*sin(alpha)
y’ = x*sin(alpha)+y*cos(alpha)
z’ = z
Подобные формулы получатся и для остальных осей поворота (другими словами Ox, Oy). Поворот относительно случайной оси, проходящей через начало координат, можно создать при помощи этих поворотов — создать поворот относительно Ox так, чтоб ось поворота стала перпендикулярна Oy, потом поворот относительно Oy так, чтоб ось поворота совпала с Oz, создать поворот, а потом оборотные повороты относительно Oy и Ox.
Вспомним о матрицах и векторах и пристально поглядим на выведенные формулы для поворота. Можно увидеть, что
[ x’ ] = [ cos(alpha) -sin(alpha) 0 ] [ x ]
[ y’ ] = [ sin(alpha) cos(alpha) 0 ] [ y ]
[ z’ ] = [ 0 0 1 ] [ z ]
Другими словами поворот на угол alpha задается одной и той же матрицей, и при помощи данной нам матрицы (умножая ее на вектор-точку) можно получить координаты повернутой точки. С одной стороны умножение матрицы на вектор просит больше операций, чем расчет x’ и y’ по формулам.
Нос иной удобство матриц для заключается как раз в свойстве
A*(B*C) = (A*B)*C. Пусть делается несколько поворотов попорядку, к примеру, 5 (столько, сколько нужно для поворота относительно случайной оси), и пусть они задаются матрицами A, B, C, D, E (A — матрица самого первого поворота, E — крайнего). Тогда для вектора p мы получаем
p’ = E*(D*(C*(B*(A*p)))) = E*D*C*B*A*p = (E*D*C*B*A)*p = (E*(D*(C*(B*A))))*p = T*p,
где T = (E*(D*(C*(B*A)))) матрица преобразования, являющегося композицией 5 поворотов. Посчитав один раз эту матрицу, можно в предстоящем применить достаточно сложное преобразование из 5 поворотов к хоть какому вектору при помощи всего 1-го умножения матрицы на вектор.
Таковым образом, можно задать хоть какой поворот матрицей, и неважно какая композиция поворотов также будет задаваться матрицей, которую можно достаточно просто посчитать. Но еще есть параллельный перенос и масштабирование.
По сути, эти преобразования тоже просто записываются в виде матриц. Лишь заместо матриц 3×3 и 3-мерных векторов употребляются так именуемые однородные 4-мерные координаты и матрицы 4×4. При всем этом заместо векторов вида
[ x ]
[ y ]
[ z ]
употребляются вектора вида
[ x ]
[ y ]
[ z ]
[ 1 ]
а заместо случайных матриц 3×3 употребляются матрицы 4×4 такового вида:
[ a b c d ]
[ e f g h ]
[ i j k l ]
[ 0 0 0 1 ]
Видно, что если d = h = l = 0, то в итоге внедрения всех операций выходит то же самое, что и для матриц 3×3.
Матрица параллельного переноса сейчас определяется как
[ 1 0 0 dx ]
[ 0 1 0 dy ]
[ 0 0 1 dz ]
[ 0 0 0 1 ]
Матрицу масштабирования можно найти и для матриц 3×3, и для матриц 4×4:
[ kx 0 0 ] [ kx 0 0 0 ]
[ 0 ky 0 ] либо [ 0 ky 0 0 ]
[ 0 0 kz ] [ 0 0 kz 0 ]
[ 0 0 0 1 ]
где kx, ky, kz — коэффициенты масштабирования по подходящим осям.
Таковым образом, получаем последующее. Хоть какое необходимое нам преобразование места можно задать матрицей 4×4 определенной структуры, разной для различных преобразований. Итог поочередного выполнений нескольких преобразований совпадает с результатом 1-го преобразования T, которое также задается матрицей 4×4, вычисляемой как произведение матриц всех этих преобразований. Важен порядок умножения, потому что A*B ¹ B*A. Итог внедрения преобразования T к вектору [ x y z ] считается как итог умножения матрицы T на вектор [ x y z 1 ].
Докажем на примере, что A*B ¹ B*A. Пусть A — матрица переноса, B — поворота. Если поначалу перенести объект, а позже повернем относительно центра координат (это будет B*A), то итог не будет соответствовать результату, при котором поначалу объект поворачивают, а потом переносят (A*B).
5.2 Создание одноцветного треугольника
Изображение треугольника на дисплее — набор горизонтальных отрезков, при этом из-за того, что треугольник — фигура выпуклая, каждой строке экрана соответствует не наиболее 1-го отрезка. Потому довольно обойти все строчки экрана, с которыми пересекается треугольник, (другими словами, от малого до наибольшего значения (y) для вершин треугольника), и нарисовать надлежащие горизонтальные отрезки.
необходимо отсортировать верхушки так, чтоб верхушка A была верхней, C — нижней, тогда min_y = A.y, max_y = C.y, и нужно обойти все полосы от min_y до max_y. Разглядим какую-то линию sy, A.y <= sy <= C.y. Если sy < B.y, то она пересекает стороны AB и AC; если sy >= B.y — то стороны BC и AC. Известны координаты всех вершин, потому можно написать уравнения сторон и отыскать пересечение подходящей стороны с прямой y = sy. Получим два конца отрезка. Потому что не понятно, какой из их левый, а какой правый, необходимо сравним их координаты по x и обменяем значения, если необходимо. Рисуя этот отрезок, повторяя функцию для каждой строчки — получаем треугольник.
Рассматривая наиболее тщательно пересечения прямой
y = sy (текущей строчки) и стороны треугольника, к примеру AB, Получим уравнение прямой AB в форме x = k*y+b:
x = A.x+(y-A.y)*(B.x-A.x)/(B.y-A.y)
сейчас нужно подставить известное для текущей прямой
x = A.x+(sy-A.y)*(B.x-A.x)/(B.y-A.y)
Для остальных сторон пересечение ищется совсем буквально так же. к примеру:
// …
// тут сортируем верхушки (A,B,C)
// …
for sy = A.y to C.y begin
x1 = A.x + (sy — A.y) * (C.x — A.x) / (C.y — A.y);
if (sy < B.y)Then
x2 = A.x + (sy — A.y) * (B.x — A.x) / (B.y — A.y);
else
x2 = B.x + (sy — B.y) * (C.x — B.x) / (C.y — B.y);
if (x1 > x2) Then
begin
tmp = x1; x1 = x2; x2 = tmp;
end;
drawHorizontalLine(sy, x1, x2);
end;
нужно защититься от варианта, когда B.y = C.y — в этом (и лишь этом, поэтому как если C.y = A.y, то треугольник пустой и отрисовывать его не необходимо, либо можно отрисовывать горизонтальную линию; а если B.y = A.y, то sy >= A.y и до деления на B.y — A.y не дойдет) случае произойдет попытка деления на ноль.
// …
// тут сортируем верхушки (A,B,C)
// …
for sy = A.y to C.y begin
x1 = A.x + (sy — A.y) * (C.x — A.x) / (C.y — A.y);
if (sy < B.y)
x2 = A.x + (sy — A.y) * (B.x — A.x) / (B.y — A.y);
else begin
if (C.y == B.y)
x2 = B.x;
else
x2 = B.x + (sy — B.y) * (C.x — B.x) / (C.y — B.y);
end;
if (x1 > x2)Then
begin
tmp = x1; x1 = x2; x2 = tmp;
drawHorizontalLine(sy, x1, x2);
end;
// …
тут drawHorizontalLine(sy, x1, x2) — горизонтальная линия. Её создание не представляет трудности и код будетвыглядеть так.
//…
For i:=x1 to x2 do
PutPixel(i,sy,Color);
//…
6. программка
unit graph3;
interface
uses Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs,
DXClass, DXDraws, StdCtrls,graph, Menus,graph3D,figures, Buttons;
const Mode:Word=0;
type TLab = class(TForm)
Vid: TDXDraw;
Timer: TDXTimer;
Enter: TButton;
Menu: TMainMenu;
N1: TMenuItem;
N2: TMenuItem;
N3: TMenuItem;
N4: TMenuItem;
N5: TMenuItem;
N6: TMenuItem;
N7: TMenuItem;
N8: TMenuItem;
N9: TMenuItem;
OpenDialog: TOpenDialog;
SaveDialog: TSaveDialog;
N10: TMenuItem;
Space: TButton;
Box1: TComboBox;
Label1: TLabel;
OK: TButton;
Cancel: TButton;
Label6: TLabel;
BCube: TBitBtn;
BSide: TBitBtn;
Box6: TComboBox;
Label7: TLabel;
procedure FormCreate(Sender: TObject);
procedure TimerTimer(Sender: TObject; LagCount: Integer);
procedure VidMouseDown(Sender: TObject; Button: TMouseButton;
Shift: TShiftState; X, Y: Integer);
procedure EnterClick(Sender: TObject);
procedure N3Click(Sender: TObject);
procedure N4Click(Sender: TObject);
procedure N2Click(Sender: TObject);
procedure N9Click(Sender: TObject);
procedure N10Click(Sender: TObject);
procedure SpaceClick(Sender: TObject);
procedure FormKeyDown(Sender: TObject; var Key: Word;
Shift: TShiftState);
procedure FormKeyUp(Sender: TObject; var Key: Word;
Shift: TShiftState);
procedure CancelClick(Sender: TObject);
procedure OKClick(Sender: TObject);
procedure N8Click(Sender: TObject);
procedure BCubeClick(Sender: TObject);
procedure BSideClick(Sender: TObject);
procedure N6Click(Sender: TObject);
private
public
end;
var Lab: TLab;
implementation
{$R *.DFM}
const ScreenX:Word=640;
ScreenY:Word=480;
x1:integer=0;
y1:integer=0;
White=$FFFFFF;
View:Boolean=False;
Figure:Word=1;
Accept:Boolean=True;
sv:Word=0;
var c:char;S,SS,TMP:PPl;t:PTexture;
CS,CO,CC,CSc,CL:Word;Rot:TRot;
x0,y0,x2,y2:integer;r:Single;
Blue,Red,Yelow,M,N,SC:Word;
Keys:array[1..255]of Boolean;
Input:array[1..12]of procedure (x,y:integer;var E:Boolean);
Bol:Boolean;Option:array[1..5] of String;
o:array[1..MaxSide]of TPoint;
w:array[1..2]of TPoint;tmp_o:TPoint;
cx,cy:Word;oc:TPoint;ClicCub:array[0..MaxSide]of Boolean;
procedure LoadObject(Name:String;var Obj:PObj);
var f1:file of TPoint; a:TSides;
f2:file of Word;
S,Tmp:TPoint;i,j:word;Name2:String;
B:Boolean;
begin
For i:=1 to Length (Name)-4 do begin
Name2:=Name2+Name[i];
end;
Name:=Name2;
Assign(f2,Name+’.res’);
Reset(f2);
Read(f2,Obj^.Count);
close(f2);
Assign(f1,Name+’.dat’);
Reset(f1);
Read(f1,Obj^.o);
For i:=1 to Obj^.Count do
New(Obj^.Side[i],Create(Obj^.o.x,Obj^.o.y,Obj^.o.z,0,0,0,a));
For i:=1 to Obj^.Count do begin
inc(CS);
if(i mod 2<>0)Then
T:=ColorText(RGB(100+Random(155),100+Random(155),100+Random(155)),GMT)
else T:=T;
Obj^.Side[i]^.Texture:=T;
Obj^.Side[i]^.Mode:=Mode;
Obj^.Side[i]^.Alpha:=0;
Read(f1,S);
For j:=1 to 3 do
begin
Read(f1,S);
Obj^.Side[i]^.S[j]:=S;
SS^[i,j]:=S;
end;
end;
close(f1);
end;
procedure SaveObject(Name:String;var Obj:PObj);
var f1:file of TPoint;
f2:file of Word;B:Boolean;
S:TPoint;i,j:word;Name2:String;
begin
B:=False;
For i:=1 to Length(Name) do
if(Name[i]=’.’)Then B:=True;
if(B=True)Then begin
For i:=1 to Length(Name)-4 do begin
Name2:=Name2+Name[i];
end;
Name:=Name2;
end;
Assign(f2,Name+’.res’);
Rewrite(f2);
Reset(f2);
Write(f2,Obj^.Count);
close(f2);
Assign(f1,Name+’.dat’);
Rewrite(f1);
Reset(f1);
Write(f1,Obj^.o);
For i:=1 to Obj^.Count do begin
Obj^.Side[i]^.o:=tmp_o;
S:=Obj^.Side[i]^.o;
Write(f1,S);
For j:=1 to 3 do
begin
S:=Obj^.Side[i]^.S[j];
Write(f1,S);
end;
end;
close(f1);
end;
procedure CreateTMP(var Obj:PObj);
var i,j,k:Word;
begin
Obj^.o.x:=0;Obj^.o.y:=0;Obj^.o.z:=100;
For i:=1 to Obj^.Count do
begin
tmp_o:=Obj^.Side[i]^.o;
Obj^.Side[i]^.o:=o[i];
For j:=1 to 3 do
Obj^.Side[i]^.S[j]:=S^[i,j];
end;
end;
procedure ShowSide;
var i,j,k,cx,cy:Word;tmp:TPoint;
begin
Lab.Vid.Surface.Fill(0);
myForm(GetMaxX,GetMaxY);
For i:=1 to CS do begin
Line(Trunc(SS^[i,1].x),Trunc(SS^[i,1].y),Trunc(SS^[i,2].x),Trunc(SS^[i,2].y),White);
Line(Trunc(SS^[i,2].x),Trunc(SS^[i,2].y),Trunc(SS^[i,3].x),Trunc(SS^[i,3].y),White);
Line(Trunc(SS^[i,3].x),Trunc(SS^[i,3].y),Trunc(SS^[i,1].x),Trunc(SS^[i,1].y),White);
Line(Trunc(SS^[i,1].x),Trunc(SS^[i,1].z),Trunc(SS^[i,2].x),Trunc(SS^[i,2].z),White);
Line(Trunc(SS^[i,2].x),Trunc(SS^[i,2].z),Trunc(SS^[i,3].x),Trunc(SS^[i,3].z),White);
Line(Trunc(SS^[i,3].x),Trunc(SS^[i,3].z),Trunc(SS^[i,1].x),Trunc(SS^[i,1].z),White);
Line(Trunc(SS^[i,1].z*r),Trunc(SS^[i,1].y),Trunc(SS^[i,2].z*r),Trunc(SS^[i,2].y),White);
Line(Trunc(SS^[i,2].z*r),Trunc(SS^[i,2].y),Trunc(SS^[i,3].z*r),Trunc(SS^[i,3].y),White);
Line(Trunc(SS^[i,3].z*r),Trunc(SS^[i,3].y),Trunc(SS^[i,1].z*r),Trunc(SS^[i,1].y),White);
if(ClicCub[i]=True)Then begin
Line(Trunc(SS^[i,1].z),Trunc(SS^[i,1].y),Trunc(SS^[i,2].z),Trunc(SS^[i,1].y),White);
Line(Trunc(SS^[i,2].z),Trunc(SS^[i,1].y),Trunc(SS^[i,1].z),Trunc(SS^[i,3].y),White);
end;
end;
S[CS]:=SS[CS];
cx:=GMX div 2;
cy:=GMY div 2+1;
For i:=1 to 3 do
begin
S^[CS,i].x:=S^[CS,i].x-cx+dF;
S^[CS,i].y:=cy-S^[CS,i].y-dF;
S^[CS,i].z:=S^[CS,i].z-cx-dF;
end;
end;
procedure CreateSide(var Obj:PObj);
var cx,cy,i,j,k:Word;
begin
r:=1;
For i:=1 to Obj^.Count do
S[i]:=Obj^.Side[i]^.S;
TMP:=S;
Lab.Vid.Surface.Fill(0);
myForm(GetMaxX,GetMaxY);
cx:=GMX div 2;
cy:=GMY div 2+1;
For j:=1 to CS do
For i:=1 to 3 do
begin
TMP^[j,i].x:=TMP^[j,i].x-cx+dF;
TMP^[j,i].y:=cy-TMP^[j,i].y-dF;
TMP^[j,i].z:=TMP^[j,i].z-cx-dF;
end;
For i:=1 to CS do begin
Line(Trunc(TMP^[i,1].x),Trunc(TMP^[i,1].y),Trunc(TMP^[i,2].x),Trunc(TMP^[i,2].y),White);
Line(Trunc(TMP^[i,2].x),Trunc(TMP^[i,2].y),Trunc(TMP^[i,3].x),Trunc(TMP^[i,3].y),White);
Line(Trunc(TMP^[i,3].x),Trunc(TMP^[i,3].y),Trunc(TMP^[i,1].x),Trunc(TMP^[i,1].y),White);
Line(Trunc(TMP^[i,1].x),Trunc(TMP^[i,1].z),Trunc(TMP^[i,2].x),Trunc(TMP^[i,2].z),White);
Line(Trunc(TMP^[i,2].x),Trunc(TMP^[i,2].z),Trunc(TMP^[i,3].x),Trunc(TMP^[i,3].z),White);
Line(Trunc(TMP^[i,3].x),Trunc(TMP^[i,3].z),Trunc(TMP^[i,1].x),Trunc(TMP^[i,1].z),White);
Line(Trunc(TMP^[i,1].z*r),Trunc(TMP^[i,1].y),Trunc(TMP^[i,2].z*r),Trunc(TMP^[i,2].y),White);
Line(Trunc(TMP^[i,2].z*r),Trunc(TMP^[i,2].y),Trunc(TMP^[i,3].z*r),Trunc(TMP^[i,3].y),White);
Line(Trunc(TMP^[i,3].z*r),Trunc(TMP^[i,3].y),Trunc(TMP^[i,1].z*r),Trunc(TMP^[i,1].y),White);
end;
Flip(Lab.Vid);
end;
procedure LoadSide(var Obj:PObj);
var i,j,k:Word;TMP:PPL;
begin
r:=1;
For i:=1 to Obj^.Count do
S[i]:=Obj^.Side[i]^.S;
SS^:=S^;
Lab.Vid.Surface.Fill(0);
myForm(GetMaxX,GetMaxY);
For j:=1 to CS do
For i:=1 to 3 do
begin
SS^[j,i].x:=SS^[j,i].x+cx-dF;
SS^[j,i].y:=cy-SS^[j,i].y-dF;
SS^[j,i].z:=SS^[j,i].z+cx+dF;
end;
For i:=1 to CS do begin
Line(Trunc(SS^[i,1].x),Trunc(SS^[i,1].y),Trunc(SS^[i,2].x),Trunc(SS^[i,2].y),White);
Line(Trunc(SS^[i,2].x),Trunc(SS^[i,2].y),Trunc(SS^[i,3].x),Trunc(SS^[i,3].y),White);
Line(Trunc(SS^[i,3].x),Trunc(SS^[i,3].y),Trunc(SS^[i,1].x),Trunc(SS^[i,1].y),White);
Line(Trunc(SS^[i,1].x),Trunc(SS^[i,1].z),Trunc(SS^[i,2].x),Trunc(SS^[i,2].z),White);
Line(Trunc(SS^[i,2].x),Trunc(SS^[i,2].z),Trunc(SS^[i,3].x),Trunc(SS^[i,3].z),White);
Line(Trunc(SS^[i,3].x),Trunc(SS^[i,3].z),Trunc(SS^[i,1].x),Trunc(SS^[i,1].z),White);
Line(Trunc(SS^[i,1].z*r),Trunc(SS^[i,1].y),Trunc(SS^[i,2].z*r),Trunc(SS^[i,2].y),White);
Line(Trunc(SS^[i,2].z*r),Trunc(SS^[i,2].y),Trunc(SS^[i,3].z*r),Trunc(SS^[i,3].y),White);
Line(Trunc(SS^[i,3].z*r),Trunc(SS^[i,3].y),Trunc(SS^[i,1].z*r),Trunc(SS^[i,1].y),White);
end;
Flip(Lab.Vid);
end;
procedure InPut1(x,y:integer;var E:Boolean);
var i,j:integer;B:Boolean;
begin
if(sv=0)Then begin
o[CS].x:=0;
o[CS].y:=0;
o[CS].z:=0;
end;
if(M<sv)Then begin
PutPixel(x,y,Yelow);
Flip(Lab.Vid);
if(x<cx)and(y<cy)Then
begin
o[CS].x:=x-cx+dF;
o[CS].y:=cy-y-dF;
end
else if(x>cx)and(y<cy)Then
begin
o[CS].z:=x-cx-dF;
o[CS].y:=cy-y-dF;
end
else if(x<cx)and(y>cy)Then
begin
o[CS].x:=x-cx+dF;
o[CS].z:=x-cx-dF;
end
else begin
ShowMessage(‘Эта четверть не является плоскостью проекций!’);
E:=True;
end;
inc(M);
end
else
if (M<6+sv)Then begin
if(N>3)Then N:=0;
if(x>cx)and(y>cy)Then
else
if(N=0)Then begin
x0:=x;y0:=y;x1:=0;y1:=0;
N:=1;
end;
if(x<cx)and(y<cy)Then
begin
SS^[CS,N].x:=x;
SS^[CS,N].y:=y;
r:=1;
end
else if(x>cx)and(y<cy)Then
begin
SS^[CS,N].z:=x;
SS^[CS,N].y:=y;
r:=1;
end
else if(x<cx)and(y>cy)Then
begin
SS^[CS,N].x:=x;
SS^[CS,N].z:=y;
r:=GetMaxX/GetMaxY;
end
else begin
ShowMessage(‘Эта четверть не является плоскостью проекций!’);
E:=True;
end;
if(E=False)Then begin
x2:=X;y2:=Y;
if(x1<>0)and(y1<>0)Then begin
Line(x0,y0,x2,y2,Red);
Line(x1,y1,x2,y2,Red);
end
else putpixel(x2,y2,Red);
Flip(Lab.Vid);
x1:=x2;y1:=y2;
For i:=1 to CS-1 do
For j:=1 to 3 do
begin
if(SS^[CS,N].x+o[CS].x>=SS^[i,j].x+o[i].x-2)and(SS^[CS,N].x+o[CS].x<=SS^[i,j].x+o[i].x+2)
and(SS^[CS,N].y+o[CS].y>=SS^[i,j].y+o[i].y-2)and(SS^[CS,N].y+o[CS].y<=SS^[i,j].y+o[i].y+2)
and(SS^[CS,N].z+o[CS].z>=SS^[i,j].z+o[i].z-2)and(SS^[CS,N].z+o[CS].z<=SS^[i,j].z+o[i].z+2)Then Accept:=True;
end;
inc(N);
inc(M);
end;
end;
if(M=6+sv)and(Accept=True)Then begin
M:=0;N:=0;
ClicCub[CS]:=False;
ShowSide;
Flip(Lab.Vid);
New(Scene^.Camera[CC]^.Obj[CO]^.Side[CS],Create(0,0,100,o[CS].x,o[CS].y,o[CS].z,S^[CS]));
if(CS mod 2<>0)Then
T:=ColorText(RGB(100+Random(155),100+Random(155),100+Random(155)),GMT)
else T:=T;
Scene^.Camera[CC]^.Obj[CO]^.Side[CS]^.Texture:=T;
Scene^.Camera[CC]^.Obj[CO]^.Side[CS]^.Mode:=Mode;
Scene^.Camera[CC]^.Obj[CO]^.Side[CS]^.Alpha:=0;
Accept:=False;
inc(CS);
end
else if(M=6+sv)and(Accept=False)Then begin
SS[CS]:=SS[CS-1];
SS[CS]:=SS[CS-1];
SS[CS]:=SS[CS-1];
M:=0;N:=0;
ShowSide;
Flip(Lab.Vid);
end;
end;
procedure InPut2(x,y:integer;var E:Boolean);
var i,j:integer;o:TPoint;Party:Single;tmp:Single;
begin
if(N<sv)Then begin
if(x<cx)and(y<cy)Then
begin
oc.x:=x-cx+dF;
oc.y:=cy-y-dF;
end
else if(x>cx)and(y<cy)Then
begin
oc.z:=x-cx-dF;
oc.y:=cy-y-dF;
end
else if(x<cx)and(y>cy)Then
begin
oc.x:=x-cx+dF;
oc.z:=x-cx-dF;
end
else begin
ShowMessage(‘Эта четверть не является плоскостью проекций!’);
E:=True;
end;
if(E=False)Then begin
PutPixel(x,y,Yelow);
Flip(Lab.Vid);
inc(N);
end;
end
else
if(N<2+sv)and(N>sv-1)Then begin
if(N=sv)Then begin
x0:=x;y0:=y;
end;
x1:=x;y1:=y;
Line(x0,y0,x1,y1,Red);
Flip(Lab.Vid);
x0:=x;y0:=y;
inc(N);
if(x>cx)and(y>cy)Then begin
ShowMessage(‘Эта четверть не является плоскостью проекций!’);
E:=True;
end
else begin
w[N-sv].x:=x;
w[N-sv].y:=y;
end;
end;
if(N=2+sv)Then begin
if(абс(w[N-sv].x-w[N-sv-1].x)/2<=абс(w[N-sv].y-w[N-sv-1].y)/2)Then Party:=абс(w[N-sv].x-w[N-sv-1].x)
else Party:=абс(w[N-sv].y-w[N-sv-1].y);
if(w[N-sv].x<cx)and(w[N-sv].y<cy)Then begin
o.x:=(w[N-sv].x+w[N-sv-1].x)/2;
o.y:=(w[N-sv].y+w[N-sv-1].y)/2;
o.z:=cx+Party/2+dF;
end
else if(w[N-sv].x>cx)and(w[N-sv-1].y<cy)Then begin
o.x:=cx-Party/2-dF;
o.y:=(w[N-sv].y+w[N-sv-1].y)/2;
o.z:=(w[N-sv].x+w[N-sv-1].x)/2;
end
else if(w[N-sv].x<cx)and(w[N-sv].y>cy)Then begin
o.x:=(w[N-sv].x+w[N-sv-1].x)/2;
o.y:=cy-Party/2-dF;
o.z:=(w[N-sv].y+w[N-sv-1].y)/2;
end;
r:=1;
SS^[CS,1].x:=o.x-Party/2;SS^[CS,1].y:=o.y+Party/2;SS^[CS,1].z:=o.z+Party/2;
SS^[CS,2].x:=o.x+Party/2;SS^[CS,2].y:=o.y+Party/2;SS^[CS,2].z:=o.z+Party/2;
SS^[CS,3].x:=o.x-Party/2;SS^[CS,3].y:=o.y-Party/2;SS^[CS,3].z:=o.z-Party/2;
SS^[CS+1,1].x:=o.x+Party/2;SS^[CS+1,1].y:=o.y-Party/2;SS^[CS+1,1].z:=o.z-Party/2;
SS^[CS+1,2].x:=o.x+Party/2;SS^[CS+1,2].y:=o.y+Party/2;SS^[CS+1,2].z:=o.z+Party/2;
SS^[CS+1,3].x:=o.x+Party/2;SS^[CS+1,3].y:=o.y+Party/2;SS^[CS+1,3].z:=o.z-Party/2;
SS^[CS+2,1].x:=o.x-Party/2;SS^[CS+2,1].y:=o.y+Party/2;SS^[CS+2,1].z:=o.z+Party/2;
SS^[CS+2,2].x:=o.x-Party/2;SS^[CS+2,2].y:=o.y-Party/2;SS^[CS+2,2].z:=o.z-Party/2;
SS^[CS+2,3]:=SS^[CS+1,1];
o.x:=o.x-cx+dF;
o.y:=cy-o.y-dF;
o.z:=o.z-cx-dF;
Scene^.Camera[CC]^.Obj[CO]^.Done;
Scene^.Camera[CC]^.Done;
Scene^.Done;
New(Cube,Create(0,0,100,o.x,o.y,o.z,Party,T,Mode,0));
Scene^.Camera[CC]^.ADD(CO,CO+1);
For i:=1 to Scene^.Camera[CC]^.Obj[CO]^.Count do
begin
For j:=1 to 3 do
S[i,j]:=Scene^.Camera[CC]^.Obj[CO]^.Side[i]^.S[j];
if(i mod 2<>0)Then
T:=ColorText(RGB(100+Random(155),100+Random(155),100+Random(155)),GMT)
else T:=T;
Scene^.Camera[CC]^.Obj[CO]^.Side[i]^.Texture:=T;
Scene^.Camera[CC]^.Obj[CO]^.Side[i]^.Mode:=Mode;
end;
For i:=CS to CS+12 do
ClicCub[i]:=True;
CS:=CS+12;
N:=0;
Scene^.Camera[CC]^.Obj[CO]^.Done;
Scene^.Camera[CC]^.Done;
Scene^.Done;
ShowSide;
Flip(Lab.Vid);
end;
end;
procedure Data;
var i,j:Word;
begin
New(S);
New(SS);
GetMaxX:=Lab.Vid.Width-1;
GetMaxY:=Lab.Vid.Height-1;
M:=0;N:=0;Rot:=YRot;Bol:=False;
GMX:=Lab.Vid.Width;
GMY:=Lab.Vid.Height;
CSc:=1;CC:=1;CL:=1;CO:=1;CS:=1;
cx:=GMX div 2;
cy:=GMY div 2+1;
Blue:=RGB(255,0,0);
Red:=RGB(200,200,200);
Yelow:=RGB(0,255,0);
New(Scene,Create);
New(Scene^.Camera[CC],Create(0,0,0,0,0,0));
New(Light[CL],Create(-100,-100,0));
New(Scene^.Camera[CC]^.Obj[CO],Create(0,0,0,0,0,0));
Accept:=True;View:=False;
end;
procedure TLab.FormCreate(Sender: TObject);
var i,j:Word;
begin
Tables;
@InPut[1]:=@InPut1;
@InPut[2]:=@InPut2;
New(t);
For i:=0 to GMT do
New(t^[i]);
For i:=0 to GMT do
For j:=0 to GMT do
t^[i]^[j]:=RGB(255,0,0);
Data;
end;
procedure TLab.TimerTimer(Sender: TObject; LagCount: Integer);
var i:Word;
begin
if(Keys[VK_Escape])Then halt;
if(Bol=False)and(Keys[VK_F9])and(CountSide>0)Then begin
if(CountSide=0)Then begin
ShowMessage(‘Для просмотра нужна хотя бы одна грань’);
exit;
end;
Bol:=True;View:=True;
Scene^.Camera[CC]^.Obj[CO]^.Done;
Scene^.Camera[CC]^.Done;
Scene^.Done;
CreateTMP(Scene^.Camera[CC]^.Obj[CO]);
For i:=1 to Scene^.Camera[CC]^.Obj[CO]^.Count do
Scene^.Camera[CC]^.Obj[CO]^.Side[i]^.o:=tmp_o;
Vid.Surface.Fill(0);
Scene^.Camera[CC]^.Obj[CO]^.Draw;
Flip(Vid);
end
else if(Bol)Then
if(Keys[ord(‘Y’)])Then
begin
Rot:=YRot;
Vid.Surface.Fill(0);
Scene^.Camera[CC]^.Obj[CO]^.Rotate(CosA,SinA,Angle,Rot);
Scene^.Camera[CC]^.Obj[CO]^.Draw;
Flip(Vid);
end
else if(Keys[Ord(‘X’)])Then
begin
Rot:=XRot;
Vid.Surface.Fill(0);
Scene^.Camera[CC]^.Obj[CO]^.Rotate(CosA,SinA,Angle,Rot);
Scene^.Camera[CC]^.Obj[CO]^.Draw;
Flip(Vid);
end
else if(Keys[Ord(‘Z’)])Then
begin
Rot:=ZRot;
Vid.Surface.Fill(0);
Scene^.Camera[CC]^.Obj[CO]^.Rotate(CosA,SinA,Angle,Rot);
Scene^.Camera[CC]^.Obj[CO]^.Draw;
Flip(Vid);
end
else if(Keys[VK_Space])Then
begin
if(Space.Visible=False)Then begin
Space.Visible:=True;
Enter.Visible:=True;
BSide.Visible:=True;
BCube.Visible:=True;
Menu.Items[0].Visible:=True;
Menu.Items[1].Visible:=True;
Menu.Items[2].Visible:=True;
Width:=455;
Height:=465;
Left:=108;
Top:=-7;
Vid.Width:=385;
Vid.Height:=385;
Vid.Top:=32;
Vid.Left:=32;
GMX:=Lab.Vid.Width;
GMY:=Lab.Vid.Height;
GetMaxX:=GMX-1;
GetMaxY:=GMY-1;
end;
M:=0;N:=0;Bol:=False;
View:=False;
ShowSide;
Flip(Vid);
end;
end;
procedure TLab.VidMouseDown(Sender: TObject; Button: TMouseButton;
Shift: TShiftState; X, Y: Integer);
var E:Boolean;
begin
E:=False;
if(View=False)Then InPut[Figure](x,y,E);
if(E=True)Then exit;
end;
procedure TLab.EnterClick(Sender: TObject);
var i:Word;
begin
if(CountSide=0)Then begin
ShowMessage(‘Для просмотра нужна хотя бы одна грань’);
exit;
end;
Bol:=True;View:=True;
Scene^.Camera[CC]^.Obj[CO]^.Done;
Scene^.Camera[CC]^.Done;
Scene^.Done;
CreateTMP(Scene^.Camera[CC]^.Obj[CO]);
For i:=1 to Scene^.Camera[CC]^.Obj[CO]^.Count do
Scene^.Camera[CC]^.Obj[CO]^.Side[i]^.o:=tmp_o;
Vid.Surface.Fill(0);
Scene^.Camera[CC]^.Obj[CO]^.Draw;
Flip(Vid);
end;
procedure TLab.N3Click(Sender: TObject);
var i,j:integer;Name:String;
begin
OpenDialog.Execute;
Name:=OpenDialog.FileName;
Lab.Vid.Surface.Fill(0);
Data;
myform(GetMaxX,GetMaxY);
Flip(Lab.Vid);
if(Name=»)Then exit;
LoadObject(Name,Scene^.Camera[CC]^.Obj[CO]);
LoadSide(Scene^.Camera[CC]^.Obj[CO]);
end;
procedure TLab.N4Click(Sender: TObject);
var Name:String;
begin
SaveDialog.Execute;
Name:=SaveDialog.FileName;
if(Name=»)Then exit;
CreateTMP(Scene^.Camera[CC]^.Obj[CO]);
SaveObject(Name,Scene^.Camera[CC]^.Obj[CO]);
end;
procedure TLab.N2Click(Sender: TObject);
begin
Flip(Lab.Vid);
if(Lab.Width>Lab.Height)Then Lab.Height:=Lab.Width
else Lab.Width:=Lab.Height;
Vid.Height:=Lab.Height-81;
Vid.Width:=Lab.Width-81;
Lab.Vid.Surface.Fill(0);
Data;
myform(GetMaxX,GetMaxY);
Flip(Lab.Vid);
end;
procedure TLab.N9Click(Sender: TObject);
begin
ShowMessage(‘Autocad version 1.2 Copiright by Anton Sazonov’);
end;
procedure TLab.N10Click(Sender: TObject);
begin
Halt;
end;
procedure TLab.SpaceClick(Sender: TObject);
var i:Word;
begin
M:=0;N:=0;Bol:=False;
View:=False;
ShowSide;
Flip(Vid);
end;
procedure TLab.FormKeyDown(Sender: TObject; var Key: Word;
Shift: TShiftState);
begin
Keys[Key]:=True;
end;
procedure TLab.FormKeyUp(Sender: TObject; var Key: Word;
Shift: TShiftState);
begin
Keys[Key]:=False;
end;
procedure TLab.CancelClick(Sender: TObject);
begin
Halt;
end;
procedure TLab.OKClick(Sender: TObject);
begin
Option[1]:=Box1.Text;
if(Option[1]=»)Then begin
ShowMessage(‘Выберитережим!’);
Exit;
end
else if(Option[1]=’4 bits’)Then PixMode:=1
else if(Option[1]=’8 bits’)Then PixMode:=2
else if(Option[1]=’16 bits’)Then PixMode:=3
else if(Option[1]=’24 bits’)Then PixMode:=4
else if(Option[1]=’32 bits’)Then PixMode:=5;
Option[2]:=Box6.Text;
if(Option[2]=»)Then begin
ShowMessage(‘Изберите разрешение!’);
Exit;
end
else if(Option[2]=’640X480′)Then begin
ScreenX:=640;ScreenY:=480;
end
else if(Option[2]=’800X600′)Then begin
ScreenX:=800;ScreenY:=600;
end
else if(Option[2]=’1024X768′)Then begin
ScreenX:=1024;ScreenY:=768;
end
else if(Option[2]=’1280X1024′)Then begin
ScreenX:=1280;ScreenY:=1024;
end;
Surf:=Vid.Surface;
SetGraphMode(PixMode);
Label1.Destroy;
Label6.Destroy;
Label7.Destroy;
Box1.Destroy;
Box6.Destroy;
Cancel.Destroy;
OK.Visible:=False;
Lab.Height:=465;
Lab.Top:=-7;
Menu.Items.Visible:=True;
Vid.Visible:=True;
Vid.Width:=385;
Vid.Height:=385;
Space.Visible:=True;
Enter.Visible:=True;
Lab.Vid.Surface.Fill(0);
myform(GetMaxX,GetMaxY);
Flip(Lab.Vid);
if(Lab.Width>Lab.Height)Then Lab.Height:=Lab.Width
else Lab.Width:=Lab.Height;
Vid.Height:=Lab.Height-81;
Vid.Width:=Lab.Width-81;
Lab.Vid.Surface.Fill(0);
Data;
myform(GetMaxX,GetMaxY);
Flip(Lab.Vid);
BCube.Visible:=True;
BSide.Visible:=True;
Menu.Items[0].Visible:=True;
Menu.Items[1].Visible:=True;
Menu.Items[2].Visible:=True;
OK.Destroy;
end;
procedure TLab.N8Click(Sender: TObject);
begin
Application.HelpContext(10);
end;
procedure TLab.BCubeClick(Sender: TObject);
begin
Figure:=2;
ClicCub[CS]:=True;
end;
procedure TLab.BSideClick(Sender: TObject);
begin
Figure:=1;
end;
procedure TLab.N6Click(Sender: TObject);
begin
if (View=True)Then begin
Space.Visible:=False;
Enter.Visible:=False;
BSide.Visible:=False;
BCube.Visible:=False;
Menu.Items[0].Visible:=False;
Menu.Items[1].Visible:=False;
Menu.Items[2].Visible:=False;
Width:=ScreenX+10;
Height:=ScreenY+20;
Left:=-10;
Top:=-30;
Vid.Width:=ScreenX+1;
Vid.Height:=ScreenY-16;
Vid.Top:=0;
Vid.Left:=0;
GMX:=ScreenX;GMY:=ScreenY;
GetMaxX:=GMX-1;GetMaxY:=GMY-1;
Scene^.Camera[CC]^.Obj[CO]^.Draw;
Flip(Vid);
end;
end;
end.
ЗАКЛЮЧЕНИЕ
Программирование с внедрением трёхмерной графики — это метод описания языком программирования объёмных тел и отображения их на мониторе.
Измерение данной графики совпадает с измерением настоящей системы, находящейся в пространстве, в каком ориентируется человек, и по этому хоть какое вещественное тело можно виртуально сделать, задать ему условия и поглядеть на реакцию этого тела, задать телу правила поведения (линию движения движения) и выяснить как оно будет себя вести, где будет находиться с течением времени.
К примеру, можно сделать программку, создающую чертежи с внедрением гостов и чертёжных обозначений. Она нужна конструкторам. Примером таковой программки является автокод.
Можно изменить данную программку таковым образом, чтоб она, виртуально создавала дом и докладывала какие перегрузки, он будет испытывать и не деформируется ли он при разных природных явлениях. Эта программка нужна конструкторам.
Ещё трёхмерную графику можно применить для сотворения устройств, которые меж собой ведут взаимодействие, показать какие силы при всем этом участвуют, и показать их взаимодействие с различных сторон. Таковая программка нужна инженерам-механикам. Эти программки могут применять как ортогональное (параллельное), так и центральное проецирование (проецирование с учётом перспективы).
программка, создающая трёхмерную анимацию (кинофильм, мульт), так же быть может реализована на компе. Эта программка обязана применять лишь центральное проецирование (перспективное), и лучше наличие неких эффектов: прозрачности, освещённости, билинейной фильтрации текстур и т.д.
Трёхмерная графика нужна всюду, где выполняются вещественные объекты, где есть инженеры, конструкторы, архитекторы, просто квалифицированные рабочие, а конкретно: в самолётостроении, в машиностроении, в кораблестроении, в строительстве, в галлактической индустрии и т. д.
С трёхмерной графикой, так либо по другому, приходится сталкиваться дизайнерам одежки, дизайнерам веб-сайтов, хоть каким иным дизайнерам, работникам отделов рекламы, продюсерам и т. д.
Трёхмерной графикой воспользовались постоянно. До объёмного изображения на компе чертили чертежи на бумаге, до чертежей отрисовывали эскизы либо картинки, до рисунков воспользовались заданием объектов аналитически на бумаге либо в уме.
Если отдать оценку трёхмерной графики в вещественном производстве, то она будет последующей: трёхмерная графика, как метод изображения объёмных фигур либо тел, является самым приятным способом представления инфы, применяемой в вещественном производстве. Без трёхмерной графики не было бы налажено хоть какое вещественное Создание. Самый удачный метод задания и использования трёхмерной либо объёмной графики осуществляется при помощи информационных технологий, а конкретно компа.
СПИСОК ЛИТЕРАТУРЫ
1 С. Бобровский «Delphi 5». 2007
2 О.Е. Акимов «Дискретная математика,
СОДЕРЖАНИЕ
ВСТУПЛЕНИЕ
1. Математические объекты
1.1 Группы
1.2 Графы
2. Справка по работе с {программой|программкой}
2.1 {Назначение|Предназначение} программного продукта
2.2 {Обучение|Обучение } работе с программным продуктом
2.3 Ограничения {применения|внедрения} 10
3. Нереализованные {возможности|способности}
4. Основная форма
5. {Методы|Способы} {создания|сотворения} {программы|программки}
5.1 Матричные преобразования
5.2 Создание одноцветного треугольника
6. {программа|программка}
ЗАКЛЮЧЕНИЕ
СПИСОК ЛИТЕРАТУРЫ
ВСТУПЛЕНИЕ
{Каждый|Любой} {материальный|вещественный} объект, имеющий форму, является {объемным|большим}, {следовательно|как следует}, его положение в пространстве можно задать {с помощью|при помощи} трёх координат X, Y, Z. {Результат|Итог} {любого|хоть какого} {материального|вещественного} производства, дома, {автомобили|авто}, станки, можно представить как 3-х мерную модель и {отобразить|показать} эту модель на {дисплее|мониторе} {компьютера|компа} {с помощью|при помощи} {соответствующей|соответственной} {программы|программки}.
Нет необходимости создавать {отдельно|раздельно} {программу|программку} для {создания|сотворения} моделей {отдельно|раздельно} домов, {отдельно|раздельно} станков и их {механизмов|устройств}, {отдельно|раздельно} для {автомобилей|каров} и т. д. {Гораздо|Еще} разумней {создать|сделать} одну {программу|программку}, которая может {учитывать|учесть} специфику {любой|хоть какой} отрасли и создавать, {практически|фактически} любые 3-х мерные модели.
Программирование уже {давно|издавна} {перестало|закончило|не стало} быть уделом энтузиастов. Современный {программист|программер} — не писатель {или|либо} ученый, а квалифицированный рабочий. Прошли те времена, когда, удавалось «удовлетворять {собственный|свой} {Интерес|Энтузиазм} за счет {государства|страны}»: месяцами {исследовать|изучить} {исходный|начальный} код {какой-нибудь|какой-либо} {совершенно|совсем} {бесполезной|никчемной} в практическом плане утилиты, забираться в недра {исходных|начальных} текстов оригинально {сделанных|изготовленных} {компонентов|компонент}.
{Конечно|Естественно}, {небольшому|маленькому} проценту {разработчиков|разрабов} по долгу службы {нужны|необходимы} {глубокие|глубочайшие} {специфические|специальные} {знания|познания}, {однако|но} от подавляющего большинства программистов {сегодня|сейчас} требуется, {прежде|до этого} всего, умение писать {программы|программки} {максимально|очень} {быстро|стремительно} и без ошибок. {Например|К примеру} {программа|программка}, представленная в этом проекте {требует|просит} {минимальное|малое} количество {умственных|интеллектуальных} {затрат|издержек} и времени {пользователя|юзера} {или|либо} {программиста|программера} {высокого|высочайшего} уровня для {достижения|заслуги} результата. {Программист|Программер} {высокого|высочайшего} уровня — это {программист|программер}, составляющий свои {программы|программки} на {основе|базе} разработок {других|остальных} программистов. Под разработками {других|остальных} программистов будем {понимать|осознавать} разработанные ими средства программирования: (язык программирования, функции, процедуры, утилиты, библиотеки, модули, {компоненты|составляющие}, драйвера и т. д.
{Любой|Хоть какой} природный процесс, которым человек {пытается|пробует} управлять({воздействовать|повлиять} на него с {предсказуемыми|прогнозируемыми} последствиями), должен быть {сначала|поначалу} представлен в виде модели той {или|либо} {иной|другой} степени {сложности|трудности}. {Специалистам|Спецам} в области компьютерных технологий приходится брать такие модели {или|либо} их фрагменты для {последующего|следующего} объединения из {конкретных|определенных} предметных областей, общих {знаний|познаний} и представлений {об|о} {окружающем мире|внешнем мире} и создавать свою модель для решения поставленной {задачи|задачки}. {После|Опосля} этого, {учитывая|беря во внимание} {особенности|индивидуальности} работы вычислительного устройства ({компьютера|компа}) и {выбранного|избранного} языка программирования – разрабатывать {алгоритм|метод} реализации модели, программировать(кодировать) этот {алгоритм|метод}. {Кроме|Не считая} реализации {основной|главный} функции, {необходимо|нужно} {подумать|поразмыслить|помыслить|пошевелить мозгами} о вспомогательных функциях(контроль входных данных, проверка адекватности результата, человека) имеют дискретную природу (дискретный
(лат. diskretus) – {разделенный|разбитый}, {прерывистый|прерывающийся}). {характер|нрав} (алфавит машинного языка двухсимвольный, {множество|огромное количество} состояний {процессора|микропроцессора} конечное), {поэтому|потому} {любая|неважно какая} модель, реализованная в виде машинного кода, дискретна. {Поэтому|Потому} для реализации рассматриваемой {программы|программки} {необходимо|нужно} соприкоснуться с {некоторыми|некими} областями дискретной {математики|арифметики}.
1. Математические объекты
1.1 Группы
Группа — оперативное {множество|огромное количество}, {в котором|в каком} действует процедура умножения и которое подчинено {следующим|последующим} условиям:
1) замкнутости: для каждой пары g1g2=g3, {причем|при этом} g3 должен принадлежать группе G: g1g2 ÎG; g1g2 = g3 ÎG;
2) наличия тождественного элемента e: {среди|посреди} {множества|огромного количества} {элементов|частей} группы G справедливы равенство eg = g; ge = g; e,gÎG;
3) наличие {обратных|оборотных} {элементов|частей}: для всякого g из G должен отыскаться единственный ему {обратный|оборотный} элемент g, принадлежащий G, при умножении на который {получился|вышел} тождественный элемент e. e = q×q; e,g,gÎG;
4) ассоциативности: для {любых|всех} трёх {элементов|частей} g1, g2, g3 из G справедливо равенство: g1(g2g3) = (g1g2)g3. Условия ассоциативности {выполняется|производится} для квадратных матриц.
Линейное преобразование A вектора x в вектор c осуществляется {с помощью|при помощи} квадратной матрицы y = Ax.
Если есть прямое преобразование, то {должно|обязано} быть и {обратное|оборотное}, при условии, что A имеет A. y = Ax;
x = Tx ;y = Tx ; {Следовательно|Как следует} Ty = ATx; Умножим с лева на T , получим y = TATx;
Отсюда следует формула прямого преобразования подобия.
Формулу {обратного|оборотного} преобразования подобия можно получить, умножив на T с права:
Если Ax = lx; то ненулевой вектор x является {собственным|своим} вектором. Для нахождения собственных векторов {используют|употребляют} формулу
*
где E — единичная матрица.
{Корни|Корешки} многочлена P(l) = det(A — lE) = 0 подставляют в формулу
и получают собственные векторы x.
Группы, лишившись {своей|собственной} предметной области, не {востребованы|нужны} в {настоящее|истинное} время. Хотя ранее, {примерно|приблизительно} {сорок|40} лет {назад|вспять}, теория групп была {распространена|всераспространена} из-за того, что группы {тесно|тесновато} увязывалась с {фундаментальными|базовыми} областями естествознания — физикой {элементарных|простых} частиц, квантовой механикой, физикой твёрдого тела и кристаллографией.
В данной {программе|программке} все объёмные фигуры состоят из треугольных граней, {каждая|любая} из которых содержит три {вершины|верхушки}. Задав положение объёмного тела в пространстве, мы задаём положение всех вершин. {Каждая|Любая} {вершина|верхушка} представляется как вектор.
{Чтобы|Чтоб} {преобразовать|конвертировать} объект,(повернуть, удалить, приблизить, сжать, растянуть и т. п.) {необходимо|нужно} {каждый|любой} вектор каждой грани объекта {умножить|помножить} на матрицу преобразования, {например|к примеру} на матрицу поворота вокруг оси Y.
{Вот как|Ах так} эта матрица будет {выглядеть|смотреться}:
1.2 Графы
{Любое|Хоть какое} объемное тело, как уже было сказано выше, можно {построить|выстроить} {с помощью|при помощи} треугольных граней, {каждая|любая} из которых имеет хотя бы одну общую {вершину|верхушку} с {соседней|примыкающей} гранью. Схематично каждую грань можно изобразить как {совокупность|совокупа} вершин, соединенную контурными линиями. Контурные {линии|полосы} — это {линии|полосы} описывающие контур. Контур — это замкнутый путь. {Таким|Таковым} образом, грань, содержащая {вершины|верхушки} упорядоченно соединённые рёбрами представляет собой {ориентированный|направленный} граф {или|либо} Орграф.
Граф G
как математический объект – это {совокупность|совокупа} {двух|2-ух} множеств: непустого {множества|огромного количества} вершин V
и {множества|огромного количества} ребер E
, элементы которого представляет собой неупорядоченные (для {ориентированного|нацеленного} графа – упорядоченные) пары {элементов|частей} из {множества|огромного количества} V
.
G (V,E) = áV; Eñ, n(V) > 0, E Ì V ´ V,
где для неориентированного графа E
=
E
–1
(бинарное отношение E
симметрично).
{Минимальный|Малый} граф состоит из одной {вершины|верхушки}.
{Каждому|Любому} неориентированному графу можно поставить в соответствие {ориентированный|направленный} граф, {в котором|в каком} каждое ребро заменено {двумя|2-мя} {противоположно|обратно} {ориентированными|нацеленными} ребрами, инцидентными {тем же|этим же} {вершинам|верхушкам}.
Пусть v
1
и v
2
– {вершины|верхушки}, e
1
= (v
1
, v
2
) – соединяющее их ребро.
Тогда {вершина|верхушка} v
1
и ребро e
1
инцидентны, {вершина|верхушка} v
2
и ребро e
1
также инцидентны. Два ребра, инцидентные одной {вершине|верхушке}, {называются|именуются} смежными; две {вершины|верхушки}, инцидентные одному ребру, также {называются|именуются} смежными.
Обычно граф изображают на плоскости в виде диаграммы: {вершины|верхушки} – точками, ребра – линиями, соединяющими инцидентные {вершины|верхушки}.
{Множество|Огромное количество} вершин и {множество|огромное количество} рёбер для конечных графов задаются, как правило, перечислением. {Возможно|Может быть} задание графа описанием {отношения|дела} инцидентности.
1 Отношение инцидентности задано матрицей смежности:
– столбцы и {строки|строчки} матрицы – {вершины|верхушки} графа;
– для смежных вершин элемент матрицы равен1, для {остальных|других} – 0;
– для неориентированного графа эта матрица {всегда|постоянно} симметрична;
– число рёбер равно числу единиц выше {или|либо} ниже главной диагонали матрицы ( включая элементы на диагонали).
2 Отношение инцидентности задано матрицей инцидентности:
– столбцы матрицы соответствуют {вершинам|верхушкам} графа, а {строки|строчки} – рёбрам;
– если ребро ei
инцидентно {вершине|верхушке} vj
, то элемент матрицы eij
=1, в {противном|неприятном} случае – eij
= 0.
{Таким|Таковым} образом, в каждой строке одна {или|либо} две единицы, {остальные|другие} нули (для петли две единицы).
Для {ориентированного|нацеленного} графа при заполнении матрицы:
eij
= –1,если vj
– начало ребра;
eij
=1,если vj
–конец ребра;
eij
= a (где a – {любое|хоть какое} число, {кроме|не считая} –1,1,0),если ребро – петля в {вершине|верхушке} vj
;
в {остальных|других} {случаях|вариантах} eij
= 0.
3 Граф задан {списком|перечнем} ребер.
ei
vi,
vj
1
a, b
2
b, d
…
Примечание. {здесь|тут} ei
–ребро, vi
,
vj
– пара вершин, соединяемых {этим|сиим} ребром.
Граф связан, если {любая|неважно какая} пара его вершин связана ребром.
Граф без кратных ребер {называют|именуют} полным, если {каждая|любая} пара вершин соединена ребром.
Граф H
{называют|именуют} частью графа G
, если {множество|огромное количество} вершин графа H
принадлежит {множеству|огромному количеству} вершин графа G
и {множество|огромное количество} рёбер графа H
принадлежит {множеству|огромному количеству} рёбер графа G
, т.е.:
V(H) ÌV(G); E(H) ÌE(G).
часть графа H
{называется|именуется} суграфом, если она содержит все {вершины|верхушки} графа G
.
Суграф H
для неориентированного графа G
{называется|именуется} покрывающим суграфом, если {любая|неважно какая} {вершина|верхушка} {последнего|крайнего} инцидентна хотя бы одному ребру из H
.
Подграф G
(
U
)
графа G
на {множестве|огромном количестве} вершин U
(
U
Ì
V
)
– это часть графа, которой принадлежат все ребра с обоими концами из U
.
Звёздный граф для {вершины|верхушки} v
(
v
Î
G
)
состоит из всех рёбер с началом и концом в {вершине|верхушке} v
. {Множество|Огромное количество} вершин звёздного графа состоит из {вершины|верхушки} v
и {других|остальных} смежных с ней вершин.
Маршрутом в единичном связном графе G
{называется|именуется} {такая|таковая} конечная последовательность ребер (e
1,
e
2….
en
), {в которой|в какой} {каждые|любые} два {соседних|примыкающих} ребра имеют общую инцидентную {вершину|верхушку}.
{Вершина|Верхушка} v
о
, инцидентная ребру e
1
и не инцидентная ребру e
2
, {называется|именуется} началом маршрута в графе G
.
{Вершина|Верхушка} vn
, инцидентная ребру en
и не инцидентная ребру en
-1
, {называется|именуется} концом маршрута.
Число ребер маршрута {называется|именуется} его длиной.
Если {вершины|верхушки} v
о
и vn
совпадают, то маршрут {называется|именуется} {циклическим|повторяющимся} ({или|либо} просто циклом).
Отрезок конечного {или|либо} {бесконечного|нескончаемого} маршрута сам является маршрутом.
Маршрут в графе G
{называется|именуется} цепью
, если все ребра в последовательности различны, и {простой|обычный|обычной} цепью
, если все {вершины|верхушки}, через которые проходит маршрут (а {значит|означает} и ребра) различны.
{Другими|Иными} словами, в цепи ребро может {встретиться|повстречаться} не {более|наиболее} {одного|1-го} раза, а в {простой|обычный|обычной} цепи {вершина|верхушка} – не {более|наиболее} {одного|1-го} раза.
{Говорят|Молвят}, что две {вершины|верхушки} в графе {связаны|соединены}, если существует соединяющая их цепь. Граф, {в котором|в каком} все {вершины|верхушки} {связаны|соединены}, {называется|именуется} связным
.
Расстоянием
{между|меж} {двумя|2-мя} {вершинами|верхушками} графа {называется|именуется} {минимальная|малая} длина {простой|обычный|обычной} цепи, связывающей эти {вершины|верхушки} (обозначение d(v¢,v²)).
Протяженностью
{между|меж} {двумя|2-мя} {вершинами|верхушками} графа {называется|именуется} {максимальная|наибольшая} длина {простой|обычный|обычной} цепи, связывающей эти {вершины|верхушки} (обозначение g(v¢,v²)).
В {частном|личном} случае расстояние и протяженность {между|меж} {вершинами|верхушками} могут быть {одинаковыми|схожими}.
2. Справка по работе с {программой|программкой}
2.1 {Назначение|Предназначение} программного продукта
Данный программный продукт предназначен для построения {любых|всех} {объемных|больших} фигур с {последующей|следующей} их демонстрацией {на экране|на дисплее} монитора. {Следует отметить|Необходимо подчеркнуть}, что фигуры могут быть как выпуклыми, так и вогнутыми, но {обязательно|непременно} не {должно|обязано} нарушаться условие целостности. Это {значит|означает}, что ни одна часть объекта не {должна|обязана} существовать {отдельно|раздельно}, а {должна|обязана} быть хоть {каким-нибудь|каким-либо} своим элементом присоедененной к объекту. {То есть|Другими словами} два {множества|огромного количества}: вершин и рёбер, которые получаются, если пройти по контуру, от первой {вершины|верхушки} к {последней|крайней}, представляют собой, согласно определению, {ориентированный|направленный} граф и имеют маршрут. {Созданный|Сделанный} объект можно сохранить на диске, {а также|также} загрузить с него, {чтобы|чтоб} {посмотреть|поглядеть} {или|либо} отредактировать {результат|итог} работы. Для построения трехмерных объектов можно {использовать|применять|употреблять} {некоторые|некие} доступные примитивы, а {именно|конкретно} грань и куб. {Каждая|Любая} грань, в создаваемом объекте, имеет {свой|собственный} цвет, {выбираемый|избираемый} {программой|программкой} случайным образом.
2.2 {Обучение|Обучение } работе с программным продукт
ом
{После|Опосля} загрузки приложения {появится|покажется} окно {настройки|опции}, {в котором|в каком} {необходимо|нужно} установить разрешение цвета, которое {обязательно|непременно} {должно|обязано} совпадать с разрешением цвета текущего графического режима, {например|к примеру} 16 бит (рис. 1).
В {противном|неприятном} случае на форме будет наблюдаться {некое|некоторое} аномальное графическое изображение, которое будет {неправильно|некорректно} отражать {результат|итог} работы, что, делает всякую работу {невозможной|неосуществимой}. {Далее|Дальше} {нужно|необходимо} установить разрешение текущего графического режима в пикселях. Это {нужно|необходимо} для полноэкранного режима просмотра. {После|Опосля} установления графического режима {появится|покажется} рабочее окно, разделённое на четыре части — четверти. {Каждая|Любая} четверть является плоскостью проекций, {расположение|размещение} которых соответствует принципам начертательной геометрии: верхняя левая — {фронтальная|передная}, правая верхняя — вертикальная, нижняя левая — горизонтальная. Нижняя — правая четверть не является плоскостью проекций, по этому там {рисовать|отрисовывать} {нельзя|недозволено}. Точки задаются нажатиями левой клавиши мыши. Если точек больше одной, то они соединяются линией, если точек три, то они образуют треугольник, где {каждая|любая} точка является {вершиной|верхушкой}.
Следует {помнить|держать в голове}, что мы строим трёхмерное изображение не по точкам {или|либо} по линиям, а по треугольным граням, и проецируем не точки, а грани, по этому если точка поставлена в одной плоскости проекций, то и {остальные|другие} две точки данной грани должны быть поставлены в {этой|данной|данной нам|данной для нас} же плоскости. Для построения {любой|хоть какой} объёмной фигуры {достаточно|довольно} любые две плоскости. {Третья|3-я}
{Рисунок|Набросок} 1
{Рисунок|Набросок} 2
{После|Опосля} того, как объект спроецирован на плоскости проекций {нужно|необходимо} {нажать|надавить} {кнопку|клавишу} «Просмотр», находящуюся на форме (рис. 2), {чтобы|чтоб} {посмотреть|поглядеть} на {объемный|большой} объект в перспективе, и получить возможность сохранить документ. Если объект {создан|сотворен}, но небыла нажата {клавиша|кнопка} «Просмотр», то {после|опосля} выбора {опции|функции} «Сохранить как» информация не будет сохранена. {После|Опосля} нажатия клавиши «Просмотр» {появится|покажется} однотонное окно чёрного цвета {в котором|в каком} будет находиться трёхмерный объект, на который можно {посмотреть|поглядеть} со всех сторон, вращая вокруг осей X,Y,Z нажатиями {соответствующих|соответственных} {клавиш|кнопок} «X», «Y», «Z», {причем|при этом} не имеет значения {заглавная|большая} {клавиша|кнопка} {или|либо} нет. Просмотрев {результат|итог} {создания|сотворения} объекта, можно его достроить, нажав на форме {кнопку|клавишу} «Добавить». {После|Опосля} нажатия {этой|данной|данной нам|данной для нас} {кнопки|клавиши} {снова|опять} {появится|покажется} окно с {тремя|3-мя} плоскостями проекций, {в котором|в каком} будут находиться проекции объекта и к которым можно добавить {новые|новейшие} проекции. Следует {заметить|увидеть}, что вращение трёхмерного объекта в перспективе {фактически|практически} не приводит к изменению его положения в пространстве. {То есть|Другими словами} если {после|опосля} вращения объекта перейти к плоскостям проекций, картина не {изменится|поменяется}, и {после|опосля} перехода {обратно|назад} к перспективе объект будет находиться в {исходном|начальном} положении: его положение в перспективе будет соответствовать его проекциям на плоскостях проекций.
Добавление примитива: «Куб», осуществляется путём чертежа {линии|полосы} на одной из плоскости проекций, причём, если {мысленно|на уровне мыслей} представить проекции {этой|данной|данной нам|данной для нас} {линии|полосы} на абсциссу и ординату, то стороной куба будет {меньшая|наименьшая} из проекций.
Если {после|опосля} {создания|сотворения} {одного|1-го} трёхмерного объекта загрузить {другой|иной} объект с диска, то {первый|1-ый} объект уничтожится, а на его месте {появится|покажется} {второй|2-ой}, который загружен с диска. Это {объясняется|разъясняется} тем, что загруженный объект не добавляется к уже имеющемуся, зато можно {наоборот|напротив}, как уже было описано выше, к загруженному объекту добавить примитивы (грани, кубы).
В программном продукте действительны такие {функциональные|многофункциональные} клавиши:
Esc — выход из {программы|программки} в операционную систему;
F1 — справка о программном продукте;
F9 — режим просмотра ;
X — вращение объекта вокруг оси X;
Y — вращение объекта вокруг оси Y;
Z — вращение объекта вокруг оси Z;
В программном продукте есть такие пункты меню «файл«, «Вид», «Помощь».
В {пункте|пт} меню «файл«, содержатся {опции|функции} для работы с файлами. Выбрав опцию «{Создать|Сделать}», создаётся {новая|новенькая} форма для {создания|сотворения} объекта. Если {изменить|поменять} длину {или|либо} ширину формы и {выбрать|избрать} опцию «{Создать|Сделать}», то рабочее окно {примет|воспримет} размеры, {соответствующие|надлежащие} {новым|новеньким} размерам формы. Причём длина и ширина будут в любом случае {одинаковы|схожи}.
{С помощью|При помощи} {опции|функции} «Открыть» можно открыть, файл, содержащий трёхмерный объект. {Опция|Функция} «Сохранить как» {необходима|нужна} для того, {чтобы|чтоб} записать трёхмерный объект в файл.
Для загрузки и сохранения файлов в данном программном продукте, {необходимо|нужно} и {достаточно|довольно} указать имя файла без разширения, {или|либо} с разширением, состоящим из {любых|всех} {обязательно|непременно} трёх {символов|знаков}.
{Опция|Функция} «Выход» {предназначена для|создана для} выхода из {программы|программки} в операционную систему.
В {пункте|пт} меню «Вид» можно задать полноэкранный режим формы. Пункт меню «Помощь» содержит опцию «Справка», выбор которой сопровождается открытием документа, содержащего подробную справку о программном продукте.
Так же пункт меню «Помощь» содержит опцию «О {программе|программке}», {в которой|в какой} можно {узнать|выяснить} {краткую|короткую} информацию о программном продукте и его {разработчике|разрабе}.
2.3 Ограничения {применения|внедрения}
В {программе|программке} {желательно|лучше} {использовать|применять|употреблять} 16 битный режим, причём разрешение графического режима, выраженное в пикселях, не имеет значения. Допускаются {только|лишь} 8 битные, 16 битные и 32 битные графические режимы. Во всех {остальных|других} режимах, {например|к примеру}, 2 битных, 4 битных, 24 битных {программа|программка} работать не будет.
При использовании {разных|различных} графических режимов рабочие окна, места на форме, где задаются грани, будут иметь {различный|разный} вид ({разные|различные} цвета линий разметки и линий проекций). Это {связано|соединено} с тем, что цвета в программном продукте не имеют значения. {Важно|Принципиально} {чтобы|чтоб} проекции на плоскости проекций были видны, и соответствовали изображаемому объекту. цвета трёхмерного объекта, {вообще|совершенно} выбираются случайным образом, по этому {здесь|тут} важен не цвет, а форма объекта.
При загрузке трёхмерного объекта из файла не {обязательно|непременно} указывать разширение файла, {или|либо} если уж оно {указано|обозначено}, то {должно|обязано} содержать не {менее|наименее} трёх {символов|знаков} {после|опосля} точки. Это {объясняется|разъясняется} тем, что {программа|программка} сохраняет объект в {двух|2-ух} файлах. Один с разширением «Res», а {другой|иной} «Dat». {При этом|При всем этом} не {важно|принципиально} какое разширение укажет {пользователь|юзер}, оно всё равно отбрасывается {программой|программкой}.
При открытии файла {программа|программка} отбрасывает {указанное|обозначенное} {пользователем|юзером} разширение, если оно есть, и добавляет к имени файла своё. Когда {программа|программка} отбрасывает разрешение, она отбрасывает три {символа|знака}, находящиеся {после|опосля} точки. {Поэтому|Потому} если {пользователь|юзер} указал разширение, {важно|принципиально}, {чтобы|чтоб} оно состояло не {менее|наименее} чем из трёх {символов|знаков}, {иначе|по другому} при открытии файла произойдёт ошибка.
Проектировать грани {нужно|необходимо} {с помощью|при помощи} задания трёх вершин, причём если задание проекций трёх вершин начато в одной плоскости проекций, то все эти три проекции должны быть заданы в {этой|данной|данной нам|данной для нас} плоскости, а не {отдельно|раздельно}: одна проекция точки на {фронтальной|передней}, {другая|иная} на горизонтальной, {третья|3-я} на профильной. Все три проекции точки должны быть в одной плоскости проекций. Это {связано|соединено}, как уже {сообщалось|говорилось}, с тем что проецируются не точки {или|либо} {линии|полосы}, а грани.
Рабочее окно, которое выводит графическую информацию, {может быть|быть может} {только|лишь} квадратным, по этому изменяя длину {или|либо} ширину рабочего окна, {нужно|необходимо} {учитывать|учесть}, что {программа|программка} уравняет эти оба параметра.
3. Нереализованные {возможности|способности}
Самый {главный|основной} элемент, который {необходимо|нужно} {реализовать|воплотить} — это полное отсутствие ошибок. На данном {этапе|шаге} разработки ошибок не выявлено, но гарантии их отсутствия не существует.
{Затем|Потом} следовало бы {увеличить|прирастить} количество примитивов: добавить к грани и кубу, возможность {создания|сотворения} призмы, многоугольника, цилиндра, сферы, диска. наличие этих фигур обеспечило бы лёгкость {создания|сотворения} {любых|всех} объёмных тел. {Потому|Поэтому} что, {например|к примеру} призма, в {основе|базе} которой {правильный|верный} восьмиугольник содержит 32 треугольной грани. естественно {пользователю|юзеру} вручную {очень|весьма} проблематично {создать|сделать} эту фигуру, {поэтому|потому} её наличие в коллекции примитивов {существенно|значительно} облегчило бы ему работу.
Так же можно было бы {разработать|создать} {методы|способы} наложения {разных|различных} текстур на трёхмерные объекты. Текстуры — это двухмерные графические изображения, которые, наложив как фотографию на грань объекта, превращают {обыкновенный|обычный} куб в {телевизор|телек}, дом с окнами, стиральную {машину|машинку} и {другие|остальные} кубические объекты.
Текстуры бывают: аффинные, {точные|четкие} (перспективно — корректные), параболические. Текстуры можно накладывать на все трёхмерные объекты.
Можно было бы {реализовать|воплотить} прозрачность трёхмерных объектов. {например|к примеру}, если {нужно|необходимо} было бы {создать|сделать} объёмное тело из стекла. В этом случае {необходимо|нужно} было бы {использовать|применять|употреблять} прозрачность. Трёхмерные объекты, как {известно|понятно}, создаются двухмерными точками. {Поэтому|Потому} {чтобы|чтоб} {создать|сделать} прозрачность {необходимо|нужно} перед выводом каждой точки объекта в определённом месте получать цвет уже стоящей на этом месте точки, если она есть, {затем|потом} вывести точку цвет которой будет средним {между|меж} цветом точки, которая ставится и которая уже стоит. {Таким|Таковым} образом, за выводимым трёхмерным объектом будет видно {расположение|размещение} другого объёмного тела. Это и есть прозрачность.
4. Основная форма
Основная форма содержит такие {компоненты|составляющие} как:
Main
Menu
— Предназначен для {добавления|прибавления} к главной {программе|программке} {главного|головного} меню. Является компонентом Standart. Компонент MainMenu не {визуальный|зрительный} компонент. Редактор меню вызывается {после|опосля} выбора {опции|функции} Items. В редакторе можно создавать пункты меню проименовывая их с помощь {свойства|характеристики} Caption. Переходя от одной компоненте к {другой|иной} можно {с помощью|при помощи} {того же|такого же} {свойства|характеристики} Caption отредактировать пункты меню. {Чтобы|Чтоб} вставить линию разделитель {нужно|необходимо} в свойстве Caption первой позицией указать {символ|знак} «-» (дефис). {После|Опосля} окончания {создания|сотворения} {пунктов|пт} меню редактор меню {надо|нужно} закрыть.
BitBtn
— Этот компонент предназначен для {создания|сотворения} {кнопки|клавиши} с картинкой. В системе имеется набор готовых шаблонов.
{После|Опосля} размещения объекта на форме изображение, помещаемое на {кнопку|клавишу}, задаётся в свойстве Glyph (Значок). {При этом|При всем этом} вызывается редактор, {с помощью|при помощи} которого выбирается {нужная|подходящая} {картинка|картина} (в формате BMP). {Каждая|Любая} {такая|таковая} {картинка|картина} может состоять из 4 частей, равных по ширине. {Первая|1-ая} часть — изображение {кнопки|клавиши} в {обычном|обыкновенном} состоянии, {вторая|2-ая} — изображение «отключённой» {кнопки|клавиши}, {третья|3-я} — изображение {кнопки|клавиши} {после|опосля} щелчка мыши, изображение на нажатой {кнопке|кнопочке}. Число составных частей задаётся в свойстве NumGlyph (от 1 до 4). Расстояние от {картинки|рисунки} до границ {кнопки|клавиши} (в пикселях) можно указать в свойстве Margin. В свойстве Kind задаётся реакция {кнопки|клавиши} на щелчок.
Button
— Компонент предназначен для {создания|сотворения} {кнопок|клавиш} на форме и обработки {события|действия} нажатия {кнопки|клавиши}. {Расположен|Размещен} на панели Standart. имя {кнопки|клавиши} указывается в поле Name, а выводимый текст на {кнопке|кнопочке} в свойстве Caption.
Combo
Box
— Компонент Поле со {списком|перечнем}. Представляет собой вариант {списка|перечня}, с присоединённым {дополнительным|доп} полем, {в котором|в каком} отображается {выбранный|избранный} элемент {списка|перечня}. {Это же|Это} поле может {использоваться|употребляться} для ввода {новых|новейших} {элементов|частей} {или|либо} для {быстрого|резвого} поиска {элементов|частей} по {начальным|исходным} символам. Если {на экране|на дисплее} отображается {только|лишь} присоединённое поле («раскрывающийся {список|перечень}»), то для раскрытия {списка|перечня} можно {использовать|применять|употреблять} клавиатурную комбинацию Alt+Вниз.
Open
Dialog
— Компонент предназначен для выбора файла с целью {последующего|следующего} открытия; {Свойства|Характеристики} класса TOpenDialog приведены в табл. 1
Таблица 1 — {Свойства|Характеристики} класса TOpenDialog
{Свойств|Параметров}
о
Назначен
и
е
DefaultExt
расширение имени, {используемое|применяемое} {по умолчанию|по дефлоту}. Добавляется в конец выбраного {пользователем|юзером} имени файла, если расширение не {указано|обозначено} {явно|очевидно}.
FileName
Выбранное {пользователем|юзером} имя файла {вместе|совместно|вкупе} с полным путём поиска
Files
{Список|Перечень} {выбранных|избранных} имён файлов. В свойстве Options должен быть включён {флажок|флаг} ofAllowMultiSelect
Filter
Набор масок, в {соответствии|согласовании} с которыми отбираются {имена файлов|названия файлов} для отображения в диалоговом окне. {Каждая|Любая} маска состоит из {двух|2-ух} частей: {названия|наименования} и шаблона, — разделённых {символом|эмблемой} |. Одному {названию|наименованию} могут соответствовать несколько шаблонов. Маски отделяются друг от друга {символом|эмблемой} |
FilterIndex
Номер текущей маски. Нумерация начинается с 1
HistoryList
{Список|Перечень} ранее {выбранных|избранных} файлов (тип Strings)
InitialDir
Текущий каталог, содержимое которого отображается при первом открытии диалогового окна
Options
Набор {флажков|флагов}, определяющих окна выбора файлов.
Title
Заголовок диалогового окна
Save
Dialog
— Этот компонент {практически|фактически} ничем не {отличается|различается} от компонента OpenDialog {за исключением|кроме} нескольких {настроек|опций}, специфичных для процесса сохранения файла.
Label
— Компонент находится на панэле Standart. Предназначен для вывода текста на форме {с помощью|при помощи} {свойства|характеристики} Text.
TDXTimer
— Компонент находится на панели DelphiX. В {программах|программках}, выполняющих {действия|деяния}, связанные с моделированием {или|либо} обработкой графики, {предназначенных для|созданных для} общения с {пользователем|юзером} в {реальном|настоящем} режиме времени {или|либо} выполняющих {продолжительные|длительные} вычисления, {необходима|нужна} компонента TDXTimer.
{С помощью|При помощи} компонента TDXTimer можно включить генерацию сообщений, поступающих от системного таймера Windows с {заданной|данной} периодичностью (в миллисекундах) и {выполнять|делать} определённую часть действий {именно|конкретно} в обработчике этого {события|действия}. Это одна из {возможностей|способностей} многозадачности приложений Windows.
TDXDraw
— Этот компонент находится на вкладке DelphiX. Он предназначен для вывода двухмерной и трёхмерной графики, используя DirectDraw и DirectX. Разрешение выводимой графики задаётся в свойстве Display. Вид курсора задаётся в свойстве Cursor. {метод|способ} DXDraw.Surface.Fill(Color) — заполняет видеостраницу цветом Color. {С помощью|При помощи} {свойства|характеристики} DXDraw.Surface.pixels[x, y]:=Color — ставится точка на виртуальной видеостранице. {С помощью|При помощи} {метода|способа} DXDraw.Flip — невидимая видеостраница делается видимой. Схема обрисовки изображения на {двух|2-ух} видеостраницах: видимой и не видимой делает {возможной|вероятной} {создать|сделать} анимацию без {мерцания|мигания} изображения.
5. {методы|способы} {создания|сотворения} {программы|программки}
Для {создания|сотворения} трёхмерных объектов {необходимо|нужно} уметь преобразовывать трёхмерную графику в двухмерную. Ведь процедура вывода точки имеет {только|лишь} две координаты, задающие положение точки (x, y), {следовательно|как следует}, реально имеется дело с двухмерной графикой. Для начала следует принять систему трёхмерных координат:
{Рисунок|Набросок} 3
{здесь|тут} {буквами|знаками} x, y, z обозначены положительные направления осей Ox, Oy и Oz соответственно. Также предполагается, что камера {неподвижна|недвижна} и находится в точке с координатами (0,0,-dist), ось зрения камеры {направлена|ориентирована} по оси Oz, а {именно|конкретно} в точку (0,0,0) (т.е. camera target = (0,0,0)), ось Ox {с точки зрения|исходя из убеждений} камеры {направлена|ориентирована} слева {направо|вправо}, ось Oy — снизу {вверх|ввысь}, ось Oz — вглубь экрана. Размер экрана — xSize на ySize пикселей.
{здесь|тут} и {далее|дальше} {используются|употребляются} обозначения:
sx, sy
координаты проекции точки {на экране|на дисплее}
x, y, z
3D координаты точки,
Dist
расстояние от камеры (она находится в точке (0,0,-dist)) до начала координат,
Для созданя трёхмерных объектов {необходимы|нужны} примитивы. {Такими|Таковыми} примитивами являются {элементарные|простые} трёхмерные объекты — треугольные грани. У каждой треугольной грани есть три {вершины|верхушки} — три точки, имеющие трёхмерные координаты. {Чтобы|Чтоб} спроецировать их на плоскость, т. е. из трёхмерных координат x,y,z получить двухмерные sx, sy {надо|нужно} {пользоваться|воспользоваться} формулой:
sx = xSize/2+x*dist/(z+dist); sy = ySize/2-y*dist/(z+dist);
5.1 Матричные преобразования
В {этой|данной|данной нам|данной для нас} главе {кратко|коротко} представлены {элементарные|простые} понятия о матричных преобразованиях, являющихся составной частью линейной алгебры, и дискретной {математики|арифметики} (см. раздел Группы).
Введем несколько {терминов|определений}. n-мерный вектор, он же вектор размерности n, он же вектор размера n: упорядоченный набор n {действительных|реальных} чисел. Матрица размера m на n (будет обозначаться как m*n, mxn): таблица размера m на n, в каждой {клетке|клеточке} которой — действительное число.
{Вот пример|Вот вам наглядный пример} матрицы 3×3:
[ 15 y*z 0.6 ]
[ 7 -3 91 ]
[ sin(x) 0.123 exp(t) ]
Вектор будем записывать в столбик и {рассматривать|разглядывать} его как матрицу размера n*1.
Операция скалярного произведения векторов: определена для {двух|2-ух} векторов {одинаковых|схожих} размеров. {Результат|Итог} есть число, равное сумме произведений {соответствующих|соответственных} {элементов|частей} векторов. Пример:
[ 1 ] [ 4 ]
[ 2 ] * [ 5 ] = 1*4 + 2*5 + 3*6 = 32
[ 3 ] [ 6 ]
Операция векторного произведения: определена для (n-1) вектора {одинакового|схожего} размера n. {Результат|Итог} — вектор, {причем|при этом}, перпендикулярный всем множителям.
Замечание:
{результат|итог} {меняется|изменяется} от перестановки мест множителей!
Формально определяется как определитель матрицы, {первая|1-ая} {строка|строчка} которой {есть все|все есть} {базисные|базовые} вектора, а все {последующие|следующие} — {соответствующие|надлежащие} координаты всех множителей. {Поскольку|Так как} она {необходима|нужна} {только|лишь} для 3D {пространства|места}, мы определим векторное произведение {двух|2-ух} 3D векторов {явно|очевидно}:
[ Ax ] [ Bx ] | i j k | [ Ay*Bz-Az*By ]
AxB = [ Ay ] x [ By ] = | Ax Ay Az | = [ Az*Bx-Ax*Bz ]
[ Az ] [ Bz ] | Bx By Bz | [ Ax*By-Ay*Bx ]
Операция сложения {двух|2-ух} матриц: определена для матриц {одинаковых|схожих} размеров. {Каждый|Любой} элемент суммы ({то есть|другими словами}, каждое число в таблице) {равняется|приравнивается} сумме {соответствующих|соответственных} {элементов|частей} слагаемых-матриц. Пример:
[ 1 x 500 ] [ 8 a 3 ] [ 9 a+x 503 ]
[ 2 y 600 ] + [ 9 b 2 ] = [ 11 b+y 602 ]
[ 3 z 700 ] [ 10 c 1 ] [ 13 c+z 701 ]
Операция умножения матрицы на число: определена для {любой|хоть какой} матрицы и {любого|хоть какого} числа; {каждый|любой} элемент результата {равняется|приравнивается} произведению {соответствующего|соответственного} элемента матрицы-множителя и числа-множителя.
Операция умножения {двух|2-ух} матриц: определена для {двух|2-ух} матриц {таких|таковых} размеров a*b и c*d, что b = c. {например|к примеру}, если b = c, но a ¹ d, то при перестановке множителей операция будет {вообще|совершенно} не определена. Результатом умножения матрицы A размером a*b на матрицу B размером b*d будет матрица C размером a*d, {в которой|в какой} элемент, стоящий в строке i и столбце j, равен произведению {строки|строчки} i матрицы A на столбец j матрицы B. Произведение {строки|строчки} на столбец определяется как сумма произведений {соответствующих|соответственных} {элементов|частей} {строки|строчки} и столбца. {например|к примеру}, умножение {строки|строчки} на столбец (они должны быть равной длины, {поэтому|потому} и такие ограничения на размеры матриц):
[ 4 ]
[ 1 2 3 ] * [ 5 ] = 1*4 + 2*5 + 3*6 = 32
[ 6 ]
А {чтобы|чтоб} перемножить две матрицы, {надо|нужно} эту операцию {проделать|сделать} для {каждого|всякого} элемента. {Вот пример|Вот вам наглядный пример}:
[ 1 2 3 ] [ 0 3 ] [ 1*0+2*1+3*2 1*3+2*4+3*5 ]
[ 4 5 6 ] * [ 1 4 ] = [ 4*0+5*1+6*2 4*3+5*4+6*5 ] = …
[ 7 8 9 ] [ 2 5 ] [ 7*0+8*1+9*2 7*3+8*4+9*5 ]
Умножение и сложение матриц {обладают|владеют} {почти|практически} {тем же|этим же} набором {свойств|параметров}, что и {обычные|обыденные} числа, хотя {некоторые|некие} {привычные|обычные} {свойства|характеристики} не {выполняются|производятся} ({например|к примеру}, A*B ¹ B*A); {на самом деле|по сути} требуется знать, что произведение вида A*B*C*D*… не зависит от того, как расставить скобки. {Или|Либо}, что
A*(B*C) = (A*B)*C.
{Любое|Хоть какое} движение ({то есть|другими словами} преобразование {пространства|места}, сохраняющее расстояние {между|меж} точками) в трехмерном пространстве, согласно {теореме|аксиоме} Шаля, {может быть|быть может} представлено в виде суперпозиции поворота и параллельного переноса, {то есть|другими словами} {последовательного|поочередного} выполнения поворота и параллельного переноса. {Поэтому|Потому} основная часть информация о поведении объекта — это его смещение, ось поворота и угол поворота. {Поэтому|Потому} нам {достаточно|довольно} знать, как {сделать|создать} два преобразования — перенос и поворот.
Перенос точки (точки будут также рассматриваться как вектора с началом {в начале|сначала} координат и концом в {собственно|фактически} точке) с координатами (x,y,z) на вектор (dx,dy,dz) делается {простым|обычным} сложением всех координат. {То есть|Другими словами} {результат|итог} — это (x+dx,y+dy,z+dz). Подобно сложению вектора-точки с вектором-переносом.
{Рассмотрим|Разглядим} для примера поворот точки (x,y,z) относительно оси z. В этом случае z не {меняется|изменяется}, а (x,y) {меняются|изменяются} так же, как и при 2D повороте относительно начала координат.
Покажем координаты точки A’ — результата поворота A(x,y) на угол alpha (рис. 4).
{Рисунок|Набросок} 4
Пусть r = sqrt(x*x+y*y). Пусть угол AOx равен phi, тогда из рисунка видно, что cos(phi) = x/r, sin(phi) = y/r. Угол A’OA равен по условию alpha. Отсюда
x’ = r*cos(alpha+phi) = r*(cos(alpha)*cos(phi)-sin(alpha)*sin(phi)) =
= (r*cos(phi))*cos(alpha)-(r*sin(phi))*sin(alpha) =
= x*cos(alpha)-y*sin(alpha)
y’ = r*sin(alpha+phi) = r*(cos(alpha)*sin(phi)+sin(alpha)*cos(phi)) =
= (r*cos(phi))*sin(alpha)+(r*sin(phi))*cos(alpha) =
= x*sin(alpha)+y*cos(alpha)
Для трехмерного {случая|варианта}, {таким|таковым} образом
x’ = x*cos(alpha)-y*sin(alpha)
y’ = x*sin(alpha)+y*cos(alpha)
z’ = z
{Аналогичные|Подобные} формулы получатся и для {других|остальных} осей поворота ({то есть|другими словами} Ox, Oy). Поворот относительно {произвольной|случайной} оси, проходящей через начало координат, можно {сделать|создать} {с помощью|при помощи} этих поворотов — {сделать|создать} поворот относительно Ox так, {чтобы|чтоб} ось поворота стала перпендикулярна Oy, {затем|потом} поворот относительно Oy так, {чтобы|чтоб} ось поворота совпала с Oz, {сделать|создать} поворот, а {затем|потом} {обратные|оборотные} повороты относительно Oy и Ox.
Вспомним о матрицах и векторах и {внимательно|пристально} {посмотрим|поглядим} на выведенные формулы для поворота. Можно {заметить|увидеть}, что
[ x’ ] = [ cos(alpha) -sin(alpha) 0 ] [ x ]
[ y’ ] = [ sin(alpha) cos(alpha) 0 ] [ y ]
[ z’ ] = [ 0 0 1 ] [ z ]
{То есть|Другими словами} поворот на угол alpha задается одной и той же матрицей, и {с помощью|при помощи} {этой|данной|данной нам|данной для нас} матрицы (умножая ее на вектор-точку) можно получить координаты повернутой точки. С одной стороны умножение матрицы на вектор {требует|просит} больше операций, чем расчет x’ и y’ по формулам.
Нос {другой|иной} удобство матриц для заключается как раз в свойстве
A*(B*C) = (A*B)*C. Пусть делается несколько поворотов {подряд|попорядку}, {например|к примеру}, {пять|5} (столько, сколько {надо|нужно} для поворота относительно {произвольной|случайной} оси), и пусть они задаются матрицами A, B, C, D, E (A — матрица самого первого поворота, E — {последнего|крайнего}). Тогда для вектора p мы получаем
p’ = E*(D*(C*(B*(A*p)))) = E*D*C*B*A*p = (E*D*C*B*A)*p = (E*(D*(C*(B*A))))*p = T*p,
где T = (E*(D*(C*(B*A)))) матрица преобразования, являющегося {комбинацией|композицией} {пяти|5} поворотов. Посчитав один раз эту матрицу, можно в {дальнейшем|предстоящем} применить {довольно|достаточно} сложное преобразование из {пяти|5} поворотов к {любому|хоть какому} вектору {с помощью|при помощи} всего {одного|1-го} умножения матрицы на вектор.
{Таким|Таковым} образом, можно задать {любой|хоть какой} поворот матрицей, и {любая|неважно какая} {комбинация|композиция} поворотов также будет задаваться матрицей, которую можно {довольно|достаточно} {легко|просто} посчитать. Но {есть еще|еще есть} параллельный перенос и масштабирование.
{На самом деле|По сути}, эти преобразования тоже {легко|просто} записываются в виде матриц. {Только|Лишь} {вместо|заместо} матриц 3×3 и 3-мерных векторов {используются|употребляются} так {называемые|именуемые} однородные 4-мерные координаты и матрицы 4×4. {При этом|При всем этом} {вместо|заместо} векторов вида
[ x ]
[ y ]
[ z ]
{используются|употребляются} вектора вида
[ x ]
[ y ]
[ z ]
[ 1 ]
а {вместо|заместо} {произвольных|случайных} матриц 3×3 {используются|употребляются} матрицы 4×4 {такого|такового} вида:
[ a b c d ]
[ e f g h ]
[ i j k l ]
[ 0 0 0 1 ]
Видно, что если d = h = l = 0, то в {результате|итоге} {применения|внедрения} всех операций {получается|выходит} то же самое, что и для матриц 3×3.
Матрица параллельного переноса {теперь|сейчас} определяется как
[ 1 0 0 dx ]
[ 0 1 0 dy ]
[ 0 0 1 dz ]
[ 0 0 0 1 ]
Матрицу масштабирования можно {определить|найти} и для матриц 3×3, и для матриц 4×4:
[ kx 0 0 ] [ kx 0 0 0 ]
[ 0 ky 0 ] {или|либо} [ 0 ky 0 0 ]
[ 0 0 kz ] [ 0 0 kz 0 ]
[ 0 0 0 1 ]
где kx, ky, kz — коэффициенты масштабирования по {соответствующим|подходящим} осям.
{Таким|Таковым} образом, получаем {следующее|последующее}. {Любое|Хоть какое} {нужное|необходимое} нам преобразование {пространства|места} можно задать матрицей 4×4 определенной структуры, разной для {разных|различных} преобразований. {Результат|Итог} {последовательного|поочередного} выполнений нескольких преобразований совпадает с результатом {одного|1-го} преобразования T, которое также задается матрицей 4×4, вычисляемой как произведение матриц всех этих преобразований. Важен порядок умножения, {так как|потому что} A*B ¹ B*A. {Результат|Итог} {применения|внедрения} преобразования T к вектору [ x y z ] считается как {результат|итог} умножения матрицы T на вектор [ x y z 1 ].
Докажем на примере, что A*B ¹ B*A. Пусть A — матрица переноса, B — поворота. Если {сначала|поначалу} перенести объект, а {потом|позже} повернем относительно центра координат (это будет B*A), то {результат|итог} не будет соответствовать результату, при котором {сначала|поначалу} объект поворачивают, а {затем|потом} переносят (A*B).
5.2 Создание одноцветного треугольника
Изображение треугольника {на экране|на дисплее} — набор горизонтальных отрезков, {причем|при этом} из-за того, что треугольник — фигура выпуклая, каждой строке экрана соответствует не {более|наиболее} {одного|1-го} отрезка. {Поэтому|Потому} {достаточно|довольно} обойти все {строки|строчки} экрана, с которыми пересекается треугольник, ({то есть|другими словами}, от {минимального|малого} до {максимального|наибольшего} значения (y) для вершин треугольника), и нарисовать {соответствующие|надлежащие} горизонтальные отрезки.
{нужно|необходимо} отсортировать {вершины|верхушки} так, {чтобы|чтоб} {вершина|верхушка} A была верхней, C — нижней, тогда min_y = A.y, max_y = C.y, и {надо|нужно} обойти все {линии|полосы} от min_y до max_y. {Рассмотрим|Разглядим} какую-то линию sy, A.y <= sy <= C.y. Если sy < B.y, то она пересекает стороны AB и AC; если sy >= B.y — то стороны BC и AC. Известны координаты всех вершин, {поэтому|потому} можно написать уравнения сторон и {найти|отыскать} пересечение {нужной|подходящей} стороны с прямой y = sy. Получим два конца отрезка. {Так как|Потому что} не {известно|понятно}, какой из {них|их} левый, а какой правый, {нужно|необходимо} сравним их координаты по x и обменяем значения, если {нужно|необходимо}. Рисуя этот отрезок, повторяя {процедуру|функцию} для каждой {строки|строчки} — получаем треугольник.
Рассматривая {более|наиболее} {подробно|тщательно} пересечения прямой
y = sy (текущей {строки|строчки}) и стороны треугольника, {например|к примеру} AB, Получим уравнение прямой AB в форме x = k*y+b:
x = A.x+(y-A.y)*(B.x-A.x)/(B.y-A.y)
{Теперь|Сейчас} {надо|нужно} подставить известное для текущей прямой
x = A.x+(sy-A.y)*(B.x-A.x)/(B.y-A.y)
Для {других|остальных} сторон пересечение ищется {совершенно|совсем} {точно|буквально} так же. {например|к примеру}:
// …
// {здесь|тут} сортируем {вершины|верхушки} (A,B,C)
// …
for sy = A.y to C.y begin
x1 = A.x + (sy — A.y) * (C.x — A.x) / (C.y — A.y);
if (sy < B.y)Then
x2 = A.x + (sy — A.y) * (B.x — A.x) / (B.y — A.y);
else
x2 = B.x + (sy — B.y) * (C.x — B.x) / (C.y — B.y);
if (x1 > x2) Then
begin
tmp = x1; x1 = x2; x2 = tmp;
end;
drawHorizontalLine(sy, x1, x2);
end;
{Необходимо|Нужно} защититься от {случая|варианта}, когда B.y = C.y — в этом (и {только|лишь} этом, {потому|поэтому} как если C.y = A.y, то треугольник пустой и {рисовать|отрисовывать} его не {нужно|необходимо}, {или|либо} можно {рисовать|отрисовывать} горизонтальную линию; а если B.y = A.y, то sy >= A.y и до деления на B.y — A.y не дойдет) случае произойдет попытка деления на ноль.
// …
// {здесь|тут} сортируем {вершины|верхушки} (A,B,C)
// …
for sy = A.y to C.y begin
x1 = A.x + (sy — A.y) * (C.x — A.x) / (C.y — A.y);
if (sy < B.y)
x2 = A.x + (sy — A.y) * (B.x — A.x) / (B.y — A.y);
else begin
if (C.y == B.y)
x2 = B.x;
else
x2 = B.x + (sy — B.y) * (C.x — B.x) / (C.y — B.y);
end;
if (x1 > x2)Then
begin
tmp = x1; x1 = x2; x2 = tmp;
drawHorizontalLine(sy, x1, x2);
end;
// …
{здесь|тут} drawHorizontalLine(sy, x1, x2) — горизонтальная линия. Её создание не представляет {сложности|трудности} и код будетвыглядеть так.
//…
For i:=x1 to x2 do
PutPixel(i,sy,Color);
//…
6. {программа|программка}
unit graph3;
interface
uses Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs,
DXClass, DXDraws, StdCtrls,graph, Menus,graph3D,figures, Buttons;
const Mode:Word=0;
type TLab = class(TForm)
Vid: TDXDraw;
Timer: TDXTimer;
Enter: TButton;
Menu: TMainMenu;
N1: TMenuItem;
N2: TMenuItem;
N3: TMenuItem;
N4: TMenuItem;
N5: TMenuItem;
N6: TMenuItem;
N7: TMenuItem;
N8: TMenuItem;
N9: TMenuItem;
OpenDialog: TOpenDialog;
SaveDialog: TSaveDialog;
N10: TMenuItem;
Space: TButton;
Box1: TComboBox;
Label1: TLabel;
OK: TButton;
Cancel: TButton;
Label6: TLabel;
BCube: TBitBtn;
BSide: TBitBtn;
Box6: TComboBox;
Label7: TLabel;
procedure FormCreate(Sender: TObject);
procedure TimerTimer(Sender: TObject; LagCount: Integer);
procedure VidMouseDown(Sender: TObject; Button: TMouseButton;
Shift: TShiftState; X, Y: Integer);
procedure EnterClick(Sender: TObject);
procedure N3Click(Sender: TObject);
procedure N4Click(Sender: TObject);
procedure N2Click(Sender: TObject);
procedure N9Click(Sender: TObject);
procedure N10Click(Sender: TObject);
procedure SpaceClick(Sender: TObject);
procedure FormKeyDown(Sender: TObject; var Key: Word;
Shift: TShiftState);
procedure FormKeyUp(Sender: TObject; var Key: Word;
Shift: TShiftState);
procedure CancelClick(Sender: TObject);
procedure OKClick(Sender: TObject);
procedure N8Click(Sender: TObject);
procedure BCubeClick(Sender: TObject);
procedure BSideClick(Sender: TObject);
procedure N6Click(Sender: TObject);
private
public
end;
var Lab: TLab;
implementation
{$R *.DFM}
const ScreenX:Word=640;
ScreenY:Word=480;
x1:integer=0;
y1:integer=0;
White=$FFFFFF;
View:Boolean=False;
Figure:Word=1;
Accept:Boolean=True;
sv:Word=0;
var c:char;S,SS,TMP:PPl;t:PTexture;
CS,CO,CC,CSc,CL:Word;Rot:TRot;
x0,y0,x2,y2:integer;r:Single;
Blue,Red,Yelow,M,N,SC:Word;
Keys:array[1..255]of Boolean;
Input:array[1..12]of procedure (x,y:integer;var E:Boolean);
Bol:Boolean;Option:array[1..5] of String;
o:array[1..MaxSide]of TPoint;
w:array[1..2]of TPoint;tmp_o:TPoint;
cx,cy:Word;oc:TPoint;ClicCub:array[0..MaxSide]of Boolean;
procedure LoadObject(Name:String;var Obj:PObj);
var f1:file of TPoint; a:TSides;
f2:file of Word;
S,Tmp:TPoint;i,j:word;Name2:String;
B:Boolean;
begin
For i:=1 to Length (Name)-4 do begin
Name2:=Name2+Name[i];
end;
Name:=Name2;
Assign(f2,Name+’.res’);
Reset(f2);
Read(f2,Obj^.Count);
close(f2);
Assign(f1,Name+’.dat’);
Reset(f1);
Read(f1,Obj^.o);
For i:=1 to Obj^.Count do
New(Obj^.Side[i],Create(Obj^.o.x,Obj^.o.y,Obj^.o.z,0,0,0,a));
For i:=1 to Obj^.Count do begin
inc(CS);
if(i mod 2<>0)Then
T:=ColorText(RGB(100+Random(155),100+Random(155),100+Random(155)),GMT)
else T:=T;
Obj^.Side[i]^.Texture:=T;
Obj^.Side[i]^.Mode:=Mode;
Obj^.Side[i]^.Alpha:=0;
Read(f1,S);
For j:=1 to 3 do
begin
Read(f1,S);
Obj^.Side[i]^.S[j]:=S;
SS^[i,j]:=S;
end;
end;
close(f1);
end;
procedure SaveObject(Name:String;var Obj:PObj);
var f1:file of TPoint;
f2:file of Word;B:Boolean;
S:TPoint;i,j:word;Name2:String;
begin
B:=False;
For i:=1 to Length(Name) do
if(Name[i]=’.’)Then B:=True;
if(B=True)Then begin
For i:=1 to Length(Name)-4 do begin
Name2:=Name2+Name[i];
end;
Name:=Name2;
end;
Assign(f2,Name+’.res’);
Rewrite(f2);
Reset(f2);
Write(f2,Obj^.Count);
close(f2);
Assign(f1,Name+’.dat’);
Rewrite(f1);
Reset(f1);
Write(f1,Obj^.o);
For i:=1 to Obj^.Count do begin
Obj^.Side[i]^.o:=tmp_o;
S:=Obj^.Side[i]^.o;
Write(f1,S);
For j:=1 to 3 do
begin
S:=Obj^.Side[i]^.S[j];
Write(f1,S);
end;
end;
close(f1);
end;
procedure CreateTMP(var Obj:PObj);
var i,j,k:Word;
begin
Obj^.o.x:=0;Obj^.o.y:=0;Obj^.o.z:=100;
For i:=1 to Obj^.Count do
begin
tmp_o:=Obj^.Side[i]^.o;
Obj^.Side[i]^.o:=o[i];
For j:=1 to 3 do
Obj^.Side[i]^.S[j]:=S^[i,j];
end;
end;
procedure ShowSide;
var i,j,k,cx,cy:Word;tmp:TPoint;
begin
Lab.Vid.Surface.Fill(0);
myForm(GetMaxX,GetMaxY);
For i:=1 to CS do begin
Line(Trunc(SS^[i,1].x),Trunc(SS^[i,1].y),Trunc(SS^[i,2].x),Trunc(SS^[i,2].y),White);
Line(Trunc(SS^[i,2].x),Trunc(SS^[i,2].y),Trunc(SS^[i,3].x),Trunc(SS^[i,3].y),White);
Line(Trunc(SS^[i,3].x),Trunc(SS^[i,3].y),Trunc(SS^[i,1].x),Trunc(SS^[i,1].y),White);
Line(Trunc(SS^[i,1].x),Trunc(SS^[i,1].z),Trunc(SS^[i,2].x),Trunc(SS^[i,2].z),White);
Line(Trunc(SS^[i,2].x),Trunc(SS^[i,2].z),Trunc(SS^[i,3].x),Trunc(SS^[i,3].z),White);
Line(Trunc(SS^[i,3].x),Trunc(SS^[i,3].z),Trunc(SS^[i,1].x),Trunc(SS^[i,1].z),White);
Line(Trunc(SS^[i,1].z*r),Trunc(SS^[i,1].y),Trunc(SS^[i,2].z*r),Trunc(SS^[i,2].y),White);
Line(Trunc(SS^[i,2].z*r),Trunc(SS^[i,2].y),Trunc(SS^[i,3].z*r),Trunc(SS^[i,3].y),White);
Line(Trunc(SS^[i,3].z*r),Trunc(SS^[i,3].y),Trunc(SS^[i,1].z*r),Trunc(SS^[i,1].y),White);
if(ClicCub[i]=True)Then begin
Line(Trunc(SS^[i,1].z),Trunc(SS^[i,1].y),Trunc(SS^[i,2].z),Trunc(SS^[i,1].y),White);
Line(Trunc(SS^[i,2].z),Trunc(SS^[i,1].y),Trunc(SS^[i,1].z),Trunc(SS^[i,3].y),White);
end;
end;
S[CS]:=SS[CS];
cx:=GMX div 2;
cy:=GMY div 2+1;
For i:=1 to 3 do
begin
S^[CS,i].x:=S^[CS,i].x-cx+dF;
S^[CS,i].y:=cy-S^[CS,i].y-dF;
S^[CS,i].z:=S^[CS,i].z-cx-dF;
end;
end;
procedure CreateSide(var Obj:PObj);
var cx,cy,i,j,k:Word;
begin
r:=1;
For i:=1 to Obj^.Count do
S[i]:=Obj^.Side[i]^.S;
TMP:=S;
Lab.Vid.Surface.Fill(0);
myForm(GetMaxX,GetMaxY);
cx:=GMX div 2;
cy:=GMY div 2+1;
For j:=1 to CS do
For i:=1 to 3 do
begin
TMP^[j,i].x:=TMP^[j,i].x-cx+dF;
TMP^[j,i].y:=cy-TMP^[j,i].y-dF;
TMP^[j,i].z:=TMP^[j,i].z-cx-dF;
end;
For i:=1 to CS do begin
Line(Trunc(TMP^[i,1].x),Trunc(TMP^[i,1].y),Trunc(TMP^[i,2].x),Trunc(TMP^[i,2].y),White);
Line(Trunc(TMP^[i,2].x),Trunc(TMP^[i,2].y),Trunc(TMP^[i,3].x),Trunc(TMP^[i,3].y),White);
Line(Trunc(TMP^[i,3].x),Trunc(TMP^[i,3].y),Trunc(TMP^[i,1].x),Trunc(TMP^[i,1].y),White);
Line(Trunc(TMP^[i,1].x),Trunc(TMP^[i,1].z),Trunc(TMP^[i,2].x),Trunc(TMP^[i,2].z),White);
Line(Trunc(TMP^[i,2].x),Trunc(TMP^[i,2].z),Trunc(TMP^[i,3].x),Trunc(TMP^[i,3].z),White);
Line(Trunc(TMP^[i,3].x),Trunc(TMP^[i,3].z),Trunc(TMP^[i,1].x),Trunc(TMP^[i,1].z),White);
Line(Trunc(TMP^[i,1].z*r),Trunc(TMP^[i,1].y),Trunc(TMP^[i,2].z*r),Trunc(TMP^[i,2].y),White);
Line(Trunc(TMP^[i,2].z*r),Trunc(TMP^[i,2].y),Trunc(TMP^[i,3].z*r),Trunc(TMP^[i,3].y),White);
Line(Trunc(TMP^[i,3].z*r),Trunc(TMP^[i,3].y),Trunc(TMP^[i,1].z*r),Trunc(TMP^[i,1].y),White);
end;
Flip(Lab.Vid);
end;
procedure LoadSide(var Obj:PObj);
var i,j,k:Word;TMP:PPL;
begin
r:=1;
For i:=1 to Obj^.Count do
S[i]:=Obj^.Side[i]^.S;
SS^:=S^;
Lab.Vid.Surface.Fill(0);
myForm(GetMaxX,GetMaxY);
For j:=1 to CS do
For i:=1 to 3 do
begin
SS^[j,i].x:=SS^[j,i].x+cx-dF;
SS^[j,i].y:=cy-SS^[j,i].y-dF;
SS^[j,i].z:=SS^[j,i].z+cx+dF;
end;
For i:=1 to CS do begin
Line(Trunc(SS^[i,1].x),Trunc(SS^[i,1].y),Trunc(SS^[i,2].x),Trunc(SS^[i,2].y),White);
Line(Trunc(SS^[i,2].x),Trunc(SS^[i,2].y),Trunc(SS^[i,3].x),Trunc(SS^[i,3].y),White);
Line(Trunc(SS^[i,3].x),Trunc(SS^[i,3].y),Trunc(SS^[i,1].x),Trunc(SS^[i,1].y),White);
Line(Trunc(SS^[i,1].x),Trunc(SS^[i,1].z),Trunc(SS^[i,2].x),Trunc(SS^[i,2].z),White);
Line(Trunc(SS^[i,2].x),Trunc(SS^[i,2].z),Trunc(SS^[i,3].x),Trunc(SS^[i,3].z),White);
Line(Trunc(SS^[i,3].x),Trunc(SS^[i,3].z),Trunc(SS^[i,1].x),Trunc(SS^[i,1].z),White);
Line(Trunc(SS^[i,1].z*r),Trunc(SS^[i,1].y),Trunc(SS^[i,2].z*r),Trunc(SS^[i,2].y),White);
Line(Trunc(SS^[i,2].z*r),Trunc(SS^[i,2].y),Trunc(SS^[i,3].z*r),Trunc(SS^[i,3].y),White);
Line(Trunc(SS^[i,3].z*r),Trunc(SS^[i,3].y),Trunc(SS^[i,1].z*r),Trunc(SS^[i,1].y),White);
end;
Flip(Lab.Vid);
end;
procedure InPut1(x,y:integer;var E:Boolean);
var i,j:integer;B:Boolean;
begin
if(sv=0)Then begin
o[CS].x:=0;
o[CS].y:=0;
o[CS].z:=0;
end;
if(M<sv)Then begin
PutPixel(x,y,Yelow);
Flip(Lab.Vid);
if(x<cx)and(y<cy)Then
begin
o[CS].x:=x-cx+dF;
o[CS].y:=cy-y-dF;
end
else if(x>cx)and(y<cy)Then
begin
o[CS].z:=x-cx-dF;
o[CS].y:=cy-y-dF;
end
else if(x<cx)and(y>cy)Then
begin
o[CS].x:=x-cx+dF;
o[CS].z:=x-cx-dF;
end
else begin
ShowMessage(‘Эта четверть не является плоскостью проекций!’);
E:=True;
end;
inc(M);
end
else
if (M<6+sv)Then begin
if(N>3)Then N:=0;
if(x>cx)and(y>cy)Then
else
if(N=0)Then begin
x0:=x;y0:=y;x1:=0;y1:=0;
N:=1;
end;
if(x<cx)and(y<cy)Then
begin
SS^[CS,N].x:=x;
SS^[CS,N].y:=y;
r:=1;
end
else if(x>cx)and(y<cy)Then
begin
SS^[CS,N].z:=x;
SS^[CS,N].y:=y;
r:=1;
end
else if(x<cx)and(y>cy)Then
begin
SS^[CS,N].x:=x;
SS^[CS,N].z:=y;
r:=GetMaxX/GetMaxY;
end
else begin
ShowMessage(‘Эта четверть не является плоскостью проекций!’);
E:=True;
end;
if(E=False)Then begin
x2:=X;y2:=Y;
if(x1<>0)and(y1<>0)Then begin
Line(x0,y0,x2,y2,Red);
Line(x1,y1,x2,y2,Red);
end
else putpixel(x2,y2,Red);
Flip(Lab.Vid);
x1:=x2;y1:=y2;
For i:=1 to CS-1 do
For j:=1 to 3 do
begin
if(SS^[CS,N].x+o[CS].x>=SS^[i,j].x+o[i].x-2)and(SS^[CS,N].x+o[CS].x<=SS^[i,j].x+o[i].x+2)
and(SS^[CS,N].y+o[CS].y>=SS^[i,j].y+o[i].y-2)and(SS^[CS,N].y+o[CS].y<=SS^[i,j].y+o[i].y+2)
and(SS^[CS,N].z+o[CS].z>=SS^[i,j].z+o[i].z-2)and(SS^[CS,N].z+o[CS].z<=SS^[i,j].z+o[i].z+2)Then Accept:=True;
end;
inc(N);
inc(M);
end;
end;
if(M=6+sv)and(Accept=True)Then begin
M:=0;N:=0;
ClicCub[CS]:=False;
ShowSide;
Flip(Lab.Vid);
New(Scene^.Camera[CC]^.Obj[CO]^.Side[CS],Create(0,0,100,o[CS].x,o[CS].y,o[CS].z,S^[CS]));
if(CS mod 2<>0)Then
T:=ColorText(RGB(100+Random(155),100+Random(155),100+Random(155)),GMT)
else T:=T;
Scene^.Camera[CC]^.Obj[CO]^.Side[CS]^.Texture:=T;
Scene^.Camera[CC]^.Obj[CO]^.Side[CS]^.Mode:=Mode;
Scene^.Camera[CC]^.Obj[CO]^.Side[CS]^.Alpha:=0;
Accept:=False;
inc(CS);
end
else if(M=6+sv)and(Accept=False)Then begin
SS[CS]:=SS[CS-1];
SS[CS]:=SS[CS-1];
SS[CS]:=SS[CS-1];
M:=0;N:=0;
ShowSide;
Flip(Lab.Vid);
end;
end;
procedure InPut2(x,y:integer;var E:Boolean);
var i,j:integer;o:TPoint;Party:Single;tmp:Single;
begin
if(N<sv)Then begin
if(x<cx)and(y<cy)Then
begin
oc.x:=x-cx+dF;
oc.y:=cy-y-dF;
end
else if(x>cx)and(y<cy)Then
begin
oc.z:=x-cx-dF;
oc.y:=cy-y-dF;
end
else if(x<cx)and(y>cy)Then
begin
oc.x:=x-cx+dF;
oc.z:=x-cx-dF;
end
else begin
ShowMessage(‘Эта четверть не является плоскостью проекций!’);
E:=True;
end;
if(E=False)Then begin
PutPixel(x,y,Yelow);
Flip(Lab.Vid);
inc(N);
end;
end
else
if(N<2+sv)and(N>sv-1)Then begin
if(N=sv)Then begin
x0:=x;y0:=y;
end;
x1:=x;y1:=y;
Line(x0,y0,x1,y1,Red);
Flip(Lab.Vid);
x0:=x;y0:=y;
inc(N);
if(x>cx)and(y>cy)Then begin
ShowMessage(‘Эта четверть не является плоскостью проекций!’);
E:=True;
end
else begin
w[N-sv].x:=x;
w[N-sv].y:=y;
end;
end;
if(N=2+sv)Then begin
if({abs|абс}(w[N-sv].x-w[N-sv-1].x)/2<={abs|абс}(w[N-sv].y-w[N-sv-1].y)/2)Then Party:={abs|абс}(w[N-sv].x-w[N-sv-1].x)
else Party:={abs|абс}(w[N-sv].y-w[N-sv-1].y);
if(w[N-sv].x<cx)and(w[N-sv].y<cy)Then begin
o.x:=(w[N-sv].x+w[N-sv-1].x)/2;
o.y:=(w[N-sv].y+w[N-sv-1].y)/2;
o.z:=cx+Party/2+dF;
end
else if(w[N-sv].x>cx)and(w[N-sv-1].y<cy)Then begin
o.x:=cx-Party/2-dF;
o.y:=(w[N-sv].y+w[N-sv-1].y)/2;
o.z:=(w[N-sv].x+w[N-sv-1].x)/2;
end
else if(w[N-sv].x<cx)and(w[N-sv].y>cy)Then begin
o.x:=(w[N-sv].x+w[N-sv-1].x)/2;
o.y:=cy-Party/2-dF;
o.z:=(w[N-sv].y+w[N-sv-1].y)/2;
end;
r:=1;
SS^[CS,1].x:=o.x-Party/2;SS^[CS,1].y:=o.y+Party/2;SS^[CS,1].z:=o.z+Party/2;
SS^[CS,2].x:=o.x+Party/2;SS^[CS,2].y:=o.y+Party/2;SS^[CS,2].z:=o.z+Party/2;
SS^[CS,3].x:=o.x-Party/2;SS^[CS,3].y:=o.y-Party/2;SS^[CS,3].z:=o.z-Party/2;
SS^[CS+1,1].x:=o.x+Party/2;SS^[CS+1,1].y:=o.y-Party/2;SS^[CS+1,1].z:=o.z-Party/2;
SS^[CS+1,2].x:=o.x+Party/2;SS^[CS+1,2].y:=o.y+Party/2;SS^[CS+1,2].z:=o.z+Party/2;
SS^[CS+1,3].x:=o.x+Party/2;SS^[CS+1,3].y:=o.y+Party/2;SS^[CS+1,3].z:=o.z-Party/2;
SS^[CS+2,1].x:=o.x-Party/2;SS^[CS+2,1].y:=o.y+Party/2;SS^[CS+2,1].z:=o.z+Party/2;
SS^[CS+2,2].x:=o.x-Party/2;SS^[CS+2,2].y:=o.y-Party/2;SS^[CS+2,2].z:=o.z-Party/2;
SS^[CS+2,3]:=SS^[CS+1,1];
o.x:=o.x-cx+dF;
o.y:=cy-o.y-dF;
o.z:=o.z-cx-dF;
Scene^.Camera[CC]^.Obj[CO]^.Done;
Scene^.Camera[CC]^.Done;
Scene^.Done;
New(Cube,Create(0,0,100,o.x,o.y,o.z,Party,T,Mode,0));
Scene^.Camera[CC]^.ADD(CO,CO+1);
For i:=1 to Scene^.Camera[CC]^.Obj[CO]^.Count do
begin
For j:=1 to 3 do
S[i,j]:=Scene^.Camera[CC]^.Obj[CO]^.Side[i]^.S[j];
if(i mod 2<>0)Then
T:=ColorText(RGB(100+Random(155),100+Random(155),100+Random(155)),GMT)
else T:=T;
Scene^.Camera[CC]^.Obj[CO]^.Side[i]^.Texture:=T;
Scene^.Camera[CC]^.Obj[CO]^.Side[i]^.Mode:=Mode;
end;
For i:=CS to CS+12 do
ClicCub[i]:=True;
CS:=CS+12;
N:=0;
Scene^.Camera[CC]^.Obj[CO]^.Done;
Scene^.Camera[CC]^.Done;
Scene^.Done;
ShowSide;
Flip(Lab.Vid);
end;
end;
procedure Data;
var i,j:Word;
begin
New(S);
New(SS);
GetMaxX:=Lab.Vid.Width-1;
GetMaxY:=Lab.Vid.Height-1;
M:=0;N:=0;Rot:=YRot;Bol:=False;
GMX:=Lab.Vid.Width;
GMY:=Lab.Vid.Height;
CSc:=1;CC:=1;CL:=1;CO:=1;CS:=1;
cx:=GMX div 2;
cy:=GMY div 2+1;
Blue:=RGB(255,0,0);
Red:=RGB(200,200,200);
Yelow:=RGB(0,255,0);
New(Scene,Create);
New(Scene^.Camera[CC],Create(0,0,0,0,0,0));
New(Light[CL],Create(-100,-100,0));
New(Scene^.Camera[CC]^.Obj[CO],Create(0,0,0,0,0,0));
Accept:=True;View:=False;
end;
procedure TLab.FormCreate(Sender: TObject);
var i,j:Word;
begin
Tables;
@InPut[1]:=@InPut1;
@InPut[2]:=@InPut2;
New(t);
For i:=0 to GMT do
New(t^[i]);
For i:=0 to GMT do
For j:=0 to GMT do
t^[i]^[j]:=RGB(255,0,0);
Data;
end;
procedure TLab.TimerTimer(Sender: TObject; LagCount: Integer);
var i:Word;
begin
if(Keys[VK_Escape])Then halt;
if(Bol=False)and(Keys[VK_F9])and(CountSide>0)Then begin
if(CountSide=0)Then begin
ShowMessage(‘Для просмотра нужна хотя бы одна грань’);
exit;
end;
Bol:=True;View:=True;
Scene^.Camera[CC]^.Obj[CO]^.Done;
Scene^.Camera[CC]^.Done;
Scene^.Done;
CreateTMP(Scene^.Camera[CC]^.Obj[CO]);
For i:=1 to Scene^.Camera[CC]^.Obj[CO]^.Count do
Scene^.Camera[CC]^.Obj[CO]^.Side[i]^.o:=tmp_o;
Vid.Surface.Fill(0);
Scene^.Camera[CC]^.Obj[CO]^.Draw;
Flip(Vid);
end
else if(Bol)Then
if(Keys[ord(‘Y’)])Then
begin
Rot:=YRot;
Vid.Surface.Fill(0);
Scene^.Camera[CC]^.Obj[CO]^.Rotate(CosA,SinA,Angle,Rot);
Scene^.Camera[CC]^.Obj[CO]^.Draw;
Flip(Vid);
end
else if(Keys[Ord(‘X’)])Then
begin
Rot:=XRot;
Vid.Surface.Fill(0);
Scene^.Camera[CC]^.Obj[CO]^.Rotate(CosA,SinA,Angle,Rot);
Scene^.Camera[CC]^.Obj[CO]^.Draw;
Flip(Vid);
end
else if(Keys[Ord(‘Z’)])Then
begin
Rot:=ZRot;
Vid.Surface.Fill(0);
Scene^.Camera[CC]^.Obj[CO]^.Rotate(CosA,SinA,Angle,Rot);
Scene^.Camera[CC]^.Obj[CO]^.Draw;
Flip(Vid);
end
else if(Keys[VK_Space])Then
begin
if(Space.Visible=False)Then begin
Space.Visible:=True;
Enter.Visible:=True;
BSide.Visible:=True;
BCube.Visible:=True;
Menu.Items[0].Visible:=True;
Menu.Items[1].Visible:=True;
Menu.Items[2].Visible:=True;
Width:=455;
Height:=465;
Left:=108;
Top:=-7;
Vid.Width:=385;
Vid.Height:=385;
Vid.Top:=32;
Vid.Left:=32;
GMX:=Lab.Vid.Width;
GMY:=Lab.Vid.Height;
GetMaxX:=GMX-1;
GetMaxY:=GMY-1;
end;
M:=0;N:=0;Bol:=False;
View:=False;
ShowSide;
Flip(Vid);
end;
end;
procedure TLab.VidMouseDown(Sender: TObject; Button: TMouseButton;
Shift: TShiftState; X, Y: Integer);
var E:Boolean;
begin
E:=False;
if(View=False)Then InPut[Figure](x,y,E);
if(E=True)Then exit;
end;
procedure TLab.EnterClick(Sender: TObject);
var i:Word;
begin
if(CountSide=0)Then begin
ShowMessage(‘Для просмотра нужна хотя бы одна грань’);
exit;
end;
Bol:=True;View:=True;
Scene^.Camera[CC]^.Obj[CO]^.Done;
Scene^.Camera[CC]^.Done;
Scene^.Done;
CreateTMP(Scene^.Camera[CC]^.Obj[CO]);
For i:=1 to Scene^.Camera[CC]^.Obj[CO]^.Count do
Scene^.Camera[CC]^.Obj[CO]^.Side[i]^.o:=tmp_o;
Vid.Surface.Fill(0);
Scene^.Camera[CC]^.Obj[CO]^.Draw;
Flip(Vid);
end;
procedure TLab.N3Click(Sender: TObject);
var i,j:integer;Name:String;
begin
OpenDialog.Execute;
Name:=OpenDialog.FileName;
Lab.Vid.Surface.Fill(0);
Data;
myform(GetMaxX,GetMaxY);
Flip(Lab.Vid);
if(Name=»)Then exit;
LoadObject(Name,Scene^.Camera[CC]^.Obj[CO]);
LoadSide(Scene^.Camera[CC]^.Obj[CO]);
end;
procedure TLab.N4Click(Sender: TObject);
var Name:String;
begin
SaveDialog.Execute;
Name:=SaveDialog.FileName;
if(Name=»)Then exit;
CreateTMP(Scene^.Camera[CC]^.Obj[CO]);
SaveObject(Name,Scene^.Camera[CC]^.Obj[CO]);
end;
procedure TLab.N2Click(Sender: TObject);
begin
Flip(Lab.Vid);
if(Lab.Width>Lab.Height)Then Lab.Height:=Lab.Width
else Lab.Width:=Lab.Height;
Vid.Height:=Lab.Height-81;
Vid.Width:=Lab.Width-81;
Lab.Vid.Surface.Fill(0);
Data;
myform(GetMaxX,GetMaxY);
Flip(Lab.Vid);
end;
procedure TLab.N9Click(Sender: TObject);
begin
ShowMessage(‘Autocad version 1.2 Copiright by Anton Sazonov’);
end;
procedure TLab.N10Click(Sender: TObject);
begin
Halt;
end;
procedure TLab.SpaceClick(Sender: TObject);
var i:Word;
begin
M:=0;N:=0;Bol:=False;
View:=False;
ShowSide;
Flip(Vid);
end;
procedure TLab.FormKeyDown(Sender: TObject; var Key: Word;
Shift: TShiftState);
begin
Keys[Key]:=True;
end;
procedure TLab.FormKeyUp(Sender: TObject; var Key: Word;
Shift: TShiftState);
begin
Keys[Key]:=False;
end;
procedure TLab.CancelClick(Sender: TObject);
begin
Halt;
end;
procedure TLab.OKClick(Sender: TObject);
begin
Option[1]:=Box1.Text;
if(Option[1]=»)Then begin
ShowMessage(‘Выберитережим!’);
Exit;
end
else if(Option[1]=’4 bits’)Then PixMode:=1
else if(Option[1]=’8 bits’)Then PixMode:=2
else if(Option[1]=’16 bits’)Then PixMode:=3
else if(Option[1]=’24 bits’)Then PixMode:=4
else if(Option[1]=’32 bits’)Then PixMode:=5;
Option[2]:=Box6.Text;
if(Option[2]=»)Then begin
ShowMessage(‘{Выберите|Изберите} разрешение!’);
Exit;
end
else if(Option[2]=’640X480′)Then begin
ScreenX:=640;ScreenY:=480;
end
else if(Option[2]=’800X600′)Then begin
ScreenX:=800;ScreenY:=600;
end
else if(Option[2]=’1024X768′)Then begin
ScreenX:=1024;ScreenY:=768;
end
else if(Option[2]=’1280X1024′)Then begin
ScreenX:=1280;ScreenY:=1024;
end;
Surf:=Vid.Surface;
SetGraphMode(PixMode);
Label1.Destroy;
Label6.Destroy;
Label7.Destroy;
Box1.Destroy;
Box6.Destroy;
Cancel.Destroy;
OK.Visible:=False;
Lab.Height:=465;
Lab.Top:=-7;
Menu.Items.Visible:=True;
Vid.Visible:=True;
Vid.Width:=385;
Vid.Height:=385;
Space.Visible:=True;
Enter.Visible:=True;
Lab.Vid.Surface.Fill(0);
myform(GetMaxX,GetMaxY);
Flip(Lab.Vid);
if(Lab.Width>Lab.Height)Then Lab.Height:=Lab.Width
else Lab.Width:=Lab.Height;
Vid.Height:=Lab.Height-81;
Vid.Width:=Lab.Width-81;
Lab.Vid.Surface.Fill(0);
Data;
myform(GetMaxX,GetMaxY);
Flip(Lab.Vid);
BCube.Visible:=True;
BSide.Visible:=True;
Menu.Items[0].Visible:=True;
Menu.Items[1].Visible:=True;
Menu.Items[2].Visible:=True;
OK.Destroy;
end;
procedure TLab.N8Click(Sender: TObject);
begin
Application.HelpContext(10);
end;
procedure TLab.BCubeClick(Sender: TObject);
begin
Figure:=2;
ClicCub[CS]:=True;
end;
procedure TLab.BSideClick(Sender: TObject);
begin
Figure:=1;
end;
procedure TLab.N6Click(Sender: TObject);
begin
if (View=True)Then begin
Space.Visible:=False;
Enter.Visible:=False;
BSide.Visible:=False;
BCube.Visible:=False;
Menu.Items[0].Visible:=False;
Menu.Items[1].Visible:=False;
Menu.Items[2].Visible:=False;
Width:=ScreenX+10;
Height:=ScreenY+20;
Left:=-10;
Top:=-30;
Vid.Width:=ScreenX+1;
Vid.Height:=ScreenY-16;
Vid.Top:=0;
Vid.Left:=0;
GMX:=ScreenX;GMY:=ScreenY;
GetMaxX:=GMX-1;GetMaxY:=GMY-1;
Scene^.Camera[CC]^.Obj[CO]^.Draw;
Flip(Vid);
end;
end;
end.
ЗАКЛЮЧЕНИЕ
Программирование с {использованием|внедрением} трёхмерной графики — это {способ|метод} описания языком программирования объёмных тел и отображения их на {дисплее|мониторе}.
Измерение данной графики совпадает с измерением {реальной|настоящей} системы, находящейся в пространстве, {в котором|в каком} ориентируется человек, и по этому {любое|хоть какое} {материальное|вещественное} тело можно виртуально {создать|сделать}, задать ему условия и {посмотреть|поглядеть} на реакцию этого тела, задать телу правила поведения ({траекторию|линию движения} движения) и {узнать|выяснить} как оно будет себя вести, где будет находиться с течением времени.
{Например|К примеру}, можно {создать|сделать} {программу|программку}, создающую чертежи с {использованием|внедрением} гостов и чертёжных обозначений. Она {необходима|нужна} конструкторам. Примером {такой|таковой} {программы|программки} является автокод.
Можно {видоизменить|изменить} данную {программу|программку} {таким|таковым} образом, {чтобы|чтоб} она, виртуально создавала дом и {сообщала|докладывала} какие {нагрузки|перегрузки}, он будет испытывать и не деформируется ли он при {различных|разных} природных явлениях. Эта {программа|программка} {необходима|нужна} {архитекторам|конструкторам}.
Ещё трёхмерную графику можно применить для {создания|сотворения} {механизмов|устройств}, которые {между|меж} собой {взаимодействуют|ведут взаимодействие}, показать какие силы {при этом|при всем этом} участвуют, и показать их взаимодействие с {разных|различных} сторон. {Такая|Таковая} {программа|программка} нужна инженерам-механикам. Эти {программы|программки} могут {использовать|применять|употреблять} как ортогональное (параллельное), так и центральное проецирование (проецирование с учётом перспективы).
{программа|программка}, создающая трёхмерную анимацию ({фильм|кинофильм}, {мультфильм|мульт}), так же {может быть|быть может} реализована на {компьютере|компе}. Эта {программа|программка} {должна|обязана} {использовать|применять|употреблять} {только|лишь} центральное проецирование (перспективное), и {желательно|лучше} наличие {некоторых|неких} {спецэффектов|эффектов}: прозрачности, освещённости, билинейной фильтрации текстур и т.д.
Трёхмерная графика {необходима|нужна} {везде|всюду}, где {производятся|выполняются} {материальные|вещественные} объекты, где есть инженеры, конструкторы, архитекторы, просто квалифицированные рабочие, а {именно|конкретно}: в самолётостроении, в машиностроении, в {судостроении|кораблестроении}, в строительстве, в {космической|галлактической} {промышленности|индустрии} и т. д.
С трёхмерной графикой, так {или|либо} {иначе|по другому}, приходится сталкиваться дизайнерам {одежды|одежки}, дизайнерам {Интернет|Веб}-сайтов, {любым|хоть каким} {другим|иным|остальным} дизайнерам, работникам отделов рекламы, продюсерам и т. д.
Трёхмерной графикой {пользовались|воспользовались} {всегда|постоянно}. До объёмного изображения на {компьютере|компе} чертили чертежи на бумаге, до чертежей {рисовали|отрисовывали} эскизы {или|либо} {рисунки|картинки}, до рисунков {пользовались|воспользовались} заданием объектов аналитически на бумаге {или|либо} в уме.
Если {дать|отдать} оценку трёхмерной графики в {материальном|вещественном} производстве, то она будет {следующей|последующей}: трёхмерная графика, как {способ|метод} изображения объёмных фигур {или|либо} тел, является самым {наглядным|приятным} {методом|способом} представления {информации|инфы}, {используемой|применяемой} в {материальном|вещественном} производстве. Без трёхмерной графики не было бы налажено {любое|хоть какое} {материальное|вещественное} {Производство|Создание}. Самый {удобный|удачный} {способ|метод} задания и использования трёхмерной {или|либо} объёмной графики осуществляется {с помощью|при помощи} информационных технологий, а {именно|конкретно} {компьютера|компа}.
СПИСОК ЛИТЕРАТУРЫ
1 С. Бобровский «Delphi 5». 2007
2 О.Е. Акимов «Дискретная математика, способности
4. Основная форма
5. Способы сотворения программки
5.1 Матричные преобразования
5.2 Создание одноцветного треугольника
6. программка
ЗАКЛЮЧЕНИЕ
СПИСОК ЛИТЕРАТУРЫ
ВСТУПЛЕНИЕ
Любой вещественный объект, имеющий форму, является большим, как следует, его положение в пространстве можно задать при помощи трёх координат X, Y, Z. Итог хоть какого вещественного производства, дома, авто, станки, можно представить как 3-х мерную модель и показать эту модель на мониторе компа при помощи соответственной программки.
Нет необходимости создавать раздельно программку для сотворения моделей раздельно домов, раздельно станков и их устройств, раздельно для каров и т. д. Еще разумней сделать одну программку, которая может учесть специфику хоть какой отрасли и создавать, фактически любые 3-х мерные модели.
Программирование уже издавна не стало быть уделом энтузиастов. Современный программер — не писатель либо ученый, а квалифицированный рабочий. Прошли те времена, когда, удавалось «удовлетворять свой Энтузиазм за счет страны»: месяцами изучить начальный код какой-либо совсем никчемной в практическом плане утилиты, забираться в недра начальных текстов оригинально изготовленных компонент.
Естественно, маленькому проценту разрабов по долгу службы необходимы глубочайшие специальные познания, но от подавляющего большинства программистов сейчас требуется, до этого всего, умение писать программки очень стремительно и без ошибок. К примеру программка, представленная в этом проекте просит малое количество интеллектуальных издержек и времени юзера либо программера высочайшего уровня для заслуги результата. Программер высочайшего уровня — это программер, составляющий свои программки на базе разработок остальных программистов. Под разработками остальных программистов будем осознавать разработанные ими средства программирования: (язык программирования, функции, процедуры, утилиты, библиотеки, модули, составляющие, драйвера и т. д.
Хоть какой природный процесс, которым человек пробует управлять(повлиять на него с прогнозируемыми последствиями), должен быть поначалу представлен в виде модели той либо другой степени трудности. Спецам в области компьютерных технологий приходится брать такие модели либо их фрагменты для следующего объединения из определенных предметных областей, общих познаний и представлений о внешнем мире и создавать свою модель для решения поставленной задачки. Опосля этого, беря во внимание индивидуальности работы вычислительного устройства (компа) и избранного языка программирования – разрабатывать метод реализации модели, программировать(кодировать) этот метод. Не считая реализации главный функции, нужно помыслить о вспомогательных функциях(контроль входных данных, проверка адекватности результата, человека) имеют дискретную природу (дискретный
(лат. diskretus) – разбитый, прерывающийся). нрав (алфавит машинного языка двухсимвольный, огромное количество состояний микропроцессора конечное), потому неважно какая модель, реализованная в виде машинного кода, дискретна. Потому для реализации рассматриваемой программки нужно соприкоснуться с некими областями дискретной арифметики.
1. Математические объекты
1.1 Группы
Группа — оперативное огромное количество, в каком действует процедура умножения и которое подчинено последующим условиям:
1) замкнутости: для каждой пары g1g2=g3, при этом g3 должен принадлежать группе G: g1g2 ÎG; g1g2 = g3 ÎG;
2) наличия тождественного элемента e: посреди огромного количества частей группы G справедливы равенство eg = g; ge = g; e,gÎG;
3) наличие оборотных частей: для всякого g из G должен отыскаться единственный ему оборотный элемент g, принадлежащий G, при умножении на который вышел тождественный элемент e. e = q×q; e,g,gÎG;
4) ассоциативности: для всех трёх частей g1, g2, g3 из G справедливо равенство: g1(g2g3) = (g1g2)g3. Условия ассоциативности производится для квадратных матриц.
Линейное преобразование A вектора x в вектор c осуществляется при помощи квадратной матрицы y = Ax.
Если есть прямое преобразование, то обязано быть и оборотное, при условии, что A имеет A. y = Ax;
x = Tx ;y = Tx ; Как следует Ty = ATx; Умножим с лева на T , получим y = TATx;
Отсюда следует формула прямого преобразования подобия.
Формулу оборотного преобразования подобия можно получить, умножив на T с права:
Если Ax = lx; то ненулевой вектор x является своим вектором. Для нахождения собственных векторов употребляют формулу
*
где E — единичная матрица.
Корешки многочлена P(l) = det(A — lE) = 0 подставляют в формулу
и получают собственные векторы x.
Группы, лишившись собственной предметной области, не нужны в истинное время. Хотя ранее, приблизительно 40 лет вспять, теория групп была всераспространена из-за того, что группы тесновато увязывалась с базовыми областями естествознания — физикой простых частиц, квантовой механикой, физикой твёрдого тела и кристаллографией.
В данной программке все объёмные фигуры состоят из треугольных граней, любая из которых содержит три верхушки. Задав положение объёмного тела в пространстве, мы задаём положение всех вершин. Любая верхушка представляется как вектор.
Чтоб конвертировать объект,(повернуть, удалить, приблизить, сжать, растянуть и т. п.) нужно любой вектор каждой грани объекта помножить на матрицу преобразования, к примеру на матрицу поворота вокруг оси Y.
Ах так эта матрица будет смотреться:
1.2 Графы
Хоть какое объемное тело, как уже было сказано выше, можно выстроить при помощи треугольных граней, любая из которых имеет хотя бы одну общую верхушку с примыкающей гранью. Схематично каждую грань можно изобразить как совокупа вершин, соединенную контурными линиями. Контурные полосы — это полосы описывающие контур. Контур — это замкнутый путь. Таковым образом, грань, содержащая верхушки упорядоченно соединённые рёбрами представляет собой направленный граф либо Орграф.
Граф G
как математический объект – это совокупа 2-ух множеств: непустого огромного количества вершин V
и огромного количества ребер E
, элементы которого представляет собой неупорядоченные (для нацеленного графа – упорядоченные) пары частей из огромного количества V
.
G (V,E) = áV; Eñ, n(V) > 0, E Ì V ´ V,
где для неориентированного графа E
=
E
–1
(бинарное отношение E
симметрично).
Малый граф состоит из одной верхушки.
Любому неориентированному графу можно поставить в соответствие направленный граф, в каком каждое ребро заменено 2-мя обратно нацеленными ребрами, инцидентными этим же верхушкам.
Пусть v
1
и v
2
– верхушки, e
1
= (v
1
, v
2
) – соединяющее их ребро.
Тогда верхушка v
1
и ребро e
1
инцидентны, верхушка v
2
и ребро e
1
также инцидентны. Два ребра, инцидентные одной верхушке, именуются смежными; две верхушки, инцидентные одному ребру, также именуются смежными.
Обычно граф изображают на плоскости в виде диаграммы: верхушки – точками, ребра – линиями, соединяющими инцидентные верхушки.
Огромное количество вершин и огромное количество рёбер для конечных графов задаются, как правило, перечислением. Может быть задание графа описанием дела инцидентности.
1 Отношение инцидентности задано матрицей смежности:
– столбцы и строчки матрицы – верхушки графа;
– для смежных вершин элемент матрицы равен1, для других – 0;
– для неориентированного графа эта матрица постоянно симметрична;
– число рёбер равно числу единиц выше либо ниже главной диагонали матрицы ( включая элементы на диагонали).
2 Отношение инцидентности задано матрицей инцидентности:
– столбцы матрицы соответствуют верхушкам графа, а строчки – рёбрам;
– если ребро ei
инцидентно верхушке vj
, то элемент матрицы eij
=1, в неприятном случае – eij
= 0.
Таковым образом, в каждой строке одна либо две единицы, другие нули (для петли две единицы).
Для нацеленного графа при заполнении матрицы:
eij
= –1,если vj
– начало ребра;
eij
=1,если vj
–конец ребра;
eij
= a (где a – хоть какое число, не считая –1,1,0),если ребро – петля в верхушке vj
;
в других вариантах eij
= 0.
3 Граф задан перечнем ребер.
ei
vi,
vj
1
a, b
2
b, d
…
Примечание. тут ei
–ребро, vi
,
vj
– пара вершин, соединяемых сиим ребром.
Граф связан, если неважно какая пара его вершин связана ребром.
Граф без кратных ребер именуют полным, если любая пара вершин соединена ребром.
Граф H
именуют частью графа G
, если огромное количество вершин графа H
принадлежит огромному количеству вершин графа G
и огромное количество рёбер графа H
принадлежит огромному количеству рёбер графа G
, т.е.:
V(H) ÌV(G); E(H) ÌE(G).
часть графа H
именуется суграфом, если она содержит все верхушки графа G
.
Суграф H
для неориентированного графа G
именуется покрывающим суграфом, если неважно какая верхушка крайнего инцидентна хотя бы одному ребру из H
.
Подграф G
(
U
)
графа G
на огромном количестве вершин U
(
U
Ì
V
)
– это часть графа, которой принадлежат все ребра с обоими концами из U
.
Звёздный граф для верхушки v
(
v
Î
G
)
состоит из всех рёбер с началом и концом в верхушке v
. Огромное количество вершин звёздного графа состоит из верхушки v
и остальных смежных с ней вершин.
Маршрутом в единичном связном графе G
именуется таковая конечная последовательность ребер (e
1,
e
2….
en
), в какой любые два примыкающих ребра имеют общую инцидентную верхушку.
Верхушка v
о
, инцидентная ребру e
1
и не инцидентная ребру e
2
, именуется началом маршрута в графе G
.
Верхушка vn
, инцидентная ребру en
и не инцидентная ребру en
-1
, именуется концом маршрута.
Число ребер маршрута именуется его длиной.
Если верхушки v
о
и vn
совпадают, то маршрут именуется повторяющимся (либо просто циклом).
Отрезок конечного либо нескончаемого маршрута сам является маршрутом.
Маршрут в графе G
именуется цепью
, если все ребра в последовательности различны, и обычной цепью
, если все верхушки, через которые проходит маршрут (а означает и ребра) различны.
Иными словами, в цепи ребро может повстречаться не наиболее 1-го раза, а в обычной цепи верхушка – не наиболее 1-го раза.
Молвят, что две верхушки в графе соединены, если существует соединяющая их цепь. Граф, в каком все верхушки соединены, именуется связным
.
Расстоянием
меж 2-мя верхушками графа именуется малая длина обычной цепи, связывающей эти верхушки (обозначение d(v¢,v²)).
Протяженностью
меж 2-мя верхушками графа именуется наибольшая длина обычной цепи, связывающей эти верхушки (обозначение g(v¢,v²)).
В личном случае расстояние и протяженность меж верхушками могут быть схожими.
2. Справка по работе с программкой
2.1 Предназначение программного продукта
Данный программный продукт предназначен для построения всех больших фигур с следующей их демонстрацией на дисплее монитора. Необходимо подчеркнуть, что фигуры могут быть как выпуклыми, так и вогнутыми, но непременно не обязано нарушаться условие целостности. Это означает, что ни одна часть объекта не обязана существовать раздельно, а обязана быть хоть каким-либо своим элементом присоедененной к объекту. Другими словами два огромного количества: вершин и рёбер, которые получаются, если пройти по контуру, от первой верхушки к крайней, представляют собой, согласно определению, направленный граф и имеют маршрут. Сделанный объект можно сохранить на диске, также загрузить с него, чтоб поглядеть либо отредактировать итог работы. Для построения трехмерных объектов можно применять некие доступные примитивы, а конкретно грань и куб. Любая грань, в создаваемом объекте, имеет собственный цвет, избираемый программкой случайным образом.
2.2 Обучение работе с программным продукт
ом
Опосля загрузки приложения покажется окно опции, в каком нужно установить разрешение цвета, которое непременно обязано совпадать с разрешением цвета текущего графического режима, к примеру 16 бит (рис. 1).
В неприятном случае на форме будет наблюдаться некоторое аномальное графическое изображение, которое будет некорректно отражать итог работы, что, делает всякую работу неосуществимой. Дальше необходимо установить разрешение текущего графического режима в пикселях. Это необходимо для полноэкранного режима просмотра. Опосля установления графического режима покажется рабочее окно, разделённое на четыре части — четверти. Любая четверть является плоскостью проекций, размещение которых соответствует принципам начертательной геометрии: верхняя левая — передная, правая верхняя — вертикальная, нижняя левая — горизонтальная. Нижняя — правая четверть не является плоскостью проекций, по этому там отрисовывать недозволено. Точки задаются нажатиями левой клавиши мыши. Если точек больше одной, то они соединяются линией, если точек три, то они образуют треугольник, где любая точка является верхушкой.
Следует держать в голове, что мы строим трёхмерное изображение не по точкам либо по линиям, а по треугольным граням, и проецируем не точки, а грани, по этому если точка поставлена в одной плоскости проекций, то и другие две точки данной грани должны быть поставлены в данной нам же плоскости. Для построения хоть какой объёмной фигуры довольно любые две плоскости. 3-я
Набросок 1
Набросок 2
Опосля того, как объект спроецирован на плоскости проекций необходимо надавить клавишу «Просмотр», находящуюся на форме (рис. 2), чтоб поглядеть на большой объект в перспективе, и получить возможность сохранить документ. Если объект сотворен, но небыла нажата кнопка «Просмотр», то опосля выбора функции «Сохранить как» информация не будет сохранена. Опосля нажатия клавиши «Просмотр» покажется однотонное окно чёрного цвета в каком будет находиться трёхмерный объект, на который можно поглядеть со всех сторон, вращая вокруг осей X,Y,Z нажатиями соответственных кнопок «X», «Y», «Z», при этом не имеет значения большая кнопка либо нет. Просмотрев итог сотворения объекта, можно его достроить, нажав на форме клавишу «Добавить». Опосля нажатия данной нам клавиши опять покажется окно с 3-мя плоскостями проекций, в каком будут находиться проекции объекта и к которым можно добавить новейшие проекции. Следует увидеть, что вращение трёхмерного объекта в перспективе практически не приводит к изменению его положения в пространстве. Другими словами если опосля вращения объекта перейти к плоскостям проекций, картина не поменяется, и опосля перехода назад к перспективе объект будет находиться в начальном положении: его положение в перспективе будет соответствовать его проекциям на плоскостях проекций.
Добавление примитива: «Куб», осуществляется путём чертежа полосы на одной из плоскости проекций, причём, если на уровне мыслей представить проекции данной нам полосы на абсциссу и ординату, то стороной куба будет наименьшая из проекций.
Если опосля сотворения 1-го трёхмерного объекта загрузить иной объект с диска, то 1-ый объект уничтожится, а на его месте покажется 2-ой, который загружен с диска. Это разъясняется тем, что загруженный объект не добавляется к уже имеющемуся, зато можно напротив, как уже было описано выше, к загруженному объекту добавить примитивы (грани, кубы).
В программном продукте действительны такие многофункциональные клавиши:
Esc — выход из программки в операционную систему;
F1 — справка о программном продукте;
F9 — режим просмотра ;
X — вращение объекта вокруг оси X;
Y — вращение объекта вокруг оси Y;
Z — вращение объекта вокруг оси Z;
В программном продукте есть такие пункты меню «файл«, «Вид», «Помощь».
В пт меню «файл«, содержатся функции для работы с файлами. Выбрав опцию «Сделать», создаётся новенькая форма для сотворения объекта. Если поменять длину либо ширину формы и избрать опцию «Сделать», то рабочее окно воспримет размеры, надлежащие новеньким размерам формы. Причём длина и ширина будут в любом случае схожи.
При помощи функции «Открыть» можно открыть, файл, содержащий трёхмерный объект. Функция «Сохранить как» нужна для того, чтоб записать трёхмерный объект в файл.
Для загрузки и сохранения файлов в данном программном продукте, нужно и довольно указать имя файла без разширения, либо с разширением, состоящим из всех непременно трёх знаков.
Функция «Выход» создана для выхода из программки в операционную систему.
В пт меню «Вид» можно задать полноэкранный режим формы. Пункт меню «Помощь» содержит опцию «Справка», выбор которой сопровождается открытием документа, содержащего подробную справку о программном продукте.
Так же пункт меню «Помощь» содержит опцию «О программке», в какой можно выяснить короткую информацию о программном продукте и его разрабе.
2.3 Ограничения внедрения
В программке лучше применять 16 битный режим, причём разрешение графического режима, выраженное в пикселях, не имеет значения. Допускаются лишь 8 битные, 16 битные и 32 битные графические режимы. Во всех других режимах, к примеру, 2 битных, 4 битных, 24 битных программка работать не будет.
При использовании различных графических режимов рабочие окна, места на форме, где задаются грани, будут иметь разный вид (различные цвета линий разметки и линий проекций). Это соединено с тем, что цвета в программном продукте не имеют значения. Принципиально чтоб проекции на плоскости проекций были видны, и соответствовали изображаемому объекту. цвета трёхмерного объекта, совершенно выбираются случайным образом, по этому тут важен не цвет, а форма объекта.
При загрузке трёхмерного объекта из файла не непременно указывать разширение файла, либо если уж оно обозначено, то обязано содержать не наименее трёх знаков опосля точки. Это разъясняется тем, что программка сохраняет объект в 2-ух файлах. Один с разширением «Res», а иной «Dat». При всем этом не принципиально какое разширение укажет юзер, оно всё равно отбрасывается программкой.
При открытии файла программка отбрасывает обозначенное юзером разширение, если оно есть, и добавляет к имени файла своё. Когда программка отбрасывает разрешение, она отбрасывает три знака, находящиеся опосля точки. Потому если юзер указал разширение, принципиально, чтоб оно состояло не наименее чем из трёх знаков, по другому при открытии файла произойдёт ошибка.
Проектировать грани необходимо при помощи задания трёх вершин, причём если задание проекций трёх вершин начато в одной плоскости проекций, то все эти три проекции должны быть заданы в данной нам плоскости, а не раздельно: одна проекция точки на передней, иная на горизонтальной, 3-я на профильной. Все три проекции точки должны быть в одной плоскости проекций. Это соединено, как уже говорилось, с тем что проецируются не точки либо полосы, а грани.
Рабочее окно, которое выводит графическую информацию, быть может лишь квадратным, по этому изменяя длину либо ширину рабочего окна, необходимо учесть, что программка уравняет эти оба параметра.
3. Нереализованные способности
Самый основной элемент, который нужно воплотить — это полное отсутствие ошибок. На данном шаге разработки ошибок не выявлено, но гарантии их отсутствия не существует.
Потом следовало бы прирастить количество примитивов: добавить к грани и кубу, возможность сотворения призмы, многоугольника, цилиндра, сферы, диска. наличие этих фигур обеспечило бы лёгкость сотворения всех объёмных тел. Поэтому что, к примеру призма, в базе которой верный восьмиугольник содержит 32 треугольной грани. естественно юзеру вручную весьма проблематично сделать эту фигуру, потому её наличие в коллекции примитивов значительно облегчило бы ему работу.
Так же можно было бы создать способы наложения различных текстур на трёхмерные объекты. Текстуры — это двухмерные графические изображения, которые, наложив как фотографию на грань объекта, превращают обычный куб в телек, дом с окнами, стиральную машинку и остальные кубические объекты.
Текстуры бывают: аффинные, четкие (перспективно — корректные), параболические. Текстуры можно накладывать на все трёхмерные объекты.
Можно было бы воплотить прозрачность трёхмерных объектов. к примеру, если необходимо было бы сделать объёмное тело из стекла. В этом случае нужно было бы применять прозрачность. Трёхмерные объекты, как понятно, создаются двухмерными точками. Потому чтоб сделать прозрачность нужно перед выводом каждой точки объекта в определённом месте получать цвет уже стоящей на этом месте точки, если она есть, потом вывести точку цвет которой будет средним меж цветом точки, которая ставится и которая уже стоит. Таковым образом, за выводимым трёхмерным объектом будет видно размещение другого объёмного тела. Это и есть прозрачность.
4. Основная форма
Основная форма содержит такие составляющие как:
Main
Menu
— Предназначен для прибавления к главной программке головного меню. Является компонентом Standart. Компонент MainMenu не зрительный компонент. Редактор меню вызывается опосля выбора функции Items. В редакторе можно создавать пункты меню проименовывая их с помощь характеристики Caption. Переходя от одной компоненте к иной можно при помощи такого же характеристики Caption отредактировать пункты меню. Чтоб вставить линию разделитель необходимо в свойстве Caption первой позицией указать знак «-» (дефис). Опосля окончания сотворения пт меню редактор меню нужно закрыть.
BitBtn
— Этот компонент предназначен для сотворения клавиши с картинкой. В системе имеется набор готовых шаблонов.
Опосля размещения объекта на форме изображение, помещаемое на клавишу, задаётся в свойстве Glyph (Значок). При всем этом вызывается редактор, при помощи которого выбирается подходящая картина (в формате BMP). Любая таковая картина может состоять из 4 частей, равных по ширине. 1-ая часть — изображение клавиши в обыкновенном состоянии, 2-ая — изображение «отключённой» клавиши, 3-я — изображение клавиши опосля щелчка мыши, изображение на нажатой кнопочке. Число составных частей задаётся в свойстве NumGlyph (от 1 до 4). Расстояние от рисунки до границ клавиши (в пикселях) можно указать в свойстве Margin. В свойстве Kind задаётся реакция клавиши на щелчок.
Button
— Компонент предназначен для сотворения клавиш на форме и обработки действия нажатия клавиши. Размещен на панели Standart. имя клавиши указывается в поле Name, а выводимый текст на кнопочке в свойстве Caption.
Combo
Box
— Компонент Поле со перечнем. Представляет собой вариант перечня, с присоединённым доп полем, в каком отображается избранный элемент перечня. Это поле может употребляться для ввода новейших частей либо для резвого поиска частей по исходным символам. Если на дисплее отображается лишь присоединённое поле («раскрывающийся перечень»), то для раскрытия перечня можно применять клавиатурную комбинацию Alt+Вниз.
Open
Dialog
— Компонент предназначен для выбора файла с целью следующего открытия; Характеристики класса TOpenDialog приведены в табл. 1
Таблица 1 — Характеристики класса TOpenDialog
Параметров
о
Назначен
и
е
DefaultExt
расширение имени, применяемое по дефлоту. Добавляется в конец выбраного юзером имени файла, если расширение не обозначено очевидно.
FileName
Выбранное юзером имя файла вкупе с полным путём поиска
Files
Перечень избранных имён файлов. В свойстве Options должен быть включён флаг ofAllowMultiSelect
Filter
Набор масок, в согласовании с которыми отбираются названия файлов для отображения в диалоговом окне. Любая маска состоит из 2-ух частей: наименования и шаблона, — разделённых эмблемой |. Одному наименованию могут соответствовать несколько шаблонов. Маски отделяются друг от друга эмблемой |
FilterIndex
Номер текущей маски. Нумерация начинается с 1
HistoryList
Перечень ранее избранных файлов (тип Strings)
InitialDir
Текущий каталог, содержимое которого отображается при первом открытии диалогового окна
Options
Набор флагов, определяющих окна выбора файлов.
Title
Заголовок диалогового окна
Save
Dialog
— Этот компонент фактически ничем не различается от компонента OpenDialog кроме нескольких опций, специфичных для процесса сохранения файла.
Label
— Компонент находится на панэле Standart. Предназначен для вывода текста на форме при помощи характеристики Text.
TDXTimer
— Компонент находится на панели DelphiX. В программках, выполняющих деяния, связанные с моделированием либо обработкой графики, созданных для общения с юзером в настоящем режиме времени либо выполняющих длительные вычисления, нужна компонента TDXTimer.
При помощи компонента TDXTimer можно включить генерацию сообщений, поступающих от системного таймера Windows с данной периодичностью (в миллисекундах) и делать определённую часть действий конкретно в обработчике этого действия. Это одна из способностей многозадачности приложений Windows.
TDXDraw
— Этот компонент находится на вкладке DelphiX. Он предназначен для вывода двухмерной и трёхмерной графики, используя DirectDraw и DirectX. Разрешение выводимой графики задаётся в свойстве Display. Вид курсора задаётся в свойстве Cursor. способ DXDraw.Surface.Fill(Color) — заполняет видеостраницу цветом Color. При помощи характеристики DXDraw.Surface.pixels[x, y]:=Color — ставится точка на виртуальной видеостранице. При помощи способа DXDraw.Flip — невидимая видеостраница делается видимой. Схема обрисовки изображения на 2-ух видеостраницах: видимой и не видимой делает вероятной сделать анимацию без мигания изображения.
5. способы сотворения программки
Для сотворения трёхмерных объектов нужно уметь преобразовывать трёхмерную графику в двухмерную. Ведь процедура вывода точки имеет лишь две координаты, задающие положение точки (x, y), как следует, реально имеется дело с двухмерной графикой. Для начала следует принять систему трёхмерных координат:
Набросок 3
тут знаками x, y, z обозначены положительные направления осей Ox, Oy и Oz соответственно. Также предполагается, что камера недвижна и находится в точке с координатами (0,0,-dist), ось зрения камеры ориентирована по оси Oz, а конкретно в точку (0,0,0) (т.е. camera target = (0,0,0)), ось Ox исходя из убеждений камеры ориентирована слева вправо, ось Oy — снизу ввысь, ось Oz — вглубь экрана. Размер экрана — xSize на ySize пикселей.
тут и дальше употребляются обозначения:
sx, sy
координаты проекции точки на дисплее
x, y, z
3D координаты точки,
Dist
расстояние от камеры (она находится в точке (0,0,-dist)) до начала координат,
Для созданя трёхмерных объектов нужны примитивы. Таковыми примитивами являются простые трёхмерные объекты — треугольные грани. У каждой треугольной грани есть три верхушки — три точки, имеющие трёхмерные координаты. Чтоб спроецировать их на плоскость, т. е. из трёхмерных координат x,y,z получить двухмерные sx, sy нужно воспользоваться формулой:
sx = xSize/2+x*dist/(z+dist); sy = ySize/2-y*dist/(z+dist);
5.1 Матричные преобразования
В данной нам главе коротко представлены простые понятия о матричных преобразованиях, являющихся составной частью линейной алгебры, и дискретной арифметики (см. раздел Группы).
Введем несколько определений. n-мерный вектор, он же вектор размерности n, он же вектор размера n: упорядоченный набор n реальных чисел. Матрица размера m на n (будет обозначаться как m*n, mxn): таблица размера m на n, в каждой клеточке которой — действительное число.
Вот вам наглядный пример матрицы 3×3:
[ 15 y*z 0.6 ]
[ 7 -3 91 ]
[ sin(x) 0.123 exp(t) ]
Вектор будем записывать в столбик и разглядывать его как матрицу размера n*1.
Операция скалярного произведения векторов: определена для 2-ух векторов схожих размеров. Итог есть число, равное сумме произведений соответственных частей векторов. Пример:
[ 1 ] [ 4 ]
[ 2 ] * [ 5 ] = 1*4 + 2*5 + 3*6 = 32
[ 3 ] [ 6 ]
Операция векторного произведения: определена для (n-1) вектора схожего размера n. Итог — вектор, при этом, перпендикулярный всем множителям.
Замечание:
итог изменяется от перестановки мест множителей!
Формально определяется как определитель матрицы, 1-ая строчка которой все есть базовые вектора, а все следующие — надлежащие координаты всех множителей. Так как она нужна лишь для 3D места, мы определим векторное произведение 2-ух 3D векторов очевидно:
[ Ax ] [ Bx ] | i j k | [ Ay*Bz-Az*By ]
AxB = [ Ay ] x [ By ] = | Ax Ay Az | = [ Az*Bx-Ax*Bz ]
[ Az ] [ Bz ] | Bx By Bz | [ Ax*By-Ay*Bx ]
Операция сложения 2-ух матриц: определена для матриц схожих размеров. Любой элемент суммы (другими словами, каждое число в таблице) приравнивается сумме соответственных частей слагаемых-матриц. Пример:
[ 1 x 500 ] [ 8 a 3 ] [ 9 a+x 503 ]
[ 2 y 600 ] + [ 9 b 2 ] = [ 11 b+y 602 ]
[ 3 z 700 ] [ 10 c 1 ] [ 13 c+z 701 ]
Операция умножения матрицы на число: определена для хоть какой матрицы и хоть какого числа; любой элемент результата приравнивается произведению соответственного элемента матрицы-множителя и числа-множителя.
Операция умножения 2-ух матриц: определена для 2-ух матриц таковых размеров a*b и c*d, что b = c. к примеру, если b = c, но a ¹ d, то при перестановке множителей операция будет совершенно не определена. Результатом умножения матрицы A размером a*b на матрицу B размером b*d будет матрица C размером a*d, в какой элемент, стоящий в строке i и столбце j, равен произведению строчки i матрицы A на столбец j матрицы B. Произведение строчки на столбец определяется как сумма произведений соответственных частей строчки и столбца. к примеру, умножение строчки на столбец (они должны быть равной длины, потому и такие ограничения на размеры матриц):
[ 4 ]
[ 1 2 3 ] * [ 5 ] = 1*4 + 2*5 + 3*6 = 32
[ 6 ]
А чтоб перемножить две матрицы, нужно эту операцию сделать для всякого элемента. Вот вам наглядный пример:
[ 1 2 3 ] [ 0 3 ] [ 1*0+2*1+3*2 1*3+2*4+3*5 ]
[ 4 5 6 ] * [ 1 4 ] = [ 4*0+5*1+6*2 4*3+5*4+6*5 ] = …
[ 7 8 9 ] [ 2 5 ] [ 7*0+8*1+9*2 7*3+8*4+9*5 ]
Умножение и сложение матриц владеют практически этим же набором параметров, что и обыденные числа, хотя некие обычные характеристики не производятся (к примеру, A*B ¹ B*A); по сути требуется знать, что произведение вида A*B*C*D*… не зависит от того, как расставить скобки. Либо, что
A*(B*C) = (A*B)*C.
Хоть какое движение (другими словами преобразование места, сохраняющее расстояние меж точками) в трехмерном пространстве, согласно аксиоме Шаля, быть может представлено в виде суперпозиции поворота и параллельного переноса, другими словами поочередного выполнения поворота и параллельного переноса. Потому основная часть информация о поведении объекта — это его смещение, ось поворота и угол поворота. Потому нам довольно знать, как создать два преобразования — перенос и поворот.
Перенос точки (точки будут также рассматриваться как вектора с началом сначала координат и концом в фактически точке) с координатами (x,y,z) на вектор (dx,dy,dz) делается обычным сложением всех координат. Другими словами итог — это (x+dx,y+dy,z+dz). Подобно сложению вектора-точки с вектором-переносом.
Разглядим для примера поворот точки (x,y,z) относительно оси z. В этом случае z не изменяется, а (x,y) изменяются так же, как и при 2D повороте относительно начала координат.
Покажем координаты точки A’ — результата поворота A(x,y) на угол alpha (рис. 4).
Набросок 4
Пусть r = sqrt(x*x+y*y). Пусть угол AOx равен phi, тогда из рисунка видно, что cos(phi) = x/r, sin(phi) = y/r. Угол A’OA равен по условию alpha. Отсюда
x’ = r*cos(alpha+phi) = r*(cos(alpha)*cos(phi)-sin(alpha)*sin(phi)) =
= (r*cos(phi))*cos(alpha)-(r*sin(phi))*sin(alpha) =
= x*cos(alpha)-y*sin(alpha)
y’ = r*sin(alpha+phi) = r*(cos(alpha)*sin(phi)+sin(alpha)*cos(phi)) =
= (r*cos(phi))*sin(alpha)+(r*sin(phi))*cos(alpha) =
= x*sin(alpha)+y*cos(alpha)
Для трехмерного варианта, таковым образом
x’ = x*cos(alpha)-y*sin(alpha)
y’ = x*sin(alpha)+y*cos(alpha)
z’ = z
Подобные формулы получатся и для остальных осей поворота (другими словами Ox, Oy). Поворот относительно случайной оси, проходящей через начало координат, можно создать при помощи этих поворотов — создать поворот относительно Ox так, чтоб ось поворота стала перпендикулярна Oy, потом поворот относительно Oy так, чтоб ось поворота совпала с Oz, создать поворот, а потом оборотные повороты относительно Oy и Ox.
Вспомним о матрицах и векторах и пристально поглядим на выведенные формулы для поворота. Можно увидеть, что
[ x’ ] = [ cos(alpha) -sin(alpha) 0 ] [ x ]
[ y’ ] = [ sin(alpha) cos(alpha) 0 ] [ y ]
[ z’ ] = [ 0 0 1 ] [ z ]
Другими словами поворот на угол alpha задается одной и той же матрицей, и при помощи данной нам матрицы (умножая ее на вектор-точку) можно получить координаты повернутой точки. С одной стороны умножение матрицы на вектор просит больше операций, чем расчет x’ и y’ по формулам.
Нос иной удобство матриц для заключается как раз в свойстве
A*(B*C) = (A*B)*C. Пусть делается несколько поворотов попорядку, к примеру, 5 (столько, сколько нужно для поворота относительно случайной оси), и пусть они задаются матрицами A, B, C, D, E (A — матрица самого первого поворота, E — крайнего). Тогда для вектора p мы получаем
p’ = E*(D*(C*(B*(A*p)))) = E*D*C*B*A*p = (E*D*C*B*A)*p = (E*(D*(C*(B*A))))*p = T*p,
где T = (E*(D*(C*(B*A)))) матрица преобразования, являющегося композицией 5 поворотов. Посчитав один раз эту матрицу, можно в предстоящем применить достаточно сложное преобразование из 5 поворотов к хоть какому вектору при помощи всего 1-го умножения матрицы на вектор.
Таковым образом, можно задать хоть какой поворот матрицей, и неважно какая композиция поворотов также будет задаваться матрицей, которую можно достаточно просто посчитать. Но еще есть параллельный перенос и масштабирование.
По сути, эти преобразования тоже просто записываются в виде матриц. Лишь заместо матриц 3×3 и 3-мерных векторов употребляются так именуемые однородные 4-мерные координаты и матрицы 4×4. При всем этом заместо векторов вида
[ x ]
[ y ]
[ z ]
употребляются вектора вида
[ x ]
[ y ]
[ z ]
[ 1 ]
а заместо случайных матриц 3×3 употребляются матрицы 4×4 такового вида:
[ a b c d ]
[ e f g h ]
[ i j k l ]
[ 0 0 0 1 ]
Видно, что если d = h = l = 0, то в итоге внедрения всех операций выходит то же самое, что и для матриц 3×3.
Матрица параллельного переноса сейчас определяется как
[ 1 0 0 dx ]
[ 0 1 0 dy ]
[ 0 0 1 dz ]
[ 0 0 0 1 ]
Матрицу масштабирования можно найти и для матриц 3×3, и для матриц 4×4:
[ kx 0 0 ] [ kx 0 0 0 ]
[ 0 ky 0 ] либо [ 0 ky 0 0 ]
[ 0 0 kz ] [ 0 0 kz 0 ]
[ 0 0 0 1 ]
где kx, ky, kz — коэффициенты масштабирования по подходящим осям.
Таковым образом, получаем последующее. Хоть какое необходимое нам преобразование места можно задать матрицей 4×4 определенной структуры, разной для различных преобразований. Итог поочередного выполнений нескольких преобразований совпадает с результатом 1-го преобразования T, которое также задается матрицей 4×4, вычисляемой как произведение матриц всех этих преобразований. Важен порядок умножения, потому что A*B ¹ B*A. Итог внедрения преобразования T к вектору [ x y z ] считается как итог умножения матрицы T на вектор [ x y z 1 ].
Докажем на примере, что A*B ¹ B*A. Пусть A — матрица переноса, B — поворота. Если поначалу перенести объект, а позже повернем относительно центра координат (это будет B*A), то итог не будет соответствовать результату, при котором поначалу объект поворачивают, а потом переносят (A*B).
5.2 Создание одноцветного треугольника
Изображение треугольника на дисплее — набор горизонтальных отрезков, при этом из-за того, что треугольник — фигура выпуклая, каждой строке экрана соответствует не наиболее 1-го отрезка. Потому довольно обойти все строчки экрана, с которыми пересекается треугольник, (другими словами, от малого до наибольшего значения (y) для вершин треугольника), и нарисовать надлежащие горизонтальные отрезки.
необходимо отсортировать верхушки так, чтоб верхушка A была верхней, C — нижней, тогда min_y = A.y, max_y = C.y, и нужно обойти все полосы от min_y до max_y. Разглядим какую-то линию sy, A.y <= sy <= C.y. Если sy < B.y, то она пересекает стороны AB и AC; если sy >= B.y — то стороны BC и AC. Известны координаты всех вершин, потому можно написать уравнения сторон и отыскать пересечение подходящей стороны с прямой y = sy. Получим два конца отрезка. Потому что не понятно, какой из их левый, а какой правый, необходимо сравним их координаты по x и обменяем значения, если необходимо. Рисуя этот отрезок, повторяя функцию для каждой строчки — получаем треугольник.
Рассматривая наиболее тщательно пересечения прямой
y = sy (текущей строчки) и стороны треугольника, к примеру AB, Получим уравнение прямой AB в форме x = k*y+b:
x = A.x+(y-A.y)*(B.x-A.x)/(B.y-A.y)
Сейчас нужно подставить известное для текущей прямой
x = A.x+(sy-A.y)*(B.x-A.x)/(B.y-A.y)
Для остальных сторон пересечение ищется совсем буквально так же. к примеру:
// …
// тут сортируем верхушки (A,B,C)
// …
for sy = A.y to C.y begin
x1 = A.x + (sy — A.y) * (C.x — A.x) / (C.y — A.y);
if (sy < B.y)Then
x2 = A.x + (sy — A.y) * (B.x — A.x) / (B.y — A.y);
else
x2 = B.x + (sy — B.y) * (C.x — B.x) / (C.y — B.y);
if (x1 > x2) Then
begin
tmp = x1; x1 = x2; x2 = tmp;
end;
drawHorizontalLine(sy, x1, x2);
end;
Нужно защититься от варианта, когда B.y = C.y — в этом (и лишь этом, поэтому как если C.y = A.y, то треугольник пустой и отрисовывать его не необходимо, либо можно отрисовывать горизонтальную линию; а если B.y = A.y, то sy >= A.y и до деления на B.y — A.y не дойдет) случае произойдет попытка деления на ноль.
// …
// тут сортируем верхушки (A,B,C)
// …
for sy = A.y to C.y begin
x1 = A.x + (sy — A.y) * (C.x — A.x) / (C.y — A.y);
if (sy < B.y)
x2 = A.x + (sy — A.y) * (B.x — A.x) / (B.y — A.y);
else begin
if (C.y == B.y)
x2 = B.x;
else
x2 = B.x + (sy — B.y) * (C.x — B.x) / (C.y — B.y);
end;
if (x1 > x2)Then
begin
tmp = x1; x1 = x2; x2 = tmp;
drawHorizontalLine(sy, x1, x2);
end;
// …
тут drawHorizontalLine(sy, x1, x2) — горизонтальная линия. Её создание не представляет трудности и код будетвыглядеть так.
//…
For i:=x1 to x2 do
PutPixel(i,sy,Color);
//…
6. программка
unit graph3;
interface
uses Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs,
DXClass, DXDraws, StdCtrls,graph, Menus,graph3D,figures, Buttons;
const Mode:Word=0;
type TLab = class(TForm)
Vid: TDXDraw;
Timer: TDXTimer;
Enter: TButton;
Menu: TMainMenu;
N1: TMenuItem;
N2: TMenuItem;
N3: TMenuItem;
N4: TMenuItem;
N5: TMenuItem;
N6: TMenuItem;
N7: TMenuItem;
N8: TMenuItem;
N9: TMenuItem;
OpenDialog: TOpenDialog;
SaveDialog: TSaveDialog;
N10: TMenuItem;
Space: TButton;
Box1: TComboBox;
Label1: TLabel;
OK: TButton;
Cancel: TButton;
Label6: TLabel;
BCube: TBitBtn;
BSide: TBitBtn;
Box6: TComboBox;
Label7: TLabel;
procedure FormCreate(Sender: TObject);
procedure TimerTimer(Sender: TObject; LagCount: Integer);
procedure VidMouseDown(Sender: TObject; Button: TMouseButton;
Shift: TShiftState; X, Y: Integer);
procedure EnterClick(Sender: TObject);
procedure N3Click(Sender: TObject);
procedure N4Click(Sender: TObject);
procedure N2Click(Sender: TObject);
procedure N9Click(Sender: TObject);
procedure N10Click(Sender: TObject);
procedure SpaceClick(Sender: TObject);
procedure FormKeyDown(Sender: TObject; var Key: Word;
Shift: TShiftState);
procedure FormKeyUp(Sender: TObject; var Key: Word;
Shift: TShiftState);
procedure CancelClick(Sender: TObject);
procedure OKClick(Sender: TObject);
procedure N8Click(Sender: TObject);
procedure BCubeClick(Sender: TObject);
procedure BSideClick(Sender: TObject);
procedure N6Click(Sender: TObject);
private
public
end;
var Lab: TLab;
implementation
{$R *.DFM}
const ScreenX:Word=640;
ScreenY:Word=480;
x1:integer=0;
y1:integer=0;
White=$FFFFFF;
View:Boolean=False;
Figure:Word=1;
Accept:Boolean=True;
sv:Word=0;
var c:char;S,SS,TMP:PPl;t:PTexture;
CS,CO,CC,CSc,CL:Word;Rot:TRot;
x0,y0,x2,y2:integer;r:Single;
Blue,Red,Yelow,M,N,SC:Word;
Keys:array[1..255]of Boolean;
Input:array[1..12]of procedure (x,y:integer;var E:Boolean);
Bol:Boolean;Option:array[1..5] of String;
o:array[1..MaxSide]of TPoint;
w:array[1..2]of TPoint;tmp_o:TPoint;
cx,cy:Word;oc:TPoint;ClicCub:array[0..MaxSide]of Boolean;
procedure LoadObject(Name:String;var Obj:PObj);
var f1:file of TPoint; a:TSides;
f2:file of Word;
S,Tmp:TPoint;i,j:word;Name2:String;
B:Boolean;
begin
For i:=1 to Length (Name)-4 do begin
Name2:=Name2+Name[i];
end;
Name:=Name2;
Assign(f2,Name+’.res’);
Reset(f2);
Read(f2,Obj^.Count);
close(f2);
Assign(f1,Name+’.dat’);
Reset(f1);
Read(f1,Obj^.o);
For i:=1 to Obj^.Count do
New(Obj^.Side[i],Create(Obj^.o.x,Obj^.o.y,Obj^.o.z,0,0,0,a));
For i:=1 to Obj^.Count do begin
inc(CS);
if(i mod 2<>0)Then
T:=ColorText(RGB(100+Random(155),100+Random(155),100+Random(155)),GMT)
else T:=T;
Obj^.Side[i]^.Texture:=T;
Obj^.Side[i]^.Mode:=Mode;
Obj^.Side[i]^.Alpha:=0;
Read(f1,S);
For j:=1 to 3 do
begin
Read(f1,S);
Obj^.Side[i]^.S[j]:=S;
SS^[i,j]:=S;
end;
end;
close(f1);
end;
procedure SaveObject(Name:String;var Obj:PObj);
var f1:file of TPoint;
f2:file of Word;B:Boolean;
S:TPoint;i,j:word;Name2:String;
begin
B:=False;
For i:=1 to Length(Name) do
if(Name[i]=’.’)Then B:=True;
if(B=True)Then begin
For i:=1 to Length(Name)-4 do begin
Name2:=Name2+Name[i];
end;
Name:=Name2;
end;
Assign(f2,Name+’.res’);
Rewrite(f2);
Reset(f2);
Write(f2,Obj^.Count);
close(f2);
Assign(f1,Name+’.dat’);
Rewrite(f1);
Reset(f1);
Write(f1,Obj^.o);
For i:=1 to Obj^.Count do begin
Obj^.Side[i]^.o:=tmp_o;
S:=Obj^.Side[i]^.o;
Write(f1,S);
For j:=1 to 3 do
begin
S:=Obj^.Side[i]^.S[j];
Write(f1,S);
end;
end;
close(f1);
end;
procedure CreateTMP(var Obj:PObj);
var i,j,k:Word;
begin
Obj^.o.x:=0;Obj^.o.y:=0;Obj^.o.z:=100;
For i:=1 to Obj^.Count do
begin
tmp_o:=Obj^.Side[i]^.o;
Obj^.Side[i]^.o:=o[i];
For j:=1 to 3 do
Obj^.Side[i]^.S[j]:=S^[i,j];
end;
end;
procedure ShowSide;
var i,j,k,cx,cy:Word;tmp:TPoint;
begin
Lab.Vid.Surface.Fill(0);
myForm(GetMaxX,GetMaxY);
For i:=1 to CS do begin
Line(Trunc(SS^[i,1].x),Trunc(SS^[i,1].y),Trunc(SS^[i,2].x),Trunc(SS^[i,2].y),White);
Line(Trunc(SS^[i,2].x),Trunc(SS^[i,2].y),Trunc(SS^[i,3].x),Trunc(SS^[i,3].y),White);
Line(Trunc(SS^[i,3].x),Trunc(SS^[i,3].y),Trunc(SS^[i,1].x),Trunc(SS^[i,1].y),White);
Line(Trunc(SS^[i,1].x),Trunc(SS^[i,1].z),Trunc(SS^[i,2].x),Trunc(SS^[i,2].z),White);
Line(Trunc(SS^[i,2].x),Trunc(SS^[i,2].z),Trunc(SS^[i,3].x),Trunc(SS^[i,3].z),White);
Line(Trunc(SS^[i,3].x),Trunc(SS^[i,3].z),Trunc(SS^[i,1].x),Trunc(SS^[i,1].z),White);
Line(Trunc(SS^[i,1].z*r),Trunc(SS^[i,1].y),Trunc(SS^[i,2].z*r),Trunc(SS^[i,2].y),White);
Line(Trunc(SS^[i,2].z*r),Trunc(SS^[i,2].y),Trunc(SS^[i,3].z*r),Trunc(SS^[i,3].y),White);
Line(Trunc(SS^[i,3].z*r),Trunc(SS^[i,3].y),Trunc(SS^[i,1].z*r),Trunc(SS^[i,1].y),White);
if(ClicCub[i]=True)Then begin
Line(Trunc(SS^[i,1].z),Trunc(SS^[i,1].y),Trunc(SS^[i,2].z),Trunc(SS^[i,1].y),White);
Line(Trunc(SS^[i,2].z),Trunc(SS^[i,1].y),Trunc(SS^[i,1].z),Trunc(SS^[i,3].y),White);
end;
end;
S[CS]:=SS[CS];
cx:=GMX div 2;
cy:=GMY div 2+1;
For i:=1 to 3 do
begin
S^[CS,i].x:=S^[CS,i].x-cx+dF;
S^[CS,i].y:=cy-S^[CS,i].y-dF;
S^[CS,i].z:=S^[CS,i].z-cx-dF;
end;
end;
procedure CreateSide(var Obj:PObj);
var cx,cy,i,j,k:Word;
begin
r:=1;
For i:=1 to Obj^.Count do
S[i]:=Obj^.Side[i]^.S;
TMP:=S;
Lab.Vid.Surface.Fill(0);
myForm(GetMaxX,GetMaxY);
cx:=GMX div 2;
cy:=GMY div 2+1;
For j:=1 to CS do
For i:=1 to 3 do
begin
TMP^[j,i].x:=TMP^[j,i].x-cx+dF;
TMP^[j,i].y:=cy-TMP^[j,i].y-dF;
TMP^[j,i].z:=TMP^[j,i].z-cx-dF;
end;
For i:=1 to CS do begin
Line(Trunc(TMP^[i,1].x),Trunc(TMP^[i,1].y),Trunc(TMP^[i,2].x),Trunc(TMP^[i,2].y),White);
Line(Trunc(TMP^[i,2].x),Trunc(TMP^[i,2].y),Trunc(TMP^[i,3].x),Trunc(TMP^[i,3].y),White);
Line(Trunc(TMP^[i,3].x),Trunc(TMP^[i,3].y),Trunc(TMP^[i,1].x),Trunc(TMP^[i,1].y),White);
Line(Trunc(TMP^[i,1].x),Trunc(TMP^[i,1].z),Trunc(TMP^[i,2].x),Trunc(TMP^[i,2].z),White);
Line(Trunc(TMP^[i,2].x),Trunc(TMP^[i,2].z),Trunc(TMP^[i,3].x),Trunc(TMP^[i,3].z),White);
Line(Trunc(TMP^[i,3].x),Trunc(TMP^[i,3].z),Trunc(TMP^[i,1].x),Trunc(TMP^[i,1].z),White);
Line(Trunc(TMP^[i,1].z*r),Trunc(TMP^[i,1].y),Trunc(TMP^[i,2].z*r),Trunc(TMP^[i,2].y),White);
Line(Trunc(TMP^[i,2].z*r),Trunc(TMP^[i,2].y),Trunc(TMP^[i,3].z*r),Trunc(TMP^[i,3].y),White);
Line(Trunc(TMP^[i,3].z*r),Trunc(TMP^[i,3].y),Trunc(TMP^[i,1].z*r),Trunc(TMP^[i,1].y),White);
end;
Flip(Lab.Vid);
end;
procedure LoadSide(var Obj:PObj);
var i,j,k:Word;TMP:PPL;
begin
r:=1;
For i:=1 to Obj^.Count do
S[i]:=Obj^.Side[i]^.S;
SS^:=S^;
Lab.Vid.Surface.Fill(0);
myForm(GetMaxX,GetMaxY);
For j:=1 to CS do
For i:=1 to 3 do
begin
SS^[j,i].x:=SS^[j,i].x+cx-dF;
SS^[j,i].y:=cy-SS^[j,i].y-dF;
SS^[j,i].z:=SS^[j,i].z+cx+dF;
end;
For i:=1 to CS do begin
Line(Trunc(SS^[i,1].x),Trunc(SS^[i,1].y),Trunc(SS^[i,2].x),Trunc(SS^[i,2].y),White);
Line(Trunc(SS^[i,2].x),Trunc(SS^[i,2].y),Trunc(SS^[i,3].x),Trunc(SS^[i,3].y),White);
Line(Trunc(SS^[i,3].x),Trunc(SS^[i,3].y),Trunc(SS^[i,1].x),Trunc(SS^[i,1].y),White);
Line(Trunc(SS^[i,1].x),Trunc(SS^[i,1].z),Trunc(SS^[i,2].x),Trunc(SS^[i,2].z),White);
Line(Trunc(SS^[i,2].x),Trunc(SS^[i,2].z),Trunc(SS^[i,3].x),Trunc(SS^[i,3].z),White);
Line(Trunc(SS^[i,3].x),Trunc(SS^[i,3].z),Trunc(SS^[i,1].x),Trunc(SS^[i,1].z),White);
Line(Trunc(SS^[i,1].z*r),Trunc(SS^[i,1].y),Trunc(SS^[i,2].z*r),Trunc(SS^[i,2].y),White);
Line(Trunc(SS^[i,2].z*r),Trunc(SS^[i,2].y),Trunc(SS^[i,3].z*r),Trunc(SS^[i,3].y),White);
Line(Trunc(SS^[i,3].z*r),Trunc(SS^[i,3].y),Trunc(SS^[i,1].z*r),Trunc(SS^[i,1].y),White);
end;
Flip(Lab.Vid);
end;
procedure InPut1(x,y:integer;var E:Boolean);
var i,j:integer;B:Boolean;
begin
if(sv=0)Then begin
o[CS].x:=0;
o[CS].y:=0;
o[CS].z:=0;
end;
if(M<sv)Then begin
PutPixel(x,y,Yelow);
Flip(Lab.Vid);
if(x<cx)and(y<cy)Then
begin
o[CS].x:=x-cx+dF;
o[CS].y:=cy-y-dF;
end
else if(x>cx)and(y<cy)Then
begin
o[CS].z:=x-cx-dF;
o[CS].y:=cy-y-dF;
end
else if(x<cx)and(y>cy)Then
begin
o[CS].x:=x-cx+dF;
o[CS].z:=x-cx-dF;
end
else begin
ShowMessage(‘Эта четверть не является плоскостью проекций!’);
E:=True;
end;
inc(M);
end
else
if (M<6+sv)Then begin
if(N>3)Then N:=0;
if(x>cx)and(y>cy)Then
else
if(N=0)Then begin
x0:=x;y0:=y;x1:=0;y1:=0;
N:=1;
end;
if(x<cx)and(y<cy)Then
begin
SS^[CS,N].x:=x;
SS^[CS,N].y:=y;
r:=1;
end
else if(x>cx)and(y<cy)Then
begin
SS^[CS,N].z:=x;
SS^[CS,N].y:=y;
r:=1;
end
else if(x<cx)and(y>cy)Then
begin
SS^[CS,N].x:=x;
SS^[CS,N].z:=y;
r:=GetMaxX/GetMaxY;
end
else begin
ShowMessage(‘Эта четверть не является плоскостью проекций!’);
E:=True;
end;
if(E=False)Then begin
x2:=X;y2:=Y;
if(x1<>0)and(y1<>0)Then begin
Line(x0,y0,x2,y2,Red);
Line(x1,y1,x2,y2,Red);
end
else putpixel(x2,y2,Red);
Flip(Lab.Vid);
x1:=x2;y1:=y2;
For i:=1 to CS-1 do
For j:=1 to 3 do
begin
if(SS^[CS,N].x+o[CS].x>=SS^[i,j].x+o[i].x-2)and(SS^[CS,N].x+o[CS].x<=SS^[i,j].x+o[i].x+2)
and(SS^[CS,N].y+o[CS].y>=SS^[i,j].y+o[i].y-2)and(SS^[CS,N].y+o[CS].y<=SS^[i,j].y+o[i].y+2)
and(SS^[CS,N].z+o[CS].z>=SS^[i,j].z+o[i].z-2)and(SS^[CS,N].z+o[CS].z<=SS^[i,j].z+o[i].z+2)Then Accept:=True;
end;
inc(N);
inc(M);
end;
end;
if(M=6+sv)and(Accept=True)Then begin
M:=0;N:=0;
ClicCub[CS]:=False;
ShowSide;
Flip(Lab.Vid);
New(Scene^.Camera[CC]^.Obj[CO]^.Side[CS],Create(0,0,100,o[CS].x,o[CS].y,o[CS].z,S^[CS]));
if(CS mod 2<>0)Then
T:=ColorText(RGB(100+Random(155),100+Random(155),100+Random(155)),GMT)
else T:=T;
Scene^.Camera[CC]^.Obj[CO]^.Side[CS]^.Texture:=T;
Scene^.Camera[CC]^.Obj[CO]^.Side[CS]^.Mode:=Mode;
Scene^.Camera[CC]^.Obj[CO]^.Side[CS]^.Alpha:=0;
Accept:=False;
inc(CS);
end
else if(M=6+sv)and(Accept=False)Then begin
SS[CS]:=SS[CS-1];
SS[CS]:=SS[CS-1];
SS[CS]:=SS[CS-1];
M:=0;N:=0;
ShowSide;
Flip(Lab.Vid);
end;
end;
procedure InPut2(x,y:integer;var E:Boolean);
var i,j:integer;o:TPoint;Party:Single;tmp:Single;
begin
if(N<sv)Then begin
if(x<cx)and(y<cy)Then
begin
oc.x:=x-cx+dF;
oc.y:=cy-y-dF;
end
else if(x>cx)and(y<cy)Then
begin
oc.z:=x-cx-dF;
oc.y:=cy-y-dF;
end
else if(x<cx)and(y>cy)Then
begin
oc.x:=x-cx+dF;
oc.z:=x-cx-dF;
end
else begin
ShowMessage(‘Эта четверть не является плоскостью проекций!’);
E:=True;
end;
if(E=False)Then begin
PutPixel(x,y,Yelow);
Flip(Lab.Vid);
inc(N);
end;
end
else
if(N<2+sv)and(N>sv-1)Then begin
if(N=sv)Then begin
x0:=x;y0:=y;
end;
x1:=x;y1:=y;
Line(x0,y0,x1,y1,Red);
Flip(Lab.Vid);
x0:=x;y0:=y;
inc(N);
if(x>cx)and(y>cy)Then begin
ShowMessage(‘Эта четверть не является плоскостью проекций!’);
E:=True;
end
else begin
w[N-sv].x:=x;
w[N-sv].y:=y;
end;
end;
if(N=2+sv)Then begin
if(абс(w[N-sv].x-w[N-sv-1].x)/2<=абс(w[N-sv].y-w[N-sv-1].y)/2)Then Party:=абс(w[N-sv].x-w[N-sv-1].x)
else Party:=абс(w[N-sv].y-w[N-sv-1].y);
if(w[N-sv].x<cx)and(w[N-sv].y<cy)Then begin
o.x:=(w[N-sv].x+w[N-sv-1].x)/2;
o.y:=(w[N-sv].y+w[N-sv-1].y)/2;
o.z:=cx+Party/2+dF;
end
else if(w[N-sv].x>cx)and(w[N-sv-1].y<cy)Then begin
o.x:=cx-Party/2-dF;
o.y:=(w[N-sv].y+w[N-sv-1].y)/2;
o.z:=(w[N-sv].x+w[N-sv-1].x)/2;
end
else if(w[N-sv].x<cx)and(w[N-sv].y>cy)Then begin
o.x:=(w[N-sv].x+w[N-sv-1].x)/2;
o.y:=cy-Party/2-dF;
o.z:=(w[N-sv].y+w[N-sv-1].y)/2;
end;
r:=1;
SS^[CS,1].x:=o.x-Party/2;SS^[CS,1].y:=o.y+Party/2;SS^[CS,1].z:=o.z+Party/2;
SS^[CS,2].x:=o.x+Party/2;SS^[CS,2].y:=o.y+Party/2;SS^[CS,2].z:=o.z+Party/2;
SS^[CS,3].x:=o.x-Party/2;SS^[CS,3].y:=o.y-Party/2;SS^[CS,3].z:=o.z-Party/2;
SS^[CS+1,1].x:=o.x+Party/2;SS^[CS+1,1].y:=o.y-Party/2;SS^[CS+1,1].z:=o.z-Party/2;
SS^[CS+1,2].x:=o.x+Party/2;SS^[CS+1,2].y:=o.y+Party/2;SS^[CS+1,2].z:=o.z+Party/2;
SS^[CS+1,3].x:=o.x+Party/2;SS^[CS+1,3].y:=o.y+Party/2;SS^[CS+1,3].z:=o.z-Party/2;
SS^[CS+2,1].x:=o.x-Party/2;SS^[CS+2,1].y:=o.y+Party/2;SS^[CS+2,1].z:=o.z+Party/2;
SS^[CS+2,2].x:=o.x-Party/2;SS^[CS+2,2].y:=o.y-Party/2;SS^[CS+2,2].z:=o.z-Party/2;
SS^[CS+2,3]:=SS^[CS+1,1];
o.x:=o.x-cx+dF;
o.y:=cy-o.y-dF;
o.z:=o.z-cx-dF;
Scene^.Camera[CC]^.Obj[CO]^.Done;
Scene^.Camera[CC]^.Done;
Scene^.Done;
New(Cube,Create(0,0,100,o.x,o.y,o.z,Party,T,Mode,0));
Scene^.Camera[CC]^.ADD(CO,CO+1);
For i:=1 to Scene^.Camera[CC]^.Obj[CO]^.Count do
begin
For j:=1 to 3 do
S[i,j]:=Scene^.Camera[CC]^.Obj[CO]^.Side[i]^.S[j];
if(i mod 2<>0)Then
T:=ColorText(RGB(100+Random(155),100+Random(155),100+Random(155)),GMT)
else T:=T;
Scene^.Camera[CC]^.Obj[CO]^.Side[i]^.Texture:=T;
Scene^.Camera[CC]^.Obj[CO]^.Side[i]^.Mode:=Mode;
end;
For i:=CS to CS+12 do
ClicCub[i]:=True;
CS:=CS+12;
N:=0;
Scene^.Camera[CC]^.Obj[CO]^.Done;
Scene^.Camera[CC]^.Done;
Scene^.Done;
ShowSide;
Flip(Lab.Vid);
end;
end;
procedure Data;
var i,j:Word;
begin
New(S);
New(SS);
GetMaxX:=Lab.Vid.Width-1;
GetMaxY:=Lab.Vid.Height-1;
M:=0;N:=0;Rot:=YRot;Bol:=False;
GMX:=Lab.Vid.Width;
GMY:=Lab.Vid.Height;
CSc:=1;CC:=1;CL:=1;CO:=1;CS:=1;
cx:=GMX div 2;
cy:=GMY div 2+1;
Blue:=RGB(255,0,0);
Red:=RGB(200,200,200);
Yelow:=RGB(0,255,0);
New(Scene,Create);
New(Scene^.Camera[CC],Create(0,0,0,0,0,0));
New(Light[CL],Create(-100,-100,0));
New(Scene^.Camera[CC]^.Obj[CO],Create(0,0,0,0,0,0));
Accept:=True;View:=False;
end;
procedure TLab.FormCreate(Sender: TObject);
var i,j:Word;
begin
Tables;
@InPut[1]:=@InPut1;
@InPut[2]:=@InPut2;
New(t);
For i:=0 to GMT do
New(t^[i]);
For i:=0 to GMT do
For j:=0 to GMT do
t^[i]^[j]:=RGB(255,0,0);
Data;
end;
procedure TLab.TimerTimer(Sender: TObject; LagCount: Integer);
var i:Word;
begin
if(Keys[VK_Escape])Then halt;
if(Bol=False)and(Keys[VK_F9])and(CountSide>0)Then begin
if(CountSide=0)Then begin
ShowMessage(‘Для просмотра нужна хотя бы одна грань’);
exit;
end;
Bol:=True;View:=True;
Scene^.Camera[CC]^.Obj[CO]^.Done;
Scene^.Camera[CC]^.Done;
Scene^.Done;
CreateTMP(Scene^.Camera[CC]^.Obj[CO]);
For i:=1 to Scene^.Camera[CC]^.Obj[CO]^.Count do
Scene^.Camera[CC]^.Obj[CO]^.Side[i]^.o:=tmp_o;
Vid.Surface.Fill(0);
Scene^.Camera[CC]^.Obj[CO]^.Draw;
Flip(Vid);
end
else if(Bol)Then
if(Keys[ord(‘Y’)])Then
begin
Rot:=YRot;
Vid.Surface.Fill(0);
Scene^.Camera[CC]^.Obj[CO]^.Rotate(CosA,SinA,Angle,Rot);
Scene^.Camera[CC]^.Obj[CO]^.Draw;
Flip(Vid);
end
else if(Keys[Ord(‘X’)])Then
begin
Rot:=XRot;
Vid.Surface.Fill(0);
Scene^.Camera[CC]^.Obj[CO]^.Rotate(CosA,SinA,Angle,Rot);
Scene^.Camera[CC]^.Obj[CO]^.Draw;
Flip(Vid);
end
else if(Keys[Ord(‘Z’)])Then
begin
Rot:=ZRot;
Vid.Surface.Fill(0);
Scene^.Camera[CC]^.Obj[CO]^.Rotate(CosA,SinA,Angle,Rot);
Scene^.Camera[CC]^.Obj[CO]^.Draw;
Flip(Vid);
end
else if(Keys[VK_Space])Then
begin
if(Space.Visible=False)Then begin
Space.Visible:=True;
Enter.Visible:=True;
BSide.Visible:=True;
BCube.Visible:=True;
Menu.Items[0].Visible:=True;
Menu.Items[1].Visible:=True;
Menu.Items[2].Visible:=True;
Width:=455;
Height:=465;
Left:=108;
Top:=-7;
Vid.Width:=385;
Vid.Height:=385;
Vid.Top:=32;
Vid.Left:=32;
GMX:=Lab.Vid.Width;
GMY:=Lab.Vid.Height;
GetMaxX:=GMX-1;
GetMaxY:=GMY-1;
end;
M:=0;N:=0;Bol:=False;
View:=False;
ShowSide;
Flip(Vid);
end;
end;
procedure TLab.VidMouseDown(Sender: TObject; Button: TMouseButton;
Shift: TShiftState; X, Y: Integer);
var E:Boolean;
begin
E:=False;
if(View=False)Then InPut[Figure](x,y,E);
if(E=True)Then exit;
end;
procedure TLab.EnterClick(Sender: TObject);
var i:Word;
begin
if(CountSide=0)Then begin
ShowMessage(‘Для просмотра нужна хотя бы одна грань’);
exit;
end;
Bol:=True;View:=True;
Scene^.Camera[CC]^.Obj[CO]^.Done;
Scene^.Camera[CC]^.Done;
Scene^.Done;
CreateTMP(Scene^.Camera[CC]^.Obj[CO]);
For i:=1 to Scene^.Camera[CC]^.Obj[CO]^.Count do
Scene^.Camera[CC]^.Obj[CO]^.Side[i]^.o:=tmp_o;
Vid.Surface.Fill(0);
Scene^.Camera[CC]^.Obj[CO]^.Draw;
Flip(Vid);
end;
procedure TLab.N3Click(Sender: TObject);
var i,j:integer;Name:String;
begin
OpenDialog.Execute;
Name:=OpenDialog.FileName;
Lab.Vid.Surface.Fill(0);
Data;
myform(GetMaxX,GetMaxY);
Flip(Lab.Vid);
if(Name=»)Then exit;
LoadObject(Name,Scene^.Camera[CC]^.Obj[CO]);
LoadSide(Scene^.Camera[CC]^.Obj[CO]);
end;
procedure TLab.N4Click(Sender: TObject);
var Name:String;
begin
SaveDialog.Execute;
Name:=SaveDialog.FileName;
if(Name=»)Then exit;
CreateTMP(Scene^.Camera[CC]^.Obj[CO]);
SaveObject(Name,Scene^.Camera[CC]^.Obj[CO]);
end;
procedure TLab.N2Click(Sender: TObject);
begin
Flip(Lab.Vid);
if(Lab.Width>Lab.Height)Then Lab.Height:=Lab.Width
else Lab.Width:=Lab.Height;
Vid.Height:=Lab.Height-81;
Vid.Width:=Lab.Width-81;
Lab.Vid.Surface.Fill(0);
Data;
myform(GetMaxX,GetMaxY);
Flip(Lab.Vid);
end;
procedure TLab.N9Click(Sender: TObject);
begin
ShowMessage(‘Autocad version 1.2 Copiright by Anton Sazonov’);
end;
procedure TLab.N10Click(Sender: TObject);
begin
Halt;
end;
procedure TLab.SpaceClick(Sender: TObject);
var i:Word;
begin
M:=0;N:=0;Bol:=False;
View:=False;
ShowSide;
Flip(Vid);
end;
procedure TLab.FormKeyDown(Sender: TObject; var Key: Word;
Shift: TShiftState);
begin
Keys[Key]:=True;
end;
procedure TLab.FormKeyUp(Sender: TObject; var Key: Word;
Shift: TShiftState);
begin
Keys[Key]:=False;
end;
procedure TLab.CancelClick(Sender: TObject);
begin
Halt;
end;
procedure TLab.OKClick(Sender: TObject);
begin
Option[1]:=Box1.Text;
if(Option[1]=»)Then begin
ShowMessage(‘Выберитережим!’);
Exit;
end
else if(Option[1]=’4 bits’)Then PixMode:=1
else if(Option[1]=’8 bits’)Then PixMode:=2
else if(Option[1]=’16 bits’)Then PixMode:=3
else if(Option[1]=’24 bits’)Then PixMode:=4
else if(Option[1]=’32 bits’)Then PixMode:=5;
Option[2]:=Box6.Text;
if(Option[2]=»)Then begin
ShowMessage(‘Изберите разрешение!’);
Exit;
end
else if(Option[2]=’640X480′)Then begin
ScreenX:=640;ScreenY:=480;
end
else if(Option[2]=’800X600′)Then begin
ScreenX:=800;ScreenY:=600;
end
else if(Option[2]=’1024X768′)Then begin
ScreenX:=1024;ScreenY:=768;
end
else if(Option[2]=’1280X1024′)Then begin
ScreenX:=1280;ScreenY:=1024;
end;
Surf:=Vid.Surface;
SetGraphMode(PixMode);
Label1.Destroy;
Label6.Destroy;
Label7.Destroy;
Box1.Destroy;
Box6.Destroy;
Cancel.Destroy;
OK.Visible:=False;
Lab.Height:=465;
Lab.Top:=-7;
Menu.Items.Visible:=True;
Vid.Visible:=True;
Vid.Width:=385;
Vid.Height:=385;
Space.Visible:=True;
Enter.Visible:=True;
Lab.Vid.Surface.Fill(0);
myform(GetMaxX,GetMaxY);
Flip(Lab.Vid);
if(Lab.Width>Lab.Height)Then Lab.Height:=Lab.Width
else Lab.Width:=Lab.Height;
Vid.Height:=Lab.Height-81;
Vid.Width:=Lab.Width-81;
Lab.Vid.Surface.Fill(0);
Data;
myform(GetMaxX,GetMaxY);
Flip(Lab.Vid);
BCube.Visible:=True;
BSide.Visible:=True;
Menu.Items[0].Visible:=True;
Menu.Items[1].Visible:=True;
Menu.Items[2].Visible:=True;
OK.Destroy;
end;
procedure TLab.N8Click(Sender: TObject);
begin
Application.HelpContext(10);
end;
procedure TLab.BCubeClick(Sender: TObject);
begin
Figure:=2;
ClicCub[CS]:=True;
end;
procedure TLab.BSideClick(Sender: TObject);
begin
Figure:=1;
end;
procedure TLab.N6Click(Sender: TObject);
begin
if (View=True)Then begin
Space.Visible:=False;
Enter.Visible:=False;
BSide.Visible:=False;
BCube.Visible:=False;
Menu.Items[0].Visible:=False;
Menu.Items[1].Visible:=False;
Menu.Items[2].Visible:=False;
Width:=ScreenX+10;
Height:=ScreenY+20;
Left:=-10;
Top:=-30;
Vid.Width:=ScreenX+1;
Vid.Height:=ScreenY-16;
Vid.Top:=0;
Vid.Left:=0;
GMX:=ScreenX;GMY:=ScreenY;
GetMaxX:=GMX-1;GetMaxY:=GMY-1;
Scene^.Camera[CC]^.Obj[CO]^.Draw;
Flip(Vid);
end;
end;
end.
ЗАКЛЮЧЕНИЕ
Программирование с внедрением трёхмерной графики — это метод описания языком программирования объёмных тел и отображения их на мониторе.
Измерение данной графики совпадает с измерением настоящей системы, находящейся в пространстве, в каком ориентируется человек, и по этому хоть какое вещественное тело можно виртуально сделать, задать ему условия и поглядеть на реакцию этого тела, задать телу правила поведения (линию движения движения) и выяснить как оно будет себя вести, где будет находиться с течением времени.
К примеру, можно сделать программку, создающую чертежи с внедрением гостов и чертёжных обозначений. Она нужна конструкторам. Примером таковой программки является автокод.
Можно изменить данную программку таковым образом, чтоб она, виртуально создавала дом и докладывала какие перегрузки, он будет испытывать и не деформируется ли он при разных природных явлениях. Эта программка нужна конструкторам.
Ещё трёхмерную графику можно применить для сотворения устройств, которые меж собой ведут взаимодействие, показать какие силы при всем этом участвуют, и показать их взаимодействие с различных сторон. Таковая программка нужна инженерам-механикам. Эти программки могут применять как ортогональное (параллельное), так и центральное проецирование (проецирование с учётом перспективы).
программка, создающая трёхмерную анимацию (кинофильм, мульт), так же быть может реализована на компе. Эта программка обязана применять лишь центральное проецирование (перспективное), и лучше наличие неких эффектов: прозрачности, освещённости, билинейной фильтрации текстур и т.д.
Трёхмерная графика нужна всюду, где выполняются вещественные объекты, где есть инженеры, конструкторы, архитекторы, просто квалифицированные рабочие, а конкретно: в самолётостроении, в машиностроении, в кораблестроении, в строительстве, в галлактической индустрии и т. д.
С трёхмерной графикой, так либо по другому, приходится сталкиваться дизайнерам одежки, дизайнерам Веб-сайтов, хоть каким иным дизайнерам, работникам отделов рекламы, продюсерам и т. д.
Трёхмерной графикой воспользовались постоянно. До объёмного изображения на компе чертили чертежи на бумаге, до чертежей отрисовывали эскизы либо картинки, до рисунков воспользовались заданием объектов аналитически на бумаге либо в уме.
Если отдать оценку трёхмерной графики в вещественном производстве, то она будет последующей: трёхмерная графика, как метод изображения объёмных фигур либо тел, является самым приятным способом представления инфы, применяемой в вещественном производстве. Без трёхмерной графики не было бы налажено хоть какое вещественное Создание. Самый удачный метод задания и использования трёхмерной либо объёмной графики осуществляется при помощи информационных технологий, а конкретно компа.
СПИСОК ЛИТЕРАТУРЫ
1 С. Бобровский «Delphi 5». 2007
2 О.Е. Акимов «Дискретная математика, программкой
2.1 Предназначение программного продукта
2.2 Обучение работе с программным продуктом
2.3 Ограничения внедрения 10
3. Нереализованные способности
4. Основная форма
5. Способы сотворения программки
5.1 Матричные преобразования
5.2 Создание одноцветного треугольника
6. программка
ЗАКЛЮЧЕНИЕ
СПИСОК ЛИТЕРАТУРЫ
ВСТУПЛЕНИЕ
Любой вещественный объект, имеющий форму, является большим, как следует, его положение в пространстве можно задать при помощи трёх координат X, Y, Z. Итог хоть какого вещественного производства, дома, авто, станки, можно представить как 3-х мерную модель и показать эту модель на мониторе компа при помощи соответственной программки.
Нет необходимости создавать раздельно программку для сотворения моделей раздельно домов, раздельно станков и их устройств, раздельно для каров и т. д. Еще разумней сделать одну программку, которая может учесть специфику хоть какой отрасли и создавать, фактически любые 3-х мерные модели.
Программирование уже издавна не стало быть уделом энтузиастов. Современный программер — не писатель либо ученый, а квалифицированный рабочий. Прошли те времена, когда, удавалось «удовлетворять свой Энтузиазм за счет страны«: месяцами изучить начальный код какой-либо совсем никчемной в практическом плане утилиты, забираться в недра начальных текстов оригинально изготовленных компонент.
Естественно, маленькому проценту разрабов по долгу службы необходимы глубочайшие специальные познания, но от подавляющего большинства программистов сейчас требуется, до этого всего, умение писать программки очень стремительно и без ошибок. К примеру программка, представленная в этом проекте просит малое количество интеллектуальных издержек и времени юзера либо программера высочайшего уровня для заслуги результата. Программер высочайшего уровня — это программер, составляющий свои программки на базе разработок остальных программистов. Под разработками остальных программистов будем осознавать разработанные ими средства программирования: (язык программирования, функции, процедуры, утилиты, библиотеки, модули, составляющие, драйвера и т. д.
Хоть какой природный процесс, которым человек пробует управлять(повлиять на него с прогнозируемыми последствиями), должен быть поначалу представлен в виде модели той либо другой степени трудности. Спецам в области компьютерных технологий приходится брать такие модели либо их фрагменты для следующего объединения из определенных предметных областей, общих познаний и представлений о внешнем мире и создавать свою модель для решения поставленной задачки. Опосля этого, беря во внимание индивидуальности работы вычислительного устройства (компа) и избранного языка программирования – разрабатывать метод реализации модели, программировать(кодировать) этот метод. Не считая реализации главный функции, нужно помыслить о вспомогательных функциях(контроль входных данных, проверка адекватности результата, обычно наименее ярки и наименее детальны чем образы восприятия но в их находит отражение самое свойственное для данного предмета Различия в яркости стойкости и точности представлений памяти весьма инди результата, контекстная подсказка, справочная система и т.п.) и их корректном содействии меж собой. Все перечисленные процессы (как и большая часть природных действий в восприятии человека) имеют дискретную природу (дискретный
(лат. diskretus) – разбитый, прерывающийся). обычно наименее ярки и наименее детальны чем образы восприятия но в их находит отражение самое свойственное для данного предмета Различия в яркости стойкости и точности представлений памяти весьма инди и процесс обработки инфы в компе, независимо от его технических характеристик, носят дискретный нрав (алфавит машинного языка двухсимвольный, огромное количество состояний микропроцессора конечное), потому неважно какая модель, реализованная в виде машинного кода, дискретна. Потому для реализации рассматриваемой программки нужно соприкоснуться с некими областями дискретной арифметики.
1. Математические объекты
1.1 Группы
Группа — оперативное огромное количество, в каком действует процедура умножения и которое подчинено последующим условиям:
1) замкнутости: для каждой пары g1g2=g3, при этом g3 должен принадлежать группе G: g1g2 ÎG; g1g2 = g3 ÎG;
2) наличия тождественного элемента e: посреди огромного количества частей группы G справедливы равенство eg = g; ge = g; e,gÎG;
3) наличие оборотных частей: для всякого g из G должен отыскаться единственный ему оборотный элемент g, принадлежащий G, при умножении на который вышел тождественный элемент e. e = q×q; e,g,gÎG;
4) ассоциативности: для всех трёх частей g1, g2, g3 из G справедливо равенство: g1(g2g3) = (g1g2)g3. Условия ассоциативности производится для квадратных матриц.
Линейное преобразование A вектора x в вектор c осуществляется при помощи квадратной матрицы y = Ax.
Если есть прямое преобразование, то обязано быть и оборотное, при условии, что A имеет A. y = Ax;
x = Tx ;y = Tx ; Как следует Ty = ATx; Умножим с лева на T , получим y = TATx;
Отсюда следует формула прямого преобразования подобия.
Формулу оборотного преобразования подобия можно получить, умножив на T с права:
Если Ax = lx; то ненулевой вектор x является своим вектором. Для нахождения собственных векторов употребляют формулу
*
где E — единичная матрица.
Корешки многочлена P(l) = det(A — lE) = 0 подставляют в формулу
и получают собственные векторы x.
Группы, лишившись собственной предметной области, не нужны в истинное время. Хотя ранее, приблизительно 40 лет вспять, теория групп была всераспространена из-за того, что группы тесновато увязывалась с базовыми областями естествознания — физикой простых частиц, квантовой механикой, физикой твёрдого тела и кристаллографией.
В данной программке все объёмные фигуры состоят из треугольных граней, любая из которых содержит три верхушки. Задав положение объёмного тела в пространстве, мы задаём положение всех вершин. Любая верхушка представляется как вектор.
Чтоб конвертировать объект,(повернуть, удалить, приблизить, сжать, растянуть и т. п.) нужно любой вектор каждой грани объекта помножить на матрицу преобразования, к примеру на матрицу поворота вокруг оси Y.
Ах так эта матрица будет смотреться:
1.2 Графы
Хоть какое объемное тело, как уже было сказано выше, можно выстроить при помощи треугольных граней, любая из которых имеет хотя бы одну общую верхушку с примыкающей гранью. Схематично каждую грань можно изобразить как совокупа вершин, соединенную контурными линиями. Контурные полосы — это полосы описывающие контур. Контур — это замкнутый путь. Таковым образом, грань, содержащая верхушки упорядоченно соединённые рёбрами представляет собой направленный граф либо Орграф.
Граф G
как математический объект – это совокупа 2-ух множеств: непустого огромного количества вершин V
и огромного количества ребер E
, элементы которого представляет собой неупорядоченные (для нацеленного графа – упорядоченные) пары частей из огромного количества V
.
G (V,E) = áV; Eñ, n(V) > 0, E Ì V ´ V,
где для неориентированного графа E
=
E
–1
(бинарное отношение E
симметрично).
Малый граф состоит из одной верхушки.
Любому неориентированному графу можно поставить в соответствие направленный граф, в каком каждое ребро заменено 2-мя обратно нацеленными ребрами, инцидентными этим же верхушкам.
Пусть v
1
и v
2
– верхушки, e
1
= (v
1
, v
2
) – соединяющее их ребро.
Тогда верхушка v
1
и ребро e
1
инцидентны, верхушка v
2
и ребро e
1
также инцидентны. Два ребра, инцидентные одной верхушке, именуются смежными; две верхушки, инцидентные одному ребру, также именуются смежными.
Обычно граф изображают на плоскости в виде диаграммы: верхушки – точками, ребра – линиями, соединяющими инцидентные верхушки.
Огромное количество вершин и огромное количество рёбер для конечных графов задаются, как правило, перечислением. Может быть задание графа описанием дела инцидентности.
1 Отношение инцидентности задано матрицей смежности:
– столбцы и строчки матрицы – верхушки графа;
– для смежных вершин элемент матрицы равен1, для других – 0;
– для неориентированного графа эта матрица постоянно симметрична;
– число рёбер равно числу единиц выше либо ниже главной диагонали матрицы ( включая элементы на диагонали).
2 Отношение инцидентности задано матрицей инцидентности:
– столбцы матрицы соответствуют верхушкам графа, а строчки – рёбрам;
– если ребро ei
инцидентно верхушке vj
, то элемент матрицы eij
=1, в неприятном случае – eij
= 0.
Таковым образом, в каждой строке одна либо две единицы, другие нули (для петли две единицы).
Для нацеленного графа при заполнении матрицы:
eij
= –1,если vj
– начало ребра;
eij
=1,если vj
–конец ребра;
eij
= a (где a – хоть какое число, не считая –1,1,0),если ребро – петля в верхушке vj
;
в других вариантах eij
= 0.
3 Граф задан перечнем ребер.
ei
vi,
vj
1
a, b
2
b, d
…
Примечание. тут ei
–ребро, vi
,
vj
– пара вершин, соединяемых сиим ребром.
Граф связан, если неважно какая пара его вершин связана ребром.
Граф без кратных ребер именуют полным, если любая пара вершин соединена ребром.
Граф H
именуют частью графа G
, если огромное количество вершин графа H
принадлежит огромному количеству вершин графа G
и огромное количество рёбер графа H
принадлежит огромному количеству рёбер графа G
, т.е.:
V(H) ÌV(G); E(H) ÌE(G).
часть графа H
именуется суграфом, если она содержит все верхушки графа G
.
Суграф H
для неориентированного графа G
именуется покрывающим суграфом, если неважно какая верхушка крайнего инцидентна хотя бы одному ребру из H
.
Подграф G
(
U
)
графа G
на огромном количестве вершин U
(
U
Ì
V
)
– это часть графа, которой принадлежат все ребра с обоими концами из U
.
Звёздный граф для верхушки v
(
v
Î
G
)
состоит из всех рёбер с началом и концом в верхушке v
. Огромное количество вершин звёздного графа состоит из верхушки v
и остальных смежных с ней вершин.
Маршрутом в единичном связном графе G
именуется таковая конечная последовательность ребер (e
1,
e
2….
en
), в какой любые два примыкающих ребра имеют общую инцидентную верхушку.
Верхушка v
о
, инцидентная ребру e
1
и не инцидентная ребру e
2
, именуется началом маршрута в графе G
.
Верхушка vn
, инцидентная ребру en
и не инцидентная ребру en
-1
, именуется концом маршрута.
Число ребер маршрута именуется его длиной.
Если верхушки v
о
и vn
совпадают, то маршрут именуется повторяющимся (либо просто циклом).
Отрезок конечного либо нескончаемого маршрута сам является маршрутом.
Маршрут в графе G
именуется цепью
, если все ребра в последовательности различны, и обычной цепью
, если все верхушки, через которые проходит маршрут (а означает и ребра) различны.
Иными словами, в цепи ребро может повстречаться не наиболее 1-го раза, а в обычной цепи верхушка – не наиболее 1-го раза.
Молвят, что две верхушки в графе соединены, если существует соединяющая их цепь. Граф, в каком все верхушки соединены, именуется связным
.
Расстоянием
меж 2-мя верхушками графа именуется малая длина обычной цепи, связывающей эти верхушки (обозначение d(v¢,v²)).
Протяженностью
меж 2-мя верхушками графа именуется наибольшая длина обычной цепи, связывающей эти верхушки (обозначение g(v¢,v²)).
В личном случае расстояние и протяженность меж верхушками могут быть схожими.
2. Справка по работе с программкой
2.1 Предназначение программного продукта
Данный программный продукт предназначен для построения всех больших фигур с следующей их демонстрацией на дисплее монитора. Необходимо подчеркнуть, что фигуры могут быть как выпуклыми, так и вогнутыми, но непременно не обязано нарушаться условие целостности. Это означает, что ни одна часть объекта не обязана существовать раздельно, а обязана быть хоть каким-либо своим элементом присоедененной к объекту. Другими словами два огромного количества: вершин и рёбер, которые получаются, если пройти по контуру, от первой верхушки к крайней, представляют собой, согласно определению, направленный граф и имеют маршрут. Сделанный объект можно сохранить на диске, также загрузить с него, чтоб поглядеть либо отредактировать итог работы. Для построения трехмерных объектов можно применять некие доступные примитивы, а конкретно грань и куб. Любая грань, в создаваемом объекте, имеет собственный цвет, избираемый программкой случайным образом.
2.2 Обучение работе с программным продукт
ом
Опосля загрузки приложения покажется окно опции, в каком нужно установить разрешение цвета, которое непременно обязано совпадать с разрешением цвета текущего графического режима, к примеру 16 бит (рис. 1).
В неприятном случае на форме будет наблюдаться некоторое аномальное графическое изображение, которое будет некорректно отражать итог работы, что, делает всякую работу неосуществимой. Дальше необходимо установить разрешение текущего графического режима в пикселях. Это необходимо для полноэкранного режима просмотра. Опосля установления графического режима покажется рабочее окно, разделённое на четыре части — четверти. Любая четверть является плоскостью проекций, размещение которых соответствует принципам начертательной геометрии: верхняя левая — передная, правая верхняя — вертикальная, нижняя левая — горизонтальная. Нижняя — правая четверть не является плоскостью проекций, по этому там отрисовывать недозволено. Точки задаются нажатиями левой клавиши мыши. Если точек больше одной, то они соединяются линией, если точек три, то они образуют треугольник, где любая точка является верхушкой.
Следует держать в голове, что мы строим трёхмерное изображение не по точкам либо по линиям, а по треугольным граням, и проецируем не точки, а грани, по этому если точка поставлена в одной плоскости проекций, то и другие две точки данной грани должны быть поставлены в данной нам же плоскости. Для построения хоть какой объёмной фигуры довольно любые две плоскости. 3-я психической защиты Ее работа обеспечивает эмоциональное разрешение за счет безотчетного приписывания субъектом его собственных мыслей переживаний вытесненных мотивов и черт нрава иным людям Эффект проекц получится из 2-ух остальных (рис. 2).
Набросок 1
Набросок 2
Опосля того, как объект спроецирован на плоскости проекций необходимо надавить клавишу «Просмотр», находящуюся на форме (рис. 2), чтоб поглядеть на большой объект в перспективе, и получить возможность сохранить документ. Если объект сотворен, но небыла нажата кнопка «Просмотр», то опосля выбора функции «Сохранить как» информация не будет сохранена. Опосля нажатия клавиши «Просмотр» покажется однотонное окно чёрного цвета в каком будет находиться трёхмерный объект, на который можно поглядеть со всех сторон, вращая вокруг осей X,Y,Z нажатиями соответственных кнопок «X», «Y», «Z», при этом не имеет значения большая кнопка либо нет. Просмотрев итог сотворения объекта, можно его достроить, нажав на форме клавишу «Добавить». Опосля нажатия данной нам клавиши опять покажется окно с 3-мя плоскостями проекций, в каком будут находиться проекции объекта и к которым можно добавить новейшие проекции. Следует увидеть, что вращение трёхмерного объекта в перспективе практически не приводит к изменению его положения в пространстве. Другими словами если опосля вращения объекта перейти к плоскостям проекций, картина не поменяется, и опосля перехода назад к перспективе объект будет находиться в начальном положении: его положение в перспективе будет соответствовать его проекциям на плоскостях проекций.
Добавление примитива: «Куб», осуществляется путём чертежа полосы на одной из плоскости проекций, причём, если на уровне мыслей представить проекции данной нам полосы на абсциссу и ординату, то стороной куба будет наименьшая из проекций.
Если опосля сотворения 1-го трёхмерного объекта загрузить иной объект с диска, то 1-ый объект уничтожится, а на его месте покажется 2-ой, который загружен с диска. Это разъясняется тем, что загруженный объект не добавляется к уже имеющемуся, зато можно напротив, как уже было описано выше, к загруженному объекту добавить примитивы (грани, кубы).
В программном продукте действительны такие многофункциональные клавиши:
Esc — выход из программки в операционную систему;
F1 — справка о программном продукте;
F9 — режим просмотра ;
X — вращение объекта вокруг оси X;
Y — вращение объекта вокруг оси Y;
Z — вращение объекта вокруг оси Z;
В программном продукте есть такие пункты меню «файл«, «Вид», «Помощь».
В пт меню «файл«, содержатся функции для работы с файлами. Выбрав опцию «Сделать«, создаётся новенькая форма для сотворения объекта. Если поменять длину либо ширину формы и избрать опцию «Сделать«, то рабочее окно воспримет размеры, надлежащие новеньким размерам формы. Причём длина и ширина будут в любом случае схожи.
При помощи функции «Открыть» можно открыть, файл, содержащий трёхмерный объект. Функция «Сохранить как» нужна для того, чтоб записать трёхмерный объект в файл.
Для загрузки и сохранения файлов в данном программном продукте, нужно и довольно указать имя файла без разширения, либо с разширением, состоящим из всех непременно трёх знаков.
Функция «Выход» создана для выхода из программки в операционную систему.
В пт меню «Вид» можно задать полноэкранный режим формы. Пункт меню «Помощь» содержит опцию «Справка», выбор которой сопровождается открытием документа, содержащего подробную справку о программном продукте.
Так же пункт меню «Помощь» содержит опцию «О программке«, в какой можно выяснить короткую информацию о программном продукте и его разрабе.
2.3 Ограничения внедрения
В программке лучше применять 16 битный режим, причём разрешение графического режима, выраженное в пикселях, не имеет значения. Допускаются лишь 8 битные, 16 битные и 32 битные графические режимы. Во всех других режимах, к примеру, 2 битных, 4 битных, 24 битных программка работать не будет.
При использовании различных графических режимов рабочие окна, места на форме, где задаются грани, будут иметь разный вид (различные цвета линий разметки и линий проекций). Это соединено с тем, что цвета в программном продукте не имеют значения. Принципиально чтоб проекции на плоскости проекций были видны, и соответствовали изображаемому объекту. цвета трёхмерного объекта, совершенно выбираются случайным образом, по этому тут важен не цвет, а форма объекта.
При загрузке трёхмерного объекта из файла не непременно указывать разширение файла, либо если уж оно обозначено, то обязано содержать не наименее трёх знаков опосля точки. Это разъясняется тем, что программка сохраняет объект в 2-ух файлах. Один с разширением «Res», а иной «Dat». При всем этом не принципиально какое разширение укажет юзер, оно всё равно отбрасывается программкой.
При открытии файла программка отбрасывает обозначенное юзером разширение, если оно есть, и добавляет к имени файла своё. Когда программка отбрасывает разрешение, она отбрасывает три знака, находящиеся опосля точки. Потому если юзер указал разширение, принципиально, чтоб оно состояло не наименее чем из трёх знаков, по другому при открытии файла произойдёт ошибка.
Проектировать грани необходимо при помощи задания трёх вершин, причём если задание проекций трёх вершин начато в одной плоскости проекций, то все эти три проекции должны быть заданы в данной нам плоскости, а не раздельно: одна проекция точки на передней, иная на горизонтальной, 3-я на профильной. Все три проекции точки должны быть в одной плоскости проекций. Это соединено, как уже говорилось, с тем что проецируются не точки либо полосы, а грани.
Рабочее окно, которое выводит графическую информацию, быть может лишь квадратным, по этому изменяя длину либо ширину рабочего окна, необходимо учесть, что программка уравняет эти оба параметра.
3. Нереализованные способности
Самый основной элемент, который нужно воплотить — это полное отсутствие ошибок. На данном шаге разработки ошибок не выявлено, но гарантии их отсутствия не существует.
Потом следовало бы прирастить количество примитивов: добавить к грани и кубу, возможность сотворения призмы, многоугольника, цилиндра, сферы, диска. наличие этих фигур обеспечило бы лёгкость сотворения всех объёмных тел. Поэтому что, к примеру призма, в базе которой верный восьмиугольник содержит 32 треугольной грани. естественно юзеру вручную весьма проблематично сделать эту фигуру, потому её наличие в коллекции примитивов значительно облегчило бы ему работу.
Так же можно было бы создать способы наложения различных текстур на трёхмерные объекты. Текстуры — это двухмерные графические изображения, которые, наложив как фотографию на грань объекта, превращают обычный куб в телек, дом с окнами, стиральную машинку и остальные кубические объекты.
Текстуры бывают: аффинные, четкие (перспективно — корректные), параболические. Текстуры можно накладывать на все трёхмерные объекты.
Можно было бы воплотить прозрачность трёхмерных объектов. к примеру, если необходимо было бы сделать объёмное тело из стекла. В этом случае нужно было бы применять прозрачность. Трёхмерные объекты, как понятно, создаются двухмерными точками. Потому чтоб сделать прозрачность нужно перед выводом каждой точки объекта в определённом месте получать цвет уже стоящей на этом месте точки, если она есть, потом вывести точку цвет которой будет средним меж цветом точки, которая ставится и которая уже стоит. Таковым образом, за выводимым трёхмерным объектом будет видно размещение другого объёмного тела. Это и есть прозрачность.
4. Основная форма
Основная форма содержит такие составляющие как:
Main
Menu
— Предназначен для прибавления к главной программке головного меню. Является компонентом Standart. Компонент MainMenu не зрительный компонент. Редактор меню вызывается опосля выбора функции Items. В редакторе можно создавать пункты меню проименовывая их с помощь характеристики Caption. Переходя от одной компоненте к иной можно при помощи такого же характеристики Caption отредактировать пункты меню. Чтоб вставить линию разделитель необходимо в свойстве Caption первой позицией указать знак «-» (дефис). Опосля окончания сотворения пт меню редактор меню нужно закрыть.
BitBtn
— Этот компонент предназначен для сотворения клавиши с картинкой. В системе имеется набор готовых шаблонов.
Опосля размещения объекта на форме изображение, помещаемое на клавишу, задаётся в свойстве Glyph (Значок). При всем этом вызывается редактор, при помощи которого выбирается подходящая картина (в формате BMP). Любая таковая картина может состоять из 4 частей, равных по ширине. 1-ая часть — изображение клавиши в обыкновенном состоянии, 2-ая — изображение «отключённой» клавиши, 3-я — изображение клавиши опосля щелчка мыши, изображение на нажатой кнопочке. Число составных частей задаётся в свойстве NumGlyph (от 1 до 4). Расстояние от рисунки до границ клавиши (в пикселях) можно указать в свойстве Margin. В свойстве Kind задаётся реакция клавиши на щелчок.
Button
— Компонент предназначен для сотворения клавиш на форме и обработки действия нажатия клавиши. Размещен на панели Standart. имя клавиши указывается в поле Name, а выводимый текст на кнопочке в свойстве Caption.
Combo
Box
— Компонент Поле со перечнем. Представляет собой вариант перечня, с присоединённым доп полем, в каком отображается избранный элемент перечня. Это поле может употребляться для ввода новейших частей либо для резвого поиска частей по исходным символам. Если на дисплее отображается лишь присоединённое поле («раскрывающийся перечень«), то для раскрытия перечня можно применять клавиатурную комбинацию Alt+Вниз.
Open
Dialog
— Компонент предназначен для выбора файла с целью следующего открытия; Характеристики класса TOpenDialog приведены в табл. 1
Таблица 1 — Характеристики класса TOpenDialog
Параметров
о
Назначен
и
е
DefaultExt
расширение имени, применяемое по дефлоту. Добавляется в конец выбраного юзером имени файла, если расширение не обозначено очевидно.
FileName
Выбранное юзером имя файла вкупе с полным путём поиска
Files
Перечень избранных имён файлов. В свойстве Options должен быть включён флаг ofAllowMultiSelect
Filter
Набор масок, в согласовании с которыми отбираются названия файлов для отображения в диалоговом окне. Любая маска состоит из 2-ух частей: наименования и шаблона, — разделённых эмблемой |. Одному наименованию могут соответствовать несколько шаблонов. Маски отделяются друг от друга эмблемой |
FilterIndex
Номер текущей маски. Нумерация начинается с 1
HistoryList
Перечень ранее избранных файлов (тип Strings)
InitialDir
Текущий каталог, содержимое которого отображается при первом открытии диалогового окна
Options
Набор флагов, определяющих окна выбора файлов.
Title
Заголовок диалогового окна
Save
Dialog
— Этот компонент фактически ничем не различается от компонента OpenDialog кроме нескольких опций, специфичных для процесса сохранения файла.
Label
— Компонент находится на панэле Standart. Предназначен для вывода текста на форме при помощи характеристики Text.
TDXTimer
— Компонент находится на панели DelphiX. В программках, выполняющих деяния, связанные с моделированием либо обработкой графики, созданных для общения с юзером в настоящем режиме времени либо выполняющих длительные вычисления, нужна компонента TDXTimer.
При помощи компонента TDXTimer можно включить генерацию сообщений, поступающих от системного таймера Windows с данной периодичностью (в миллисекундах) и делать определённую часть действий конкретно в обработчике этого действия. Это одна из способностей многозадачности приложений Windows.
TDXDraw
— Этот компонент находится на вкладке DelphiX. Он предназначен для вывода двухмерной и трёхмерной графики, используя DirectDraw и DirectX. Разрешение выводимой графики задаётся в свойстве Display. Вид курсора задаётся в свойстве Cursor. способ DXDraw.Surface.Fill(Color) — заполняет видеостраницу цветом Color. При помощи характеристики DXDraw.Surface.pixels[x, y]:=Color — ставится точка на виртуальной видеостранице. При помощи способа DXDraw.Flip — невидимая видеостраница делается видимой. Схема обрисовки изображения на 2-ух видеостраницах: видимой и не видимой делает вероятной сделать анимацию без мигания изображения.
5. способы сотворения программки
Для сотворения трёхмерных объектов нужно уметь преобразовывать трёхмерную графику в двухмерную. Ведь процедура вывода точки имеет лишь две координаты, задающие положение точки (x, y), как следует, реально имеется дело с двухмерной графикой. Для начала следует принять систему трёхмерных координат:
Набросок 3
тут знаками x, y, z обозначены положительные направления осей Ox, Oy и Oz соответственно. Также предполагается, что камера недвижна и находится в точке с координатами (0,0,-dist), ось зрения камеры ориентирована по оси Oz, а конкретно в точку (0,0,0) (т.е. camera target = (0,0,0)), ось Ox исходя из убеждений камеры ориентирована слева вправо, ось Oy — снизу ввысь, ось Oz — вглубь экрана. Размер экрана — xSize на ySize пикселей.
тут и дальше употребляются обозначения:
sx, sy
координаты проекции точки на дисплее
x, y, z
3D координаты точки,
Dist
расстояние от камеры (она находится в точке (0,0,-dist)) до начала координат,
Для созданя трёхмерных объектов нужны примитивы. Таковыми примитивами являются простые трёхмерные объекты — треугольные грани. У каждой треугольной грани есть три верхушки — три точки, имеющие трёхмерные координаты. Чтоб спроецировать их на плоскость, т. е. из трёхмерных координат x,y,z получить двухмерные sx, sy нужно воспользоваться формулой:
sx = xSize/2+x*dist/(z+dist); sy = ySize/2-y*dist/(z+dist);
5.1 Матричные преобразования
В данной нам главе коротко представлены простые понятия о матричных преобразованиях, являющихся составной частью линейной алгебры, и дискретной арифметики (см. раздел Группы).
Введем несколько определений. n-мерный вектор, он же вектор размерности n, он же вектор размера n: упорядоченный набор n реальных чисел. Матрица размера m на n (будет обозначаться как m*n, mxn): таблица размера m на n, в каждой клеточке которой — действительное число.
Вот вам наглядный пример матрицы 3×3:
[ 15 y*z 0.6 ]
[ 7 -3 91 ]
[ sin(x) 0.123 exp(t) ]
Вектор будем записывать в столбик и разглядывать его как матрицу размера n*1.
Операция скалярного произведения векторов: определена для 2-ух векторов схожих размеров. Итог есть число, равное сумме произведений соответственных частей векторов. Пример:
[ 1 ] [ 4 ]
[ 2 ] * [ 5 ] = 1*4 + 2*5 + 3*6 = 32
[ 3 ] [ 6 ]
Операция векторного произведения: определена для (n-1) вектора схожего размера n. Итог — вектор, при этом, перпендикулярный всем множителям.
Замечание:
итог изменяется от перестановки мест множителей!
Формально определяется как определитель матрицы, 1-ая строчка которой все есть базовые вектора, а все следующие — надлежащие координаты всех множителей. Так как она нужна лишь для 3D места, мы определим векторное произведение 2-ух 3D векторов очевидно:
[ Ax ] [ Bx ] | i j k | [ Ay*Bz-Az*By ]
AxB = [ Ay ] x [ By ] = | Ax Ay Az | = [ Az*Bx-Ax*Bz ]
[ Az ] [ Bz ] | Bx By Bz | [ Ax*By-Ay*Bx ]
Операция сложения 2-ух матриц: определена для матриц схожих размеров. Любой элемент суммы (другими словами, каждое число в таблице) приравнивается сумме соответственных частей слагаемых-матриц. Пример:
[ 1 x 500 ] [ 8 a 3 ] [ 9 a+x 503 ]
[ 2 y 600 ] + [ 9 b 2 ] = [ 11 b+y 602 ]
[ 3 z 700 ] [ 10 c 1 ] [ 13 c+z 701 ]
Операция умножения матрицы на число: определена для хоть какой матрицы и хоть какого числа; любой элемент результата приравнивается произведению соответственного элемента матрицы-множителя и числа-множителя.
Операция умножения 2-ух матриц: определена для 2-ух матриц таковых размеров a*b и c*d, что b = c. к примеру, если b = c, но a ¹ d, то при перестановке множителей операция будет совершенно не определена. Результатом умножения матрицы A размером a*b на матрицу B размером b*d будет матрица C размером a*d, в какой элемент, стоящий в строке i и столбце j, равен произведению строчки i матрицы A на столбец j матрицы B. Произведение строчки на столбец определяется как сумма произведений соответственных частей строчки и столбца. к примеру, умножение строчки на столбец (они должны быть равной длины, потому и такие ограничения на размеры матриц):
[ 4 ]
[ 1 2 3 ] * [ 5 ] = 1*4 + 2*5 + 3*6 = 32
[ 6 ]
А чтоб перемножить две матрицы, нужно эту операцию сделать для всякого элемента. Вот вам наглядный пример:
[ 1 2 3 ] [ 0 3 ] [ 1*0+2*1+3*2 1*3+2*4+3*5 ]
[ 4 5 6 ] * [ 1 4 ] = [ 4*0+5*1+6*2 4*3+5*4+6*5 ] = …
[ 7 8 9 ] [ 2 5 ] [ 7*0+8*1+9*2 7*3+8*4+9*5 ]
Умножение и сложение матриц владеют практически этим же набором параметров, что и обыденные числа, хотя некие обычные характеристики не производятся (к примеру, A*B ¹ B*A); по сути требуется знать, что произведение вида A*B*C*D*… не зависит от того, как расставить скобки. Либо, что
A*(B*C) = (A*B)*C.
Хоть какое движение (другими словами преобразование места, сохраняющее расстояние меж точками) в трехмерном пространстве, согласно аксиоме Шаля, быть может представлено в виде суперпозиции поворота и параллельного переноса, другими словами поочередного выполнения поворота и параллельного переноса. Потому основная часть информация о поведении объекта — это его смещение, ось поворота и угол поворота. Потому нам довольно знать, как создать два преобразования — перенос и поворот.
Перенос точки (точки будут также рассматриваться как вектора с началом сначала координат и концом в фактически точке) с координатами (x,y,z) на вектор (dx,dy,dz) делается обычным сложением всех координат. Другими словами итог — это (x+dx,y+dy,z+dz). Подобно сложению вектора-точки с вектором-переносом.
Разглядим для примера поворот точки (x,y,z) относительно оси z. В этом случае z не изменяется, а (x,y) изменяются так же, как и при 2D повороте относительно начала координат.
Покажем координаты точки A’ — результата поворота A(x,y) на угол alpha (рис. 4).
Набросок 4
Пусть r = sqrt(x*x+y*y). Пусть угол AOx равен phi, тогда из рисунка видно, что cos(phi) = x/r, sin(phi) = y/r. Угол A’OA равен по условию alpha. Отсюда
x’ = r*cos(alpha+phi) = r*(cos(alpha)*cos(phi)-sin(alpha)*sin(phi)) =
= (r*cos(phi))*cos(alpha)-(r*sin(phi))*sin(alpha) =
= x*cos(alpha)-y*sin(alpha)
y’ = r*sin(alpha+phi) = r*(cos(alpha)*sin(phi)+sin(alpha)*cos(phi)) =
= (r*cos(phi))*sin(alpha)+(r*sin(phi))*cos(alpha) =
= x*sin(alpha)+y*cos(alpha)
Для трехмерного варианта, таковым образом
x’ = x*cos(alpha)-y*sin(alpha)
y’ = x*sin(alpha)+y*cos(alpha)
z’ = z
Подобные формулы получатся и для остальных осей поворота (другими словами Ox, Oy). Поворот относительно случайной оси, проходящей через начало координат, можно создать при помощи этих поворотов — создать поворот относительно Ox так, чтоб ось поворота стала перпендикулярна Oy, потом поворот относительно Oy так, чтоб ось поворота совпала с Oz, создать поворот, а потом оборотные повороты относительно Oy и Ox.
Вспомним о матрицах и векторах и пристально поглядим на выведенные формулы для поворота. Можно увидеть, что
[ x’ ] = [ cos(alpha) -sin(alpha) 0 ] [ x ]
[ y’ ] = [ sin(alpha) cos(alpha) 0 ] [ y ]
[ z’ ] = [ 0 0 1 ] [ z ]
Другими словами поворот на угол alpha задается одной и той же матрицей, и при помощи данной нам матрицы (умножая ее на вектор-точку) можно получить координаты повернутой точки. С одной стороны умножение матрицы на вектор просит больше операций, чем расчет x’ и y’ по формулам.
Нос иной удобство матриц для заключается как раз в свойстве
A*(B*C) = (A*B)*C. Пусть делается несколько поворотов попорядку, к примеру, 5 (столько, сколько нужно для поворота относительно случайной оси), и пусть они задаются матрицами A, B, C, D, E (A — матрица самого первого поворота, E — крайнего). Тогда для вектора p мы получаем
p’ = E*(D*(C*(B*(A*p)))) = E*D*C*B*A*p = (E*D*C*B*A)*p = (E*(D*(C*(B*A))))*p = T*p,
где T = (E*(D*(C*(B*A)))) матрица преобразования, являющегося композицией 5 поворотов. Посчитав один раз эту матрицу, можно в предстоящем применить достаточно сложное преобразование из 5 поворотов к хоть какому вектору при помощи всего 1-го умножения матрицы на вектор.
Таковым образом, можно задать хоть какой поворот матрицей, и неважно какая композиция поворотов также будет задаваться матрицей, которую можно достаточно просто посчитать. Но еще есть параллельный перенос и масштабирование.
По сути, эти преобразования тоже просто записываются в виде матриц. Лишь заместо матриц 3×3 и 3-мерных векторов употребляются так именуемые однородные 4-мерные координаты и матрицы 4×4. При всем этом заместо векторов вида
[ x ]
[ y ]
[ z ]
употребляются вектора вида
[ x ]
[ y ]
[ z ]
[ 1 ]
а заместо случайных матриц 3×3 употребляются матрицы 4×4 такового вида:
[ a b c d ]
[ e f g h ]
[ i j k l ]
[ 0 0 0 1 ]
Видно, что если d = h = l = 0, то в итоге внедрения всех операций выходит то же самое, что и для матриц 3×3.
Матрица параллельного переноса сейчас определяется как
[ 1 0 0 dx ]
[ 0 1 0 dy ]
[ 0 0 1 dz ]
[ 0 0 0 1 ]
Матрицу масштабирования можно найти и для матриц 3×3, и для матриц 4×4:
[ kx 0 0 ] [ kx 0 0 0 ]
[ 0 ky 0 ] либо [ 0 ky 0 0 ]
[ 0 0 kz ] [ 0 0 kz 0 ]
[ 0 0 0 1 ]
где kx, ky, kz — коэффициенты масштабирования по подходящим осям.
Таковым образом, получаем последующее. Хоть какое необходимое нам преобразование места можно задать матрицей 4×4 определенной структуры, разной для различных преобразований. Итог поочередного выполнений нескольких преобразований совпадает с результатом 1-го преобразования T, которое также задается матрицей 4×4, вычисляемой как произведение матриц всех этих преобразований. Важен порядок умножения, потому что A*B ¹ B*A. Итог внедрения преобразования T к вектору [ x y z ] считается как итог умножения матрицы T на вектор [ x y z 1 ].
Докажем на примере, что A*B ¹ B*A. Пусть A — матрица переноса, B — поворота. Если поначалу перенести объект, а позже повернем относительно центра координат (это будет B*A), то итог не будет соответствовать результату, при котором поначалу объект поворачивают, а потом переносят (A*B).
5.2 Создание одноцветного треугольника
Изображение треугольника на дисплее — набор горизонтальных отрезков, при этом из-за того, что треугольник — фигура выпуклая, каждой строке экрана соответствует не наиболее 1-го отрезка. Потому довольно обойти все строчки экрана, с которыми пересекается треугольник, (другими словами, от малого до наибольшего значения (y) для вершин треугольника), и нарисовать надлежащие горизонтальные отрезки.
необходимо отсортировать верхушки так, чтоб верхушка A была верхней, C — нижней, тогда min_y = A.y, max_y = C.y, и нужно обойти все полосы от min_y до max_y. Разглядим какую-то линию sy, A.y <= sy <= C.y. Если sy < B.y, то она пересекает стороны AB и AC; если sy >= B.y — то стороны BC и AC. Известны координаты всех вершин, потому можно написать уравнения сторон и отыскать пересечение подходящей стороны с прямой y = sy. Получим два конца отрезка. Потому что не понятно, какой из их левый, а какой правый, необходимо сравним их координаты по x и обменяем значения, если необходимо. Рисуя этот отрезок, повторяя функцию для каждой строчки — получаем треугольник.
Рассматривая наиболее тщательно пересечения прямой
y = sy (текущей строчки) и стороны треугольника, к примеру AB, Получим уравнение прямой AB в форме x = k*y+b:
x = A.x+(y-A.y)*(B.x-A.x)/(B.y-A.y)
Сейчас нужно подставить известное для текущей прямой индивидумом общественно-исторического опыта Запечатлено в схемах действий понятиях соц ролях нормах и ценностях Система значений индивидума обусловливает управление действиями его деятельности» y = sy:
x = A.x+(sy-A.y)*(B.x-A.x)/(B.y-A.y)
Для остальных сторон пересечение ищется совсем буквально так же. к примеру:
// …
// тут сортируем верхушки (A,B,C)
// …
for sy = A.y to C.y begin
x1 = A.x + (sy — A.y) * (C.x — A.x) / (C.y — A.y);
if (sy < B.y)Then
x2 = A.x + (sy — A.y) * (B.x — A.x) / (B.y — A.y);
else
x2 = B.x + (sy — B.y) * (C.x — B.x) / (C.y — B.y);
if (x1 > x2) Then
begin
tmp = x1; x1 = x2; x2 = tmp;
end;
drawHorizontalLine(sy, x1, x2);
end;
Нужно защититься от варианта, когда B.y = C.y — в этом (и лишь этом, поэтому как если C.y = A.y, то треугольник пустой и отрисовывать его не необходимо, либо можно отрисовывать горизонтальную линию; а если B.y = A.y, то sy >= A.y и до деления на B.y — A.y не дойдет) случае произойдет попытка деления на ноль.
// …
// тут сортируем верхушки (A,B,C)
// …
for sy = A.y to C.y begin
x1 = A.x + (sy — A.y) * (C.x — A.x) / (C.y — A.y);
if (sy < B.y)
x2 = A.x + (sy — A.y) * (B.x — A.x) / (B.y — A.y);
else begin
if (C.y == B.y)
x2 = B.x;
else
x2 = B.x + (sy — B.y) * (C.x — B.x) / (C.y — B.y);
end;
if (x1 > x2)Then
begin
tmp = x1; x1 = x2; x2 = tmp;
drawHorizontalLine(sy, x1, x2);
end;
// …
тут drawHorizontalLine(sy, x1, x2) — горизонтальная линия. Её создание не представляет трудности и код будетвыглядеть так.
//…
For i:=x1 to x2 do
PutPixel(i,sy,Color);
//…
6. программка
unit graph3;
interface
uses Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs,
DXClass, DXDraws, StdCtrls,graph, Menus,graph3D,figures, Buttons;
const Mode:Word=0;
type TLab = class(TForm)
Vid: TDXDraw;
Timer: TDXTimer;
Enter: TButton;
Menu: TMainMenu;
N1: TMenuItem;
N2: TMenuItem;
N3: TMenuItem;
N4: TMenuItem;
N5: TMenuItem;
N6: TMenuItem;
N7: TMenuItem;
N8: TMenuItem;
N9: TMenuItem;
OpenDialog: TOpenDialog;
SaveDialog: TSaveDialog;
N10: TMenuItem;
Space: TButton;
Box1: TComboBox;
Label1: TLabel;
OK: TButton;
Cancel: TButton;
Label6: TLabel;
BCube: TBitBtn;
BSide: TBitBtn;
Box6: TComboBox;
Label7: TLabel;
procedure FormCreate(Sender: TObject);
procedure TimerTimer(Sender: TObject; LagCount: Integer);
procedure VidMouseDown(Sender: TObject; Button: TMouseButton;
Shift: TShiftState; X, Y: Integer);
procedure EnterClick(Sender: TObject);
procedure N3Click(Sender: TObject);
procedure N4Click(Sender: TObject);
procedure N2Click(Sender: TObject);
procedure N9Click(Sender: TObject);
procedure N10Click(Sender: TObject);
procedure SpaceClick(Sender: TObject);
procedure FormKeyDown(Sender: TObject; var Key: Word;
Shift: TShiftState);
procedure FormKeyUp(Sender: TObject; var Key: Word;
Shift: TShiftState);
procedure CancelClick(Sender: TObject);
procedure OKClick(Sender: TObject);
procedure N8Click(Sender: TObject);
procedure BCubeClick(Sender: TObject);
procedure BSideClick(Sender: TObject);
procedure N6Click(Sender: TObject);
private
public
end;
var Lab: TLab;
implementation
const ScreenX:Word=640;
ScreenY:Word=480;
x1:integer=0;
y1:integer=0;
White=$FFFFFF;
View:Boolean=False;
Figure:Word=1;
Accept:Boolean=True;
sv:Word=0;
var c:char;S,SS,TMP:PPl;t:PTexture;
CS,CO,CC,CSc,CL:Word;Rot:TRot;
x0,y0,x2,y2:integer;r:Single;
Blue,Red,Yelow,M,N,SC:Word;
Keys:array[1..255]of Boolean;
Input:array[1..12]of procedure (x,y:integer;var E:Boolean);
Bol:Boolean;Option:array[1..5] of String;
o:array[1..MaxSide]of TPoint;
w:array[1..2]of TPoint;tmp_o:TPoint;
cx,cy:Word;oc:TPoint;ClicCub:array[0..MaxSide]of Boolean;
procedure LoadObject(Name:String;var Obj:PObj);
var f1:file of TPoint; a:TSides;
f2:file of Word;
S,Tmp:TPoint;i,j:word;Name2:String;
B:Boolean;
begin
For i:=1 to Length (Name)-4 do begin
Name2:=Name2+Name[i];
end;
Name:=Name2;
Assign(f2,Name+’.res’);
Reset(f2);
Read(f2,Obj^.Count);
close(f2);
Assign(f1,Name+’.dat’);
Reset(f1);
Read(f1,Obj^.o);
For i:=1 to Obj^.Count do
New(Obj^.Side[i],Create(Obj^.o.x,Obj^.o.y,Obj^.o.z,0,0,0,a));
For i:=1 to Obj^.Count do begin
inc(CS);
if(i mod 2<>0)Then
T:=ColorText(RGB(100+Random(155),100+Random(155),100+Random(155)),GMT)
else T:=T;
Obj^.Side[i]^.Texture:=T;
Obj^.Side[i]^.Mode:=Mode;
Obj^.Side[i]^.Alpha:=0;
Read(f1,S);
For j:=1 to 3 do
begin
Read(f1,S);
Obj^.Side[i]^.S[j]:=S;
SS^[i,j]:=S;
end;
end;
close(f1);
end;
procedure SaveObject(Name:String;var Obj:PObj);
var f1:file of TPoint;
f2:file of Word;B:Boolean;
S:TPoint;i,j:word;Name2:String;
begin
B:=False;
For i:=1 to Length(Name) do
if(Name[i]=’.’)Then B:=True;
if(B=True)Then begin
For i:=1 to Length(Name)-4 do begin
Name2:=Name2+Name[i];
end;
Name:=Name2;
end;
Assign(f2,Name+’.res’);
Rewrite(f2);
Reset(f2);
Write(f2,Obj^.Count);
close(f2);
Assign(f1,Name+’.dat’);
Rewrite(f1);
Reset(f1);
Write(f1,Obj^.o);
For i:=1 to Obj^.Count do begin
Obj^.Side[i]^.o:=tmp_o;
S:=Obj^.Side[i]^.o;
Write(f1,S);
For j:=1 to 3 do
begin
S:=Obj^.Side[i]^.S[j];
Write(f1,S);
end;
end;
close(f1);
end;
procedure CreateTMP(var Obj:PObj);
var i,j,k:Word;
begin
Obj^.o.x:=0;Obj^.o.y:=0;Obj^.o.z:=100;
For i:=1 to Obj^.Count do
begin
tmp_o:=Obj^.Side[i]^.o;
Obj^.Side[i]^.o:=o[i];
For j:=1 to 3 do
Obj^.Side[i]^.S[j]:=S^[i,j];
end;
end;
procedure ShowSide;
var i,j,k,cx,cy:Word;tmp:TPoint;
begin
Lab.Vid.Surface.Fill(0);
myForm(GetMaxX,GetMaxY);
For i:=1 to CS do begin
Line(Trunc(SS^[i,1].x),Trunc(SS^[i,1].y),Trunc(SS^[i,2].x),Trunc(SS^[i,2].y),White);
Line(Trunc(SS^[i,2].x),Trunc(SS^[i,2].y),Trunc(SS^[i,3].x),Trunc(SS^[i,3].y),White);
Line(Trunc(SS^[i,3].x),Trunc(SS^[i,3].y),Trunc(SS^[i,1].x),Trunc(SS^[i,1].y),White);
Line(Trunc(SS^[i,1].x),Trunc(SS^[i,1].z),Trunc(SS^[i,2].x),Trunc(SS^[i,2].z),White);
Line(Trunc(SS^[i,2].x),Trunc(SS^[i,2].z),Trunc(SS^[i,3].x),Trunc(SS^[i,3].z),White);
Line(Trunc(SS^[i,3].x),Trunc(SS^[i,3].z),Trunc(SS^[i,1].x),Trunc(SS^[i,1].z),White);
Line(Trunc(SS^[i,1].z*r),Trunc(SS^[i,1].y),Trunc(SS^[i,2].z*r),Trunc(SS^[i,2].y),White);
Line(Trunc(SS^[i,2].z*r),Trunc(SS^[i,2].y),Trunc(SS^[i,3].z*r),Trunc(SS^[i,3].y),White);
Line(Trunc(SS^[i,3].z*r),Trunc(SS^[i,3].y),Trunc(SS^[i,1].z*r),Trunc(SS^[i,1].y),White);
if(ClicCub[i]=True)Then begin
Line(Trunc(SS^[i,1].z),Trunc(SS^[i,1].y),Trunc(SS^[i,2].z),Trunc(SS^[i,1].y),White);
Line(Trunc(SS^[i,2].z),Trunc(SS^[i,1].y),Trunc(SS^[i,1].z),Trunc(SS^[i,3].y),White);
end;
end;
S[CS]:=SS[CS];
cx:=GMX div 2;
cy:=GMY div 2+1;
For i:=1 to 3 do
begin
S^[CS,i].x:=S^[CS,i].x-cx+dF;
S^[CS,i].y:=cy-S^[CS,i].y-dF;
S^[CS,i].z:=S^[CS,i].z-cx-dF;
end;
end;
procedure CreateSide(var Obj:PObj);
var cx,cy,i,j,k:Word;
begin
r:=1;
For i:=1 to Obj^.Count do
S[i]:=Obj^.Side[i]^.S;
TMP:=S;
Lab.Vid.Surface.Fill(0);
myForm(GetMaxX,GetMaxY);
cx:=GMX div 2;
cy:=GMY div 2+1;
For j:=1 to CS do
For i:=1 to 3 do
begin
TMP^[j,i].x:=TMP^[j,i].x-cx+dF;
TMP^[j,i].y:=cy-TMP^[j,i].y-dF;
TMP^[j,i].z:=TMP^[j,i].z-cx-dF;
end;
For i:=1 to CS do begin
Line(Trunc(TMP^[i,1].x),Trunc(TMP^[i,1].y),Trunc(TMP^[i,2].x),Trunc(TMP^[i,2].y),White);
Line(Trunc(TMP^[i,2].x),Trunc(TMP^[i,2].y),Trunc(TMP^[i,3].x),Trunc(TMP^[i,3].y),White);
Line(Trunc(TMP^[i,3].x),Trunc(TMP^[i,3].y),Trunc(TMP^[i,1].x),Trunc(TMP^[i,1].y),White);
Line(Trunc(TMP^[i,1].x),Trunc(TMP^[i,1].z),Trunc(TMP^[i,2].x),Trunc(TMP^[i,2].z),White);
Line(Trunc(TMP^[i,2].x),Trunc(TMP^[i,2].z),Trunc(TMP^[i,3].x),Trunc(TMP^[i,3].z),White);
Line(Trunc(TMP^[i,3].x),Trunc(TMP^[i,3].z),Trunc(TMP^[i,1].x),Trunc(TMP^[i,1].z),White);
Line(Trunc(TMP^[i,1].z*r),Trunc(TMP^[i,1].y),Trunc(TMP^[i,2].z*r),Trunc(TMP^[i,2].y),White);
Line(Trunc(TMP^[i,2].z*r),Trunc(TMP^[i,2].y),Trunc(TMP^[i,3].z*r),Trunc(TMP^[i,3].y),White);
Line(Trunc(TMP^[i,3].z*r),Trunc(TMP^[i,3].y),Trunc(TMP^[i,1].z*r),Trunc(TMP^[i,1].y),White);
end;
Flip(Lab.Vid);
end;
procedure LoadSide(var Obj:PObj);
var i,j,k:Word;TMP:PPL;
begin
r:=1;
For i:=1 to Obj^.Count do
S[i]:=Obj^.Side[i]^.S;
SS^:=S^;
Lab.Vid.Surface.Fill(0);
myForm(GetMaxX,GetMaxY);
For j:=1 to CS do
For i:=1 to 3 do
begin
SS^[j,i].x:=SS^[j,i].x+cx-dF;
SS^[j,i].y:=cy-SS^[j,i].y-dF;
SS^[j,i].z:=SS^[j,i].z+cx+dF;
end;
For i:=1 to CS do begin
Line(Trunc(SS^[i,1].x),Trunc(SS^[i,1].y),Trunc(SS^[i,2].x),Trunc(SS^[i,2].y),White);
Line(Trunc(SS^[i,2].x),Trunc(SS^[i,2].y),Trunc(SS^[i,3].x),Trunc(SS^[i,3].y),White);
Line(Trunc(SS^[i,3].x),Trunc(SS^[i,3].y),Trunc(SS^[i,1].x),Trunc(SS^[i,1].y),White);
Line(Trunc(SS^[i,1].x),Trunc(SS^[i,1].z),Trunc(SS^[i,2].x),Trunc(SS^[i,2].z),White);
Line(Trunc(SS^[i,2].x),Trunc(SS^[i,2].z),Trunc(SS^[i,3].x),Trunc(SS^[i,3].z),White);
Line(Trunc(SS^[i,3].x),Trunc(SS^[i,3].z),Trunc(SS^[i,1].x),Trunc(SS^[i,1].z),White);
Line(Trunc(SS^[i,1].z*r),Trunc(SS^[i,1].y),Trunc(SS^[i,2].z*r),Trunc(SS^[i,2].y),White);
Line(Trunc(SS^[i,2].z*r),Trunc(SS^[i,2].y),Trunc(SS^[i,3].z*r),Trunc(SS^[i,3].y),White);
Line(Trunc(SS^[i,3].z*r),Trunc(SS^[i,3].y),Trunc(SS^[i,1].z*r),Trunc(SS^[i,1].y),White);
end;
Flip(Lab.Vid);
end;
procedure InPut1(x,y:integer;var E:Boolean);
var i,j:integer;B:Boolean;
begin
if(sv=0)Then begin
o[CS].x:=0;
o[CS].y:=0;
o[CS].z:=0;
end;
if(M<sv)Then begin
PutPixel(x,y,Yelow);
Flip(Lab.Vid);
if(x<cx)and(y<cy)Then
begin
o[CS].x:=x-cx+dF;
o[CS].y:=cy-y-dF;
end
else if(x>cx)and(y<cy)Then
begin
o[CS].z:=x-cx-dF;
o[CS].y:=cy-y-dF;
end
else if(x<cx)and(y>cy)Then
begin
o[CS].x:=x-cx+dF;
o[CS].z:=x-cx-dF;
end
else begin
ShowMessage(‘Эта четверть не является плоскостью проекций!’);
E:=True;
end;
inc(M);
end
else
if (M<6+sv)Then begin
if(N>3)Then N:=0;
if(x>cx)and(y>cy)Then
else
if(N=0)Then begin
x0:=x;y0:=y;x1:=0;y1:=0;
N:=1;
end;
if(x<cx)and(y<cy)Then
begin
SS^[CS,N].x:=x;
SS^[CS,N].y:=y;
r:=1;
end
else if(x>cx)and(y<cy)Then
begin
SS^[CS,N].z:=x;
SS^[CS,N].y:=y;
r:=1;
end
else if(x<cx)and(y>cy)Then
begin
SS^[CS,N].x:=x;
SS^[CS,N].z:=y;
r:=GetMaxX/GetMaxY;
end
else begin
ShowMessage(‘Эта четверть не является плоскостью проекций!’);
E:=True;
end;
if(E=False)Then begin
x2:=X;y2:=Y;
if(x1<>0)and(y1<>0)Then begin
Line(x0,y0,x2,y2,Red);
Line(x1,y1,x2,y2,Red);
end
else putpixel(x2,y2,Red);
Flip(Lab.Vid);
x1:=x2;y1:=y2;
For i:=1 to CS-1 do
For j:=1 to 3 do
begin
if(SS^[CS,N].x+o[CS].x>=SS^[i,j].x+o[i].x-2)and(SS^[CS,N].x+o[CS].x<=SS^[i,j].x+o[i].x+2)
and(SS^[CS,N].y+o[CS].y>=SS^[i,j].y+o[i].y-2)and(SS^[CS,N].y+o[CS].y<=SS^[i,j].y+o[i].y+2)
and(SS^[CS,N].z+o[CS].z>=SS^[i,j].z+o[i].z-2)and(SS^[CS,N].z+o[CS].z<=SS^[i,j].z+o[i].z+2)Then Accept:=True;
end;
inc(N);
inc(M);
end;
end;
if(M=6+sv)and(Accept=True)Then begin
M:=0;N:=0;
ClicCub[CS]:=False;
ShowSide;
Flip(Lab.Vid);
New(Scene^.Camera[CC]^.Obj[CO]^.Side[CS],Create(0,0,100,o[CS].x,o[CS].y,o[CS].z,S^[CS]));
if(CS mod 2<>0)Then
T:=ColorText(RGB(100+Random(155),100+Random(155),100+Random(155)),GMT)
else T:=T;
Scene^.Camera[CC]^.Obj[CO]^.Side[CS]^.Texture:=T;
Scene^.Camera[CC]^.Obj[CO]^.Side[CS]^.Mode:=Mode;
Scene^.Camera[CC]^.Obj[CO]^.Side[CS]^.Alpha:=0;
Accept:=False;
inc(CS);
end
else if(M=6+sv)and(Accept=False)Then begin
SS[CS]:=SS[CS-1];
SS[CS]:=SS[CS-1];
SS[CS]:=SS[CS-1];
M:=0;N:=0;
ShowSide;
Flip(Lab.Vid);
end;
end;
procedure InPut2(x,y:integer;var E:Boolean);
var i,j:integer;o:TPoint;Party:Single;tmp:Single;
begin
if(N<sv)Then begin
if(x<cx)and(y<cy)Then
begin
oc.x:=x-cx+dF;
oc.y:=cy-y-dF;
end
else if(x>cx)and(y<cy)Then
begin
oc.z:=x-cx-dF;
oc.y:=cy-y-dF;
end
else if(x<cx)and(y>cy)Then
begin
oc.x:=x-cx+dF;
oc.z:=x-cx-dF;
end
else begin
ShowMessage(‘Эта четверть не является плоскостью проекций!’);
E:=True;
end;
if(E=False)Then begin
PutPixel(x,y,Yelow);
Flip(Lab.Vid);
inc(N);
end;
end
else
if(N<2+sv)and(N>sv-1)Then begin
if(N=sv)Then begin
x0:=x;y0:=y;
end;
x1:=x;y1:=y;
Line(x0,y0,x1,y1,Red);
Flip(Lab.Vid);
x0:=x;y0:=y;
inc(N);
if(x>cx)and(y>cy)Then begin
ShowMessage(‘Эта четверть не является плоскостью проекций!’);
E:=True;
end
else begin
w[N-sv].x:=x;
w[N-sv].y:=y;
end;
end;
if(N=2+sv)Then begin
if(абс(w[N-sv].x-w[N-sv-1].x)/2<=абс(w[N-sv].y-w[N-sv-1].y)/2)Then Party:=абс(w[N-sv].x-w[N-sv-1].x)
else Party:=абс(w[N-sv].y-w[N-sv-1].y);
if(w[N-sv].x<cx)and(w[N-sv].y<cy)Then begin
o.x:=(w[N-sv].x+w[N-sv-1].x)/2;
o.y:=(w[N-sv].y+w[N-sv-1].y)/2;
o.z:=cx+Party/2+dF;
end
else if(w[N-sv].x>cx)and(w[N-sv-1].y<cy)Then begin
o.x:=cx-Party/2-dF;
o.y:=(w[N-sv].y+w[N-sv-1].y)/2;
o.z:=(w[N-sv].x+w[N-sv-1].x)/2;
end
else if(w[N-sv].x<cx)and(w[N-sv].y>cy)Then begin
o.x:=(w[N-sv].x+w[N-sv-1].x)/2;
o.y:=cy-Party/2-dF;
o.z:=(w[N-sv].y+w[N-sv-1].y)/2;
end;
r:=1;
SS^[CS,1].x:=o.x-Party/2;SS^[CS,1].y:=o.y+Party/2;SS^[CS,1].z:=o.z+Party/2;
SS^[CS,2].x:=o.x+Party/2;SS^[CS,2].y:=o.y+Party/2;SS^[CS,2].z:=o.z+Party/2;
SS^[CS,3].x:=o.x-Party/2;SS^[CS,3].y:=o.y-Party/2;SS^[CS,3].z:=o.z-Party/2;
SS^[CS+1,1].x:=o.x+Party/2;SS^[CS+1,1].y:=o.y-Party/2;SS^[CS+1,1].z:=o.z-Party/2;
SS^[CS+1,2].x:=o.x+Party/2;SS^[CS+1,2].y:=o.y+Party/2;SS^[CS+1,2].z:=o.z+Party/2;
SS^[CS+1,3].x:=o.x+Party/2;SS^[CS+1,3].y:=o.y+Party/2;SS^[CS+1,3].z:=o.z-Party/2;
SS^[CS+2,1].x:=o.x-Party/2;SS^[CS+2,1].y:=o.y+Party/2;SS^[CS+2,1].z:=o.z+Party/2;
SS^[CS+2,2].x:=o.x-Party/2;SS^[CS+2,2].y:=o.y-Party/2;SS^[CS+2,2].z:=o.z-Party/2;
SS^[CS+2,3]:=SS^[CS+1,1];
o.x:=o.x-cx+dF;
o.y:=cy-o.y-dF;
o.z:=o.z-cx-dF;
Scene^.Camera[CC]^.Obj[CO]^.Done;
Scene^.Camera[CC]^.Done;
Scene^.Done;
New(Cube,Create(0,0,100,o.x,o.y,o.z,Party,T,Mode,0));
Scene^.Camera[CC]^.ADD(CO,CO+1);
For i:=1 to Scene^.Camera[CC]^.Obj[CO]^.Count do
begin
For j:=1 to 3 do
S[i,j]:=Scene^.Camera[CC]^.Obj[CO]^.Side[i]^.S[j];
if(i mod 2<>0)Then
T:=ColorText(RGB(100+Random(155),100+Random(155),100+Random(155)),GMT)
else T:=T;
Scene^.Camera[CC]^.Obj[CO]^.Side[i]^.Texture:=T;
Scene^.Camera[CC]^.Obj[CO]^.Side[i]^.Mode:=Mode;
end;
For i:=CS to CS+12 do
ClicCub[i]:=True;
CS:=CS+12;
N:=0;
Scene^.Camera[CC]^.Obj[CO]^.Done;
Scene^.Camera[CC]^.Done;
Scene^.Done;
ShowSide;
Flip(Lab.Vid);
end;
end;
procedure Data;
var i,j:Word;
begin
New(S);
New(SS);
GetMaxX:=Lab.Vid.Width-1;
GetMaxY:=Lab.Vid.Height-1;
M:=0;N:=0;Rot:=YRot;Bol:=False;
GMX:=Lab.Vid.Width;
GMY:=Lab.Vid.Height;
CSc:=1;CC:=1;CL:=1;CO:=1;CS:=1;
cx:=GMX div 2;
cy:=GMY div 2+1;
Blue:=RGB(255,0,0);
Red:=RGB(200,200,200);
Yelow:=RGB(0,255,0);
New(Scene,Create);
New(Scene^.Camera[CC],Create(0,0,0,0,0,0));
New(Light[CL],Create(-100,-100,0));
New(Scene^.Camera[CC]^.Obj[CO],Create(0,0,0,0,0,0));
Accept:=True;View:=False;
end;
procedure TLab.FormCreate(Sender: TObject);
var i,j:Word;
begin
Tables;
@InPut[1]:=@InPut1;
@InPut[2]:=@InPut2;
New(t);
For i:=0 to GMT do
New(t^[i]);
For i:=0 to GMT do
For j:=0 to GMT do
t^[i]^[j]:=RGB(255,0,0);
Data;
end;
procedure TLab.TimerTimer(Sender: TObject; LagCount: Integer);
var i:Word;
begin
if(Keys[VK_Escape])Then halt;
if(Bol=False)and(Keys[VK_F9])and(CountSide>0)Then begin
if(CountSide=0)Then begin
ShowMessage(‘Для просмотра нужна хотя бы одна грань’);
exit;
end;
Bol:=True;View:=True;
Scene^.Camera[CC]^.Obj[CO]^.Done;
Scene^.Camera[CC]^.Done;
Scene^.Done;
CreateTMP(Scene^.Camera[CC]^.Obj[CO]);
For i:=1 to Scene^.Camera[CC]^.Obj[CO]^.Count do
Scene^.Camera[CC]^.Obj[CO]^.Side[i]^.o:=tmp_o;
Vid.Surface.Fill(0);
Scene^.Camera[CC]^.Obj[CO]^.Draw;
Flip(Vid);
end
else if(Bol)Then
if(Keys[ord(‘Y’)])Then
begin
Rot:=YRot;
Vid.Surface.Fill(0);
Scene^.Camera[CC]^.Obj[CO]^.Rotate(CosA,SinA,Angle,Rot);
Scene^.Camera[CC]^.Obj[CO]^.Draw;
Flip(Vid);
end
else if(Keys[Ord(‘X’)])Then
begin
Rot:=XRot;
Vid.Surface.Fill(0);
Scene^.Camera[CC]^.Obj[CO]^.Rotate(CosA,SinA,Angle,Rot);
Scene^.Camera[CC]^.Obj[CO]^.Draw;
Flip(Vid);
end
else if(Keys[Ord(‘Z’)])Then
begin
Rot:=ZRot;
Vid.Surface.Fill(0);
Scene^.Camera[CC]^.Obj[CO]^.Rotate(CosA,SinA,Angle,Rot);
Scene^.Camera[CC]^.Obj[CO]^.Draw;
Flip(Vid);
end
else if(Keys[VK_Space])Then
begin
if(Space.Visible=False)Then begin
Space.Visible:=True;
Enter.Visible:=True;
BSide.Visible:=True;
BCube.Visible:=True;
Menu.Items[0].Visible:=True;
Menu.Items[1].Visible:=True;
Menu.Items[2].Visible:=True;
Width:=455;
Height:=465;
Left:=108;
Top:=-7;
Vid.Width:=385;
Vid.Height:=385;
Vid.Top:=32;
Vid.Left:=32;
GMX:=Lab.Vid.Width;
GMY:=Lab.Vid.Height;
GetMaxX:=GMX-1;
GetMaxY:=GMY-1;
end;
M:=0;N:=0;Bol:=False;
View:=False;
ShowSide;
Flip(Vid);
end;
end;
procedure TLab.VidMouseDown(Sender: TObject; Button: TMouseButton;
Shift: TShiftState; X, Y: Integer);
var E:Boolean;
begin
E:=False;
if(View=False)Then InPut[Figure](x,y,E);
if(E=True)Then exit;
end;
procedure TLab.EnterClick(Sender: TObject);
var i:Word;
begin
if(CountSide=0)Then begin
ShowMessage(‘Для просмотра нужна хотя бы одна грань’);
exit;
end;
Bol:=True;View:=True;
Scene^.Camera[CC]^.Obj[CO]^.Done;
Scene^.Camera[CC]^.Done;
Scene^.Done;
CreateTMP(Scene^.Camera[CC]^.Obj[CO]);
For i:=1 to Scene^.Camera[CC]^.Obj[CO]^.Count do
Scene^.Camera[CC]^.Obj[CO]^.Side[i]^.o:=tmp_o;
Vid.Surface.Fill(0);
Scene^.Camera[CC]^.Obj[CO]^.Draw;
Flip(Vid);
end;
procedure TLab.N3Click(Sender: TObject);
var i,j:integer;Name:String;
begin
OpenDialog.Execute;
Name:=OpenDialog.FileName;
Lab.Vid.Surface.Fill(0);
Data;
myform(GetMaxX,GetMaxY);
Flip(Lab.Vid);
if(Name=»)Then exit;
LoadObject(Name,Scene^.Camera[CC]^.Obj[CO]);
LoadSide(Scene^.Camera[CC]^.Obj[CO]);
end;
procedure TLab.N4Click(Sender: TObject);
var Name:String;
begin
SaveDialog.Execute;
Name:=SaveDialog.FileName;
if(Name=»)Then exit;
CreateTMP(Scene^.Camera[CC]^.Obj[CO]);
SaveObject(Name,Scene^.Camera[CC]^.Obj[CO]);
end;
procedure TLab.N2Click(Sender: TObject);
begin
Flip(Lab.Vid);
if(Lab.Width>Lab.Height)Then Lab.Height:=Lab.Width
else Lab.Width:=Lab.Height;
Vid.Height:=Lab.Height-81;
Vid.Width:=Lab.Width-81;
Lab.Vid.Surface.Fill(0);
Data;
myform(GetMaxX,GetMaxY);
Flip(Lab.Vid);
end;
procedure TLab.N9Click(Sender: TObject);
begin
ShowMessage(‘Autocad version 1.2 Copiright by Anton Sazonov’);
end;
procedure TLab.N10Click(Sender: TObject);
begin
Halt;
end;
procedure TLab.SpaceClick(Sender: TObject);
var i:Word;
begin
M:=0;N:=0;Bol:=False;
View:=False;
ShowSide;
Flip(Vid);
end;
procedure TLab.FormKeyDown(Sender: TObject; var Key: Word;
Shift: TShiftState);
begin
Keys[Key]:=True;
end;
procedure TLab.FormKeyUp(Sender: TObject; var Key: Word;
Shift: TShiftState);
begin
Keys[Key]:=False;
end;
procedure TLab.CancelClick(Sender: TObject);
begin
Halt;
end;
procedure TLab.OKClick(Sender: TObject);
begin
Option[1]:=Box1.Text;
if(Option[1]=»)Then begin
ShowMessage(‘Выберитережим!’);
Exit;
end
else if(Option[1]=’4 bits’)Then PixMode:=1
else if(Option[1]=’8 bits’)Then PixMode:=2
else if(Option[1]=’16 bits’)Then PixMode:=3
else if(Option[1]=’24 bits’)Then PixMode:=4
else if(Option[1]=’32 bits’)Then PixMode:=5;
Option[2]:=Box6.Text;
if(Option[2]=»)Then begin
ShowMessage(‘Изберите разрешение!’);
Exit;
end
else if(Option[2]=’640X480′)Then begin
ScreenX:=640;ScreenY:=480;
end
else if(Option[2]=’800X600′)Then begin
ScreenX:=800;ScreenY:=600;
end
else if(Option[2]=’1024X768′)Then begin
ScreenX:=1024;ScreenY:=768;
end
else if(Option[2]=’1280X1024′)Then begin
ScreenX:=1280;ScreenY:=1024;
end;
Surf:=Vid.Surface;
SetGraphMode(PixMode);
Label1.Destroy;
Label6.Destroy;
Label7.Destroy;
Box1.Destroy;
Box6.Destroy;
Cancel.Destroy;
OK.Visible:=False;
Lab.Height:=465;
Lab.Top:=-7;
Menu.Items.Visible:=True;
Vid.Visible:=True;
Vid.Width:=385;
Vid.Height:=385;
Space.Visible:=True;
Enter.Visible:=True;
Lab.Vid.Surface.Fill(0);
myform(GetMaxX,GetMaxY);
Flip(Lab.Vid);
if(Lab.Width>Lab.Height)Then Lab.Height:=Lab.Width
else Lab.Width:=Lab.Height;
Vid.Height:=Lab.Height-81;
Vid.Width:=Lab.Width-81;
Lab.Vid.Surface.Fill(0);
Data;
myform(GetMaxX,GetMaxY);
Flip(Lab.Vid);
BCube.Visible:=True;
BSide.Visible:=True;
Menu.Items[0].Visible:=True;
Menu.Items[1].Visible:=True;
Menu.Items[2].Visible:=True;
OK.Destroy;
end;
procedure TLab.N8Click(Sender: TObject);
begin
Application.HelpContext(10);
end;
procedure TLab.BCubeClick(Sender: TObject);
begin
Figure:=2;
ClicCub[CS]:=True;
end;
procedure TLab.BSideClick(Sender: TObject);
begin
Figure:=1;
end;
procedure TLab.N6Click(Sender: TObject);
begin
if (View=True)Then begin
Space.Visible:=False;
Enter.Visible:=False;
BSide.Visible:=False;
BCube.Visible:=False;
Menu.Items[0].Visible:=False;
Menu.Items[1].Visible:=False;
Menu.Items[2].Visible:=False;
Width:=ScreenX+10;
Height:=ScreenY+20;
Left:=-10;
Top:=-30;
Vid.Width:=ScreenX+1;
Vid.Height:=ScreenY-16;
Vid.Top:=0;
Vid.Left:=0;
GMX:=ScreenX;GMY:=ScreenY;
GetMaxX:=GMX-1;GetMaxY:=GMY-1;
Scene^.Camera[CC]^.Obj[CO]^.Draw;
Flip(Vid);
end;
end;
end.
ЗАКЛЮЧЕНИЕ
Программирование с внедрением трёхмерной графики — это метод описания языком программирования объёмных тел и отображения их на мониторе.
Измерение данной графики совпадает с измерением настоящей системы, находящейся в пространстве, в каком ориентируется человек, и по этому хоть какое вещественное тело можно виртуально сделать, задать ему условия и поглядеть на реакцию этого тела, задать телу правила поведения (линию движения движения) и выяснить как оно будет себя вести, где будет находиться с течением времени.
К примеру, можно сделать программку, создающую чертежи с внедрением гостов и чертёжных обозначений. Она нужна конструкторам. Примером таковой программки является автокод.
Можно изменить данную программку таковым образом, чтоб она, виртуально создавала дом и докладывала какие перегрузки, он будет испытывать и не деформируется ли он при разных природных явлениях. Эта программка нужна конструкторам.
Ещё трёхмерную графику можно применить для сотворения устройств, которые меж собой ведут взаимодействие, показать какие силы при всем этом участвуют, и показать их взаимодействие с различных сторон. Таковая программка нужна инженерам-механикам. Эти программки могут применять как ортогональное (параллельное), так и центральное проецирование (проецирование с учётом перспективы).
программка, создающая трёхмерную анимацию (кинофильм, мульт), так же быть может реализована на компе. Эта программка обязана применять лишь центральное проецирование (перспективное), и лучше наличие неких эффектов: прозрачности, освещённости, билинейной фильтрации текстур и т.д.
Трёхмерная графика нужна всюду, где выполняются вещественные объекты, где есть инженеры, конструкторы, архитекторы, просто квалифицированные рабочие, а конкретно: в самолётостроении, в машиностроении, в кораблестроении, в строительстве, в галлактической индустрии и т. д.
С трёхмерной графикой, так либо по другому, приходится сталкиваться дизайнерам одежки, дизайнерам Веб-сайтов, хоть каким иным дизайнерам, работникам отделов рекламы, продюсерам и т. д.
Трёхмерной графикой воспользовались постоянно. До объёмного изображения на компе чертили чертежи на бумаге, до чертежей отрисовывали эскизы либо картинки, до рисунков воспользовались заданием объектов аналитически на бумаге либо в уме.
Если отдать оценку трёхмерной графики в вещественном производстве, то она будет последующей: трёхмерная графика, как метод изображения объёмных фигур либо тел, является самым приятным способом представления инфы, применяемой в вещественном производстве. Без трёхмерной графики не было бы налажено хоть какое вещественное Создание. Самый удачный метод задания и использования трёхмерной либо объёмной графики осуществляется при помощи информационных технологий, а конкретно компа.
СПИСОК ЛИТЕРАТУРЫ
1 С. Бобровский «Delphi 5». 2007
2 О.Е. Акимов «Дискретная математика, методах доказательств и опровержений; совокупа научных теорий в каждой из которых рассматриваются определенные методы доказательств и опровержений Основоположником логики считается Аристотель Различают инд, группы, графы».
3 М.П. Богдан «Конспект лекций» 2006
4 ФароновВ. В. «Turbo Pascal 7.0» 2005
5 В. Кулаков «Программирование на аппаратном уровне»
6 А.В. Потапкин, Д. Ф., Кучвальский «3DStudioMax» 2006
]]>
СОДЕРЖАНИЕ
ВСТУПЛЕНИЕ
1. Математические объекты
1.1 Группы
1.2 Графы
2. Справка по работе с программой
2.1 Назначение программного продукта
2.2 Обучение работе с программным продуктом
2.3 Ограничения применения 10
3. Нереализованные возможности
4. Основная форма
5. Методы создания программы
5.1 Матричные преобразования
5.2 Создание одноцветного треугольника
6. программа
ЗАКЛЮЧЕНИЕ
СПИСОК ЛИТЕРАТУРЫ
ВСТУПЛЕНИЕ
Каждый материальный объект, имеющий форму, является объемным, следовательно, его положение в пространстве можно задать с помощью трёх координат X, Y, Z. Результат любого материального производства, дома, автомобили, станки, можно представить как 3-х мерную модель и отобразить эту модель на дисплее компьютера с помощью соответствующей программы.
Нет необходимости создавать отдельно программу для создания моделей отдельно домов, отдельно станков и их механизмов, отдельно для автомобилей и т. д. Гораздо разумней создать одну программу, которая может учитывать специфику любой отрасли и создавать, практически любые 3-х мерные модели.
Программирование уже давно перестало быть уделом энтузиастов. Современный программист — не писатель или ученый, а квалифицированный рабочий. Прошли те времена, когда, удавалось «удовлетворять собственный Интерес за счет государства»: месяцами исследовать исходный код какой-нибудь совершенно бесполезной в практическом плане утилиты, забираться в недра исходных текстов оригинально сделанных компонентов.
Конечно, небольшому проценту разработчиков по долгу службы нужны глубокие специфические знания, однако от подавляющего большинства программистов сегодня требуется, прежде всего, умение писать программы максимально быстро и без ошибок. Например программа, представленная в этом проекте требует минимальное количество умственных затрат и времени пользователя или программиста высокого уровня для достижения результата. Программист высокого уровня — это программист, составляющий свои программы на основе разработок других программистов. Под разработками других программистов будем понимать разработанные ими средства программирования: (язык программирования, функции, процедуры, утилиты, библиотеки, модули, компоненты, драйвера и т. д.
Любой природный процесс, которым человек пытается управлять(воздействовать на него с предсказуемыми последствиями), должен быть сначала представлен в виде модели той или иной степени сложности. Специалистам в области компьютерных технологий приходится брать такие модели или их фрагменты для последующего объединения из конкретных предметных областей, общих знаний и представлений об окружающем мире и создавать свою модель для решения поставленной задачи. После этого, учитывая особенности работы вычислительного устройства (компьютера) и выбранного языка программирования – разрабатывать алгоритм реализации модели, программировать(кодировать) этот алгоритм. Кроме реализации основной функции, необходимо подумать о вспомогательных функциях(контроль входных данных, проверка адекватности результата, человека) имеют дискретную природу (дискретный
(лат. diskretus) – разделенный, прерывистый). характер (алфавит машинного языка двухсимвольный, множество состояний процессора конечное), поэтому любая модель, реализованная в виде машинного кода, дискретна. Поэтому для реализации рассматриваемой программы необходимо соприкоснуться с некоторыми областями дискретной математики.
1. Математические объекты
1.1 Группы
Группа — оперативное множество, в котором действует процедура умножения и которое подчинено следующим условиям:
1) замкнутости: для каждой пары g1g2=g3, причем g3 должен принадлежать группе G: g1g2 ÎG; g1g2 = g3 ÎG;
2) наличия тождественного элемента e: среди множества элементов группы G справедливы равенство eg = g; ge = g; e,gÎG;
3) наличие обратных элементов: для всякого g из G должен отыскаться единственный ему обратный элемент g, принадлежащий G, при умножении на который получился тождественный элемент e. e = q×q; e,g,gÎG;
4) ассоциативности: для любых трёх элементов g1, g2, g3 из G справедливо равенство: g1(g2g3) = (g1g2)g3. Условия ассоциативности выполняется для квадратных матриц.
Линейное преобразование A вектора x в вектор c осуществляется с помощью квадратной матрицы y = Ax.
Если есть прямое преобразование, то должно быть и обратное, при условии, что A имеет A. y = Ax;
x = Tx ;y = Tx ; Следовательно Ty = ATx; Умножим с лева на T , получим y = TATx;
Отсюда следует формула прямого преобразования подобия.
Формулу обратного преобразования подобия можно получить, умножив на T с права:
Если Ax = lx; то ненулевой вектор x является собственным вектором. Для нахождения собственных векторов используют формулу
*
где E — единичная матрица.
Корни многочлена P(l) = det(A — lE) = 0 подставляют в формулу (*) и получают собственные векторы x.
Группы, лишившись своей предметной области, не востребованы в настоящее время. Хотя ранее, примерно сорок лет назад, теория групп была распространена из-за того, что группы тесно увязывалась с фундаментальными областями естествознания — физикой элементарных частиц, квантовой механикой, физикой твёрдого тела и кристаллографией.
В данной программе все объёмные фигуры состоят из треугольных граней, каждая из которых содержит три вершины. Задав положение объёмного тела в пространстве, мы задаём положение всех вершин. Каждая вершина представляется как вектор.
Чтобы преобразовать объект,(повернуть, удалить, приблизить, сжать, растянуть и т. п.) необходимо каждый вектор каждой грани объекта умножить на матрицу преобразования, например на матрицу поворота вокруг оси Y.
Вот как эта матрица будет выглядеть:
1.2 Графы
Любое объемное тело, как уже было сказано выше, можно построить с помощью треугольных граней, каждая из которых имеет хотя бы одну общую вершину с соседней гранью. Схематично каждую грань можно изобразить как совокупность вершин, соединенную контурными линиями. Контурные линии — это линии описывающие контур. Контур — это замкнутый путь. Таким образом, грань, содержащая вершины упорядоченно соединённые рёбрами представляет собой ориентированный граф или Орграф.
Граф G
как математический объект – это совокупность двух множеств: непустого множества вершин V
и множества ребер E
, элементы которого представляет собой неупорядоченные (для ориентированного графа – упорядоченные) пары элементов из множества V
.
G (V,E) = áV; Eñ, n(V) > 0, E Ì V ´ V,
где для неориентированного графа E
=
E
–1
(бинарное отношение E
симметрично).
Минимальный граф состоит из одной вершины.
Каждому неориентированному графу можно поставить в соответствие ориентированный граф, в котором каждое ребро заменено двумя противоположно ориентированными ребрами, инцидентными тем же вершинам.
Пусть v
1
и v
2
– вершины, e
1
= (v
1
, v
2
) – соединяющее их ребро.
Тогда вершина v
1
и ребро e
1
инцидентны, вершина v
2
и ребро e
1
также инцидентны. Два ребра, инцидентные одной вершине, называются смежными; две вершины, инцидентные одному ребру, также называются смежными.
Обычно граф изображают на плоскости в виде диаграммы: вершины – точками, ребра – линиями, соединяющими инцидентные вершины.
Множество вершин и множество рёбер для конечных графов задаются, как правило, перечислением. Возможно задание графа описанием отношения инцидентности.
1 Отношение инцидентности задано матрицей смежности:
– столбцы и строки матрицы – вершины графа;
– для смежных вершин элемент матрицы равен1, для остальных – 0;
– для неориентированного графа эта матрица всегда симметрична;
– число рёбер равно числу единиц выше или ниже главной диагонали матрицы ( включая элементы на диагонали).
2 Отношение инцидентности задано матрицей инцидентности:
– столбцы матрицы соответствуют вершинам графа, а строки – рёбрам;
– если ребро ei
инцидентно вершине vj
, то элемент матрицы eij
=1, в противном случае – eij
= 0.
Таким образом, в каждой строке одна или две единицы, остальные нули (для петли две единицы).
Для ориентированного графа при заполнении матрицы:
eij
= –1,если vj
– начало ребра;
eij
=1,если vj
–конец ребра;
eij
= a (где a – любое число, кроме –1,1,0),если ребро – петля в вершине vj
;
в остальных случаях eij
= 0.
3 Граф задан списком ребер.
ei
vi,
vj
1
a, b
2
b, d
…
Примечание. здесь ei
–ребро, vi
,
vj
– пара вершин, соединяемых этим ребром.
Граф связан, если любая пара его вершин связана ребром.
Граф без кратных ребер называют полным, если каждая пара вершин соединена ребром.
Граф H
называют частью графа G
, если множество вершин графа H
принадлежит множеству вершин графа G
и множество рёбер графа H
принадлежит множеству рёбер графа G
, т.е.:
V(H) ÌV(G); E(H) ÌE(G).
часть графа H
называется суграфом, если она содержит все вершины графа G
.
Суграф H
для неориентированного графа G
называется покрывающим суграфом, если любая вершина последнего инцидентна хотя бы одному ребру из H
.
Подграф G
(
U
)
графа G
на множестве вершин U
(
U
Ì
V
)
– это часть графа, которой принадлежат все ребра с обоими концами из U
.
Звёздный граф для вершины v
(
v
Î
G
)
состоит из всех рёбер с началом и концом в вершине v
. Множество вершин звёздного графа состоит из вершины v
и других смежных с ней вершин.
Маршрутом в единичном связном графе G
называется такая конечная последовательность ребер (e
1,
e
2….
en
), в которой каждые два соседних ребра имеют общую инцидентную вершину.
Вершина v
о
, инцидентная ребру e
1
и не инцидентная ребру e
2
, называется началом маршрута в графе G
.
Вершина vn
, инцидентная ребру en
и не инцидентная ребру en
-1
, называется концом маршрута.
Число ребер маршрута называется его длиной.
Если вершины v
о
и vn
совпадают, то маршрут называется циклическим (или просто циклом).
Отрезок конечного или бесконечного маршрута сам является маршрутом.
Маршрут в графе G
называется цепью
, если все ребра в последовательности различны, и простой цепью
, если все вершины, через которые проходит маршрут (а значит и ребра) различны.
Другими словами, в цепи ребро может встретиться не более одного раза, а в простой цепи вершина – не более одного раза.
Говорят, что две вершины в графе связаны, если существует соединяющая их цепь. Граф, в котором все вершины связаны, называется связным
.
Расстоянием
между двумя вершинами графа называется минимальная длина простой цепи, связывающей эти вершины (обозначение d(v¢,v²)).
Протяженностью
между двумя вершинами графа называется максимальная длина простой цепи, связывающей эти вершины (обозначение g(v¢,v²)).
В частном случае расстояние и протяженность между вершинами могут быть одинаковыми.
2. Справка по работе с программой
2.1 Назначение программного продукта
Данный программный продукт предназначен для построения любых объемных фигур с последующей их демонстрацией на экране монитора. Следует отметить, что фигуры могут быть как выпуклыми, так и вогнутыми, но обязательно не должно нарушаться условие целостности. Это значит, что ни одна часть объекта не должна существовать отдельно, а должна быть хоть каким-нибудь своим элементом присоедененной к объекту. То есть два множества: вершин и рёбер, которые получаются, если пройти по контуру, от первой вершины к последней, представляют собой, согласно определению, ориентированный граф и имеют маршрут. Созданный объект можно сохранить на диске, а также загрузить с него, чтобы посмотреть или отредактировать результат работы. Для построения трехмерных объектов можно использовать некоторые доступные примитивы, а именно грань и куб. Каждая грань, в создаваемом объекте, имеет свой цвет, выбираемый программой случайным образом.
2.2 Обучение работе с программным продукт
ом
После загрузки приложения появится окно настройки, в котором необходимо установить разрешение цвета, которое обязательно должно совпадать с разрешением цвета текущего графического режима, например 16 бит (рис. 1).
В противном случае на форме будет наблюдаться некое аномальное графическое изображение, которое будет неправильно отражать результат работы, что, делает всякую работу невозможной. Далее нужно установить разрешение текущего графического режима в пикселях. Это нужно для полноэкранного режима просмотра. После установления графического режима появится рабочее окно, разделённое на четыре части — четверти. Каждая четверть является плоскостью проекций, расположение которых соответствует принципам начертательной геометрии: верхняя левая — фронтальная, правая верхняя — вертикальная, нижняя левая — горизонтальная. Нижняя — правая четверть не является плоскостью проекций, по этому там рисовать нельзя. Точки задаются нажатиями левой клавиши мыши. Если точек больше одной, то они соединяются линией, если точек три, то они образуют треугольник, где каждая точка является вершиной.
Следует помнить, что мы строим трёхмерное изображение не по точкам или по линиям, а по треугольным граням, и проецируем не точки, а грани, по этому если точка поставлена в одной плоскости проекций, то и остальные две точки данной грани должны быть поставлены в этой же плоскости. Для построения любой объёмной фигуры достаточно любые две плоскости. Третья
Рисунок 1
Рисунок 2
После того, как объект спроецирован на плоскости проекций нужно нажать кнопку «Просмотр», находящуюся на форме (рис. 2), чтобы посмотреть на объемный объект в перспективе, и получить возможность сохранить документ. Если объект создан, но небыла нажата клавиша «Просмотр», то после выбора опции «Сохранить как» информация не будет сохранена. После нажатия клавиши «Просмотр» появится однотонное окно чёрного цвета в котором будет находиться трёхмерный объект, на который можно посмотреть со всех сторон, вращая вокруг осей X,Y,Z нажатиями соответствующих клавиш «X», «Y», «Z», причем не имеет значения заглавная клавиша или нет. Просмотрев результат создания объекта, можно его достроить, нажав на форме кнопку «Добавить». После нажатия этой кнопки снова появится окно с тремя плоскостями проекций, в котором будут находиться проекции объекта и к которым можно добавить новые проекции. Следует заметить, что вращение трёхмерного объекта в перспективе фактически не приводит к изменению его положения в пространстве. То есть если после вращения объекта перейти к плоскостям проекций, картина не изменится, и после перехода обратно к перспективе объект будет находиться в исходном положении: его положение в перспективе будет соответствовать его проекциям на плоскостях проекций.
Добавление примитива: «Куб», осуществляется путём чертежа линии на одной из плоскости проекций, причём, если мысленно представить проекции этой линии на абсциссу и ординату, то стороной куба будет меньшая из проекций.
Если после создания одного трёхмерного объекта загрузить другой объект с диска, то первый объект уничтожится, а на его месте появится второй, который загружен с диска. Это объясняется тем, что загруженный объект не добавляется к уже имеющемуся, зато можно наоборот, как уже было описано выше, к загруженному объекту добавить примитивы (грани, кубы).
В программном продукте действительны такие функциональные клавиши:
Esc — выход из программы в операционную систему;
F1 — справка о программном продукте;
F9 — режим просмотра ;
X — вращение объекта вокруг оси X;
Y — вращение объекта вокруг оси Y;
Z — вращение объекта вокруг оси Z;
В программном продукте есть такие пункты меню «файл«, «Вид», «Помощь».
В пункте меню «файл«, содержатся опции для работы с файлами. Выбрав опцию «Создать», создаётся новая форма для создания объекта. Если изменить длину или ширину формы и выбрать опцию «Создать», то рабочее окно примет размеры, соответствующие новым размерам формы. Причём длина и ширина будут в любом случае одинаковы.
С помощью опции «Открыть» можно открыть, файл, содержащий трёхмерный объект. Опция «Сохранить как» необходима для того, чтобы записать трёхмерный объект в файл.
Для загрузки и сохранения файлов в данном программном продукте, необходимо и достаточно указать имя файла без разширения, или с разширением, состоящим из любых обязательно трёх символов.
Опция «Выход» предназначена для выхода из программы в операционную систему.
В пункте меню «Вид» можно задать полноэкранный режим формы. Пункт меню «Помощь» содержит опцию «Справка», выбор которой сопровождается открытием документа, содержащего подробную справку о программном продукте.
Так же пункт меню «Помощь» содержит опцию «О программе», в которой можно узнать краткую информацию о программном продукте и его разработчике.
2.3 Ограничения применения
В программе желательно использовать 16 битный режим, причём разрешение графического режима, выраженное в пикселях, не имеет значения. Допускаются только 8 битные, 16 битные и 32 битные графические режимы. Во всех остальных режимах, например, 2 битных, 4 битных, 24 битных программа работать не будет.
При использовании разных графических режимов рабочие окна, места на форме, где задаются грани, будут иметь различный вид (разные цвета линий разметки и линий проекций). Это связано с тем, что цвета в программном продукте не имеют значения. Важно чтобы проекции на плоскости проекций были видны, и соответствовали изображаемому объекту. цвета трёхмерного объекта, вообще выбираются случайным образом, по этому здесь важен не цвет, а форма объекта.
При загрузке трёхмерного объекта из файла не обязательно указывать разширение файла, или если уж оно указано, то должно содержать не менее трёх символов после точки. Это объясняется тем, что программа сохраняет объект в двух файлах. Один с разширением «Res», а другой «Dat». При этом не важно какое разширение укажет пользователь, оно всё равно отбрасывается программой.
При открытии файла программа отбрасывает указанное пользователем разширение, если оно есть, и добавляет к имени файла своё. Когда программа отбрасывает разрешение, она отбрасывает три символа, находящиеся после точки. Поэтому если пользователь указал разширение, важно, чтобы оно состояло не менее чем из трёх символов, иначе при открытии файла произойдёт ошибка.
Проектировать грани нужно с помощью задания трёх вершин, причём если задание проекций трёх вершин начато в одной плоскости проекций, то все эти три проекции должны быть заданы в этой плоскости, а не отдельно: одна проекция точки на фронтальной, другая на горизонтальной, третья на профильной. Все три проекции точки должны быть в одной плоскости проекций. Это связано, как уже сообщалось, с тем что проецируются не точки или линии, а грани.
Рабочее окно, которое выводит графическую информацию, может быть только квадратным, по этому изменяя длину или ширину рабочего окна, нужно учитывать, что программа уравняет эти оба параметра.
3. Нереализованные возможности
Самый главный элемент, который необходимо реализовать — это полное отсутствие ошибок. На данном этапе разработки ошибок не выявлено, но гарантии их отсутствия не существует.
Затем следовало бы увеличить количество примитивов: добавить к грани и кубу, возможность создания призмы, многоугольника, цилиндра, сферы, диска. наличие этих фигур обеспечило бы лёгкость создания любых объёмных тел. Потому что, например призма, в основе которой правильный восьмиугольник содержит 32 треугольной грани. естественно пользователю вручную очень проблематично создать эту фигуру, поэтому её наличие в коллекции примитивов существенно облегчило бы ему работу.
Так же можно было бы разработать методы наложения разных текстур на трёхмерные объекты. Текстуры — это двухмерные графические изображения, которые, наложив как фотографию на грань объекта, превращают обыкновенный куб в телевизор, дом с окнами, стиральную машину и другие кубические объекты.
Текстуры бывают: аффинные, точные (перспективно — корректные), параболические. Текстуры можно накладывать на все трёхмерные объекты.
Можно было бы реализовать прозрачность трёхмерных объектов. например, если нужно было бы создать объёмное тело из стекла. В этом случае необходимо было бы использовать прозрачность. Трёхмерные объекты, как известно, создаются двухмерными точками. Поэтому чтобы создать прозрачность необходимо перед выводом каждой точки объекта в определённом месте получать цвет уже стоящей на этом месте точки, если она есть, затем вывести точку цвет которой будет средним между цветом точки, которая ставится и которая уже стоит. Таким образом, за выводимым трёхмерным объектом будет видно расположение другого объёмного тела. Это и есть прозрачность.
4. Основная форма
Основная форма содержит такие компоненты как:
Main
Menu
— Предназначен для добавления к главной программе главного меню. Является компонентом Standart. Компонент MainMenu не визуальный компонент. Редактор меню вызывается после выбора опции Items. В редакторе можно создавать пункты меню проименовывая их с помощь свойства Caption. Переходя от одной компоненте к другой можно с помощью того же свойства Caption отредактировать пункты меню. Чтобы вставить линию разделитель нужно в свойстве Caption первой позицией указать символ «-» (дефис). После окончания создания пунктов меню редактор меню надо закрыть.
BitBtn
— Этот компонент предназначен для создания кнопки с картинкой. В системе имеется набор готовых шаблонов.
После размещения объекта на форме изображение, помещаемое на кнопку, задаётся в свойстве Glyph (Значок). При этом вызывается редактор, с помощью которого выбирается нужная картинка (в формате BMP). Каждая такая картинка может состоять из 4 частей, равных по ширине. Первая часть — изображение кнопки в обычном состоянии, вторая — изображение «отключённой» кнопки, третья — изображение кнопки после щелчка мыши, изображение на нажатой кнопке. Число составных частей задаётся в свойстве NumGlyph (от 1 до 4). Расстояние от картинки до границ кнопки (в пикселях) можно указать в свойстве Margin. В свойстве Kind задаётся реакция кнопки на щелчок.
Button
— Компонент предназначен для создания кнопок на форме и обработки события нажатия кнопки. Расположен на панели Standart. имя кнопки указывается в поле Name, а выводимый текст на кнопке в свойстве Caption.
Combo
Box
— Компонент Поле со списком. Представляет собой вариант списка, с присоединённым дополнительным полем, в котором отображается выбранный элемент списка. Это же поле может использоваться для ввода новых элементов или для быстрого поиска элементов по начальным символам. Если на экране отображается только присоединённое поле («раскрывающийся список»), то для раскрытия списка можно использовать клавиатурную комбинацию Alt+Вниз.
Open
Dialog
— Компонент предназначен для выбора файла с целью последующего открытия; Свойства класса TOpenDialog приведены в табл. 1
Таблица 1 — Свойства класса TOpenDialog
Свойств
о
Назначен
и
е
DefaultExt
расширение имени, используемое по умолчанию. Добавляется в конец выбраного пользователем имени файла, если расширение не указано явно.
FileName
Выбранное пользователем имя файла вместе с полным путём поиска
Files
Список выбранных имён файлов. В свойстве Options должен быть включён флажок ofAllowMultiSelect
Filter
Набор масок, в соответствии с которыми отбираются имена файлов для отображения в диалоговом окне. Каждая маска состоит из двух частей: названия и шаблона, — разделённых символом |. Одному названию могут соответствовать несколько шаблонов. Маски отделяются друг от друга символом |
FilterIndex
Номер текущей маски. Нумерация начинается с 1
HistoryList
Список ранее выбранных файлов (тип Strings)
InitialDir
Текущий каталог, содержимое которого отображается при первом открытии диалогового окна
Options
Набор флажков, определяющих окна выбора файлов.
Title
Заголовок диалогового окна
Save
Dialog
— Этот компонент практически ничем не отличается от компонента OpenDialog за исключением нескольких настроек, специфичных для процесса сохранения файла.
Label
— Компонент находится на панэле Standart. Предназначен для вывода текста на форме с помощью свойства Text.
TDXTimer
— Компонент находится на панели DelphiX. В программах, выполняющих действия, связанные с моделированием или обработкой графики, предназначенных для общения с пользователем в реальном режиме времени или выполняющих продолжительные вычисления, необходима компонента TDXTimer.
С помощью компонента TDXTimer можно включить генерацию сообщений, поступающих от системного таймера Windows с заданной периодичностью (в миллисекундах) и выполнять определённую часть действий именно в обработчике этого события. Это одна из возможностей многозадачности приложений Windows.
TDXDraw
— Этот компонент находится на вкладке DelphiX. Он предназначен для вывода двухмерной и трёхмерной графики, используя DirectDraw и DirectX. Разрешение выводимой графики задаётся в свойстве Display. Вид курсора задаётся в свойстве Cursor. метод DXDraw.Surface.Fill(Color) — заполняет видеостраницу цветом Color. С помощью свойства DXDraw.Surface.pixels[x, y]:=Color — ставится точка на виртуальной видеостранице. С помощью метода DXDraw.Flip — невидимая видеостраница делается видимой. Схема обрисовки изображения на двух видеостраницах: видимой и не видимой делает возможной создать анимацию без мерцания изображения.
5. методы создания программы
Для создания трёхмерных объектов необходимо уметь преобразовывать трёхмерную графику в двухмерную. Ведь процедура вывода точки имеет только две координаты, задающие положение точки (x, y), следовательно, реально имеется дело с двухмерной графикой. Для начала следует принять систему трёхмерных координат:
Рисунок 3
здесь буквами x, y, z обозначены положительные направления осей Ox, Oy и Oz соответственно. Также предполагается, что камера неподвижна и находится в точке с координатами (0,0,-dist), ось зрения камеры направлена по оси Oz, а именно в точку (0,0,0) (т.е. camera target = (0,0,0)), ось Ox с точки зрения камеры направлена слева направо, ось Oy — снизу вверх, ось Oz — вглубь экрана. Размер экрана — xSize на ySize пикселей.
здесь и далее используются обозначения:
sx, sy
координаты проекции точки на экране
x, y, z
3D координаты точки,
Dist
расстояние от камеры (она находится в точке (0,0,-dist)) до начала координат,
Для созданя трёхмерных объектов необходимы примитивы. Такими примитивами являются элементарные трёхмерные объекты — треугольные грани. У каждой треугольной грани есть три вершины — три точки, имеющие трёхмерные координаты. Чтобы спроецировать их на плоскость, т. е. из трёхмерных координат x,y,z получить двухмерные sx, sy надо пользоваться формулой:
sx = xSize/2+x*dist/(z+dist); sy = ySize/2-y*dist/(z+dist);
5.1 Матричные преобразования
В этой главе кратко представлены элементарные понятия о матричных преобразованиях, являющихся составной частью линейной алгебры, и дискретной математики (см. раздел Группы).
Введем несколько терминов. n-мерный вектор, он же вектор размерности n, он же вектор размера n: упорядоченный набор n действительных чисел. Матрица размера m на n (будет обозначаться как m*n, mxn): таблица размера m на n, в каждой клетке которой — действительное число.
Вот пример матрицы 3×3:
[ 15 y*z 0.6 ]
[ 7 -3 91 ]
[ sin(x) 0.123 exp(t) ]
Вектор будем записывать в столбик и рассматривать его как матрицу размера n*1.
Операция скалярного произведения векторов: определена для двух векторов одинаковых размеров. Результат есть число, равное сумме произведений соответствующих элементов векторов. Пример:
[ 1 ] [ 4 ]
[ 2 ] * [ 5 ] = 1*4 + 2*5 + 3*6 = 32
[ 3 ] [ 6 ]
Операция векторного произведения: определена для (n-1) вектора одинакового размера n. Результат — вектор, причем, перпендикулярный всем множителям.
Замечание:
результат меняется от перестановки мест множителей!
Формально определяется как определитель матрицы, первая строка которой есть все базисные вектора, а все последующие — соответствующие координаты всех множителей. Поскольку она необходима только для 3D пространства, мы определим векторное произведение двух 3D векторов явно:
[ Ax ] [ Bx ] | i j k | [ Ay*Bz-Az*By ]
AxB = [ Ay ] x [ By ] = | Ax Ay Az | = [ Az*Bx-Ax*Bz ]
[ Az ] [ Bz ] | Bx By Bz | [ Ax*By-Ay*Bx ]
Операция сложения двух матриц: определена для матриц одинаковых размеров. Каждый элемент суммы (то есть, каждое число в таблице) равняется сумме соответствующих элементов слагаемых-матриц. Пример:
[ 1 x 500 ] [ 8 a 3 ] [ 9 a+x 503 ]
[ 2 y 600 ] + [ 9 b 2 ] = [ 11 b+y 602 ]
[ 3 z 700 ] [ 10 c 1 ] [ 13 c+z 701 ]
Операция умножения матрицы на число: определена для любой матрицы и любого числа; каждый элемент результата равняется произведению соответствующего элемента матрицы-множителя и числа-множителя.
Операция умножения двух матриц: определена для двух матриц таких размеров a*b и c*d, что b = c. например, если b = c, но a ¹ d, то при перестановке множителей операция будет вообще не определена. Результатом умножения матрицы A размером a*b на матрицу B размером b*d будет матрица C размером a*d, в которой элемент, стоящий в строке i и столбце j, равен произведению строки i матрицы A на столбец j матрицы B. Произведение строки на столбец определяется как сумма произведений соответствующих элементов строки и столбца. например, умножение строки на столбец (они должны быть равной длины, поэтому и такие ограничения на размеры матриц):
[ 4 ]
[ 1 2 3 ] * [ 5 ] = 1*4 + 2*5 + 3*6 = 32
[ 6 ]
А чтобы перемножить две матрицы, надо эту операцию проделать для каждого элемента. Вот пример:
[ 1 2 3 ] [ 0 3 ] [ 1*0+2*1+3*2 1*3+2*4+3*5 ]
[ 4 5 6 ] * [ 1 4 ] = [ 4*0+5*1+6*2 4*3+5*4+6*5 ] = …
[ 7 8 9 ] [ 2 5 ] [ 7*0+8*1+9*2 7*3+8*4+9*5 ]
Умножение и сложение матриц обладают почти тем же набором свойств, что и обычные числа, хотя некоторые привычные свойства не выполняются (например, A*B ¹ B*A); на самом деле требуется знать, что произведение вида A*B*C*D*… не зависит от того, как расставить скобки. Или, что
A*(B*C) = (A*B)*C.
Любое движение (то есть преобразование пространства, сохраняющее расстояние между точками) в трехмерном пространстве, согласно теореме Шаля, может быть представлено в виде суперпозиции поворота и параллельного переноса, то есть последовательного выполнения поворота и параллельного переноса. Поэтому основная часть информация о поведении объекта — это его смещение, ось поворота и угол поворота. Поэтому нам достаточно знать, как сделать два преобразования — перенос и поворот.
Перенос точки (точки будут также рассматриваться как вектора с началом в начале координат и концом в собственно точке) с координатами (x,y,z) на вектор (dx,dy,dz) делается простым сложением всех координат. То есть результат — это (x+dx,y+dy,z+dz). Подобно сложению вектора-точки с вектором-переносом.
Рассмотрим для примера поворот точки (x,y,z) относительно оси z. В этом случае z не меняется, а (x,y) меняются так же, как и при 2D повороте относительно начала координат.
Покажем координаты точки A’ — результата поворота A(x,y) на угол alpha (рис. 4).
Рисунок 4
Пусть r = sqrt(x*x+y*y). Пусть угол AOx равен phi, тогда из рисунка видно, что cos(phi) = x/r, sin(phi) = y/r. Угол A’OA равен по условию alpha. Отсюда
x’ = r*cos(alpha+phi) = r*(cos(alpha)*cos(phi)-sin(alpha)*sin(phi)) =
= (r*cos(phi))*cos(alpha)-(r*sin(phi))*sin(alpha) =
= x*cos(alpha)-y*sin(alpha)
y’ = r*sin(alpha+phi) = r*(cos(alpha)*sin(phi)+sin(alpha)*cos(phi)) =
= (r*cos(phi))*sin(alpha)+(r*sin(phi))*cos(alpha) =
= x*sin(alpha)+y*cos(alpha)
Для трехмерного случая, таким образом
x’ = x*cos(alpha)-y*sin(alpha)
y’ = x*sin(alpha)+y*cos(alpha)
z’ = z
Аналогичные формулы получатся и для других осей поворота (то есть Ox, Oy). Поворот относительно произвольной оси, проходящей через начало координат, можно сделать с помощью этих поворотов — сделать поворот относительно Ox так, чтобы ось поворота стала перпендикулярна Oy, затем поворот относительно Oy так, чтобы ось поворота совпала с Oz, сделать поворот, а затем обратные повороты относительно Oy и Ox.
Вспомним о матрицах и векторах и внимательно посмотрим на выведенные формулы для поворота. Можно заметить, что
[ x’ ] = [ cos(alpha) -sin(alpha) 0 ] [ x ]
[ y’ ] = [ sin(alpha) cos(alpha) 0 ] [ y ]
[ z’ ] = [ 0 0 1 ] [ z ]
То есть поворот на угол alpha задается одной и той же матрицей, и с помощью этой матрицы (умножая ее на вектор-точку) можно получить координаты повернутой точки. С одной стороны умножение матрицы на вектор требует больше операций, чем расчет x’ и y’ по формулам.
Нос другой удобство матриц для заключается как раз в свойстве
A*(B*C) = (A*B)*C. Пусть делается несколько поворотов подряд, например, пять (столько, сколько надо для поворота относительно произвольной оси), и пусть они задаются матрицами A, B, C, D, E (A — матрица самого первого поворота, E — последнего). Тогда для вектора p мы получаем
p’ = E*(D*(C*(B*(A*p)))) = E*D*C*B*A*p = (E*D*C*B*A)*p = (E*(D*(C*(B*A))))*p = T*p,
где T = (E*(D*(C*(B*A)))) матрица преобразования, являющегося комбинацией пяти поворотов. Посчитав один раз эту матрицу, можно в дальнейшем применить довольно сложное преобразование из пяти поворотов к любому вектору с помощью всего одного умножения матрицы на вектор.
Таким образом, можно задать любой поворот матрицей, и любая комбинация поворотов также будет задаваться матрицей, которую можно довольно легко посчитать. Но есть еще параллельный перенос и масштабирование.
На самом деле, эти преобразования тоже легко записываются в виде матриц. Только вместо матриц 3×3 и 3-мерных векторов используются так называемые однородные 4-мерные координаты и матрицы 4×4. При этом вместо векторов вида
[ x ]
[ y ]
[ z ]
используются вектора вида
[ x ]
[ y ]
[ z ]
[ 1 ]
а вместо произвольных матриц 3×3 используются матрицы 4×4 такого вида:
[ a b c d ]
[ e f g h ]
[ i j k l ]
[ 0 0 0 1 ]
Видно, что если d = h = l = 0, то в результате применения всех операций получается то же самое, что и для матриц 3×3.
Матрица параллельного переноса теперь определяется как
[ 1 0 0 dx ]
[ 0 1 0 dy ]
[ 0 0 1 dz ]
[ 0 0 0 1 ]
Матрицу масштабирования можно определить и для матриц 3×3, и для матриц 4×4:
[ kx 0 0 ] [ kx 0 0 0 ]
[ 0 ky 0 ] или [ 0 ky 0 0 ]
[ 0 0 kz ] [ 0 0 kz 0 ]
[ 0 0 0 1 ]
где kx, ky, kz — коэффициенты масштабирования по соответствующим осям.
Таким образом, получаем следующее. Любое нужное нам преобразование пространства можно задать матрицей 4×4 определенной структуры, разной для разных преобразований. Результат последовательного выполнений нескольких преобразований совпадает с результатом одного преобразования T, которое также задается матрицей 4×4, вычисляемой как произведение матриц всех этих преобразований. Важен порядок умножения, так как A*B ¹ B*A. Результат применения преобразования T к вектору [ x y z ] считается как результат умножения матрицы T на вектор [ x y z 1 ].
Докажем на примере, что A*B ¹ B*A. Пусть A — матрица переноса, B — поворота. Если сначала перенести объект, а потом повернем относительно центра координат (это будет B*A), то результат не будет соответствовать результату, при котором сначала объект поворачивают, а затем переносят (A*B).
5.2 Создание одноцветного треугольника
Изображение треугольника на экране — набор горизонтальных отрезков, причем из-за того, что треугольник — фигура выпуклая, каждой строке экрана соответствует не более одного отрезка. Поэтому достаточно обойти все строки экрана, с которыми пересекается треугольник, (то есть, от минимального до максимального значения (y) для вершин треугольника), и нарисовать соответствующие горизонтальные отрезки.
нужно отсортировать вершины так, чтобы вершина A была верхней, C — нижней, тогда min_y = A.y, max_y = C.y, и надо обойти все линии от min_y до max_y. Рассмотрим какую-то линию sy, A.y <= sy <= C.y. Если sy < B.y, то она пересекает стороны AB и AC; если sy >= B.y — то стороны BC и AC. Известны координаты всех вершин, поэтому можно написать уравнения сторон и найти пересечение нужной стороны с прямой y = sy. Получим два конца отрезка. Так как не известно, какой из них левый, а какой правый, нужно сравним их координаты по x и обменяем значения, если нужно. Рисуя этот отрезок, повторяя процедуру для каждой строки — получаем треугольник.
Рассматривая более подробно пересечения прямой
y = sy (текущей строки) и стороны треугольника, например AB, Получим уравнение прямой AB в форме x = k*y+b:
x = A.x+(y-A.y)*(B.x-A.x)/(B.y-A.y)
Теперь надо подставить известное для текущей прямой
x = A.x+(sy-A.y)*(B.x-A.x)/(B.y-A.y)
Для других сторон пересечение ищется совершенно точно так же. например:
// …
// здесь сортируем вершины (A,B,C)
// …
for sy = A.y to C.y begin
x1 = A.x + (sy — A.y) * (C.x — A.x) / (C.y — A.y);
if (sy < B.y)Then
x2 = A.x + (sy — A.y) * (B.x — A.x) / (B.y — A.y);
else
x2 = B.x + (sy — B.y) * (C.x — B.x) / (C.y — B.y);
if (x1 > x2) Then
begin
tmp = x1; x1 = x2; x2 = tmp;
end;
drawHorizontalLine(sy, x1, x2);
end;
Необходимо защититься от случая, когда B.y = C.y — в этом (и только этом, потому как если C.y = A.y, то треугольник пустой и рисовать его не нужно, или можно рисовать горизонтальную линию; а если B.y = A.y, то sy >= A.y и до деления на B.y — A.y не дойдет) случае произойдет попытка деления на ноль.
// …
// здесь сортируем вершины (A,B,C)
// …
for sy = A.y to C.y begin
x1 = A.x + (sy — A.y) * (C.x — A.x) / (C.y — A.y);
if (sy < B.y)
x2 = A.x + (sy — A.y) * (B.x — A.x) / (B.y — A.y);
else begin
if (C.y == B.y)
x2 = B.x;
else
x2 = B.x + (sy — B.y) * (C.x — B.x) / (C.y — B.y);
end;
if (x1 > x2)Then
begin
tmp = x1; x1 = x2; x2 = tmp;
drawHorizontalLine(sy, x1, x2);
end;
// …
здесь drawHorizontalLine(sy, x1, x2) — горизонтальная линия. Её создание не представляет сложности и код будетвыглядеть так.
//…
For i:=x1 to x2 do
PutPixel(i,sy,Color);
//…
6. программа
unit graph3;
interface
uses Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs,
DXClass, DXDraws, StdCtrls,graph, Menus,graph3D,figures, Buttons;
const Mode:Word=0;
type TLab = class(TForm)
Vid: TDXDraw;
Timer: TDXTimer;
Enter: TButton;
Menu: TMainMenu;
N1: TMenuItem;
N2: TMenuItem;
N3: TMenuItem;
N4: TMenuItem;
N5: TMenuItem;
N6: TMenuItem;
N7: TMenuItem;
N8: TMenuItem;
N9: TMenuItem;
OpenDialog: TOpenDialog;
SaveDialog: TSaveDialog;
N10: TMenuItem;
Space: TButton;
Box1: TComboBox;
Label1: TLabel;
OK: TButton;
Cancel: TButton;
Label6: TLabel;
BCube: TBitBtn;
BSide: TBitBtn;
Box6: TComboBox;
Label7: TLabel;
procedure FormCreate(Sender: TObject);
procedure TimerTimer(Sender: TObject; LagCount: Integer);
procedure VidMouseDown(Sender: TObject; Button: TMouseButton;
Shift: TShiftState; X, Y: Integer);
procedure EnterClick(Sender: TObject);
procedure N3Click(Sender: TObject);
procedure N4Click(Sender: TObject);
procedure N2Click(Sender: TObject);
procedure N9Click(Sender: TObject);
procedure N10Click(Sender: TObject);
procedure SpaceClick(Sender: TObject);
procedure FormKeyDown(Sender: TObject; var Key: Word;
Shift: TShiftState);
procedure FormKeyUp(Sender: TObject; var Key: Word;
Shift: TShiftState);
procedure CancelClick(Sender: TObject);
procedure OKClick(Sender: TObject);
procedure N8Click(Sender: TObject);
procedure BCubeClick(Sender: TObject);
procedure BSideClick(Sender: TObject);
procedure N6Click(Sender: TObject);
private
public
end;
var Lab: TLab;
implementation
{$R *.DFM}
const ScreenX:Word=640;
ScreenY:Word=480;
x1:integer=0;
y1:integer=0;
White=$FFFFFF;
View:Boolean=False;
Figure:Word=1;
Accept:Boolean=True;
sv:Word=0;
var c:char;S,SS,TMP:PPl;t:PTexture;
CS,CO,CC,CSc,CL:Word;Rot:TRot;
x0,y0,x2,y2:integer;r:Single;
Blue,Red,Yelow,M,N,SC:Word;
Keys:array[1..255]of Boolean;
Input:array[1..12]of procedure (x,y:integer;var E:Boolean);
Bol:Boolean;Option:array[1..5] of String;
o:array[1..MaxSide]of TPoint;
w:array[1..2]of TPoint;tmp_o:TPoint;
cx,cy:Word;oc:TPoint;ClicCub:array[0..MaxSide]of Boolean;
procedure LoadObject(Name:String;var Obj:PObj);
var f1:file of TPoint; a:TSides;
f2:file of Word;
S,Tmp:TPoint;i,j:word;Name2:String;
B:Boolean;
begin
For i:=1 to Length (Name)-4 do begin
Name2:=Name2+Name[i];
end;
Name:=Name2;
Assign(f2,Name+’.res’);
Reset(f2);
Read(f2,Obj^.Count);
close(f2);
Assign(f1,Name+’.dat’);
Reset(f1);
Read(f1,Obj^.o);
For i:=1 to Obj^.Count do
New(Obj^.Side[i],Create(Obj^.o.x,Obj^.o.y,Obj^.o.z,0,0,0,a));
For i:=1 to Obj^.Count do begin
inc(CS);
if(i mod 2<>0)Then
T:=ColorText(RGB(100+Random(155),100+Random(155),100+Random(155)),GMT)
else T:=T;
Obj^.Side[i]^.Texture:=T;
Obj^.Side[i]^.Mode:=Mode;
Obj^.Side[i]^.Alpha:=0;
Read(f1,S);
For j:=1 to 3 do
begin
Read(f1,S);
Obj^.Side[i]^.S[j]:=S;
SS^[i,j]:=S;
end;
end;
close(f1);
end;
procedure SaveObject(Name:String;var Obj:PObj);
var f1:file of TPoint;
f2:file of Word;B:Boolean;
S:TPoint;i,j:word;Name2:String;
begin
B:=False;
For i:=1 to Length(Name) do
if(Name[i]=’.’)Then B:=True;
if(B=True)Then begin
For i:=1 to Length(Name)-4 do begin
Name2:=Name2+Name[i];
end;
Name:=Name2;
end;
Assign(f2,Name+’.res’);
Rewrite(f2);
Reset(f2);
Write(f2,Obj^.Count);
close(f2);
Assign(f1,Name+’.dat’);
Rewrite(f1);
Reset(f1);
Write(f1,Obj^.o);
For i:=1 to Obj^.Count do begin
Obj^.Side[i]^.o:=tmp_o;
S:=Obj^.Side[i]^.o;
Write(f1,S);
For j:=1 to 3 do
begin
S:=Obj^.Side[i]^.S[j];
Write(f1,S);
end;
end;
close(f1);
end;
procedure CreateTMP(var Obj:PObj);
var i,j,k:Word;
begin
Obj^.o.x:=0;Obj^.o.y:=0;Obj^.o.z:=100;
For i:=1 to Obj^.Count do
begin
tmp_o:=Obj^.Side[i]^.o;
Obj^.Side[i]^.o:=o[i];
For j:=1 to 3 do
Obj^.Side[i]^.S[j]:=S^[i,j];
end;
end;
procedure ShowSide;
var i,j,k,cx,cy:Word;tmp:TPoint;
begin
Lab.Vid.Surface.Fill(0);
myForm(GetMaxX,GetMaxY);
For i:=1 to CS do begin
Line(Trunc(SS^[i,1].x),Trunc(SS^[i,1].y),Trunc(SS^[i,2].x),Trunc(SS^[i,2].y),White);
Line(Trunc(SS^[i,2].x),Trunc(SS^[i,2].y),Trunc(SS^[i,3].x),Trunc(SS^[i,3].y),White);
Line(Trunc(SS^[i,3].x),Trunc(SS^[i,3].y),Trunc(SS^[i,1].x),Trunc(SS^[i,1].y),White);
Line(Trunc(SS^[i,1].x),Trunc(SS^[i,1].z),Trunc(SS^[i,2].x),Trunc(SS^[i,2].z),White);
Line(Trunc(SS^[i,2].x),Trunc(SS^[i,2].z),Trunc(SS^[i,3].x),Trunc(SS^[i,3].z),White);
Line(Trunc(SS^[i,3].x),Trunc(SS^[i,3].z),Trunc(SS^[i,1].x),Trunc(SS^[i,1].z),White);
Line(Trunc(SS^[i,1].z*r),Trunc(SS^[i,1].y),Trunc(SS^[i,2].z*r),Trunc(SS^[i,2].y),White);
Line(Trunc(SS^[i,2].z*r),Trunc(SS^[i,2].y),Trunc(SS^[i,3].z*r),Trunc(SS^[i,3].y),White);
Line(Trunc(SS^[i,3].z*r),Trunc(SS^[i,3].y),Trunc(SS^[i,1].z*r),Trunc(SS^[i,1].y),White);
if(ClicCub[i]=True)Then begin
Line(Trunc(SS^[i,1].z),Trunc(SS^[i,1].y),Trunc(SS^[i,2].z),Trunc(SS^[i,1].y),White);
Line(Trunc(SS^[i,2].z),Trunc(SS^[i,1].y),Trunc(SS^[i,1].z),Trunc(SS^[i,3].y),White);
end;
end;
S[CS]:=SS[CS];
cx:=GMX div 2;
cy:=GMY div 2+1;
For i:=1 to 3 do
begin
S^[CS,i].x:=S^[CS,i].x-cx+dF;
S^[CS,i].y:=cy-S^[CS,i].y-dF;
S^[CS,i].z:=S^[CS,i].z-cx-dF;
end;
end;
procedure CreateSide(var Obj:PObj);
var cx,cy,i,j,k:Word;
begin
r:=1;
For i:=1 to Obj^.Count do
S[i]:=Obj^.Side[i]^.S;
TMP:=S;
Lab.Vid.Surface.Fill(0);
myForm(GetMaxX,GetMaxY);
cx:=GMX div 2;
cy:=GMY div 2+1;
For j:=1 to CS do
For i:=1 to 3 do
begin
TMP^[j,i].x:=TMP^[j,i].x-cx+dF;
TMP^[j,i].y:=cy-TMP^[j,i].y-dF;
TMP^[j,i].z:=TMP^[j,i].z-cx-dF;
end;
For i:=1 to CS do begin
Line(Trunc(TMP^[i,1].x),Trunc(TMP^[i,1].y),Trunc(TMP^[i,2].x),Trunc(TMP^[i,2].y),White);
Line(Trunc(TMP^[i,2].x),Trunc(TMP^[i,2].y),Trunc(TMP^[i,3].x),Trunc(TMP^[i,3].y),White);
Line(Trunc(TMP^[i,3].x),Trunc(TMP^[i,3].y),Trunc(TMP^[i,1].x),Trunc(TMP^[i,1].y),White);
Line(Trunc(TMP^[i,1].x),Trunc(TMP^[i,1].z),Trunc(TMP^[i,2].x),Trunc(TMP^[i,2].z),White);
Line(Trunc(TMP^[i,2].x),Trunc(TMP^[i,2].z),Trunc(TMP^[i,3].x),Trunc(TMP^[i,3].z),White);
Line(Trunc(TMP^[i,3].x),Trunc(TMP^[i,3].z),Trunc(TMP^[i,1].x),Trunc(TMP^[i,1].z),White);
Line(Trunc(TMP^[i,1].z*r),Trunc(TMP^[i,1].y),Trunc(TMP^[i,2].z*r),Trunc(TMP^[i,2].y),White);
Line(Trunc(TMP^[i,2].z*r),Trunc(TMP^[i,2].y),Trunc(TMP^[i,3].z*r),Trunc(TMP^[i,3].y),White);
Line(Trunc(TMP^[i,3].z*r),Trunc(TMP^[i,3].y),Trunc(TMP^[i,1].z*r),Trunc(TMP^[i,1].y),White);
end;
Flip(Lab.Vid);
end;
procedure LoadSide(var Obj:PObj);
var i,j,k:Word;TMP:PPL;
begin
r:=1;
For i:=1 to Obj^.Count do
S[i]:=Obj^.Side[i]^.S;
SS^:=S^;
Lab.Vid.Surface.Fill(0);
myForm(GetMaxX,GetMaxY);
For j:=1 to CS do
For i:=1 to 3 do
begin
SS^[j,i].x:=SS^[j,i].x+cx-dF;
SS^[j,i].y:=cy-SS^[j,i].y-dF;
SS^[j,i].z:=SS^[j,i].z+cx+dF;
end;
For i:=1 to CS do begin
Line(Trunc(SS^[i,1].x),Trunc(SS^[i,1].y),Trunc(SS^[i,2].x),Trunc(SS^[i,2].y),White);
Line(Trunc(SS^[i,2].x),Trunc(SS^[i,2].y),Trunc(SS^[i,3].x),Trunc(SS^[i,3].y),White);
Line(Trunc(SS^[i,3].x),Trunc(SS^[i,3].y),Trunc(SS^[i,1].x),Trunc(SS^[i,1].y),White);
Line(Trunc(SS^[i,1].x),Trunc(SS^[i,1].z),Trunc(SS^[i,2].x),Trunc(SS^[i,2].z),White);
Line(Trunc(SS^[i,2].x),Trunc(SS^[i,2].z),Trunc(SS^[i,3].x),Trunc(SS^[i,3].z),White);
Line(Trunc(SS^[i,3].x),Trunc(SS^[i,3].z),Trunc(SS^[i,1].x),Trunc(SS^[i,1].z),White);
Line(Trunc(SS^[i,1].z*r),Trunc(SS^[i,1].y),Trunc(SS^[i,2].z*r),Trunc(SS^[i,2].y),White);
Line(Trunc(SS^[i,2].z*r),Trunc(SS^[i,2].y),Trunc(SS^[i,3].z*r),Trunc(SS^[i,3].y),White);
Line(Trunc(SS^[i,3].z*r),Trunc(SS^[i,3].y),Trunc(SS^[i,1].z*r),Trunc(SS^[i,1].y),White);
end;
Flip(Lab.Vid);
end;
procedure InPut1(x,y:integer;var E:Boolean);
var i,j:integer;B:Boolean;
begin
if(sv=0)Then begin
o[CS].x:=0;
o[CS].y:=0;
o[CS].z:=0;
end;
if(M<sv)Then begin
PutPixel(x,y,Yelow);
Flip(Lab.Vid);
if(x<cx)and(y<cy)Then
begin
o[CS].x:=x-cx+dF;
o[CS].y:=cy-y-dF;
end
else if(x>cx)and(y<cy)Then
begin
o[CS].z:=x-cx-dF;
o[CS].y:=cy-y-dF;
end
else if(x<cx)and(y>cy)Then
begin
o[CS].x:=x-cx+dF;
o[CS].z:=x-cx-dF;
end
else begin
ShowMessage(‘Эта четверть не является плоскостью проекций!’);
E:=True;
end;
inc(M);
end
else
if (M<6+sv)Then begin
if(N>3)Then N:=0;
if(x>cx)and(y>cy)Then
else
if(N=0)Then begin
x0:=x;y0:=y;x1:=0;y1:=0;
N:=1;
end;
if(x<cx)and(y<cy)Then
begin
SS^[CS,N].x:=x;
SS^[CS,N].y:=y;
r:=1;
end
else if(x>cx)and(y<cy)Then
begin
SS^[CS,N].z:=x;
SS^[CS,N].y:=y;
r:=1;
end
else if(x<cx)and(y>cy)Then
begin
SS^[CS,N].x:=x;
SS^[CS,N].z:=y;
r:=GetMaxX/GetMaxY;
end
else begin
ShowMessage(‘Эта четверть не является плоскостью проекций!’);
E:=True;
end;
if(E=False)Then begin
x2:=X;y2:=Y;
if(x1<>0)and(y1<>0)Then begin
Line(x0,y0,x2,y2,Red);
Line(x1,y1,x2,y2,Red);
end
else putpixel(x2,y2,Red);
Flip(Lab.Vid);
x1:=x2;y1:=y2;
For i:=1 to CS-1 do
For j:=1 to 3 do
begin
if(SS^[CS,N].x+o[CS].x>=SS^[i,j].x+o[i].x-2)and(SS^[CS,N].x+o[CS].x<=SS^[i,j].x+o[i].x+2)
and(SS^[CS,N].y+o[CS].y>=SS^[i,j].y+o[i].y-2)and(SS^[CS,N].y+o[CS].y<=SS^[i,j].y+o[i].y+2)
and(SS^[CS,N].z+o[CS].z>=SS^[i,j].z+o[i].z-2)and(SS^[CS,N].z+o[CS].z<=SS^[i,j].z+o[i].z+2)Then Accept:=True;
end;
inc(N);
inc(M);
end;
end;
if(M=6+sv)and(Accept=True)Then begin
M:=0;N:=0;
ClicCub[CS]:=False;
ShowSide;
Flip(Lab.Vid);
New(Scene^.Camera[CC]^.Obj[CO]^.Side[CS],Create(0,0,100,o[CS].x,o[CS].y,o[CS].z,S^[CS]));
if(CS mod 2<>0)Then
T:=ColorText(RGB(100+Random(155),100+Random(155),100+Random(155)),GMT)
else T:=T;
Scene^.Camera[CC]^.Obj[CO]^.Side[CS]^.Texture:=T;
Scene^.Camera[CC]^.Obj[CO]^.Side[CS]^.Mode:=Mode;
Scene^.Camera[CC]^.Obj[CO]^.Side[CS]^.Alpha:=0;
Accept:=False;
inc(CS);
end
else if(M=6+sv)and(Accept=False)Then begin
SS[CS]:=SS[CS-1];
SS[CS]:=SS[CS-1];
SS[CS]:=SS[CS-1];
M:=0;N:=0;
ShowSide;
Flip(Lab.Vid);
end;
end;
procedure InPut2(x,y:integer;var E:Boolean);
var i,j:integer;o:TPoint;Party:Single;tmp:Single;
begin
if(N<sv)Then begin
if(x<cx)and(y<cy)Then
begin
oc.x:=x-cx+dF;
oc.y:=cy-y-dF;
end
else if(x>cx)and(y<cy)Then
begin
oc.z:=x-cx-dF;
oc.y:=cy-y-dF;
end
else if(x<cx)and(y>cy)Then
begin
oc.x:=x-cx+dF;
oc.z:=x-cx-dF;
end
else begin
ShowMessage(‘Эта четверть не является плоскостью проекций!’);
E:=True;
end;
if(E=False)Then begin
PutPixel(x,y,Yelow);
Flip(Lab.Vid);
inc(N);
end;
end
else
if(N<2+sv)and(N>sv-1)Then begin
if(N=sv)Then begin
x0:=x;y0:=y;
end;
x1:=x;y1:=y;
Line(x0,y0,x1,y1,Red);
Flip(Lab.Vid);
x0:=x;y0:=y;
inc(N);
if(x>cx)and(y>cy)Then begin
ShowMessage(‘Эта четверть не является плоскостью проекций!’);
E:=True;
end
else begin
w[N-sv].x:=x;
w[N-sv].y:=y;
end;
end;
if(N=2+sv)Then begin
if(abs(w[N-sv].x-w[N-sv-1].x)/2<=abs(w[N-sv].y-w[N-sv-1].y)/2)Then Party:=abs(w[N-sv].x-w[N-sv-1].x)
else Party:=abs(w[N-sv].y-w[N-sv-1].y);
if(w[N-sv].x<cx)and(w[N-sv].y<cy)Then begin
o.x:=(w[N-sv].x+w[N-sv-1].x)/2;
o.y:=(w[N-sv].y+w[N-sv-1].y)/2;
o.z:=cx+Party/2+dF;
end
else if(w[N-sv].x>cx)and(w[N-sv-1].y<cy)Then begin
o.x:=cx-Party/2-dF;
o.y:=(w[N-sv].y+w[N-sv-1].y)/2;
o.z:=(w[N-sv].x+w[N-sv-1].x)/2;
end
else if(w[N-sv].x<cx)and(w[N-sv].y>cy)Then begin
o.x:=(w[N-sv].x+w[N-sv-1].x)/2;
o.y:=cy-Party/2-dF;
o.z:=(w[N-sv].y+w[N-sv-1].y)/2;
end;
r:=1;
SS^[CS,1].x:=o.x-Party/2;SS^[CS,1].y:=o.y+Party/2;SS^[CS,1].z:=o.z+Party/2;
SS^[CS,2].x:=o.x+Party/2;SS^[CS,2].y:=o.y+Party/2;SS^[CS,2].z:=o.z+Party/2;
SS^[CS,3].x:=o.x-Party/2;SS^[CS,3].y:=o.y-Party/2;SS^[CS,3].z:=o.z-Party/2;
SS^[CS+1,1].x:=o.x+Party/2;SS^[CS+1,1].y:=o.y-Party/2;SS^[CS+1,1].z:=o.z-Party/2;
SS^[CS+1,2].x:=o.x+Party/2;SS^[CS+1,2].y:=o.y+Party/2;SS^[CS+1,2].z:=o.z+Party/2;
SS^[CS+1,3].x:=o.x+Party/2;SS^[CS+1,3].y:=o.y+Party/2;SS^[CS+1,3].z:=o.z-Party/2;
SS^[CS+2,1].x:=o.x-Party/2;SS^[CS+2,1].y:=o.y+Party/2;SS^[CS+2,1].z:=o.z+Party/2;
SS^[CS+2,2].x:=o.x-Party/2;SS^[CS+2,2].y:=o.y-Party/2;SS^[CS+2,2].z:=o.z-Party/2;
SS^[CS+2,3]:=SS^[CS+1,1];
o.x:=o.x-cx+dF;
o.y:=cy-o.y-dF;
o.z:=o.z-cx-dF;
Scene^.Camera[CC]^.Obj[CO]^.Done;
Scene^.Camera[CC]^.Done;
Scene^.Done;
New(Cube,Create(0,0,100,o.x,o.y,o.z,Party,T,Mode,0));
Scene^.Camera[CC]^.ADD(CO,CO+1);
For i:=1 to Scene^.Camera[CC]^.Obj[CO]^.Count do
begin
For j:=1 to 3 do
S[i,j]:=Scene^.Camera[CC]^.Obj[CO]^.Side[i]^.S[j];
if(i mod 2<>0)Then
T:=ColorText(RGB(100+Random(155),100+Random(155),100+Random(155)),GMT)
else T:=T;
Scene^.Camera[CC]^.Obj[CO]^.Side[i]^.Texture:=T;
Scene^.Camera[CC]^.Obj[CO]^.Side[i]^.Mode:=Mode;
end;
For i:=CS to CS+12 do
ClicCub[i]:=True;
CS:=CS+12;
N:=0;
Scene^.Camera[CC]^.Obj[CO]^.Done;
Scene^.Camera[CC]^.Done;
Scene^.Done;
ShowSide;
Flip(Lab.Vid);
end;
end;
procedure Data;
var i,j:Word;
begin
New(S);
New(SS);
GetMaxX:=Lab.Vid.Width-1;
GetMaxY:=Lab.Vid.Height-1;
M:=0;N:=0;Rot:=YRot;Bol:=False;
GMX:=Lab.Vid.Width;
GMY:=Lab.Vid.Height;
CSc:=1;CC:=1;CL:=1;CO:=1;CS:=1;
cx:=GMX div 2;
cy:=GMY div 2+1;
Blue:=RGB(255,0,0);
Red:=RGB(200,200,200);
Yelow:=RGB(0,255,0);
New(Scene,Create);
New(Scene^.Camera[CC],Create(0,0,0,0,0,0));
New(Light[CL],Create(-100,-100,0));
New(Scene^.Camera[CC]^.Obj[CO],Create(0,0,0,0,0,0));
Accept:=True;View:=False;
end;
procedure TLab.FormCreate(Sender: TObject);
var i,j:Word;
begin
Tables;
@InPut[1]:=@InPut1;
@InPut[2]:=@InPut2;
New(t);
For i:=0 to GMT do
New(t^[i]);
For i:=0 to GMT do
For j:=0 to GMT do
t^[i]^[j]:=RGB(255,0,0);
Data;
end;
procedure TLab.TimerTimer(Sender: TObject; LagCount: Integer);
var i:Word;
begin
if(Keys[VK_Escape])Then halt;
if(Bol=False)and(Keys[VK_F9])and(CountSide>0)Then begin
if(CountSide=0)Then begin
ShowMessage(‘Для просмотра нужна хотя бы одна грань’);
exit;
end;
Bol:=True;View:=True;
Scene^.Camera[CC]^.Obj[CO]^.Done;
Scene^.Camera[CC]^.Done;
Scene^.Done;
CreateTMP(Scene^.Camera[CC]^.Obj[CO]);
For i:=1 to Scene^.Camera[CC]^.Obj[CO]^.Count do
Scene^.Camera[CC]^.Obj[CO]^.Side[i]^.o:=tmp_o;
Vid.Surface.Fill(0);
Scene^.Camera[CC]^.Obj[CO]^.Draw;
Flip(Vid);
end
else if(Bol)Then
if(Keys[ord(‘Y’)])Then
begin
Rot:=YRot;
Vid.Surface.Fill(0);
Scene^.Camera[CC]^.Obj[CO]^.Rotate(CosA,SinA,Angle,Rot);
Scene^.Camera[CC]^.Obj[CO]^.Draw;
Flip(Vid);
end
else if(Keys[Ord(‘X’)])Then
begin
Rot:=XRot;
Vid.Surface.Fill(0);
Scene^.Camera[CC]^.Obj[CO]^.Rotate(CosA,SinA,Angle,Rot);
Scene^.Camera[CC]^.Obj[CO]^.Draw;
Flip(Vid);
end
else if(Keys[Ord(‘Z’)])Then
begin
Rot:=ZRot;
Vid.Surface.Fill(0);
Scene^.Camera[CC]^.Obj[CO]^.Rotate(CosA,SinA,Angle,Rot);
Scene^.Camera[CC]^.Obj[CO]^.Draw;
Flip(Vid);
end
else if(Keys[VK_Space])Then
begin
if(Space.Visible=False)Then begin
Space.Visible:=True;
Enter.Visible:=True;
BSide.Visible:=True;
BCube.Visible:=True;
Menu.Items[0].Visible:=True;
Menu.Items[1].Visible:=True;
Menu.Items[2].Visible:=True;
Width:=455;
Height:=465;
Left:=108;
Top:=-7;
Vid.Width:=385;
Vid.Height:=385;
Vid.Top:=32;
Vid.Left:=32;
GMX:=Lab.Vid.Width;
GMY:=Lab.Vid.Height;
GetMaxX:=GMX-1;
GetMaxY:=GMY-1;
end;
M:=0;N:=0;Bol:=False;
View:=False;
ShowSide;
Flip(Vid);
end;
end;
procedure TLab.VidMouseDown(Sender: TObject; Button: TMouseButton;
Shift: TShiftState; X, Y: Integer);
var E:Boolean;
begin
E:=False;
if(View=False)Then InPut[Figure](x,y,E);
if(E=True)Then exit;
end;
procedure TLab.EnterClick(Sender: TObject);
var i:Word;
begin
if(CountSide=0)Then begin
ShowMessage(‘Для просмотра нужна хотя бы одна грань’);
exit;
end;
Bol:=True;View:=True;
Scene^.Camera[CC]^.Obj[CO]^.Done;
Scene^.Camera[CC]^.Done;
Scene^.Done;
CreateTMP(Scene^.Camera[CC]^.Obj[CO]);
For i:=1 to Scene^.Camera[CC]^.Obj[CO]^.Count do
Scene^.Camera[CC]^.Obj[CO]^.Side[i]^.o:=tmp_o;
Vid.Surface.Fill(0);
Scene^.Camera[CC]^.Obj[CO]^.Draw;
Flip(Vid);
end;
procedure TLab.N3Click(Sender: TObject);
var i,j:integer;Name:String;
begin
OpenDialog.Execute;
Name:=OpenDialog.FileName;
Lab.Vid.Surface.Fill(0);
Data;
myform(GetMaxX,GetMaxY);
Flip(Lab.Vid);
if(Name=»)Then exit;
LoadObject(Name,Scene^.Camera[CC]^.Obj[CO]);
LoadSide(Scene^.Camera[CC]^.Obj[CO]);
end;
procedure TLab.N4Click(Sender: TObject);
var Name:String;
begin
SaveDialog.Execute;
Name:=SaveDialog.FileName;
if(Name=»)Then exit;
CreateTMP(Scene^.Camera[CC]^.Obj[CO]);
SaveObject(Name,Scene^.Camera[CC]^.Obj[CO]);
end;
procedure TLab.N2Click(Sender: TObject);
begin
Flip(Lab.Vid);
if(Lab.Width>Lab.Height)Then Lab.Height:=Lab.Width
else Lab.Width:=Lab.Height;
Vid.Height:=Lab.Height-81;
Vid.Width:=Lab.Width-81;
Lab.Vid.Surface.Fill(0);
Data;
myform(GetMaxX,GetMaxY);
Flip(Lab.Vid);
end;
procedure TLab.N9Click(Sender: TObject);
begin
ShowMessage(‘Autocad version 1.2 Copiright by Anton Sazonov’);
end;
procedure TLab.N10Click(Sender: TObject);
begin
Halt;
end;
procedure TLab.SpaceClick(Sender: TObject);
var i:Word;
begin
M:=0;N:=0;Bol:=False;
View:=False;
ShowSide;
Flip(Vid);
end;
procedure TLab.FormKeyDown(Sender: TObject; var Key: Word;
Shift: TShiftState);
begin
Keys[Key]:=True;
end;
procedure TLab.FormKeyUp(Sender: TObject; var Key: Word;
Shift: TShiftState);
begin
Keys[Key]:=False;
end;
procedure TLab.CancelClick(Sender: TObject);
begin
Halt;
end;
procedure TLab.OKClick(Sender: TObject);
begin
Option[1]:=Box1.Text;
if(Option[1]=»)Then begin
ShowMessage(‘Выберитережим!’);
Exit;
end
else if(Option[1]=’4 bits’)Then PixMode:=1
else if(Option[1]=’8 bits’)Then PixMode:=2
else if(Option[1]=’16 bits’)Then PixMode:=3
else if(Option[1]=’24 bits’)Then PixMode:=4
else if(Option[1]=’32 bits’)Then PixMode:=5;
Option[2]:=Box6.Text;
if(Option[2]=»)Then begin
ShowMessage(‘Выберите разрешение!’);
Exit;
end
else if(Option[2]=’640X480′)Then begin
ScreenX:=640;ScreenY:=480;
end
else if(Option[2]=’800X600′)Then begin
ScreenX:=800;ScreenY:=600;
end
else if(Option[2]=’1024X768′)Then begin
ScreenX:=1024;ScreenY:=768;
end
else if(Option[2]=’1280X1024′)Then begin
ScreenX:=1280;ScreenY:=1024;
end;
Surf:=Vid.Surface;
SetGraphMode(PixMode);
Label1.Destroy;
Label6.Destroy;
Label7.Destroy;
Box1.Destroy;
Box6.Destroy;
Cancel.Destroy;
OK.Visible:=False;
Lab.Height:=465;
Lab.Top:=-7;
Menu.Items.Visible:=True;
Vid.Visible:=True;
Vid.Width:=385;
Vid.Height:=385;
Space.Visible:=True;
Enter.Visible:=True;
Lab.Vid.Surface.Fill(0);
myform(GetMaxX,GetMaxY);
Flip(Lab.Vid);
if(Lab.Width>Lab.Height)Then Lab.Height:=Lab.Width
else Lab.Width:=Lab.Height;
Vid.Height:=Lab.Height-81;
Vid.Width:=Lab.Width-81;
Lab.Vid.Surface.Fill(0);
Data;
myform(GetMaxX,GetMaxY);
Flip(Lab.Vid);
BCube.Visible:=True;
BSide.Visible:=True;
Menu.Items[0].Visible:=True;
Menu.Items[1].Visible:=True;
Menu.Items[2].Visible:=True;
OK.Destroy;
end;
procedure TLab.N8Click(Sender: TObject);
begin
Application.HelpContext(10);
end;
procedure TLab.BCubeClick(Sender: TObject);
begin
Figure:=2;
ClicCub[CS]:=True;
end;
procedure TLab.BSideClick(Sender: TObject);
begin
Figure:=1;
end;
procedure TLab.N6Click(Sender: TObject);
begin
if (View=True)Then begin
Space.Visible:=False;
Enter.Visible:=False;
BSide.Visible:=False;
BCube.Visible:=False;
Menu.Items[0].Visible:=False;
Menu.Items[1].Visible:=False;
Menu.Items[2].Visible:=False;
Width:=ScreenX+10;
Height:=ScreenY+20;
Left:=-10;
Top:=-30;
Vid.Width:=ScreenX+1;
Vid.Height:=ScreenY-16;
Vid.Top:=0;
Vid.Left:=0;
GMX:=ScreenX;GMY:=ScreenY;
GetMaxX:=GMX-1;GetMaxY:=GMY-1;
Scene^.Camera[CC]^.Obj[CO]^.Draw;
Flip(Vid);
end;
end;
end.
ЗАКЛЮЧЕНИЕ
Программирование с использованием трёхмерной графики — это способ описания языком программирования объёмных тел и отображения их на дисплее.
Измерение данной графики совпадает с измерением реальной системы, находящейся в пространстве, в котором ориентируется человек, и по этому любое материальное тело можно виртуально создать, задать ему условия и посмотреть на реакцию этого тела, задать телу правила поведения (траекторию движения) и узнать как оно будет себя вести, где будет находиться с течением времени.
Например, можно создать программу, создающую чертежи с использованием гостов и чертёжных обозначений. Она необходима конструкторам. Примером такой программы является автокод.
Можно видоизменить данную программу таким образом, чтобы она, виртуально создавала дом и сообщала какие нагрузки, он будет испытывать и не деформируется ли он при различных природных явлениях. Эта программа необходима архитекторам.
Ещё трёхмерную графику можно применить для создания механизмов, которые между собой взаимодействуют, показать какие силы при этом участвуют, и показать их взаимодействие с разных сторон. Такая программа нужна инженерам-механикам. Эти программы могут использовать как ортогональное (параллельное), так и центральное проецирование (проецирование с учётом перспективы).
программа, создающая трёхмерную анимацию (фильм, мультфильм), так же может быть реализована на компьютере. Эта программа должна использовать только центральное проецирование (перспективное), и желательно наличие некоторых спецэффектов: прозрачности, освещённости, билинейной фильтрации текстур и т.д.
Трёхмерная графика необходима везде, где производятся материальные объекты, где есть инженеры, конструкторы, архитекторы, просто квалифицированные рабочие, а именно: в самолётостроении, в машиностроении, в судостроении, в строительстве, в космической промышленности и т. д.
С трёхмерной графикой, так или иначе, приходится сталкиваться дизайнерам одежды, дизайнерам Интернет-сайтов, любым другим дизайнерам, работникам отделов рекламы, продюсерам и т. д.
Трёхмерной графикой пользовались всегда. До объёмного изображения на компьютере чертили чертежи на бумаге, до чертежей рисовали эскизы или рисунки, до рисунков пользовались заданием объектов аналитически на бумаге или в уме.
Если дать оценку трёхмерной графики в материальном производстве, то она будет следующей: трёхмерная графика, как способ изображения объёмных фигур или тел, является самым наглядным методом представления информации, используемой в материальном производстве. Без трёхмерной графики не было бы налажено любое материальное Производство. Самый удобный способ задания и использования трёхмерной или объёмной графики осуществляется с помощью информационных технологий, а именно компьютера.
СПИСОК ЛИТЕРАТУРЫ
1 С. Бобровский «Delphi 5». 2007
2 О.Е. Акимов «Дискретная математика, http://wpgrabbestrefedu.loc/2019/02/14/utchebnaya-rabota-referat-arm-konetchnogo-polyzovaniya-ispolyzovanie-ms-office/
1. Теоретическая часть. 3
Автоматическое рабочее пространство конечного юзера. 3
2. Практическая часть. 6
2.1 Внедрение программки MSWord 2003. 6
2.2 Внедрение программки MSExcel 2003. 7
2.3 Внедрение программки MSAccess 2003. 11
1. Теоретическая часть
Автоматическое рабочее пространство конечного юзера
Внедрение разных технологий на рабочем месте, децентрализация управления тянет за собой децентрализованную обработку инфы и децентрализованное применение средств вычислительной техники конкретно на рабочем месте.
В таковых системах автоматическое рабочее пространство является основным инвентарем общения человека с вычислительных системах, применяемый для контроля производственно-хозяйственной деятель, конфигурации значений отдельных данных в автоматических информационных системах для решения текущих задач и анализа функций управления.
Автоматическое рабочее пространство (АРМ) можно найти как совокупа информационно-программно-технических ресурсов, обеспечивающую конечному юзеру обработку данных и автоматизацию управленческой предметной области.
Принципы сотворения АРМ:
1. Наибольшая ориентация на конечного юзера, достигаемая созданием инструментальных средств адаптации АРМ к уровню подготовки юзера, способностей его обучения и самообучения.
2. Формализация проф познаний, другими словами возможность предоставления при помощи АРМ без помощи других заавтоматизировать новейшие функции и решать новейшие задачки в процессе скопления опыта работы с системой.
3. Проблемная ориентация АРМ на решение определенного класса задач, объединенных общей технологией обработки инфы, единством режимов работы и эксплуатации, что типично для профессионалов экономических служб.
4. Модульность построения, обеспечивающая сопряжение АРМ с иными элементами системы обработки инфы, также модификацию и наращивание способностей АРМ без прерывания его функционирования.
5. Эргономичность, другими словами создание для юзера удобных критерий труда и дружеского интерфейса общения с системой.
Эффективность АРМ следует разглядывать как интегральный показатель уровня реализации приведенных выше принципов, отнесенного к затратам по созданию и эксплуатации системы.
Функционирование АРМ может отдать эффект лишь при условии правильного распределения функций и перегрузки меж человеком и машинными средствами обработки инфы, ядром которых является ЭВМ .
Только тогда АРМ станет средством увеличения не только лишь производительности труда и эффективности управления, да и социальной комфортности профессионалов.
В базу систематизации АРМ быть может положен ряд классификационных признаков.
С учетом областей внедрения вероятна систематизация АРМ по многофункциональному признаку:
1. АРМ административно — управленческого персонала;
2. АРМ проектировщика радиоэлектронной аппаратуры, автоматических систем управления и т.д.
3. АРМ спеца в области экономики, арифметики, физики, и т. д.
4. АРМ производственно — технологического предназначения.
Принципиальным классификационным признаком АРМ является режим его эксплуатации, по которому выделяются: одиночный, групповой и сетевой режимы эксплуатации.
Одним из подходов к систематизации АРМ является их классификация по видам решаемых задач. Вероятны последующие группы АРМ:
1. Для решения информационно-вычислительных задач;
2. Для решения задач подготовки и ввода данных;
3. Для решения информационно-справочных задач;
4. Для решения задач бухгалтерского учета;
5. Для решения задач статистической обработки данных,
6. Для решения задач аналитических расчетов.
Обоснованное отнесения АРМ к определенной группе будет содействовать наиболее глубочайшему и кропотливому анализу, способности сравнительной оценки разных однотипных АРМ с целью выбора более желательного.
2. Практическая часть
2.1 Внедрение программки
MSWord2003
1) Открыть документ Microsoft Word.
2) В меню «Файл» избрать команду «Сделать».
3) В области задач «Создание документа» в группе Шаблоны избрать параметр «На моем компе».
4) Избрать вкладку «Письма и факсы».
5) Два раза щелкнуть значок «Мастер писем».
6) Следуя инструкциям Мастера писем ввести реквизиты письма.
7) Открыть таблицу Microsoft Excel, содержащую диаграмму.
8) Выделить нужную диаграмму.
9) Надавить клавишу «Копировать».
10) Перейти в документ Word с письмом, а потом избрать пространство вставки объекта и установить в нем курсор.
11) Избрать команду «Особая вставка» в меню «Правка».
12) Для сотворения внедренного объекта в открывшемся окне избрать вариант «Вставить». В перечне «Как» избрать объект).
13) Выполнить нужную редакцию документа.
1) На панели инструментов «Рисование» надавить клавишу «Добавить объект WordArt»
2) Избрать подходящий объект WordArt, а потом нажмите клавишу «OK».
3) В диалоговом окне «Изменение текста WordArt» ввести подходящий текст. В этом же окне можно выполнить одно из последующих действий:
— чтоб поменять тип шрифта, избрать подходящий шрифт в поле «Шрифт»;
— чтоб поменять размер шрифта, избрать размер в поле «Размер»;
— чтоб создать текст полужирным, надавить клавишу «Полужирный»;
— чтоб создать текст наклонным, надавить клавишу «Курсив».
4) Для окончания сотворения объекта надавить клавишу «ОК»
2.2 Внедрение программки
MSExcel2003
2
Введя в ячейку В2 формулу для решения данного уравнения и оставив ячейку В1 пустой для нахождения неведомого параметра Х, выберем в меню «Сервис» команду «Подбор параметра». Установим начальные значения (Набросок 1).
Набросок 1 – Внедрение функции Подбор параметра
Нажав клавишу «ОК» получим итог подбора параметра (Набросок 2).
Набросок 2 – Итог подбора параметра
Таковым образом, уравнение 2*X2
-9*X+5=1 правильно при Х=0,5.
Таблица 1 – Начальные данные
Фамилия рабочего
количество деталей
Премия
Январь
Февраль
Март
I квартал
Шнуров
15
18
17
50
0
Ломов
18
21
19
58
800 р.
Носов
17
15
14
46
0
Денисов
17
14
16
47
0
Захаров
20
20
11
51
100 р.
Для решения задачки воспользуемся функцией ЕСЛИ. Ее внедрение представлено на Рисунке 3. Так, для рабочего по фамилии Шнуров размер премии будет рассчитываться в ячейке F3 по формуле ЕСЛИ(I16;100*(E3-50);0).
Набросок 3 – Задание аргументов функции ЕСЛИ для ячейки F3
Таковым образом, в столбец Fдля расчета суммы премий нужно ввести формулы выставленные в Таблице 2.
Таблица 2 – Решение задачки с внедрением функции ЕСЛИ MSExcel
A
B
C
D
E
F
1
Фамилия рабочего
количество деталей
Премия
2
Январь
Февраль
Март
I квартал
3
Шнуров
15
18
17
50
=ЕСЛИ(I16;100*(E3-50);0)
4
Ломов
18
21
19
58
=ЕСЛИ(E4>50;100*(E4-50);0)
5
Носов
17
15
14
46
=ЕСЛИ(E5>50;100*(E5-50);0)
6
Денисов
17
14
16
47
=ЕСЛИ(E6>50;100*(E6-50);0)
7
Захаров
20
20
11
51
=ЕСЛИ(E7>50;100*(E7-50);0)
Таблица 3 — Зарплата служащих компании
Фамилия
Апрель
Май
Июнь
2-ой квартал
Савин
2500
3400
3100
9000
Рублев
4200
4700
4500
13400
Якимов
3800
4100
3900
11800
Итого:
10500
12200
11500
34200
Для вычисления суммарных значений в столбце «2-ой квартал» нужно ввести в данный столбец формулы, осуществляющие расчет по начальным данным. Данные формулы приведены в Таблице 4.
Таблица 4 – Решение задачки с внедрением СУММ MS Excel
A
B
C
D
E
1
Фамилия
Апрель
Май
Июнь
2-ой
квартал
2
Савин
2 500,00р.
3 400,00р.
3 100,00р.
=СУММ(B2:D2)
3
Рублев
4 200,00р.
4 700,00р.
4 500,00р.
=СУММ(B3:D3)
4
Якимов
3 800,00р.
4 100,00р.
3 900,00р.
=СУММ(B4:D4)
5
Итого:
10 500,00р.
12 200,00р.
11 500,00р.
=СУММ(B5:D5)
или
=СУММ(E2:E4)
Последовательность построения диаграммы:
1) Избрать в меню «Вставка» команду «Диаграмма».
2) В открывшемся окне избрать тип диаграммы: рядовая гистограмма. Надавить клавишу «Дальше».
3) Указать спектр данных в столбцах: =Лист2!$B$2:$D$4
4) Перейти во вкладку «Ряд» и задать имена ряда, указать спектр подписей по оси Х (Набросок 4). Надавить клавишу «Дальше».
Набросок 4 – Начальные данные для построения диаграммы
5) В открывшемся окне во вкладке «Заглавия» ввести заглавие диаграммы. Надавить клавишу «Дальше».
6) Задать пространство размещения диаграммы и надавить клавишу «Готово».
7) Вызвать контекстное меню щелкнув правой клавишей по оси ординат избрать в нем команду «Формат оси». В открывшемся окне открыть вкладку «Шкала» и задать наибольшее
8) Вручную отредактировать длину и ширину диаграммы. Приобретенная диаграмма представлена на рисунке 5.
Набросок 5 – Зарплата служащих.
2.3 Внедрение программки
MSAccess2003
Таблица 5 – Начальные данные
Фамилия оператора
Оклад
количество проданных путевок
Дата реализации
Стоимость одной путевки
Федоров
2600р
4
24.04.2006
5200р
Устинов
3100р
2
25.04.2006
4170р
Федоров
2600р
3
25.04.2006
7500р
Ласкина
2400р
5
26.04.2006
6090р
В объекте «Таблицы» избрать «Создание таблицы в режиме конструктора». В открывшейся таблице ввести последующие значения:
Набросок 6 – Создание таблицы «Работники» в режиме конструктора
Правой клавишей щелкнуть по строке «Код» в открывшемся меню избрать команду «Ключевое поле».
Дальше закрыть таблицу. программка предложит сохранить конфигурации структуры таблицы, надавить клавишу «Да». В открывшемся окне задать имя таблицы, к примеру «Работники», надавить клавишу «ОК».
В объекте «Таблицы» избрать сделанную базу данных «Работники», заполнить ее (Набросок 7) и закрыть.
Набросок 7 – Таблица «Работники»
Аналогичным образом сделать таблицу «Начальные данные» (Набросок 8). Но при заполнении типа данных поля «Фамилия» избрать «Мастер подстановок» и в открывшихся окнах избрать подстановку фамилии из сделанной ранее таблицы «Работники». Аналогичным образом заполнить тип данных для поля «Оклад».
Набросок 8 — Создание таблицы «Начальные данные» в режиме конструктора
В объекте «Таблицы» избрать сделанную базу данных «Начальные данные», заполнить ее (Набросок 9) и закрыть.
Набросок 9 – Таблица «Начальные данные»
В объекте «Запросы» избрать команду «Создание запроса в режиме конструктора». Добавить таблицу «Начальные данные», в поля запроса из данной таблицы добавить поля «Код операции» и «Фамилия». Добавить поле «Премия», для расчета которой при помощи построителя выражений ввести выражение: Премия: [Исходные данные]![количество проданных путевок]*[Исходные данные]![Стоимость одной путевки]*0,015. Закрыть запрос, сохранить его под именованием «Расчет премии».
В объекте «Запросы» избрать команду «Создание запроса при помощи мастера». Из таблицы «Работники» избрать поля «Фамилия» и «Оклад» из запроса «Расчет премии» избрать поле «Премия», надавить клавишу «Дальше». В открывшемся окне избрать итоговый отчет с вычислением итоговых значений по полю «Премия». Надавить клавишу «Дальше». Задать имя запроса, к примеру «Оклад и премия». Надавить клавишу «Готово» и просмотреть получившиеся результаты (Набросок 10).
Набросок 10 – запрос «Оклад и премия»
В объекте «Запросы» избрать команду «Создание запроса в режиме конструктора». Добавить поле «Фамилия» из запроса «Оклад и премия». Сделать поле «Заработная плата», в которое ввести выражение: Заработная плата: [Оклад и премия]!Оклад+[Оклад и премия]![Sum-Премия]. Сохранить конфигурации запроса под именованием «Заработная плата» (Набросок 11).
Набросок 11 – запрос «Заработная плата»
]]>