Создание и редактирование трехмерных кривых и тел
Сейчас мы будем рассматривать построение трехмерных кривых и тел (твердотельные 3D-объекты) с помощью класса Curve3D. Трехмерные кривые сами не отображаются в Renga, но используются для создания тел, поэтому рассмотрим некоторые из них прежде, чем перейти к созданию тел.
Начнём с рассмотрения примеров тел, при построении которых не используются трехмерные кривые. Нам понадобятся методы (функции) класса Solid, которые можно найти по ссылке.
Рассмотрим пример создания столба забора, представляющего собой параллелепипид размером 300х200х1700, на котором сверху расположен шар радусом 70. Сначала создадим параллелепипид с помощью функции CreateBlock, центр нижней поверхности которого будет находиться в точке (0,0,0). Затем создадим сферу с центром в точке (0,0,0) функцией CreateSphere и передвинем её на высоту 1800, чтобы нижняя точка сферы оказалась на верхней поверхности шара.
Параметров в этом примере не будет, поэтому файл параметров имеет вид
{
"metadata": {
"defaultName": "Дуга",
"description": "Описание",
"version": "1.0.0",
"author": "Мусиенко"
},
"styleParameters": [ ],
"ports": [ ]
}
Файл скрипта будет выглядеть следующим образом:
p= CreateBlock(240, 200, 1700) -- создание параллелепипида с центром основания в точке (0,0,0)
s=CreateSphere(100) -- создание шара с центром в точке (0,0,0)
s:Shift(0,0,1800) -- сдвиг шара по вертиткали
m=ModelGeometry()
m:AddSolid(p)
m:AddSolid(s)
Style.SetDetailedGeometry(m) -- для детального уровня задёем m
Style.SetSymbolicGeometry(m) -- для условного уровня задаём m
Style.SetSymbolGeometry(m) -- для символьного уровня задаём m
А теперь создадим параметрический забор, изображенный на рисунке, параметрами которого будут: количество столбов, размеры столбов и размеры панелей между ними и высота низа панели.
Столбы будут иметь вид такой, как мы рассматривали в предыдущем примере, а панелями будут параллелепипиды.
Файл параметров будет иметь вид:
{
"metadata": {
"defaultName": "Забор",
"description": "Бетонный забор",
"version": "1.0.0",
"author": "Мусиенко"
},
"styleParameters": [
{ "name": "Dimensions",
"text": "Размер",
"params": [
{ "name": "Ns",
"text": "Количество столбов",
"type": "Integer",
"default": 4,
"min": 2,
"max": 20
},
{ "name": "Hs",
"text": "Высота столба",
"type": "Length",
"default": 1700,
"min": 500,
"max": 3000
},
{ "name": "Xs",
"text": "Размер столба вдоль оси x",
"type": "Length",
"default": 240,
"min": 100,
"max": 500
},
{ "name": "Ys",
"text": "Размер столба вдоль оси y",
"type": "Length",
"default": 200,
"min": 100,
"max": 500
},
{ "name": "Rs",
"text": "Радиус сферы",
"type": "Length",
"default": 70,
"min": 50,
"max": 250
},
{ "name": "Xp",
"text": "Длина панели",
"type": "Length",
"default": 2000,
"min": 100,
"max": 5000
},
{ "name": "Yp",
"text": "Толщина панели",
"type": "Length",
"default": 50,
"min": 10,
"max": 200
},
{ "name": "Hp",
"text": "Высота панели",
"type": "Length",
"default": 1500,
"min": 1000,
"max": 2500
},
{ "name": "Bp",
"text": "Высота низа панели",
"type": "Length",
"default": 100,
"min": 10,
"max": 500
}
]
}
],
"ports": [ ]
}
Создание столбов и панелей нужно повторять многократно, поэтому необходимо использовать цикл. Новые столбы и панели смещаются относительно более старых, поэтому для создания параллелепипида в функции CreateBlock нужно добавить параметр, задающий локальную систему координат, а для создания сферы в функции CreateSphere нужно добавить параметр точки вставки. Локальную систему координат и точку центра сферы будем смещать при выполнении каждого цикла.
Файл скрипта будет иметь следующий вид
parameters = Style.GetParameterValues()
dimensions = parameters.Dimensions
n=dimensions.Ns -- число столбов
w=dimensions.Xs d=dimensions.Ys h=dimensions.Hs -- размеры параллелепипида столба
r=dimensions.Rs -- радиус сферы столба
u=dimensions.Xp v=dimensions.Yp hp=dimensions.Hp -- размеры панели
b=dimensions.Bp -- высота низа панели
m=ModelGeometry()
p_s=Placement3D(Point3D(0,0,0),Vector3D(0,0,1),Vector3D(1,0,0))
-- p_s - локальная система координат (ЛСК) для параллепипида столба
p_p=Placement3D(Point3D(w/2+u/2,0,b),Vector3D(0,0,1),Vector3D(1,0,0))
-- p_p - локальная систенма координат (ЛСК) для параллепипида панели
p_t=Point3D(0,0,h+r) -- точка центра сферы
for i=1,n do
p= CreateBlock(w, d, h, p_s) -- создаём параллелепипид столба
s=CreateSphere(r,p_t) -- создаём сферу столба
m:AddSolid(p)
m:AddSolid(s)
if i
m:AddSolid(pn)
p_s:Shift(w+u,0,0) -- сдвигаем ЛСК для создания следующего столба
p_t:Shift(w+u,0,0) -- сдвигаем точку для создания следующей сферы
p_p:Shift(w+u,0,0) -- сдвигаем ЛСК для создания следующей панели
end
end
Style.SetDetailedGeometry(m) -- для детального уровня задёем m
Style.SetSymbolicGeometry(m) -- для условного уровня задаём m
Style.SetSymbolGeometry(m) -- для символьного уровня задаём m
Если есть желание задавать материал стилю, формируемому с помощью создаваемой категории, то в файл параметров нужно добавить группу параметров
{ "name": "General",
"text": "Материал",
"params": [
{
"name": "Material",
"text": "Материал",
"type": "Id",
"entityTypeId": "0abcb18f-0aaf-4509-bf89-5c5fad9d5d8b"
}
]
}
Её нужно добавлять в "styleParameters" внутри скобок [ ] перед группой "Dimensions" или посленее, отделяя эти группы запятой.
Для создания многих тел необходимо с использовать трехмерные кривые. Рассмотрим создание некоторых трехмерных кривых. Нам понадобятся методы (функции) класса Curve3D, которые можно найти по ссылке
В пространстве можно построить окружность O с центром в точке (x0,y0,z0), вызвав функцию O=CreateCircle3D(Point3D(x0,y0,z0), normal, radius), где вектор normal задает вектор нормали, ортогональный плоскости фигуры, а число radius задает радиус. Вектором является объект из класса Vector3D, вызываемый конструктором Vector3D(x, y, z), где (x, y, z) — координаты, задающие направления. Длина вектора нормали в данном случае роли не играет, задание Vector3D(1, 2, 3) или Vector3D(3, 6, 9) приведёт к одному и тому же результату.
Например, чтобы создать окружность O1 с центром в точке (100, 200, 300) радиусом 500, расположенную перпендикулярно оси x, нужно написать O1=CreateCircle3D(Point3D(100,200,300), Vector3D(1, 0, 0), 500)
Чтобы в пространстве построить отрезок L, с началом в точке (x1,y1,z1) и концом в точке (x2,y2,z2), вызвав функцию L=CreateLineSegment3D(Point3D(x1,y1,z1), Point3D(x2,y2,z2)) . Отличие от плоского отрезка состоит числа 3 (вместо 2) в имени функции и количестве координат начальной и конечной точек отрезка.
В пространстве можно построить дугу D с помощью вызова функции D=CreateArc3DByCenterStartEndPoints(centerPoint,startPoint,endPoint,clockwise), где centerPoint — точка центра окружности, startPoint — начальная, а endPoint - конечная точка, а параметр clockwise - задает направление дуги (True - по часовой стрелке, False - против часовой стрелки).
Из трехмерных кривых можно создать составную кривую с помощью вызова функции CreateCompositeCurve3D(tab). Кривые нужно поместиь в таблицу tab, а результатом функции будет единая кривая.
Например, построим кривую, изображенную на рисунке, состоящую из двух вертикальных отрезков длиной 500 и двух половинок окружностей радиусом 250, дежащих в горизонтальных плоскостях.
Построим 2 отрезка и 3 дуги и объединим их в единую кривую. Для того, чтобы кривые можно было объединить важно, чтобы конец одной кривой совпадал с началом другой.
L1=CreateLineSegment3D(Point3D(0,0,0),Point3D(0,0,500))
D1=CreateArc3DByThreePoints(Point3D(0,0,500),Point3D(250,250,500),Point3D(500,0,500))
L2=CreateLineSegment3D(Point3D(500,0,500),Point3D(500,0,0))
D2=CreateArc3DByThreePoints(Point3D(500,0,0),Point3D(250,250,0),Point3D(0,0,0))
tab={L1,D1,L2,D2} — помещает кривые в таблицу
curve=CreateCompositeCurve3D(tab) -- создаём единую кривую
Для того, чтобы увидеть результат, нам нужно использовать созданную кривую в построении какого-либо тела. Например, построим тело перемещения диска по пути, выбрав в качестве пути эту кривую. Для этого используем функцию CreateSweptDiskSolid, параметрами которой являются: радиус внешней и радиус внутренней окружностей диска и кривая, задающая путь.
t=CreateSweptDiskSolid(10, 0, curve) -- создаём тело перемещения диска по пути
m=ModelGeometry()
m:AddSolid(t)
Style.SetDetailedGeometry(m) -- для детального уровня
В данном случае параметров нет, поэтому файл параметров может иметь вид
{
"metadata": {
"defaultName": "Дуга",
"description": "Описание",
"version": "1.0.0",
"author": "Мусиенко"
},
"styleParameters": [ ],
"ports": [ ]
}
Полный текст файла скрипта:
L1=CreateLineSegment3D(Point3D(0,0,0),Point3D(0,0,500))
D1=CreateArc3DByThreePoints(Point3D(0,0,500),Point3D(250,250,500),Point3D(500,0,500))
L2=CreateLineSegment3D(Point3D(500,0,500),Point3D(500,0,0))
D2=CreateArc3DByThreePoints(Point3D(500,0,0),Point3D(250,250,0),Point3D(0,0,0))
tab={L1,D1,L2,D2} — помещает кривые в таблицу
curve=CreateCompositeCurve3D(tab) -- создаём единую кривую
t=CreateSweptDiskSolid(10, 0, curve) -- создаём тело перемещения диска по пути
m=ModelGeometry()
m:AddSolid(t)
Style.SetDetailedGeometry(m) -- для детального уровня
Если нужно, чтобы вдоль пути протягивалась не диск, а какая-то другая плоская фигура, то с помощью функции Evolve создаётся тело заметания путем движения образующей кривой вдоль направляющей кривой. В качестве направляющей выберем созданную нами кривую, а в качестве образующей возьмём прямоугольник размером 10x20 с центром в начале координат локальной системы координат. Локальную систему зададим совпадающей с глобальной системой координат.
pr=CreateRectangle2D(Point2D(0,0), 0, 10, 20) -- создаём прямоугольник
-- (второй параметр функции задаёт угол поворота прямоугольника)
placement=Placement3D(Point3D(0,0,0),Vector3D(0,0,1),Vector3D(1,0,0))
-- создаем локальную систему координат, на плоскости XoY которой лежит окружность
t=Evolve(pr,placement, curve) -- создаем тело заметания
Этими строчками нужно заметить в файле скрипта строку
t=CreateSweptDiskSolid(10, 0, curve)
Построим выключатель, изображенный на рисунке. Сейчас будем создавать категорию для электроустановочного изделия, для него нужны 2 представления: детальлное и символьное. Это можно видеть в диалоговом окне «Стили отображения», выбрав соответствующий тип оборудования и открыв для него падающей список в колонке «Уровень детализации».
Файл параметров будет содержать параметр задания материала, размеры цилиндра выключателя и параллелепипида его кнопки.
{ "metadata": {
"defaultName": "Выключатель",
"description": "Древний выключатель",
"version": "1.0.0",
"author": "Мусиенко"
},
"styleParameters": [
{ "name": "General",
"text": "Материал",
"params": [
{ "name": "Material",
"text": "Материал",
"type": "Id",
"entityTypeId": "0abcb18f-0aaf-4509-bf89-5c5fad9d5d8b"v
}
]
},
{ "name": "Dimensions",
"text": "Размер",
"params": [
{ "name": "vr",
"text": "Радиус",
"type": "Length",
"default": 35,
"min": 10,
"max": 80
},
{ "name": "vh",
"text": "Высота",
"type": "Length",
"default": 30,
"min": 5,
"max": 100
},
{ "name": "knw",
"text": "Ширина кнопки",
"type": "Length",
"default": 12,
"min": 6,
"max": 60
},
{ "name": "knd",
"text": "Длина кнопки",
"type": "Length",
"default": 40,
"min": 10,
"max": 80
},
{ "name": "knh",
"text": "Высота кнопки",
"type": "Length",
"default": 20,
"min": 10,
"max": 80
}
]
}
],
"ports": [ ]
}
Файл скрипта:
parameters = Style.GetParameterValues()
dimensions = parameters.Dimensions
h=dimensions.vh -- высота выключателя
r=dimensions.vr -- радиус выключателя
w=dimensions.knw -- ширина кнопки
d=dimensions.knd -- длина кнопки
hk=dimensions.knh -- высота кнопки
v=CreateRightCircularCylinder (r,h) -- создаём цилиндр корпуса
kn=CreateBlock (w,d,hk) -- создаём кнопку
kn:Shift(0,0,h) — сдвигаем кнопку вверх
v2=CreateCircle2D (Point2D(0,0),r)
-- создаём окружность для символьного представления, она состоит из отрезков
FilletCorners2D(v2,1000) -- скругление углов радиусом дуги 1000, чтобы окружность стала более гладкой
points={Point2D(r,0),Point2D(2*r,0),Point2D(2*r+r/4,r/6)}
kr2=CreatePolyline2D(points) -- создаем по точкам кривую для символьного представления
kr2:Rotate(Point2D(0,0),-math.pi/4) -- поворачиваем кривую на 45 градусов по часовой стрелке
v2:Scale (Point2D(0,0), 0.01, 0.01) -- масштабируем окружность
kr2:Scale (Point2D(0,0), 0.01, 0.01) -- масштабируем кривую
geometrySet = GeometrySet2D() -- набор плоских кривых
geometrySet:AddCurve(v2) -- добавляем в набор окружность
geometrySet:AddCurve(kr2) -- добавляем в набор кривую
g3D = ModelGeometry() - для детального представления
g3D:AddSolid(v)
g3D:AddSolid(kn)
g2D= ModelGeometry() -- для символьного представления
Placement = Placement3D(Point3D(0, 0, r),Vector3D(0, 1, 0),Vector3D(1, 0, 0))
g2D:AddGeometrySet2D(geometrySet, Placement)
Style.SetDetailedGeometry(g3D)
Style.SetSymbolGeometry(g2D)
На рисунке показано символьное представление выключателя. Буквой а) оно обозначено в момент создания, а буквой б) - после поворота на 45 градусов по часовой стрелке
Если необходимо, чтобы корпус выключателя был без ребер, то вместо цилиндра нужно использовать тело вращения, создаваемое функцией Revolve(contour, placement, axis, revolutionParameters) , параметрами которой являются:
contour — плоская замкнутая образующая кривая (она будет вращаться),
placement — локальная система координат,
axis — ось вращения,
revolutionParameters — угол вращения
В тексте скрипта нужно заменить строку
v=CreateRightCircularCylinder (r,h) -- создаём цилиндр корпуса
на следующие строки
contour=CreatePolyline2D({Point2D(-r,0),
Point2D(-,h),Point2D(0,h),Point2D(0,0),Point2D(-r,0)})
-- создаем образующую кривую
FilletCornerAfterSegment2D (contour,1,math.min(r/4, h/4))
-- скругляем угол после первого сегмента кривой
Placement = Placement3D(Point3D(0, 0, 0),Vector3D(0, -1, 0),Vector3D(1, 0, 0))
-- задаём локальную систему координат (ЛСК) так, чтобы плоскость образующей кривой
-- располагалась перпендикулярно оси Y глобальной системы координат (ГСК)
-- (ось z ЛСК поворачиваем против оси Y ГСК)
axis=Axis3D(Point3D(0,0,0), Vector3D(0,0,1)) -- ось вращения вдоль оси Z ЛСК
params = RevolutionParameters(math.rad(360)) -- угол поворота задаётся в радианах
v=Revolve(contour,Placement,axis,params) — корпус выключателя
На рисунке буквой а) обозначена кривая contour в момент создания функцией CreatePolyline2D, а буквой б) обозначен результат скругления угла кривой после её первого сегмента фнукцией FilletCornerAfterSegment2D. Эта кривая должна вращаться вокруг оси Z глобальной системы координат, поэтому локальная плоскость Placement для нее задается так, чтобы ось z ЛСК была направлена против оси Y глобальной системы координат.