Предисловие

Данный документ содержит примеры по работе с API системы БАЗИС для создания пользовательских скриптов. Описываемая версия API = 2.

Словарь терминов

Встроенный модуль

Встроенный модуль - глобальный объект, содержащий какие-либо свойства и методы, предоставляющий функционал для скриптов системы Базис.

Подключаемый модуль

Подключаемый модуль - объект, содержащий какие-либо свойства и методы, предоставляющий дополнительный функционал и недоступный без подключения модуля. Подключение модуля с помощью функции require "собирает" модуль и возвращает объект модуля как результат выполнения функции require.

СК - система координат

Система координат - матрица 4х4, описывающая положение и поворот какого либо обьекта. Также она задаёт положение локальной точки объекта (0, 0, 0) и направление его осей

ЛСК

ЛСК - локальная система координат объекта. Она описывает положение и поворот объекта относительно ЛСК объекта-владельца, в котором исходный объект находится.
Примечание: положение объекта - точка (0, 0, 0), переведённая из ЛСК объекта в ЛСК его объекта-владельца.

ГСК

ГСК - глобальная система координат модели. Она описывает некоторую "точку отсчёта", относительно которой расположены объекты модели.

Материал объекта

Материал объекта - листовой или погонный материал с базовым набором свойств, используемый в 3D объектах, либо его частях (например, облицовка пласти панели).

Материал кромки

Материал кромки - кромочный материал, обладающий дополнительными свойствами. Он используется для облицовки кромок панелей.

Версия API

Функционал скриптов может меняться со временем. Для обеспечения работоспособности скриптов, написанных с использованием устаревшего функционала, добавлен модуль apiVersion, содержащий функции для получения/назначения версии API. Если задать новое значение версии API (меньше текущего, но не меньше 0), автоматически подгрузятся скрипты, реализующие устаревший функционал.

Пример скрипта
// Пример создания формы с использованием устаревшей функции "CreateForm"
// Если закомментировать строку с назначением нового значения версии API,
// то скрипт вернёт ошибку при доступе к глобальному свойству "CreateForm",
// т.к. в функционале версий от 1 и выше такой функции нет.

console.log(`Текущее значение версии API скриптов = ${apiVersion.GetScriptApiVersion()}`);
console.log(`Назначение нового значения версии API = 0`);
apiVersion.SetScriptApiVersion(0);
console.log(`Текущее значение версии API скриптов = ${apiVersion.GetScriptApiVersion()}`);
console.log(`Настоящее значение версии API скриптов = ${apiVersion.GetRealScriptApiVersion()}`);

//-- UserForm1 #def_starts
let UserForm1 = CreateForm(FileControl.Owner);
UserForm1.Width = 328;
UserForm1.Height = 289;
UserForm1.Caption = 'Форма-пример';
UserForm1.Show();
//-- UserForm1 #def_ends
  

Создание и редактирование 3D объектов

Встроенный модуль objects3d содержит методы создания новых ообъектов.

Создание панели

Пример скрипта
// Пример создания панели размером 100х200 в разных пространственных положениях 
// (фронтальное, вертикальное и горизонтальное)
const width = 100; // Ширина панели
const height = 200; // Высота панели

// Фронтальная - панель с единичной матрицей поворота, то есть, оси её ЛСК
// сонаправлены осям ГСК
let panel = objects3d.NewPanel(width, height, objects3d.PanelOrientation.front);
panel.Name = 'Фронтальная';

panel = objects3d.NewPanel(width, height, objects3d.PanelOrientation.vertical);
panel.Name = 'Вертикальная';

panel = objects3d.NewPanel(width, height, objects3d.PanelOrientation.horizont);
panel.Name = 'Горизонтальная';  

Создание панели и редактирование её контура.

Пример скрипта
// Пример создания панели и изменения её контура
const radius = 200; // Радиус будущей окружности контура
let panel = objects3d.NewPanel(0, 0, objects3d.PanelOrientation.front);
panel.Name = 'Фронтальная';
let contour = panel.Contour;
contour.Clear(); // Очищение контура
contour.AddCircle(0, 0, radius); // Создание окружности с центром в точке (0, 0) и заданным радиусом  

Создание профиля (тела выдавливания).

Пример скрипта
// Пример создания профиля с прямоугольным сечением 100х200 и длиной 300
const width = 100; // Ширина профили
const height = 200; // Высота профиля
const length = 300; // Длина профиля

let extrusion = objects3d.NewExtrusionBody(width, height, length);
extrusion.Name = 'Профиль';  

Создание тела по траектории и редактирование его параметров.

Пример скрипта
// Пример создания тела по траектории с прямоугольным сечением 100х200 и траекторией в виде окружности с радиусом 300
const width = 100; // Ширина сечения
const height = 200; // Высота сечения 
const radius = 300; // Радиус окружности траектории

let trajectoryBody = objects3d.NewTrajectoryBody();
trajectoryBody.Name = 'Тело по траектории';

// Редактирование сечения
let contour = trajectoryBody.Contour2D;
contour.Clear();
contour.AddRectangle(0, 0, width, height);

// Редактирование траектории
let trajectory = trajectoryBody.Trajectory2D;
trajectory.Clear();
trajectory.AddCircle(0, 0, radius);  

Создание структурных объектов (блок, полуфабрикат, покупное изделие)

Основные типы структурных объектов создаются с помощью соответствующих функций встроенного модуля objects3d.

Пример скрипта
// Пример создания основных структурных объектов - блока, полуфабриката, покупного изделия.

objects3d.NewBlock('Блок');

objects3d.NewDraftBlock('Полуфабрикат');

objects3d.NewAssembly('Покупное изделие');  

Создание объектов внутри определённых структурных объектов

По умолчанию, все объекты, создаваемые с помощью функций встроенного модуля objects3d размещаются во временной области модели. Многие методы позволяют задать объект-владельца для создаваемых объектов, также каждый объект имеет свойство Owner, хранящее ссылку на объект-владельца.

Пример скрипта
// Пример создания панели внутри нового блока.

// Вариант 1 - задать объект-владельца при создании объекта
let block1 = objects3d.NewBlock('Блок 1');
let panel1 = objects3d.NewPanel(100, 100, objects3d.PanelOrientation.front, block1);
panel1.Name = 'Панель 1';

// Вариант 2 - задать объект-владельца после создания объекта
let panel2 = objects3d.NewPanel(200, 300, objects3d.PanelOrientation.front);
panel2.Name = 'Панель 2';
panel2.Owner = block1;  

Изменение объекта-владельца

Общая ориентация объекта относительно ГСК определеяется всеми ЛСК объектов-владельцев. При изменении объекта-владельца может измениться ориентация объекта относительно ГСК. Чтобы этого не произошло, следует использовать функцию ReTransform для изменения ЛСК панели.

Пример скрипта
// Пример изменения объекта-владельца панели

/** @param {TFurnPanel} panel */
function LogPanelCoordinates(panel) {
    console.log(`Координаты минимальной точки панели относительно ГСК = ${JSON.stringify(panel.GabMin)}\n Координаты максимальной точки панели относительно ГСК = ${JSON.stringify(panel.GabMax)}`);
}

let block1 = objects3d.NewBlock('Блок 1');
let block2 = objects3d.NewBlock('Блок 2');
block2.PositionX = 1000;

let panel = objects3d.NewPanel(100, 100, objects3d.PanelOrientation.front, block1);
panel.Build();
console.log('Панель создана в блоке "Блок 1"');
LogPanelCoordinates(panel);
panel.Owner = block2
console.log('Объект-владелец панели изменён на "Блок 2"');
LogPanelCoordinates(panel);
panel.ReTransform(block1, block2);
console.log('Лск панели преобразована из блока "Блок 1" в блок "Блок 2"');
LogPanelCoordinates(panel);
  

Работа с историей.

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

Сохранение текущих изменений модели в отдельной операции истории.

Пример скрипта
// Пример создания панелей с записью создания каждой панели в отдельную 
// операцию истории
let panel = objects3d.NewPanel(100, 100, objects3d.PanelOrientation.front);
panel.Name = 'Фронтальная';
historyOperations.CommitCurrentChanges('Создание фронтальной панели');

panel = objects3d.NewPanel(200, 200, objects3d.PanelOrientation.vertical);
panel.Name = 'Вертикальная';
historyOperations.CommitCurrentChanges('Создание вертикальной панели');  

Отмена текущих изменений модели.

Пример скрипта
// Пример отмены изменений в модели - отмена создания первой панели
// По завершении скрипта в модель будет добавлена только вертикальная панель
let panel = objects3d.NewPanel(100, 100, objects3d.PanelOrientation.front);
panel.Name = 'Фронтальная';
historyOperations.RevertCurrentChanges();

panel = objects3d.NewPanel(1200, 200, objects3d.PanelOrientation.vertical);
panel.Name = 'Вертикальная';  

Регистрация изменения объекта.

Регистрация изменения выполняется до непосредственного изменения, так как во время вызова метода в историю сохраняется текущее состояние объекта.

Пример скрипта
// Пример регистрации изменения объекта - создание фронтальной панели в одной 
// операции и изменение её контура в другой
let panel = objects3d.NewPanel(100, 100, objects3d.PanelOrientation.front);
panel.Name = 'Фронтальная';
historyOperations.CommitCurrentChanges('Создание фронтальной панели');
// Регистрация изменения объекта производится до изменения объекта
historyOperations.RegisterObjectChanging(panel);
let contour = panel.Contour;
contour.Clear();
contour.AddCircle(0, 0, 100);
panel.Build();
historyOperations.CommitCurrentChanges('Изменение контура панели');  

Очистка истории.

Пример скрипта
// Пример очистки истории
historyOperations.ClearHistory();  

Работа с материалами

Встроенный модуль materialData содержит методы для работы с материалами.

Изменение текущих активных материала объекта и материала кромки

Встроенный модуль materialData предоставляет несколько функций для изменения текущего активного материала:

Пример скрипта
//Пример изменения текущих активных материала объекта и материала кромки
materialData.SetupActiveMaterial('ДСП бук 16\r60', 16, 0);
materialData.SetupActiveButtMaterial('Кромка ПВХ Бук 0,4/19', 0.4, 0, true, 'Бук0,4/19', 0, true, 0);
let panel = objects3d.NewPanel(100, 100, objects3d.PanelOrientation.front);
panelOperations.AddButt(panel, 0);

materialData.ChooseActiveFurnMaterial();
materialData.ChooseActiveButtMaterial();
panel = objects3d.NewPanel(100, 100, objects3d.PanelOrientation.vertical);
panelOperations.AddButt(panel, 0);  

Разделение полного имени материала на наименование и артикул

Наименование и артикул материала хранятся в одной строке (полное имя материала), разделённые символом перевода каретки \r. Встроенный модуль materialData содержит следующие методы для упрощения извлечения наименования и артикула и форматирования полного имени материала:

Пример скрипта
// Пример извлечения частей и форматирования полного имени материала
const material = materialData.ChooseActiveFurnMaterial();
const name = materialData.ExtractMaterialName(material.name);
const code = materialData.ExtractMaterialCode(material.name);
const formatted = materialData.FormatMaterialName(material.name);
console.log(`Наименование: "${name}"\nАртикул: "${code}"\nФорматированное имя материала: "${formatted}"`)  

Работа с трёхмерной геометрией

Математические операции над векторами

Встроенный модуль geometry3d предоставляет методы для совершения математических операций над векторами.

Пример скрипта
// Пример математических операций над векторами без привязки к СК

const stringify = JSON.stringify;

// Создание вектора с помощью функции 
let axisX = geometry3d.VectorMake(1, 0, 0);
let axisY = geometry3d.VectorMake(0, 1, 0);
// Создание вектора с использованием константы
let axisZ = AxisZ;

console.log(`Сумма векторов X и Y = ${stringify(geometry3d.VectorAdd(axisX, axisY))}`);
console.log(`Разность векторов X и Y = ${stringify(geometry3d.VectorSub(axisX, axisY))}`);
console.log(`Вектор X умноженный на 3 = ${stringify(geometry3d.VectorMul(axisX, 3))}`);
console.log(`Коллинеарность (со-/противонаправленность) векторов X и Y = ${stringify(geometry3d.VectorsAreColinear(axisX, axisY))}`);
let zResult = geometry3d.VectorCross(axisX, axisY);
console.log(`Вектороное умножение векторов X и Y = ${stringify(zResult)}`);
console.log(`Оно равно оси Z? - ${geometry3d.VectorEqual(axisZ, zResult)}`);
console.log(`Скалярное умножение векторов X и Y = ${geometry3d.VectorDot(axisX, axisY)}`);
console.log(`Инвертированный вектор X = ${stringify(geometry3d.VectorInvert(axisX))}`);
let v = geometry3d.VectorMake(30, 40, 50);
console.log(`Исходный вектор = ${stringify(v)}`);
console.log(`Длина вектора = ${geometry3d.VectorLength(v)}`);
console.log(`Нормализованный вектор (сонаправленный вектор единичной длины) = ${stringify(geometry3d.VectorNormalize(v))}`);  

Работа с векторами относительно СК объектов

Каждый 3D объект предоставляет методы для преобразования векторов из/в СК объекта:

Пример скрипта
// Пример математических операций над векторами относительно СК объектов 1
function LogVector(v, prefix) {
    console.log(`${prefix}: ${JSON.stringify(v)}`);
}

/**
 * Вывод минимальной и максимальной точек объектов относительно ГСК и ЛСК друг друга
 * @param {TObject3D[]} objList Список объектов
 */
function LogObjectsMinMax(objList) {
    let zero = geometry3d.VectorMake(0, 0, 0);
    for (let i = 0; i < objList.length; i++) {
        let obj = objList[i];
        let min = obj.GMin;
        LogVector(obj.ToObject(zero), `Начало ГСК относительно СК объекта ${obj.Name}`);
        LogVector(min, `Минимальная координата объекта ${obj.Name} относительно её СК`);
        LogVector(panel1.ToGlobal(min), `Минимальная координата объекта ${obj.Name} относительно  ГСК`)
        for (let k = 0; k < objList.length; k++)
            if (k != i) {
                let obj2 = objList[k]
                LogVector(obj2.ObjectToObject(obj, min), `Минимальная координата объекта ${obj.Name} относительно СК объекта ${obj2.Name}`);
            }

        let max = obj.GMax;
        LogVector(max, `Максимальная координата объекта ${obj.Name} относительно её СК`);
        LogVector(panel1.ToGlobal(max), `Максимальная координата объекта ${obj.Name} относительно  ГСК`)
        for (let k = 0; k < objList.length; k++)
            if (k != i) {
                let obj2 = objList[k]
                LogVector(obj2.ObjectToObject(obj, max), `Максимальная координата объекта ${obj.Name} относительно СК объекта ${obj2.Name}`);
            }
    }
}

let block1 = objects3d.NewBlock('Блок 1');
block1.PositionX = 200;
let panel1 = objects3d.NewPanel(100, 100, objects3d.PanelOrientation.front, block1);
panel1.Name = 'Панель 1';
panel1.PositionX = 500;
panel1.Build();


let block2 = objects3d.NewBlock('Блок 2');
block2.PositionY = 300;
let panel2 = objects3d.NewPanel(50, 50, objects3d.PanelOrientation.front, block2);
panel2.Name = 'Панель 2';
panel2.PositionY = 600;
panel2.Build();

LogObjectsMinMax([panel1, panel2, block1, block2]);  
Пример скрипта
// Пример математических операций над векторами относительно СК объектов 2
function LogVector(v, prefix) {
    console.log(`${prefix}: ${JSON.stringify(v)}`);
}

/**
 * Вывод направления осей XYZ объектов относительно ГСК и ЛСК друг друга
 * @param {TObject3D[]} objList Список объектов
 */
function LogObjectsAxes(objList) {
    for (let i = 0; i < objList.length; i++) {
        let obj = objList[i];
        LogVector(obj.NToObject(AxisX), `Ось Х ГСК относительно СК объекта ${obj.Name}`);
        LogVector(obj.NToObject(AxisY), `Ось Y ГСК относительно СК объекта ${obj.Name}`);
        LogVector(obj.NToObject(AxisZ), `Ось Z ГСК относительно СК объекта ${obj.Name}`);
        LogVector(obj.NToGlobal(AxisX), `Ось Х СК объекта ${obj.Name} относительно ГСК`);
        LogVector(obj.NToGlobal(AxisY), `Ось Y СК объекта ${obj.Name} относительно ГСК`);
        LogVector(obj.NToGlobal(AxisZ), `Ось Z СК объекта ${obj.Name} относительно ГСК`);
        for (let k = 0; k < objList.length; k++)
            if (k != i) {
                let obj2 = objList[k]
                LogVector(obj2.ObjectToObject(obj, AxisX), `Ось X объекта ${obj.Name} относительно СК объекта ${obj2.Name}`);
                LogVector(obj2.ObjectToObject(obj, AxisY), `Ось Y объекта ${obj.Name} относительно СК объекта ${obj2.Name}`);
                LogVector(obj2.ObjectToObject(obj, AxisZ), `Ось Z объекта ${obj.Name} относительно СК объекта ${obj2.Name}`);
            }
    }
}

let front = objects3d.NewPanel(100, 100, objects3d.PanelOrientation.front);
front.Name = "Фронтальная"
let vertical = objects3d.NewPanel(100, 100, objects3d.PanelOrientation.vertical);
vertical.Name = "Вертикальная"
let horizont = objects3d.NewPanel(100, 100, objects3d.PanelOrientation.horizont);
horizont.Name = "Горизонтальная"

LogObjectsAxes([front, vertical, horizont]);  

Работа с данными объекта

Встроенный модуль objectData предоставляет функции для работы с данными объекта (пользовательскими свойствами, дополнительными материалами и т.д.)

Получение списка пользовательских свойств выделенного объекта

Пример скрипта
// Пример получения списка пользовательских свойств выделенного объекта

// Проверка версии скрипта и выброс ошибки, если версия < 3
apiVersion.AwareAndThrowIfApiVersionIsLowestThan(3);
let obj = currentFileData.model.SelectedObj;
if (obj) {
    const properties = objectData.GetObjectUserProperties(obj);
    let output = '';
    // Добавление пользовательских свойств в строку вывода в виде
    // <Имя>: <Значение>
    properties.forEach((value, key) => {
        output += `"${value}": "${key}"\r\n`;
    })
    UI.dialogs.MessageBox(output);
}  

Получение списка дополнительных материалов выделенного объекта

Пример скрипта
// Пример получения списка дополнительных материалов выделенного объекта

// Проверка версии скрипта и выброс ошибки, если версия < 3
apiVersion.AwareAndThrowIfApiVersionIsLowestThan(3);
let obj = currentFileData.model.SelectedObj;
if (obj) {
    const properties = objectData.GetObjectAdditionalMaterials(obj);
    if (properties.length > 0) {
        let output = '';
        // Добавление дополнительных материалов в строку вывода в виде
        // <Наименование> <Артикул> <Количество> <Единицы измерения>
        properties.forEach(mt => {
            output += `"${mt.name}" "${mt.art}" ${mt.count} "${mt.measure}"\r\n`
        });
        UI.dialogs.MessageBox(output);
    }
    else
        UI.dialogs.MessageBox('У выделенного объекта нет дополнительных материалов')
}  

Получение заметок выделенного объекта

Пример скрипта
// Пример получения заметок выделенного объекта

// Проверка версии скрипта и выброс ошибки, если версия < 3
apiVersion.AwareAndThrowIfApiVersionIsLowestThan(3);
let obj = currentFileData.model.SelectedObj;
if (obj) {
    const notes = objectData.GetObjectNotes(obj);
    if (notes)
        UI.dialogs.MessageBox('Заметки:\r\n' + notes);
    else
        UI.dialogs.MessageBox('Объект не содержит заметок');
}  

Сохранение и загрузка объектов модели

Встроенный модуль modelIOOperations предоставляет функции для сохранения/загрузки модели в разные форматы.
Важно: при загрузке новой модели, старая модель уничтожается. Все переменные, указывающие на объекты старой модели, будут указывать на некорректные объекты. Следует отказаться от использования таких переменных, либо задать им новое значение во избежание возникновения ошибок.

Стандартные форматы системы Базис

Пример скрипта
// Пример добавления панели и сохранения изменённой модели в файл, а также 
// загрузки модели
objects3d.NewPanel(100, 100);
historyOperations.CommitCurrentChanges('тест 1');
modelIOOperations.SaveModelToFile('тест 1.b3d');

objects3d.NewPanel(200, 200);
historyOperations.CommitCurrentChanges('тест 2');
modelIOOperations.SaveModelToFile('тест 2.b3d');

modelIOOperations.LoadModelFromFile('тест 1.b3d');  

Сторонние форматы

Пример скрипта
// Пример экспорта панели в файл полигонального формата и импорта 
// экспортированной панели обратно в модель.
const fileName = 'Панель.obj';

let panel = objects3d.NewPanel(1000, 1000);
panel.Name = 'Исходная панель';
panel.Build();
modelIOOperations.ExportModelMeshFormat(panel, fileName);

let importedObjects = modelIOOperations.ImportModelMeshFormat(fileName);
importedObjects.PositionX += 1500;
importedObjects.Objects[0].Name = 'Импортированная панель'
  

Актуальные данные модели

Встроенный модуль currentFileData предоставляет свойства, возвращающие актуальные (на момент доступа к свойству) значения.
Примечание: в большинстве случаев достаточно лишь одного обращения к свойствам встроенного модуля currentFileData. В случае загрузки/создания новой модели следует произвести повторное обращение к используемым свойствам модуля для получения новых данных.

Пример скрипта
// Пример получения актуальных данных модели и опериования ими

/**
 * Получить количество вложенных объектов (рекурсивно)
 * @param {T3DObjectList} objList 
 */
function GetInternalObjectCountRecursive(objList) {
    let result = objList.Count;
    for (let i = 0; i < objList.Count; i++) {
        let obj = objList.Objects[i];
        if (obj.List)
            result += GetInternalObjectCountRecursive(obj);
    }
    return result;
}

let model = currentFileData.model;
console.log(`Количество объектов верхнего уровня в модели: ${model.Count}`);
console.log(`Общее количество объектов в модели: ${GetInternalObjectCountRecursive(model)}`);

let article = currentFileData.article;
console.log('Параметры изделия:');
console.log(`Наименование: ${article.Name}\nАртикул: ${article.Code}`);

console.log(`Имя текущего редактруемого файла: ${currentFileData.filename}`);  

Расстановка позиций

Встроенный модуль arrangePositions предоставляет свойства и функции, необходимые для расстановки позиций и обозначений на объекты.

Запуск расстановки позиций

Пример скрипта
let arranger = arrangePositions.NewArranger();
arranger.parameters.arrangeMode = arrangePositions.ArrangeMode.allObjects;
arranger.parameters.designationPrefix = currentFileData.article.ShortSign;
arranger.parameters.list = currentFileData.model;
arranger.parameters.options.LoadFromSettings();
arranger.parameters.selectedOnly = false;
if (arranger.ArrangeObjects()) {
    UI.dialogs.MessageBox('Расстановка позиций выполнена успешно');
    historyOperations.CommitCurrentChanges('Выполнение расстановки позиций в скрипте')
}  

Сохранение параметров расстановки в файл JSON

Пример скрипта
// Пример сохранения параметров расстановки позиций в файл в формате JSON
// Необходимая версия API >=2
let options = arrangePositions.NewArranger().parameters.options;
options.SaveToJSON('arrangeOptions.json');  

Загрузка параметров расстановки из файла JSON

Пример скрипта
// Пример загрузки параметров расстановки из файла в формате JSON и
// выполнения расстановки позиций с загруженными параметрами.
// Необходимая версия API >=2
let arranger = arrangePositions.NewArranger();
arranger.parameters.options.LoadFromJSON('arrangeOptions.json');
arranger.parameters.arrangeMode = arrangePositions.ArrangeMode.allObjects;
arranger.parameters.designationPrefix = currentFileData.article.ShortSign;
arranger.parameters.list = currentFileData.model;
arranger.parameters.selectedOnly = false;
arranger.ArrangeObjects();
historyOperations.CommitCurrentChanges(
    'Выполнение расстановки позиций в скрипте по загруженным параметрам'
);  

Работа с 3D объектами

Определение ориентации панели относительно СК

Понятия "вертикальная", "горизонтальная", "фронтальная" для ориентации панели являются абстрактными. Они используются для определения поворота панели относительно ГСК при её создании. Для самостоятельного определения ориентации панели необходимо:

  1. Определить, относительно какой СК будет считаться ориентация панели
  2. Перевести нормаль по оси Z из ЛСК панели в определённую СК
  3. Сравнить полученную нормаль с нормалями по осям X, Y и Z
Пример скрипта
// Пример определения ориентации панели относительно другой СК.
// В этом примере будут созданы два блока, каждый с панелью внутри, имитирующей
// фасад модуля.
// Один из блоков будет повёрнут так, что фасад относительно ГСК станет 
// "вертикальной" панелью.
//
// Ориентация панели определяется с помощью перевода вектора, направленного
// вдоль оси Z, из ЛСК панели в необходимую СК. Полученный вектор определяет,
// куда "смотрит" лицевая пласть панели относительно использованной СК
const { NewBlock, NewPanel, PanelOrientation } = objects3d;
const { VectorsAreColinear } = geometry3d;
let centerBlock = NewBlock('Центральный блок');
let leftBlock = NewBlock('Левый блок');
objectTransformation.RotateObject(leftBlock, AxisY, 90, true);;
let centerPanel = NewPanel(100, 100, PanelOrientation.front, centerBlock);
centerPanel.Name = 'Фасад центральный';
let leftPanel = NewPanel(200, 200, PanelOrientation.front, leftBlock);
leftPanel.Name = 'Фасад левый';
leftBlock.Build();
centerBlock.Build();
leftPanel.TranslateLCS(geometry3d.VectorMake(-leftPanel.GSize.x - centerPanel.Thickness, 0, - leftPanel.Thickness));
// Ориентация панели относительно ГСК
LogPanelOrientationDependOnCS(centerPanel);
// Ориентация панели относительно объекта-владельца
LogPanelOrientationDependOnCS(centerPanel, centerBlock);
// Ориентация панели относительно ГСК
LogPanelOrientationDependOnCS(leftPanel);
// Ориентация панели относительно объекта-владельца
LogPanelOrientationDependOnCS(leftPanel, leftBlock);


/**
 * Записать в лог ориентацию панели относительно заданной СК.
 * Если объект, задающий СК не указан, ориентация считается относительно ГСК
 * @param {TFurnPanel} panel Панель.
 * @param {TObject3D} [CSOwner] Объект, задающий СК
 */
function LogPanelOrientationDependOnCS(panel, CSOwner) {
    let dir, dependString;
    if (CSOwner) {
        dir = CSOwner.NObjectToObject(panel, AxisZ);
        dependString = `СК объекта "${CSOwner.Name}"`;
    }
    else {
        dir = panel.NToGlobal(AxisZ);
        dependString = 'ГСК';
    }
    let orientationString;
    if (VectorsAreColinear(dir, AxisX))
        orientationString = 'Вертикальная';
    else if (VectorsAreColinear(dir, AxisY))
        orientationString = 'Горизонтальная';
    else if (VectorsAreColinear(dir, AxisZ))
        orientationString = 'Фронтальная';
    else
        orientationString = 'Не ортогональная';
    console.log(`Ориентация объекта "${panel.Name}" относительно ${dependString} : "${orientationString}"`);
}  

Добавление облицовки пласти на панель

Пример скрипта
//Пример добавления облицовки пласти разными материалами с обеих сторон панели.
let panel = objects3d.NewPanel(100, 100);
panel.Name = 'Панель с облицовкой пласти (скрипт)';
let material = materialData.CreateMaterialData('"Передний" пластик', 0.5);
panelOperations.AddPlastic(panel, true, material);
material = materialData.CreateMaterialData('"Задний" пластик', 1);
panelOperations.AddPlastic(panel, false, material);  

Добавление облицовки кромки на панель

Пример скрипта
//Пример добавления облицовки кромки на каждый элемент контура панели.
let panel = objects3d.NewPanel(100, 100);
panel.Name = 'Панель с облицовкой кромки (скрипт)';
for (let i = 0; i < panel.Contour.Count; i++) {
    panelOperations.AddButt(panel, i);
}  

Добавление пазов на панель

Пример скрипта
//Пример добавления пазов (четверть и выемка) на панель.
let panel = objects3d.NewPanel(200, 200);
panel.Name = 'Панель с пазами (скрипт)';
// СК контура паза типа "выемка" эквивалентна СК контура панели
// Знак толщины выемки определяет сторону панели:
// При значении толщины больше нуля, выемка строится по направлени +Z от минимальной глубины панели
// При значении толщины меньше нуля, выемка строится по направлени -Z от максимальной глубины панели
let pocket = panelOperations.AddCut(panel, panelOperations.cutType.extrusion, 'Выемка', 'Выемка 10');
pocket.Contour.AddRectangle(50, 50, 150, 150);
pocket.Thickness = -10;

// СК траектории паза типа "по произвольной траектории" эквивалентна СК контура панели.
// СК контура сечения паза вычисляется относительно ЛСК панели и направления траектории в её начале:
// 1. Направлении оси Z сонаправлено с направлением траекториии в её начальной точке.
// 2. Направление оси Y всегда сонаправлено с направлением оси Z СК траектории (и ЛСК панели).
// 3. Направление оси X вычисляется векторным произведением найденных направлений осей Y и Z
let quarter1 = panelOperations.AddCut(panel, panelOperations.cutType.freeForm, 'Четверть', '5x5');
// В этом примере траектория паза идёт по направлению -X.
// Соответственно, остальные оси СК контура сечения паза:
// Ось Y идёт по направлению +Z панели (всегда) 
// Ось Х вычисляется по описанию выше: (+Z) * (-X) = (-Y)
// Таким образом в СК сечения паза тело панели находится в прямоугольнике с углами (0, 0) и (-200, 200);
quarter1.Trajectory.AddLine(200, 0, 0, 0);
quarter1.Contour.AddRectangle(0, 0, -5, 5);


const contourY = AxisZ;
let ref = new ReferenceObject();
// Получение двумерного вектора направления первого элемента траектории в его начальной точке
quarter1.Trajectory.Objects[0].TangentOn(0, ref);
let contourZ = ref.value;
// Добавление двумерному вектору значения по оси Z во избежание ошибок в трёмерных вычислениях 
contourZ.z = 0;
const contourX = geometry3d.VectorCross(contourY, contourZ);
console.log(`Направление оси X контура сечения паза относительно ЛСК панели: ${JSON.stringify(contourX)}`);
console.log(`Направление оси Y контура сечения паза относительно ЛСК панели: ${JSON.stringify(contourY)}`);
console.log(`Направление оси Z контура сечения паза относительно ЛСК панели: ${JSON.stringify(contourZ)}`);  

Операции с фурнутурой

Работа с объектом для сверления отверстий

Пример скрипта
// Пример определения отверстий, которые сверлятся в выделенную панель.

/**Объект, использующийся для сверления отверстий в объектах */
const holeDrilling = fastenerOperations.NewHoleDrilling();

/**
 * Вывести сообщения об отверстиях, сверлящихся в тело
 * Каждое сообщение содержит наименование фурнитуры и 
 * параметры сверлящегося отверстия фурнитуры
 * @param {TBodyDrillInfo} info Информация о сверлении тела
 */
function OutputDrilledHoles(info) {
    for (let i = 0; i < info.Holes.Count; i++) {
        currentFileData.model.UnPickAll();
        const hole = info.Holes.Items[i];
        const fastener = hole.Fastener;
        fastener.Selected = true;
        UI.dialogs.MessageBox(`Фурнитура: "${materialData.FormatMaterialName(fastener.Name)}"\r\nОтверстие:${hole.Diameter.toFixed(3)}x${hole.Depth.toFixed(3)}`);
    }

}

/**
 * Вывести сообщения о фурнитуре, сверлящейся в тело
 * Каждое сообщение содержит наименование фурнитуры и 
 * набор параметров сверлящихся отверстий, принадлежащих фурнитуре
 * @param {TBodyDrillInfo} info Информация о сверлении тела
 */
function OutputDrilledHoles_GroupByFastener(info) {
    /** Коллекция "фурнитура - список отверстий" @type {Map<TFastener, TDrilledHole[]>} */
    const fastenerMap = new Map();
    for (let i = 0; i < info.Holes.Count; i++) {
        const hole = info.Holes.Items[i];
        const fastener = hole.Fastener;
        const holes = fastenerMap.get(fastener);
        if (holes)
            fastenerMap.set(fastener, holes.concat([hole]));
        else
            fastenerMap.set(fastener, [hole]);
    }
    // Вывод сообщений
    fastenerMap.forEach((holes, fastener) => {
        currentFileData.model.UnPickAll();
        fastener.Selected = true;
        const holeData = [];
        holes.forEach(hole => holeData.push(`  ${hole.Diameter.toFixed(3)}x${hole.Depth.toFixed(3)}`))
        UI.dialogs.MessageBox(`Фурнитура: "${materialData.FormatMaterialName(fastener.Name)}"\r\nОтверстия:\r\n${holeData.join('\r\n')}`);
    })
}

const selectedObject = currentFileData.model.Selected;
if (selectedObject) {
    currentFileData.model.UnPickAll();
    // Добавление панели, как тела, для которого будет рассчитываться сверление
    holeDrilling.AddBody(selectedObject);
    // Добавление всей фурнитуры из модели, т.к. заранее не известно, 
    // какая фурнитура сверлится в выделенную панель
    holeDrilling.AddFasteners(currentFileData.model);
    // Выполнение расчёта сверления отверстий
    holeDrilling.DrillHoles();
    const info = holeDrilling.Bodies.FindBodyInfo(selectedObject);

    // Вывод сообщения для каждого сверлящегося отверстия
    // OutputDrilledHoles(info);

    // Вывод сообщения для каждой сверлящейся фурнитуры 
    // (в одном сообщение могут быть параметры нескольких отверстий)
    OutputDrilledHoles_GroupByFastener(info);


    // Возвращение выделения панели
    currentFileData.model.UnPickAll();
    selectedObject.Selected = true;
}
else
    UI.dialogs.ErrorBox('Не выделен объект!')  

Работа с информацией о фурнитуре

Пример скрипта
// Пример выбора и установки фурнитуры.

/**
 * Установка обычной фурнитуры
 * @param {TFurnitureInfo} info 
 */
function MountFastener(info) {
    /**@type {MountParams} */
    const params = {};
    params.panel1 = interaction.getRequest.GetObject('Укажите панель 1', objectTypeChecker.ObjectTypeValue.panel);
    params.panel2 = interaction.getRequest.GetObject('Укажите панель 2', objectTypeChecker.ObjectTypeValue.panel);
    params.position = interaction.getRequest.GetVector('Укажите положение фурнитуры');
    fastenerOperations.MountFurniture(info, params);
}

/**
 * Установка схемы крепежа
 * @param {TFurnitureInfo} info 
 */
function MountScheme(info) {
    /**@type {MountParams} */
    const params = {};
    params.panel1 = interaction.getRequest.GetObject('Укажите панель 1', objectTypeChecker.ObjectTypeValue.panel);
    params.panel2 = interaction.getRequest.GetObject('Укажите панель 2', objectTypeChecker.ObjectTypeValue.panel);
    params.basePlane = fastenerOperations.basePlaneMount.inside;
    fastenerOperations.MountFurniture(info, params);
}

/**
 * Установка секции
 * @param {TFurnitureInfo} info 
 */
function MountBox(info) {
    /**@type {MountParams} */
    const params = {};
    const limits = objects3d.NewLimits();
    const position = interaction.getRequest.GetVector('Укажите положение секции');
    limits.Position = position;
    interaction.windowData.SetPointAnchoring(true);
    // Установка обработчика движения мыши для изменения размеров габаритной рамки,
    // описывающей будущий размер секции.
    interaction.events.SetMouseMoveHandler(() => {
        let newPoint = interaction.windowData.GetPoint3D();
        limits.LimitSize = geometry3d.VectorSub(newPoint, position);
        limits.Build()
    })
    const secondPos = interaction.getRequest.GetVector('Укажите положение противоположного угла секции');
    // Сброс обработчика движения мыши
    interaction.events.SetMouseMoveHandler(undefined);
    const size = geometry3d.VectorSub(secondPos, position);
    // Проверка всех координат во избежание некорректной установки.
    // Размер по каждой оси должен иметь положительное значение.
    ['x', 'y', 'z'].forEach(key => {
        if (size[key] < 0) {
            size[key] = Math.abs(size[key]);
            position[key] -= size[key];
        }
    });
    params.boxSize = size;
    objects3d.DeleteObject(limits);
    const box = fastenerOperations.MountFurniture(info, params);
    box.Position = position;
}

const furnInfo = fastenerOperations.CreateFurnitureInfo();
if (fastenerOperations.ChooseFurnitureInfo(furnInfo,
    fastenerOperations.PARAM_FASTENER_FILTER_ALL, fastenerOperations.DATUM_MODE_FILTER_ALL)) {
    const datumMode = furnInfo.FindDatumMode();
    switch (datumMode) {
        case fastenerOperations.datumMode.faceButt:
        case fastenerOperations.datumMode.faceEdge:
        case fastenerOperations.datumMode.faceFace:
        case fastenerOperations.datumMode.parallelFaces:
            MountFastener(furnInfo);
            break;
        case fastenerOperations.datumMode.joint:
            MountScheme(furnInfo);
            break;
        case fastenerOperations.datumMode.box:
            MountBox(furnInfo);
            break;
    }
}  

Взаимодействие с пользователем

Запрос элементов из окна модели

Пример скрипта
// Пример вызова функций для запроса элементов из окна модели и обработки полученного результата
let vector = interaction.getRequest.GetVector('Укажите точку');
console.log(`Указана точка с координатами ${JSON.stringify(vector)}`);
let object = interaction.getRequest.GetObject('Укажите объект');
console.log(`Указан объект с именем "${object.Name}"`);
let panel = interaction.getRequest.GetObject('Укажите панель', objectTypeChecker.ObjectTypeValue.panel);
console.log(`Указана панель с именем "${panel.Name}"`);  

Работа с диалоговыми окнами

Пример скрипта
// Пример скрипта, использующего функционал диалоговых окон
// В этом скрипте: 
// 1. Запрашивается файл у пользователя
// 2. Выводится содержимое выбранного файла и запрашивается подтверждение
//    сохранения содержимого в файл под другим именем
// 3. В случае подверждения сохраняется содержимое в выбранный файл и выводится
//    сообщение о сохранении файла
const fs = require('fs');
const path = require('path');
/**@type {DialogParams} */
let dialogParams = {
    extensions: ['txt'],
    initialDir: '',
    title: 'Выберите файл для отображения его содержимого'
}
const button = UI.dialogs.DialogMessageButton;
let filename = UI.dialogs.RunOpenFileDialog(dialogParams);
if (filename) {
    let content = fs.readFileSync(filename, 'utf8');
    if (UI.dialogs.RunMessageDialog(
        `Содержимое файла:\n${content}\nСохранить его под другим именем?`,
        UI.dialogs.DialogMessageType.confirmation,
        new Set([button.yes, button.no])) == UI.dialogs.DialogMessageResult.yes) {
        dialogParams.initialDir = path.dirname(filename);
        filename = UI.dialogs.RunSaveFileDialog(dialogParams);
        if (filename) {
            fs.writeFileSync(filename, content);
            UI.dialogs.MessageBox(`Файл ${filename} сохранён!`);
        }
        else
            UI.dialogs.ErrorBox('Файл не был выбран');
    }
}
else
    UI.dialogs.ErrorBox('Файл не был выбран');  

Обработка событий в окне модели

Пример скрипта
// Пример обработки событий в окне модели.
// В этом примере задаются обработчики событий нажатия клавиши мыши и нажатия кнопок.
// При нажатии кнопки мыши работает следующий алгоритм:
// 1. Продолжаем, если нажата левая клавиша мыши
// 2. Если в списке клавиш-модификаторов нет клавиши "Shift", снимаем выделение
//    со всех объектов.
// 3. Получаем данные модели относительно положения мыши.
// 4. Если под курсором мыши есть объект, инвертируем его выделение
//
// При нажатии клавиши клавиатуры работает следующий алгоритм:
// 1. Продолжаем, если нажата кнопка "Escape",
// 2. Запрашиваем подтверждение отмены скрипта у пользователя.
// 3. Если пользователь не подтвердил отмену скрипта, выставляем значение 
//    кнопки равное нулю. Это предотвратит дальнейшую обработку нажатия
//    клавиши "Escape" после завершения назначенного обработчика

execution.ContinueExecution();
const shiftState = UI.constants.shiftState;

let clickPos;
interaction.events.SetMouseDownHandler((sender, button, shift, x, y) => {
    if (button == UI.constants.mouseButton.left) {
        if (!shift.has(shiftState.shift)) {
            currentFileData.model.UnSelectAll();
        }
        let pointInfo = interaction.windowData.GetPointInfo(x, y);
        if (pointInfo && pointInfo.obj)
            pointInfo.obj.Selected = !pointInfo.obj.Selected;
    }
})

interaction.events.SetKeyDownHandler((sender, key, shift) => {
    if (key.value == UI.constants.keys.escape) {
        if (!UI.dialogs.RunYesNoDialog('Отменить выполнение скрипта?'))
            key.value = 0;
    }
})