Урок 2

Урок 2. Учимся на простых примерах. Модели плоских (2D) фигур.

Примеры с функциями minkowski, hull. Дополнительные методы обработки и отладки моделей.

Векторная сумма - minkowski.

Результатом работы функции является Сумма Минковского для нескольких тел. Точки поверхностей N тел векторно суммируются - результатом является некая новая фигура. На слух это воспринимается с трудом, но визуально это достаточно легко понять - поэтому приведу несколько примеров работы с данной функцией.

minkowski(){
  cube([10,10,1], true);
  scale([0.7,0.5,0.3]) sphere(3, $fn=50);
};
minkowski(){
  rotate([0,90,0])cylinder(3,1,2, true, $fn=50);
  cubeM([10,10,1]);   
 }
minkowski(){
  cylinder(3,1,2, true, $fn=50);
  cubeM([10,10,1]);   
 }

Для случая когда одно из слагаемых имеет прямые грани - сумма Минковского выглядит достаточно просто. Если же применять её к сферическим поверхностям с искажённой формой - результат будет сложно представить заранее. Предлагаю вам поэкспериментировать самостоятельно.

Общая оболочка - hull.

Принцип действия полностью соответствует названию. Представьте, что вы размещаете в пространстве две произвольные фигура, а потом обтягиваете их резиновой оболочкой - это и есть функция hull().

hull(){
    translate([5,5,5]) sphere(10, $fn=100);
    cube([10,10,10], true);
};

hull- в некоторых случаях просто незаменима. С трудом могу себе представить, как в OpenSCAD, без неё, можно сделать переход из трубы с круглым сечением в трубу с квадратным сечением. С hull() это можно сделать в несколько строк кода.

difference(){
  hull(){
    translate([0,0,10])cylinder(0.1, 5, 5, true, $fn=100);
    translate([0,0,-10])cube([10,10,0.1], true);
  };
  
  scale([0.9,0.9,1.01])
  hull(){
    translate([0,0,10])cylinder(0.1, 5, 5, true, $fn=100);
    translate([0,0,-10])cube([10,10,0.1], true);
  };
}

Отражение модели - mirror.

Из несимметричных объектов, с помощью вращений и переносов, невозможно получить зеркальную копию. От мучений по постройке зеркальной копии модели нас спасает функция с одноимённым названием mirror. В качестве входного параметра, функция принимает координаты вектора нормали (перпендикуляра) к плоскости отражения. По умолчанию вектором нормали является ось X ( mirror([1,0,0]) ), а плоскостью отражения соответственно является плоскость YZ. Плоскость отражения всегда проходит через центр координат

translate([-5,0,5])
color("green")mirror()
union(){
    translate([0,0,7])cylinder(14,5,5, true, $fn=100);
    rotate([0,90,0])cylinder(25,5,5, $fn=100);
    rotate([90,0,0])translate([0,0,-20])cube([10,10,40], true);
};
translate([5,0,-5])
rotate([0,90,0]) 
union(){
    translate([0,0,7])cylinder(14,5,5, true, $fn=100);
    rotate([0,90,0])cylinder(25,5,5, $fn=100);
    rotate([90,0,0])translate([0,0,-20])cube([10,10,40], true);
};

Изменение размера - resize.

resize - аналог функции scale. Отличие в том, что вместо относительных единиц допустим 0.2 (20%) от исходной величины, задаётся размер по осям XYZ в который нужно вписать объект. Зачем она нужна? Ведь есть scale! Допустим, вы скачали с интернет ресурса - модель, и не знаете её размер. Вам нужно её вписать в определённые рамки - будете подбирать проценты? Не факт, что вы их точно подберёте. Используйте resize.

resize([10,20,30]) cube(5);
resize([10,20,30]) { ... };

Аффинные преобразования - multmatrix.

Функция multmatrix - реализует в OpenSCAD аффинные преобразования. Одной матрицей можно задать вращение, перемещение, отражение, сжатие/растяжение и "искревление". Что понимать под "искривлением"? Представьте, что вместо декартовых координат с углом между осями в 90 гр. вам дали систему координат с углом, например 60 гр. соответственно все точки поверхности "наклонили". Например, квадрат стал параллелограммом с углами 60 и 120 гр.

В каких то ситуациях функция multmatrix может быть удобна, но она достаточно сложна для понимания - особенно для тех кто не знаком с линейной алгеброй, матрицами и т.д.. Приведу пример, большей частью для информации, чтобы иметь представление, о чём идёт речь.

a=45; b=45; c=45;
kx=0.5; ky=0.5; kz=1;
A=[[1,      0,       0, 0],
   [0, cos(a), -sin(a), 0],
   [0, sin(a),  cos(a), 0],
   [0,      0,       0, 1]]; 
B=[[ cos(b), 0,  sin(b), 0],
   [      0, 1,       0, 0],
   [-sin(b), 0,  cos(b), 0],
   [0,       0,       0, 1]];
C=[[ cos(c),-sin(c), 0, 0],
   [ sin(c), cos(c), 0, 0],
   [      0,      0, 1, 0],
   [      0,      0, 0, 1]];
K=[[kx, 0,  0, 0],
   [0, ky,  0, 0],
   [0,  0, kz, 0],
   [0,  0,  0, 1]]; 
M=A*K;
multmatrix(M)cube(10,true);

Здесь матрицы A, B, C - это вращение, матрица K - это коэффициенты сжатия растяжения по осям X, Y, Z.

Если вы имели дело с линейной алгеброй (матрицами), то обратили внимание, что в приведённом примере OpenSCAD поддерживает операции умножения и сложения матриц.

Работа с матрицами как с объектами линейной алгеброй, не совсем простая задача - это не совсем "просто" таблицы. В общем случае применение таких "монстров" неоправданно, и вполне можно обойтись стандартными функциями перемещение, вращение, сжатие/растяжение.

Цвет и прозрачность - color.

Функция color для расцветки модели и её частей. На вход функции попадает или массив, или текстовая переменна обозначающая "название цвета". Для случая массива - задаётся [R, G, B], где R, G, B соответственно доля красного, зелёного и синего цветов в значениях от 0 до 1. Например [0,1,0] это чисто зелёный (стандартно параметры RGB задаются от 0 до 255, но разработчики OpenSCAD почему то решили сделать по-другому). Для случая задания цвета текстовой переменной - пишется название цвета в двойных кавычках "red", "black", "green" и т.п., или цвета с заковыристыми названиями "cadetblue". Таблица с названиями цветов приведена ниже.

Кроме цвета, может быть задан четвёртый параметр - прозрачность объекта от 0 (полностью прозрачный), до 1 (совсем не прозрачный). Единственно с прозрачностью вышел "косяк", а может и не "косяк", может так задумывалось - в общем, иногда прозрачность только местами прозрачная. Смотрите пример ниже.

color("red", 0.5) cube(10,true);
color([0,0,1], 0.5)cube([5,5,20],true);

Модификаторы *, !, #, %.

В OpenSCAD есть 4 модификатора:

  • * выводит модифицированный участок кода из программы - соответственно эта часть кода не отображается в окне просмотра; удобен в тех случаях, когда хочется посмотреть результат работы кода без какого-то его участка, чтобы не стирать код - используют модификатор * {...};
  • ! оставляет только выделенный модификатором элемент - остальное исключается из кода и модели; в окне просмотра отображается только модифицированный элемент;
  • # в окне просмотра, результат работы помеченного участка кода - отображает как полупрозрачный красный объект; не меняет результат работы программы; пожалуй самый нужный модификатор при отладке модели; используется если не видно внутреннюю часть модели, или не видно вычитаемый объект и т.п.;
  • % исключает элемент из хода выполнения программы и её результата, но при этом объект отображается полупрозрачно серым цветом в окне просмотра;

Пример кода.

difference(){
  cube(10,true);
  cube([5,5,20],true); // * ! # %
};
Без модификатора.

*...;
*{...}
!...;
!{...}
#...;
#{...}
%...;
%{...}

Плоские модели.

Проекция и сечение модели - projection.

Функция реализует проекцию и сечение. Проекция модели осуществляется НА плоскости XY, а сечение модели осуществляется ПО самой плоскости XY - других вариантов не предусмотрено. Если вам нужны "нестандартные" сечения или проекции - вращаете и перемещаете модель относительно плоскости XY до тех пор пока не получите желаемого.

Если входной параметр функции - cut установлен в true функция будет выдавать сечение объекта плоскостью XY ( Z=0 ).

Если cut установлен в false, или параметр незадан - получаем проекцию фигуры на XY.

Следует иметь ввиду, что на выходе projection получается 2D объект - не имеющий высоты, хотя визуально она присутствует. Соответственно, как и с любым другим 2D объектом - можно выполнять операции linear_extrude и rotate_extrude, которые будут рассмотрены ниже, но нельзя выполнять многие функции для 3D объектов.

В примере ниже приведены несколько сечений модели при разных её положениях по оси Z. Проекция фигуры совпадает с первым её сечением на картинке.

color("green")
projection(cut=true)
translate([0,0,-5]) // z = 5, 2, -2, -5
difference(){
  hull(){
    translate([0,0,5])cylinder(0.1, 5, 5, true, $fn=100);
    translate([0,0,-5])cube([10,10,0.1], true);
  };
  cube([5,5,11], true);
  rotate([0,90,0])cylinder(12, 3, 2, true, $fn=100);
};

Объекты 2D square(), circle(), polygon()

Плоские примитивы в OpenSCAD представлены прямоугольником square, кругом circle, многоугольником polygon. Прямоугольник и круг в пояснении они особо не нуждаются примеры кода с полным и сокращенным написанием приведены ниже. Остановлюсь на многоугольнике. Очень мощный инструмент при правильном использовании (смотри пример ниже). Входными параметрами функции polygon являются массив точки заданных координатами X,Y (Z=0) и массив содержащий путь обхода точек, если массив обхода по точкам не задан принимается та очерёдность, в которой указаны точки. Зачем нужен путь обхода по точкам? Надеюсь, у всех в детстве была игра соединить точки по номерам и получить интересный рисунок, если вы отклонялись от указанного порядка - рисунок не получался. Здесь ситуация аналогична. Забыл указать про ещё один параметр convexity. Данный параметр необходим только для правильного отображения фигуры, в окне пред просмотра на конечный результат он не влияет. Параметр определяет количество точек пересечения произвольного луча с многоугольником. Допусти для буквы "С" 4 точки пересечения (луч вошёл, вышел, вошёл, вышел), а для буквы "Е" уже 6 точек пересечения. На практике не помню, чтоб у меня появлялись проблемы в отображении объекта если я не указывал этот параметр. (Позже: если параметр не указан проблемы с отображением всё таки появляются. Пример бага приведу ниже при разборе функции linear_extrude. )

Функция Polygon поддерживает возможность многократного "вырезания" отверстий в многограннике за счёт указания путей обхода точек, внутренних отверстий. Давайте посмотрим, как это реализуется.

И так функция polygon:

polygon(
points=[
   [10,10], [10,-10], [-10,-10], [-10,10],
   [4,4], [4,8], [8,8], [8,4],
   [-4, -4], [-6,-8], [-3,-8]
        ],
paths=[ [0,1,2,3], [4,5,6,7], [8,9,10] ],
convexity=6
);
polygon(
[
  [10,10], [10,-10], [-10,-10], [-10,10],
  [4,4], [4,8], [8,8], [8,4],
  [-4, -4], [-6,-8], [-3,-8]
],
[
  [0,1,2,3], [4,5,6,7], [8,9,10]
] );
square(size = 10);
square(10);
square([10,20]);
square(10,false);
square([10,20],true);
square([10,20],center=false);
square(size=[10, 20], center=true);
square(center=false, size=[10, 20]);
/*через задание радиуса*/
circle(10); circle(r=10);
/*через задание диаметра*/
circle(d=20); circle(d=20, $fn=50);
color("green") translate([0,0,5])
resize([30,10]) circle(d=20, $fn=50);
color("red") translate([0,0,10])
circle(d=20, $fn=3);
color("green") translate([0,0,5])
polygon([[0,10],[5,5],[0,0],[7,3],[15,0],[10,8],[12,15], [7,8]]);
color("red") polygon( [
  for (a = [0 : 1 : 359])
    [(sin(5*a)+5)* sin(a),
     (sin(5*a)+5)* cos(a)]
] );

Функция text.

Функция служит для создания плоской модели текста. Если же вам нужен объёмный текс - вам помогут text + linear_extrude(см. ниже), всё это займёт пару строк кода.

Теперь подробней. Функция поддерживает все основные языки (в том числе и русский). Шрифты берутся из набора Windows, если вам не хватает, для полного счастья, какого либо шрифта загружаете в Windows новый шрифт, соответственно он появляется в openscad. Шрифт удобно выбирать через Word - выбранный шрифт должен поддерживать русский алфавит (Word - в окне выбора шрифта, кроме английских букв, должна быть строчка с русскими буквами). В самом OpenSCAD так же есть шпаргалка шрифтов Справка/Список шрифтов - здесь удобно подсмотреть поддерживаемые стили шрифта: жирный (bold), курсив (italic) и т.п..

Функция поддерживает символы Юникода \u... (см. пример ниже). Коды можно посмотреть в том же Word. Выбираем вставить символ - в открывшемся окне можно найти символ и его код.

  • text - строковое значение, тот текст который вы хотите получить на выходе;
  • size - числовое значение, размер шрифта, по умолчанию 10;
  • font - строковое значение, тип шрифта, стиль шрифта например font = "Times New Roman:style=Bold Italic" (Bold и Italic должны поддерживаться шрифтом см. Справка/Список шрифтов);
  • halign - строковое значение, выравнивание по горизонтали, может принимать значения (left, center, right);
  • valign - строковое значение, выравнивание по вертикале, может принимать значения (top, center, bottom) по умолчанию baseline;
  • spacing - числовое значение, расстояние между буквами (символами), по умолчанию 1;
  • direction - строковое значение, направление текста, возможные значения ltr (left to right по умолчанию), rtl, ttb, btt;
  • language - строковое значение, язык "en" (английский по умолчанию), "ru" (русский);
  • script - строковое значение, сценарий языка, по умолчанию latin, честно говоря, не разобрался в назначении этой переменной;
  • $fn - числовое значение, степень сегментации кривой.

Небольшой совет: помните функцию resize - она здесь хорошо подходит, если вам нужно получить буквы строго заданной высоты.

text(text="\u00A9 Наш Колхоз",
     language="ru",
     font="Segoe Script:style=Bold",
     $fn=30);

Смещение контура фигуры - offset.

Функция смещает контур плоской фигуры внутрь или наружу, относительно исходной фигуры, а так же закругляет или срезать углы. Получается нечто вроде фаски.

Исходные параметры:

  • r или delta - если параметр r / delta положительный - контур фигуры увеличивается на эту величину в каждом направлении, при отрицательном параметре - контур уменьшается;
    • параметр r>0 все ВНЕШНИЕ углы закругляются с этим радиусом;
    • параметр r<0 все ВНУТРЕННИЕ углы закругляются с этим радиусом;
    • параметр delta>0 и chamfer=true все ВНЕШНИЕ углы срезаются на удалении delta от вершины под углом 90 гр. к серединному углу;
    • параметр delta<0 и chamfer=true все ВНУТРЕННИЕ углы срезаются на удалении delta от вершины под углом 90 гр. к серединному углу;
  • chamfer - логическое значение (правда или ложь - true/false); определяет срезать или нет, углы под 90 гр.; применим, если указан параметр delta; по умолчанию false;
  • $fn - степень детализации радиусов скругления, при использовании параметра r;

Всё это гораздо проще, чем изложено выше, достаточно посмотреть на рисунки примеров ниже. При наведении курсора на рисунок всплывёт подсказка с функцией.

Зелёный - исходный многоугольник. Жёлтый - результат работы функции.

Небольшой совет: если вы хотите закруглить и внешние, и внутренние углы - примените функцию offset дважды: offset(r=4, $fn=30) offset(r=-2, $fn=30). Итоговый радиус скругления +2.

offset(r=-2)offset(r=4)
difference(){
    square(20,true);
    translate([30*sqrt(2),0,0])
    rotate([0,0,45]) square(60,true);
};

Преобразование плоских (2D) фигур в объёмные (3D).

Выдавливаем объёмные фигуры из плоских - функция linear_extrude.

linear_extrude - позволяет "выдавливать" из произвольной, плоской модели колонки ("колбаски") необходимой длинны. Параметры функции:

  • height - высота "выдавливаемой" колонки;
  • center - расположить середину колонки по центру;
  • convexity - параметр необходимый для нормального отображения объекта в окне просмотра, характеризует максимально возможное количество точек пересечения поверхности и произвольного луча (на практике не помню случая, чтоб понадобился);
  • twist - винтовой угол вращения; насколько повернуть вокруг оси Z, плоскую модель, по мере его "подъёма"
  • slices - число слоёв разбиения; характеризует степень сглаженности объекта по вертикали;
  • scale - изменение в размерах X, Y исходного плоского объекта по мере его подъёма "выдавливания"
linear_extrude(height=50,
               center=true,
               scale=[2,2],
               slices=100,
               twist=240)
polygon( [
  for (a = [0 : 1 : 359])
    [(2*sin(6*a)+10)* sin(a),
     (2*sin(6*a)+10)* cos(a)]
] );

Обещанное отступление про параметр convexity.

Уже после написания данной урока - наткнулся на необходимость указывать параметр convexity в нескольких своих моделях. Насколько я понимаю, convexity призван помочь графическому движку openscad правильно отобразить 3D модель на 2D мониторе. Условно графический движок иногда не совсем может "сообразить" какую грань объекта показать пользователю впереди, а какую позади. Соответственно чем больше в модели "складок" тем сложнее справится графическому движку.

Из полученного опыта хотелось бы выделить. Первое, случаи, в которых необходимо указывать параметр convexity - достаточно редки. Возникают они при использовании linear_extrude, rotate_extrude. В моделях с относительно сложной геометрией, наличием множества отверстий или граней. Второе, опыт говорит о том, что указание параметра convexity иногда помогает, а иногда делает ещё хуже - баг с неправильным отображение модели ни только не уходит, изображение становится хуже. Третье, такие модели иногда рендерятся (создание итогового файла stl) с ошибками. В множестве точек, создающих полую обрешётку модели, появляются незакрытые дырки.

Общая рекомендация - не забывайте, что такой параметр в некоторых функциях есть и в случае появления ошибок отображения пробуйте применять его. Ниже приведу несколько картинок, как примерно выглядит модель с данным багом. В данной модели указание convexity=8 даже ухудшало ситуацию (смотри всплывающие подсказки к рисункам).

Тела вращения - функция rotate_extrude.

Функция реализует тела вращения на основе плоских моделей. Плоская модель должна располагаться полностью в положительной части оси X.

Входных параметров всего 3. Угол поворота относительно оси Z - angle (в старых версиях OpenSCAD, если я не ошибаюсь, параметр не поддерживался и всегда вращался на 360 гр.). Convexity - параметр, отвечающий за нормальное отображение в окне просмотра сложных моделей, уже упоминался ранее. Последний параметр $fn - отвечает за степень детализации кривых, по умолчанию его значение низкое и модель получается угловатой, поэтому лучше его указывать.

rotate_extrude(angle=300, convexity=10, $fn=200)
translate([0,0,0])
text("\u00A9 Наш Колхоз", language="ru", font="Segoe Script:style=Bold", $fn=30);
rotate_extrude(angle=300, convexity=10, $fn=200)
translate([15,0,0]) circle(10);

Создаём модель на основе файла с данными или рисунка - функция surface.

Функция surface позволяет строить модели на основе файла с данными, или рисунка в формате ".png".

Параметры функции:

  • file - строковое значение, имя файла вместе с расширением, на основе которого будет строится модель;
  • center - логическое значение (правда/ложь - true/false), отвечает за то как располагать объект - по центру или нет; по умолчанию false
  • invert - логическое значение (true/false), только для случая, если модель строится на основе картинки; по умолчанию false - более тёмным цветам соответствуют более "глубокие" точки модели; если установлено true - более тёмным цветам соответствую более "высокие" точки модели;

Что должен представлять собой файл данных? Файл с данными должен содержать карту высот и иметь расширение ".dat", создать его можно в обычном Windows блокноте. В файл пишутся только координаты по оси Z, разделённые пробелом. Каждая строка обозначает перебор координаты X, а каждый элемент строки отделённый пробелом - перебор координаты Y. По сути мы имеем таблицу с координатами [ 0, 0, Z(0,0) ], [ 0, 1, Z(0,1) ], [ 0, 2, Z(0,2)], ..., [ 0, N, Z(0,N)] потом перенос строки, и координаты для X=1 [ 1, 0, Z(1,0)], [ 1, 1, Z(1,1) ], [ 1, 2, Z(1,2)], ..., [ 1, N, Z(1,N)] и т.д.. Файл с данными нужно размещать в одной папке вместе с сохраняемым кодом OpenSCAD.

На вид это должно выглядеть как то так:

10.5 12.1 12 13 15.6 20 25.1 26
10 15 15.1 16.1 16.6 17.8 18 19
11 18 14 11 15 10 -5 6
.... 

Можно записать данные в блокнот руками, или, если вы знакомы с программированием на каком либо языке, создать небольшую программу, которая бы считала координату Z и записывала её в файл. Я поступил по последнему варианту (не хотелось забивать 10 000 значений) - написал программку на языке PHP, которая выдала мне файл с нужными мне данными и подходящим расширением. Результат того, что у меня получилось - приведён ниже.

Второй вариант построения модели - это взять в качестве исходных данных любой рисунок с расширением .png. Можете нарисовать в Paint, что захотите и сохранить рисунок с расширением png.

Модель строится из соображений, что более тёмный цвет соответствует более глубоким точкам модели, а более светлый цвет - более высоким точкам. Можно инвертировать действие, введя invert=true. В общем проще посмотреть на пример.

surface(file="raschet.dat", center=true, convexity=5);
surface(
        file="kart_color.png",
        center=true,
        invert=true,
        convexity=5
);