В продолжении статьи
В первой статье уже использовался самый первый примитив, который можно назвать просто «произвольная форма».
Перед описанием примитивов-объектов еще раз повторю два основных требования-замечания от нашей системы:
По мимо требований к каждому примитиву мы можем подключить матрицу. После подключения можно с легкостью производить следующие манипуляции:
Сами примитивы можно разделить:
Данный примитив из себя представляет плоский прямоугольник. Для построения достаточно 2 треугольника. Необходимый минимум входных данных, который мы должны получить — это центр нашей фигуры, его ширина и высота. На выходе мы должны получить как минимум 2 массива: массив вершин, массив индексов. Каждую вершину рассчитываем через центр, ширину и высоту, так верхняя левая вершина — это точка, у которой x смещен от центра на половину ширины влево, а y на половину высоты вверх, z не смещается — фигура плоская. Таким же образом находим и все остальные вершины.
В массиве индексов мы определяем, как наши точки будут объединяться. В данном примитиве 2 треугольника
Верхняя левая точка, верхняя правая точка, нижняя левая точка. В массиве вершин это элементы — 0,1,3
Верхняя правая точка, нижняя правая точка, нижняя левая точка. В массиве вершин это элементы — 1,2,3
Соответственно, массив индексов выглядит следующим образом:
[0,1,3,1,2,3,];
Порядок индексов меняться не будет, а вот с вершинами могут быть некие изменения. Для того, чтобы легко было совершать манипуляции с нашим примитивом, переведем массив вершин в матрицу.
При манипуляции с матрицей будет изменяться массив, переданный как входящий параметр, в данном случае массив вершин. При описании матрицы были указаны возможные манипуляции с матрицей. Подключим данные манипуляции к нашему примитиву.
Данные операции не зависят от примитива, поэтому в дальнейшем будем их подключать без комментариев.
Данный примитив представляет из себя гексаэдр. Почти куб, только грани могут быть как квадраты, так и прямоугольники.
Описание будет такое же, как и у прямоугольника, только добавим ещё один входящий параметр — глубину.
У куба будет 8 вершин, как будто дальний и ближний прямоугольник. От простого прямоугольника, описанного выше, отличие будет заключаться в расчете координаты Z, которая в ближнем прямоугольнике будет уменьшаться на половину глубины, а в дальнем увеличиваться также на половину глубины.
Для этого просто возьмем два центра
И в массиве будем создавать 2 прямоугольника, первый с центром frontCenter, второй с центром backCenter.
По поводу вершин индексов. В кубе 6 граней, каждая из который состоит из 2-х треугольников.
/*ближайший к нам прямоугольник, единственный, который мы видим, до манипуляций с кубом*/
0,1,3,
1,2,3,
/*левая грань*/
0,4,7,
0,3,7,
/*нижняя грань*/
3,7,6,
6,3,2,
/*правая грань*/
2,6,1,
1,5,6,
/*верхняя грань*/
0,4,5,
0,5,1,
/*задняя грань*/
7,4,5,
7,5,6
Простые примитивы. которые мы создали состоят из треугольников, и перед тем как создать данный примитив мы мысленно разбивали его на треугольники. Сложные примитивы будут состоять из любой другой геометрической, двухмерной фигуры. В данной статье будет рассмотрен единственный «сложный примитив» — шар. Который будет состоять из прямоугольников.
Что необходимо знать, чтобы нарисовать шар — координаты и радиус? Да. Но я добавлю ещё 1 маленький параметр — детализация.
Здесь один и тот же круг, с одним и тем же радиусом, только разной детализацией. О том, что в описании примитива будет пониматься под детализацией — чуть позже.
Детализация — 35
Детализация — 10
Пример работы.
В первой статье уже использовался самый первый примитив, который можно назвать просто «произвольная форма».
Перед описанием примитивов-объектов еще раз повторю два основных требования-замечания от нашей системы:
- Каждый примитив должен содержать вектор вершин и вектор индексов (vertex, indices).
- Строится каждый примитив по индексам через треугольники (TRIANGLE), то есть каждые 3 точки образуют независимый треугольник.
По мимо требований к каждому примитиву мы можем подключить матрицу. После подключения можно с легкостью производить следующие манипуляции:
- Перемещение по любой из осей. Перемещение на определенное кол-во единиц или перемещение к любой точки в пространстве
- Поворот вокруг любой точки. При подключении матрицы нам становится известен центр, соответственно, мы можем поворачивать примитив вокруг любой точки указав точку или же вокруг собственного центра.
Сами примитивы можно разделить:
- Простые. Состоят только из одного примитива.
- Сложные-составные. Состоят из нескольких примитивов
Простые примитивы
Plain
Описание
Данный примитив из себя представляет плоский прямоугольник. Для построения достаточно 2 треугольника. Необходимый минимум входных данных, который мы должны получить — это центр нашей фигуры, его ширина и высота. На выходе мы должны получить как минимум 2 массива: массив вершин, массив индексов. Каждую вершину рассчитываем через центр, ширину и высоту, так верхняя левая вершина — это точка, у которой x смещен от центра на половину ширины влево, а y на половину высоты вверх, z не смещается — фигура плоская. Таким же образом находим и все остальные вершины.
[this.frontCenter[0] - width / 2, this.frontCenter[1] + height / 2, this.frontCenter[2], /*верхняя левая точка - 0*/
this.frontCenter[0] + width / 2, this.frontCenter[1] + height / 2, this.frontCenter[2], /*верхняя правая точка - 1*/
this.frontCenter[0] + width / 2, this.frontCenter[1] - height / 2, this.frontCenter[2], /*нижняя правая точка - 2*/
this.frontCenter[0] - width / 2, this.frontCenter[1] - height / 2, this.frontCenter[2]]; /*нижняя левая точка - 3*/
В массиве индексов мы определяем, как наши точки будут объединяться. В данном примитиве 2 треугольника
Верхняя левая точка, верхняя правая точка, нижняя левая точка. В массиве вершин это элементы — 0,1,3
Верхняя правая точка, нижняя правая точка, нижняя левая точка. В массиве вершин это элементы — 1,2,3
Соответственно, массив индексов выглядит следующим образом:
[0,1,3,1,2,3,];
Порядок индексов меняться не будет, а вот с вершинами могут быть некие изменения. Для того, чтобы легко было совершать манипуляции с нашим примитивом, переведем массив вершин в матрицу.
this.matrix = new botuMatrix(this.vertex,3);
Операции с примитивом
При манипуляции с матрицей будет изменяться массив, переданный как входящий параметр, в данном случае массив вершин. При описании матрицы были указаны возможные манипуляции с матрицей. Подключим данные манипуляции к нашему примитиву.
moveByX:function(value){
this.matrix.move(value,0);
},
moveByY:function(value){
this.matrix.move(value,1);
},
moveByZ:function(value){
this.matrix.move(value,2);
},
testToPoint:function(value){
this.matrix.toPoint(value);
},
rotateAroundCenter:function(angle,xyzType)
{
this.matrix.rotate(angle,this.matrix.center,xyzType);
},
rotateAroundMaxPoint:function(angle,xyzType)
{
this.matrix.rotate(angle,this.matrix.maxval,xyzType);
},
rotateAroundPoint:function(angle,point,xyzType)
{
this.matrix.rotate(angle,point,xyzType);
},
- moveByX,moveByY,moveByZ — перемещение примитива по X, Y и Z, соответственно. Единственный входящий параметр — кол-во единиц. Пример, obj.moveByX(50) — перемещение obj на 50 единиц вправо.
- testToPoint — перемещаем примитив (ориентируясь на цент) к определенной точки. Входящий параметр — вектор-точка. Пример, obj.testToPoint([0,50,10]);
- rotateAroundCenter,rotateAroundMaxPoint — разворот вокруг центра или вокруг максимальных координат. (в примере с прямоугольником, максимальные координаты совпадают с правой верхней точкой, однако максимальные координаты не всегда совпадают с какой либо точкой примитива. Если грубо каждый трехмерный объект упаковывать в куб, то максимальная координата — это верхняя дальняя точка этого куба.). Входящий параметр — угол разворота и ось по которой должен быть разворот. Пример: obj.rotateAroundCenter(45,«byX») — разворот вокруг оси X на 45 градусов. Угол указывается в градусах, оси — «byX»,«byY»,«byZ».
- rotateAroundPoint — разворот вокруг произвольной точки в пространстве. Пример, obj.rotateAroundPoint(45,[0,0,0],«byZ»);
Данные операции не зависят от примитива, поэтому в дальнейшем будем их подключать без комментариев.
Cub
Данный примитив представляет из себя гексаэдр. Почти куб, только грани могут быть как квадраты, так и прямоугольники.
Описание будет такое же, как и у прямоугольника, только добавим ещё один входящий параметр — глубину.
У куба будет 8 вершин, как будто дальний и ближний прямоугольник. От простого прямоугольника, описанного выше, отличие будет заключаться в расчете координаты Z, которая в ближнем прямоугольнике будет уменьшаться на половину глубины, а в дальнем увеличиваться также на половину глубины.
Для этого просто возьмем два центра
this.frontCenter=[centerPoint[0],centerPoint[1],centerPoint[2] - depth / 2];
this.backCenter=[centerPoint[0],centerPoint[1],centerPoint[2] + depth / 2];
И в массиве будем создавать 2 прямоугольника, первый с центром frontCenter, второй с центром backCenter.
/*ближниий прямоугольник*/
this.frontCenter[0] - width / 2, this.frontCenter[1] + height / 2, this.frontCenter[2], /*индекс - 0*/
this.frontCenter[0] + width / 2, this.frontCenter[1] + height / 2, this.frontCenter[2],/*индекс - 1*/
this.frontCenter[0] + width / 2, this.frontCenter[1] - height / 2, this.frontCenter[2],/*индекс - 2*/
this.frontCenter[0] - width / 2, this.frontCenter[1] - height / 2, this.frontCenter[2],/*индекс - 3*/
/*дальний прямоугольник*/
this.backCenter[0] - width / 2, this.backCenter[1] + height / 2, this.backCenter[2],/*индекс - 4*/
this.backCenter[0] + width / 2, this.backCenter[1] + height / 2, this.backCenter[2],/*индекс - 5*/
this.backCenter[0] + width / 2, this.backCenter[1] - height / 2, this.backCenter[2],/*индекс - 6*/
this.backCenter[0] - width / 2, this.backCenter[1] - height / 2, this.backCenter[2]/*индекс - 7*/
По поводу вершин индексов. В кубе 6 граней, каждая из который состоит из 2-х треугольников.
/*ближайший к нам прямоугольник, единственный, который мы видим, до манипуляций с кубом*/
0,1,3,
1,2,3,
/*левая грань*/
0,4,7,
0,3,7,
/*нижняя грань*/
3,7,6,
6,3,2,
/*правая грань*/
2,6,1,
1,5,6,
/*верхняя грань*/
0,4,5,
0,5,1,
/*задняя грань*/
7,4,5,
7,5,6
Сложные-составные примитивы
Простые примитивы. которые мы создали состоят из треугольников, и перед тем как создать данный примитив мы мысленно разбивали его на треугольники. Сложные примитивы будут состоять из любой другой геометрической, двухмерной фигуры. В данной статье будет рассмотрен единственный «сложный примитив» — шар. Который будет состоять из прямоугольников.
Шар
Что необходимо знать, чтобы нарисовать шар — координаты и радиус? Да. Но я добавлю ещё 1 маленький параметр — детализация.
Здесь один и тот же круг, с одним и тем же радиусом, только разной детализацией. О том, что в описании примитива будет пониматься под детализацией — чуть позже.
Детализация — 35
Детализация — 10
Алгоритм:
- Строим прямоугольник по касательной, то есть центр прямоугольника — это центр круга смещенный по оси Z на величину радиуса. Высота и ширина прямоугольника — длина окружности, которая рассчитывается через радиус, разделенная на детализацию. Чем больше детализация, чем меньше высота-ширина прямоугольников, тем больше самих прямоугольников.
- Сверху добавляем ещё один прямоугольник, развернутый на угол, равный 360* / (кол-во прямоугольников, которое было найдено на предыдущем шаге).
- Повторяю предыдущий этап n-раз. Где n — кол-во прямоугольников. В результате получаем колесо.
- Делаем копию данного колеса с разворотом по оси Y на угол, равный 360* / (длинна круга, деленная на ширину).
- Повторяем предыдущую операцию n-раз, где n — это длина круга, деленная на ширину.
Для реализации данного алгоритма
- Создаем объект к которому можно подсоединять другие объекты. С точки зрения кода, в массивы вершин и индексов добавляются массивы вершин и индексов разных объектов.
function botuGroupedObject(){ this.vertex = []; this.indices = []; } botuGroupedObject.prototype = { addObject:function(obj){ this.vertex.size = obj.vertex.size; var next = Math.max(this.vertex.length / this.vertex.size,0); this.vertex = this.vertex.concat(obj.vertex); this.indices = this.indices.concat(obj.indices.map(function(i){return i + next})); this.vertex.size = obj.vertex.size; } }
- Создаем вспомогательную функцию создания копии объекта в новом объекте.
function makeCopy(object,type){ var vertex = object.vertex.slice(0); var indices = object.indices.slice(0); return new type(vertex,indices); }
- К примитиву Plain добавляем метод
, которые строит примитив — plain вплотную к верхней границе другого примитива plain. getDownLeft() — это нижняя левая точка, то есть элемент из массива вершин с индексом 3. (см. выше в описании примитива Plain). getUpperLeft() — это верхняя левая точка, то есть элемент из массива вершин с индексом 0.connectUP:function(anotherPlain){ var downLeftPoint = anotherPlain.getDownLeft(); var dvPoint = downLeftPoint.map(function(value,index){return value - this.getUpperLeft()[index]},this); this.testToPoint(dvPoint); }
Полный код первых 3-х статей с подробными комментариями
Пример работы.