// module: TRUE
// @ts-ignore
import { gui } from "./d3plot_automotive_assessment_gui.jsi";
import { JSPath } from "../../modules/shared/path.mjs";
import { WorkflowOccupant } from "../../modules/shared/workflow_occupant.mjs";
import {
ProtocolAssessment,
DoStructureAssessmentStructureData
} from "../../modules/post/d3plot/d3plot_automotive_assessment.mjs";
import { Protocol, Protocols } from "../../modules/shared/protocols.mjs";
import { AssessmentType } from "../../modules/shared/assessment_types.mjs";
import { Structure } from "../../modules/shared/structure.mjs";
/**
* Object to store data for B-Pillar
* @typedef {Object} BPillarStructure
* @property {string} cut_section_method Cut section method
* @property {number[]} cut_section_nodes Cut section nodes
* @property {number[]} pre_crash_parts Pre-crash parts
* @property {number[]} post_crash_parts Post-crash parts
* @property {number[]} shift_deform_nodes Shift deform nodes
* @property {number} ground_z Ground Z coordinate
* @property {number} seat_centre_y Seat centre Y coordinate
* @property {number} h_point_z H-Point Z coordinate
*/
/**
* Object to store data for Head Excursion
* @typedef {Object} HeadExcursionStructure
* @property {number} cut_section_thickness Cut section thickness
* @property {number} cut_section_node Cut section node
* @property {string} vehicle_direction Vehicle direction (either 'positive X' or 'negative X')
* @property {number[]} head_parts Head parts
* @property {number[]} barrier_parts Barrier parts
* @property {number[]} shift_deform_nodes Shift deform nodes
* @property {number} seat_centre_y Seat centre Y coordinate
* @property {number} intrusion_from_seat_centre_y Intrusion From Seat centre Y coordinate
* @property {string} countermeasure Countermeasure (either 'Yes' or 'No')
* @property {string} keyword_file Keyword filename
*/
/**
* The user data object in the workflow file
* @typedef {Object} UserData
* @property {string} crash_test Crash test
* @property {string[]} regulations Regulations
* @property {string} version Version
* @property {string} drive_side either 'LHD' or 'RHD' vehicle
* @property {WorkflowOccupant[]} occupants Array of WorkflowOccupants
* @property {Structure[]} structures Array of Structures
* @property {BPillarStructure} b_pillar B-Pillar structure
* @property {HeadExcursionStructure} head_excursion Head Excursion structure
*/
/* Set the workflows directory */
JSPath.SetWorkflowsDirectory(JSPath.POST);
/* Initialise the Protocols class */
Protocols.Initialise();
/* Setup the GUI and show the main window */
if (gui) {
try {
setup_gui();
} catch (e) {
ErrorMessage(`Something went wrong: ${e}.\n${e.stack}`);
}
}
function setup_gui() {
/* Callbacks */
gui.wdw_d3plot_automotive.btn_run.onClick = run;
gui.wdw_d3plot_automotive.cbx_regulation.onChange = update_versions;
gui.wdw_d3plot_automotive.cbx_version.onChange = update_menu;
gui.wdw_d3plot_automotive.lbx_structures.onClick = update_menu;
gui.wdw_d3plot_automotive.lbx_structure_assessment_types.onClick = update_menu;
gui.wdw_d3plot_automotive.btn_select_all_structures.onClick = toggle_all_widget_items;
gui.wdw_d3plot_automotive.btn_select_all_structure_assessment_types.onClick = toggle_all_widget_items;
gui.wdw_d3plot_automotive.btn_deselect_all_structures.onClick = toggle_all_widget_items;
gui.wdw_d3plot_automotive.btn_deselect_all_structure_assessment_types.onClick = toggle_all_widget_items;
gui.wdw_d3plot_automotive.cbx_images.onChange = update_image;
/* Add ticks to select all buttons and crosses to deselect all buttons */
gui.wdw_d3plot_automotive.btn_select_all_structures.Tick();
gui.wdw_d3plot_automotive.btn_select_all_structure_assessment_types.Tick();
gui.wdw_d3plot_automotive.btn_deselect_all_structures.Cross();
gui.wdw_d3plot_automotive.btn_deselect_all_structure_assessment_types.Cross();
/* Check the crash test type for each selected model is the same
* don't allow different ones for now */
let num_selected_models = Workflow.NumberOfSelectedModels();
let crash_test = "";
for (let i = 0; i < num_selected_models; i++) {
/** @type {UserData} */
// @ts-ignore
let user_data = Workflow.ModelUserDataFromIndex(i);
if (i == 0) {
crash_test = user_data.crash_test;
} else {
if (user_data.crash_test != crash_test) {
Window.Message("", "The crash test type for each model must be the same.");
return;
}
}
}
/* Store the crash test type and display on GUI label */
gui.crash_test = crash_test;
gui.wdw_d3plot_automotive.lbl_crash_test.text = `Crash Test: ${gui.crash_test}`;
/* Get list of possible options for the regulation combobox and add widget items */
/** @type Set<string> */
let regulations = new Set(); /* Use a Set to get unique list of regulations */
for (let i = 0; i < num_selected_models; i++) {
/** @type {UserData} */
// @ts-ignore
let user_data = Workflow.ModelUserDataFromIndex(i);
for (let regulation of user_data.regulations) {
regulations.add(regulation);
}
}
for (let regulation of regulations) new WidgetItem(gui.wdw_d3plot_automotive.cbx_regulation, regulation);
/* Get a list of all the different assessment types to store whether they are selected or not.
* Initially all are selected. Ideally we'd store the selection status on the widget items, but
* they get removed and created in the update_assessment_types() function, so this separate list
* is required */
let assessment_types = AssessmentType.GetAll();
gui.selected_assessment_types = {};
for (let assessment_type of assessment_types) {
gui.selected_assessment_types[assessment_type] = true;
}
/* Add Structures to listboxes */
for (let i = 0; i < num_selected_models; i++) {
/** @type {UserData} */
// @ts-ignore
let user_data = Workflow.ModelUserDataFromIndex(i);
let model_id = Workflow.ModelIdFromIndex(i);
let unit_system = Workflow.ModelUnitSystemFromIndex(i);
let structures = user_data.structures;
if (structures) {
for (let s of structures) {
let structure = Structure.CreateFromUserData(s);
/* Add a structure widget item to the combobox
*
* Store the structure instance, model_id and unit_system on it in
* a DoStructureAssessmentStructureData instance. This is used in
* the call to DoStructureAssessment() */
let structure_string = "";
if (num_selected_models > 1) {
structure_string = `M${model_id} - ${structure.toString()}`;
} else {
structure_string = structure.toString();
}
let structure_widget_item = new WidgetItem(gui.wdw_d3plot_automotive.lbx_structures, structure_string);
let extra_data = {};
if (structure.component_type == Structure.B_PILLAR) {
extra_data = user_data.b_pillar;
} else if (structure.component_type == Structure.HEAD_EXCURSION) {
extra_data = user_data.head_excursion;
}
// @ts-ignore
structure_widget_item.structure_data = new DoStructureAssessmentStructureData(
structure,
model_id,
unit_system,
extra_data
);
structure_widget_item.hover = structure.component_type;
}
}
}
/* Update the menu */
update_versions();
update_menu();
/* Show the window */
gui.wdw_d3plot_automotive.Show(false);
}
/**
* Returns an array of the selected structures
* @returns {DoStructureAssessmentStructureData[]}
*/
function get_selected_structures_data() {
/** @type {DoStructureAssessmentStructureData[]} */
let structures = [];
/** @type {WidgetItem[]} */
let widget_items = gui.wdw_d3plot_automotive.lbx_structures.WidgetItems();
if (!widget_items) return structures;
for (let wi of widget_items) {
if (wi.selected) {
// @ts-ignore
structures.push(wi.structure_data);
}
}
return structures;
}
/**
* Returns an array of the selected assessment types
* @returns {string[]}
*/
function get_selected_assessment_types() {
/** @type {string[]} */
let assessment_types = [];
/** @type {?WidgetItem[]} */
let widget_items = gui.wdw_d3plot_automotive.lbx_structure_assessment_types.WidgetItems();
if (!widget_items) return assessment_types;
for (let wi of widget_items) {
if (wi.selected) {
assessment_types.push(wi.text);
}
}
return assessment_types;
}
/**
* Update the menu
*/
function update_menu() {
/* Update the list of assessment types */
update_assessment_types();
}
/**
* Updates the combobox versions based on the currently selected regulation
*/
function update_versions() {
let regulation = gui.wdw_d3plot_automotive.cbx_regulation.selectedItem.text;
let versions = Protocol.Versions(regulation, gui.crash_test);
gui.wdw_d3plot_automotive.cbx_version.RemoveAllWidgetItems();
for (let version of versions) {
new WidgetItem(gui.wdw_d3plot_automotive.cbx_version, version);
}
}
/**
* Carries out assessments on the selected structures according
* to the selected regulation
*/
function run() {
/* Get the selected structures */
let structures_data = get_selected_structures_data();
/* Get the selected assessment_types */
let structure_assessment_types = get_selected_assessment_types();
if (structure_assessment_types.length == 0) {
Message("No structure assessment types selected");
return;
}
/* Get the selected regulation */
/** @type {string} */
let regulation = gui.wdw_d3plot_automotive.cbx_regulation.selectedItem.text;
/* Create a protocol instance */
let protocol = ProtocolAssessment.CreateDefaultProtocol(
regulation,
gui.crash_test,
gui.wdw_d3plot_automotive.cbx_version.selectedItem.text
);
/* Clear the widget items in the output listbox */
gui.wdw_d3plot_automotive.lbx_output.RemoveAllWidgetItems();
/* Clear the image on the widget */
gui.wdw_d3plot_automotive.lbl_image.ReadImageFile(null);
/* Do assessment on each structure */
let results = protocol.DoStructureAssessment(structures_data, structure_assessment_types);
/* Display the results in the output listbox */
for (let prop in results.output) {
new WidgetItem(gui.wdw_d3plot_automotive.lbx_output, `${prop}: ${results.output[prop]}`);
}
/* Populate the combobox to select images */
gui.wdw_d3plot_automotive.cbx_images.RemoveAllWidgetItems();
for (let image_filename of results.image_filenames) {
/* Get the model from the filename, which will be of the form
*
* <dir>/b_pillar_image_M<model_id>.png
* OR
* <dir>/head_excursion_image_M<model_id>.png */
let regex = /.*\/b_pillar_image_(M\d+)/;
let match = image_filename.match(regex);
if (match) {
let wi = new WidgetItem(gui.wdw_d3plot_automotive.cbx_images, match[1]);
/* Store the filename so we can update the image when it's selected */
// @ts-ignore
wi.image_filename = image_filename;
}
}
/* Get the first image and display it on a widget */
if (results.image_filenames.length == 0) {
gui.wdw_d3plot_automotive.lbl_image.ReadImageFile(null);
} else {
display_image(results.image_filenames[0]);
}
/* Redraw to show image */
gui.wdw_d3plot_automotive.Redraw();
}
/**
* Displays an image in the image widget
* @param {string} image_filename Filename of image to display
*/
function display_image(image_filename) {
if (!File.Exists(image_filename)) {
ErrorMessage(`Unable to find the image ${image_filename}.`);
gui.wdw_d3plot_automotive.lbl_image.ReadImageFile(null);
} else {
gui.wdw_d3plot_automotive.lbl_image.ReadImageFile(image_filename, Widget.SCALE);
}
gui.wdw_d3plot_automotive.Redraw();
}
/**
* Callback function when the user selects an image from the combobox
*/
function update_image() {
let image_filename = gui.wdw_d3plot_automotive.cbx_images.selectedItem.image_filename;
display_image(image_filename);
}
/**
* Selects or deselect all the widget items in a
* listbox depending on the button that is pressed.
*/
function toggle_all_widget_items() {
/** @type {WidgetItem[]} */
let widget_items = null;
if (
this == gui.wdw_d3plot_automotive.btn_select_all_structures ||
this == gui.wdw_d3plot_automotive.btn_deselect_all_structures
) {
widget_items = gui.wdw_d3plot_automotive.lbx_structures.WidgetItems();
} else if (
this == gui.wdw_d3plot_automotive.btn_select_all_structure_assessment_types ||
this == gui.wdw_d3plot_automotive.btn_deselect_all_structure_assessment_types
) {
widget_items = gui.wdw_d3plot_automotive.lbx_structure_assessment_types.WidgetItems();
} else {
ErrorMessage("Unknown button in <toggle_all_widget_items>");
return;
}
if (!widget_items) return;
for (let widget_item of widget_items) {
if (
this == gui.wdw_d3plot_automotive.btn_select_all_structures ||
this == gui.wdw_d3plot_automotive.btn_select_all_structure_assessment_types
) {
widget_item.selected = true;
} else if (
this == gui.wdw_d3plot_automotive.btn_deselect_all_structures ||
this == gui.wdw_d3plot_automotive.btn_deselect_all_structure_assessment_types
) {
widget_item.selected = false;
}
}
if (
this == gui.wdw_d3plot_automotive.btn_select_all_structures ||
this == gui.wdw_d3plot_automotive.btn_deselect_all_structures
) {
update_assessment_types();
}
update_selected_assessment_types();
}
/*
/**
* Update the list of assessment types in the structure assessment type
* listbox based on the selected regulation and selected structures
*/
function update_assessment_types() {
update_selected_assessment_types();
let regulation = gui.wdw_d3plot_automotive.cbx_regulation.selectedItem.text;
let structures_data = get_selected_structures_data();
/** @type {Structure[]} */
let structures = [];
for (let sd of structures_data) {
structures.push(sd.structure);
}
/* Get the current assessment types listed */
/** @type {string[]} */
let listed_assessment_types = [];
/** @type {WidgetItem[]} */
let assessment_type_widget_items = gui.wdw_d3plot_automotive.lbx_structure_assessment_types.WidgetItems();
if (assessment_type_widget_items) {
for (let wi of assessment_type_widget_items) {
listed_assessment_types.push(wi.text);
}
}
/* Get the assessment types relevant to the selected regulation,
* body part type and occupants or structures */
let protocol = ProtocolAssessment.CreateDefaultProtocol(
regulation,
gui.crash_test,
gui.wdw_d3plot_automotive.cbx_version.selectedItem.text
);
let assessment_types = protocol.UniqueStructureAssessmentTypes(structures);
/* If the assessment types to show is different to the ones currently listed
* clear it and rebuild the list */
let rebuild = false;
if (listed_assessment_types.length != assessment_types.length) {
rebuild = true;
} else {
for (let assessment_type of assessment_types) {
if (!listed_assessment_types.includes(assessment_type)) {
rebuild = true;
break;
}
}
}
if (!rebuild) return;
/* Clear the list of assessment types and add the required ones */
/** @type {Widget} */
let lbx = gui.wdw_d3plot_automotive.lbx_structure_assessment_types;
lbx.RemoveAllWidgetItems();
let valid_assessment_types = AssessmentType.GetAll("d3plot");
for (let assessment_type of assessment_types) {
if (!valid_assessment_types.includes(assessment_type)) continue;
let wi = new WidgetItem(lbx, assessment_type);
/* Set selected status */
wi.selected = gui.selected_assessment_types[assessment_type];
}
/* If the number of widget items in a list box has decreased you can end up
* with a grey row, so redraw the window */
gui.wdw_d3plot_automotive.Redraw();
}
/**
* Update the gui.selected_assessment_types object
* based on the selected assessment types in the listbox
*/
function update_selected_assessment_types() {
/* Structure assessment types */
let structure_assessment_type_widget_items = gui.wdw_d3plot_automotive.lbx_structure_assessment_types.WidgetItems();
if (structure_assessment_type_widget_items) {
for (let wi of structure_assessment_type_widget_items) {
gui.selected_assessment_types[wi.text] = wi.selected;
}
}
}