Цитата:
Сообщение от IStiv
(Сообщение 471161)
Всем доброго времени.
прошу совета помощи.
Задача состоит в определении где находится присадка под петли верх/низ/лево/право у панели. Скриптом.
Определить верх/низ решаемо, Y ось всегда вверх и получив координаты контура и приведя их к мировым - можно определить где верх, а где низ, и исходя из этого определить вверху или внизу присадка, вытащив координаты отверстий.
Ситуация еще осложнена тем что панель не одна(так бы можно было привязаться к какой то координате), а целая кухня панелей любой конфигурации.
Однако ситуация когда отверстия могут быть с двух сторон - исключена.
А вот с лево/право не понятно как быть.
Т.е. вопрос есть ли какой то способ , прогрммно определить лево/право панели ?
( лицом при этом будет задняя сторона панели , где отверстия).
|
Есть решение. Ограничения этого решения: - Ось Х или У панели должна быть параллельна глобальной оси У (Если над панелью не было никаких "извращений", то так должно быть всегда).
- Проверка идёт по фурнитуре с типом установки "пласть-пласть", поэтому может выдать результат не только для петель, а, например, и для уголков.
- Если установлена одна петля (например, снизу слева), то в текущем варианте присадка не определится. Можно проверять наличие направления побитовым И, например код под условием "if (pos & LEFT)" выполнится, только если в результате определения позиции есть "лево".
Я попытался максимально откомментировать код, чтобы не возникали вопросы "что за переменная/условие/функция и как оно работает".
Всё, что идёт до Model.UnselectAll() - основные функции для работы скрипта. Ниже - пример, как этим пользоваться (им я и тестировал). В текущем варианте скрипт бегает по всем панелям, определяет позицию, потом выделяет панель, алертом сообщает информацию, снимает выделение и обрабатывает следующую панель.
Код
Код:
// Четыре константы направлений - бит смещённый на несколько порядков.
// В побитовом представлении выглядит так:
// Лево = 0001, Право = 0010, Верх = 0100, Низ = 1000.
// Для взаимодействия с ними используются "побитовое И" и "побитовое ИЛИ".
// "Побитовое И" возвращает 1 если в обоих сравниваемых разрядах оба значения
// равны 1. Например: 11 & 5 = 1 (побитово: 1011 & 0101 = 0001).
// "Побитовое И" возвращает 1 если в хотя бы в одном из сравниваемых разрядах
// значение равно 1. Например: 9 | 5 = 13 (побитово: 1001 | 0101 = 1101).
const LEFT = 1 << 0, RIGHT = 1 << 1, UP = 1 << 2, DOWN= 1 << 3
/**векторное произведение векторов */
function VectorCross(v1, v2) {
return {
x: v1.y * v2.z - v1.z * v2.y,
y: v1.z * v2.x - v1.x * v2.z,
z: v1.x * v2.y - v1.y * v2.x
}
}
/** получить обратный вектор */
function VectorNegate(v) {
return {
x: -v.x,
y: -v.y,
z: -v.z
}
}
/** расстояние от точки до плоскости
* Плоскость определяется точкой на плоскости и нормалью
*/
function PointPlaneDistance(point, planePoint, planeNormal) {
return (point.x - planePoint.x) * planeNormal.x +
(point.y - planePoint.y) * planeNormal.y +
(point.z - planePoint.z) * planeNormal.z;
}
/** сравнение двух вещественных чисел с точностью 0.001*/
function Equals(n1, n2) {
return Math.abs(n1 - n2) < 0.001;
}
/** сравнение двух векторов */
function EqualsV(v1, v2) {
return Equals(v1.x, v2.x) &&
Equals(v1.y, v2.y) &&
Equals(v1.z, v2.z);
}
/** Проверка направления (отверстия)
* Возвращает true, если вектор gDir сонаправлен с осью Z панели
*/
function CheckDirection(gDir, panel) {
return (EqualsV(gDir, panel.NToGlobal(AxisZ)) ||
EqualsV(gDir, panel.NToGlobal(Axis_Z)))
}
/**
* Проверка отверстия
* Возвращает комбинацию смещения относительно панели
* @param {Panel} panel
*/
function CheckHole(hole, fast, panel) {
var result = 0;
/** Направление отверстия в ЛСК панели
* Указывает направление "вперёд" для панели
*/
var holeDir = panel.NToObject(fast.NToGlobal(hole.Direction));
/** Глобальная ось Y в ЛСК панели (вектор "вверх" панели)*/
var UpAxis = panel.NToObject(AxisY);
/** Вектор, указывающий направление "влево" панели */
var leftAxis = VectorCross(UpAxis, holeDir);
/** Центр отверстия */
var holeCenter = fast.ToGlobal({
x: hole.Position.x + hole.Direction.x * (hole.Depth / 2),
y: hole.Position.y + hole.Direction.y * (hole.Depth / 2),
z: hole.Position.z + hole.Direction.z * (hole.Depth / 2),
})
// перевод центра отверстия в ЛСК панели
holeCenter = panel.ToObject(holeCenter);
/** центр панели в её ЛСК. Используется как точка на плоскости разделяющей
* "право-лево" и "верх-низ"
*/
var panelCenter = {
x: (panel.GMin.x + panel.GMax.x) / 2,
y: (panel.GMin.y + panel.GMax.y) / 2,
z: (panel.GMin.z + panel.GMax.z) / 2
};
// Если расстояние от центра отверстия до плоскости, определённой центром
// панели и направлением "влево" положительное, значит отверстие слева.
// Иначе - справа. Если центр отверстия находится в центре панели, с большой
// вероятностью результатом будет "слева"
if (PointPlaneDistance(holeCenter, panelCenter, leftAxis) > -0.001)
result |= LEFT;
else
result |= RIGHT;
// Если расстояние от центра отверстия до плоскости, определённой центром
// панели и направлением "вверх" положительное, значит отверстие сверху.
// Иначе - снизу. Если центр отверстия находится в центре панели, с большой
// вероятностью результатом будет "снизу"
if (PointPlaneDistance(holeCenter, panelCenter, UpAxis) > -0.001)
result |= UP;
else
result |= DOWN;
return result;
}
/**
* Получение позиций петель (или другой фурнитуры по типу "пласть-пласть")
* @param {Panel} panel
*/
function GetHingePosition(panel) {
var fasts = panel.FindConnectedFasteners();
// Изначально в результате все направления. Побитово: 1111
var result = LEFT | RIGHT | UP | DOWN;
for (var i = 0; i < fasts.length; i++) {
var fast = fasts[i];
// (DatumMode == 2) - проверка, что фурнитура устанавливается по типу
// "пласть-пласть"
if (fast.DatumMode == 2) {
var holes = fast.Holes;
if (holes && holes.Count > 0) {
for (var j = 0; j < holes.Count; j++) {
var hole = holes[j]
// Проверка направления отверстия (переведённого в ГСК)
if (CheckDirection(fast.NToGlobal(hole.Direction), panel)) {
// Побитовым И из результата отсекаются те направления,
// которые не были возвращены функцией CheckHole.
result &= CheckHole(hole, fast, panel);
}
}
}
}
}
return result;
}
Model.UnSelectAll();
Model.forEachPanel((panel) => {
var pos = GetHingePosition(panel);
var msg = `панель ${panel.Name} : ${panel.UID}\n`;
// Сверяем результат с константами направления.
switch (pos) {
case LEFT:
msg += 'Присадка слева';
break;
case RIGHT:
msg += 'Присадка справа';
break;
case UP:
msg += 'Присадка сверху';
break;
case DOWN:
msg += 'Присадка снизу';
break;
default:
msg += 'Присадка не определена';
}
panel.Selected = true;
alert(msg);
panel.Selected = false;
})
[свернуть]
|