post_entities_of_interest.js

// module: TRUE
// @ts-ignore
import { gui } from "./post_entities_of_interest_gui.jsi";

/**
 * @typedef {Object} EntityOfInterest
 * @property {string} name
 * @property {string[]} parts
 * @property {String} type
 * @property {String[]} partsOfEntity
 */

/**
 * The data object in the workflow file
 * @typedef {Object} data
 * @property {EntityOfInterest[]} entities_of_interest
 */
/** @type {data} */
let data; // where the workflows data is stored from the .JSON
let model_id; // the current model in use being passed around the program
let window_id; // the current window in use being passed around the program
let entryModelList; // the list of models in use when a button is pressed
let dir; // the file directory for saving GLB Exports
let GLBOutput; // the GLB output option - Current Frame or Animation
let GLBFrameRate; // the GLB Frame Rate option if Animation is selected for GLBOutput

set_up();

/* Show the first window "Window1" */
if (gui) gui.Window1.Show(false);

/**
 * Sets up the window with callback functions from the GUI, fills the WidgetItems for the listbox based on JSON data, checks there are just 1 model per window and sets current model
 */
function set_up() {
    for (let m = 0; m < Workflow.NumberOfSelectedModels(); m++) {
        model_id = Workflow.ModelIdFromIndex(m);
        /** @type {data} */
        // @ts-ignore
        data = Workflow.ModelUserDataFromIndex(m);

        if (SetCurrentModel(model_id)) {
            // TRUE if model is present (false if model is deleted etc)
            // Adds each entry from the JSON including the model number
            for (let i = 0; i < data.entities_of_interest.length; i++) {
                let newEntry =
                    "M" +
                    model_id +
                    " | " +
                    data.entities_of_interest[i].name +
                    " | " +
                    data.entities_of_interest[i].type +
                    " | " +
                    data.entities_of_interest[i].parts;
                let newItem = new WidgetItem(gui.Window1.lbEntity, newEntry);
                newItem.hover = data.entities_of_interest[i].parts.toString();
            }
        }
    }

    SetCurrentModel(model_id);

    /* Blank all the parts in the model first. Need to get
     * the window the model is in to do this. */

    let n = GetNumberOf(WINDOW);
    window_id = -1;
    let nm = [1, 1];
    for (let m = 1; m <= n; m++) {
        let wm = GetWindowModels(m);
        for (var j = 0; j < wm.nm; j++) {
            if (wm.list[j] == model_id) {
                window_id = m;
                nm[0] = wm.nm;
                nm[1] = j + 1;
                break;
            }
        }
    }

    // Checks if each window just contains 1 model
    if (nm[0] > 1) {
        let answer = Window.Error(
            "Error",
            "This tool does not work correctly with multiple models in one window. \nExit the script?",
            Window.YES | Window.NO
        );
        if (answer == Window.YES) {
            Exit();
        }
    }

    if (window_id == -1) {
        WarningMessage("Unable to find model window");
        return;
    }

    // callbacks
    gui.Window1.lbEntity.onClick = update_buttons;
    gui.Window1.btnReset.onClick = reset_button;
    gui.Window1.btnOnly.onClick = only;
    gui.Window1.btnHighlight.onClick = highlight;
    gui.Window1.btnGLB.onClick = GLB_edit;
    gui.Window1.helpBtn.onClick = workflow_manual;
    gui.WindowGLB.btnFile.onClick = GLB_dir;
    gui.WindowGLB.cbOutput.onChange = GLB_output_change;
    gui.WindowGLB.btnExport.onClick = GLB;
    gui.WindowGLB.btnCancel.onClick = GLB_cancel;
    gui.WindowGLB.txtDir.onChange = update_export_button;

    // Directory icon added to file button
    gui.WindowGLB.btnFile.DirectoryIcon(Widget.BLACK, Widget.GREY);

    // These become active if GLB Output 'Animation' is selected
    gui.WindowGLB.lblFrameRate.active = false;
    gui.WindowGLB.txtFrameRate.active = false;
}

/**
 * Enables and Disables the buttons on the window according to how many widgetItems are selected
 */
function update_buttons() {
    let widgetItems = gui.Window1.lbEntity.WidgetItems();
    let widgetItemsSelected = 0;
    for (let i = 0; i < widgetItems.length; i++) {
        if (widgetItems[i].selected) {
            widgetItemsSelected++;
        }
    }
    if (widgetItemsSelected == 0) {
        gui.Window1.btnOnly.active = false;
        gui.Window1.btnHighlight.active = false;
        gui.Window1.btnGLB.active = false;
    } else {
        gui.Window1.btnOnly.active = true;
        gui.Window1.btnHighlight.active = true;
        gui.Window1.btnGLB.active = true;
    }
}

/**
 * Resets the model back to default ready for the new action (button press) to commence and checks that an entry is selected
 * @returns {Boolean} true if entities are selected, false if not
 */
function reset(option) {
    // Sets window based on the model being looped through
    DialogueInput("/CW " + window_id, "UNBLANK ALL");

    if (option == "full") {
        DialogueInput("/PROPERTIES TRANSPARENCY ALL", "0");
        DialogueInput("/PROPERTIES COLOUR ORIGINAL");
    }

    // checks if entry is selected and returns result
    let widgetItems = gui.Window1.lbEntity.WidgetItems();
    let widgetsSelected = 0;
    for (let i = 0; i < widgetItems.length; i++) {
        if (widgetItems[i].selected) {
            widgetsSelected++;
        }
    }
    if (widgetsSelected == 0) {
        return false;
    }

    return true;
}

/**
 * Resets the model back to default, a button that can be pressed by the user
 */
function reset_button() {
    for (let m = 0; m < Workflow.NumberOfSelectedModels(); m++) {
        model_id = Workflow.ModelIdFromIndex(m);

        // Finds the window number for the model in the loop
        find_window_number_for_model();

        DialogueInput("/CW " + window_id, "UNBLANK ALL");
        DialogueInput("/PROPERTIES TRANSPARENCY ALL", "0");
        DialogueInput("/PROPERTIES COLOUR ORIGINAL");
        DialogueInput("/AC");
    }
}

/**
 * Completes the action as selected by the button pressing by the user
 * @param {string} method the action being completed by the user. Valid options are Only, Highlight or GLB
 */
function action(method) {
    let modelID;
    // Loops through the workflows selected and gets the data for the model
    for (let m = 0; m < Workflow.NumberOfSelectedModels(); m++) {
        modelID = Workflow.ModelIdFromIndex(m);
        /** @type {data} */
        // @ts-ignore
        data = Workflow.ModelUserDataFromIndex(m);

        // Checks if the workflows modelID matches the model_id of the D3PLOT screen
        if (modelID.toString() == model_id.toString()) {
            // if TRUE then loops through the WidgetItems (entries) and makes sure they are selected
            let widgetItems = gui.Window1.lbEntity.WidgetItems();
            for (let i = 0; i < widgetItems.length; i++) {
                if (widgetItems[i].selected) {
                    // if TRUE then WidgetItem is selected

                    // Splits up the WidgetItem text to determine the model that the entry is connected to
                    let text = widgetItems[i].text;
                    text = text.split(" |");
                    text = text[0].split("M");
                    text = text[1];

                    // Checks if the model matches the model in the entry that has been selected
                    if (modelID.toString() == text.toString()) {
                        // if TRUE sets it to the model that we have confirmed is the model that is specified in the entry
                        SetCurrentModel(modelID);

                        // Loops through the data for the selected model
                        for (let j = 0; j < data.entities_of_interest.length; j++) {
                            // Gets the name of the widgetItem that has been selected that we are looping through
                            let name = widgetItems[i].text;
                            name = name.split(" |");
                            name = name[1].trim(); /* Name */

                            // if the name in the data for the selected model we are looping through matches the name of the WidgetItem entry we are looping through
                            if (data.entities_of_interest[j].name == name) {
                                // if TRUE we have now got the correct data entry to match with the correct widgetItem entry for the model

                                // Determines if the entry type is a part or a part set and gets the list of parts for the entry
                                let parts;
                                if (data.entities_of_interest[j].type.toString() == "Parts") {
                                    parts = data.entities_of_interest[j].parts;
                                }
                                if (data.entities_of_interest[j].type.toString() == "Part Set") {
                                    parts = data.entities_of_interest[j].partsOfEntity;
                                }

                                let neg_pid = parts.map((x) => -x);

                                // Completes the action
                                if (method == "Only") {
                                    Unblank(PART, neg_pid, window_id);
                                }
                                if (method == "Highlight") {
                                    Blank(PART, "ALL", window_id);
                                    Unblank(PART, neg_pid, window_id);
                                    DialogueInput("/REDRAW");
                                    DialogueInput("/PROPERTIES COLOUR PARTS AV", "RED");
                                    DialogueInput("/PROPERTIES TRANSPARENCY PARTS AV", "0");
                                    Unblank(PART, "ALL", window_id);
                                }
                                if (method == "GLB") {
                                    Blank(PART, "ALL", window_id);
                                    Unblank(PART, neg_pid, window_id);
                                    DialogueInput("/AC");
                                    if (GLBOutput == "Current Frame") {
                                        DialogueInput(
                                            "/IMAGES ONLY_WINDOW",
                                            window_id,
                                            "EXPORT_3D",
                                            "CURRENT_FRAME",
                                            dir + "/" + data.entities_of_interest[j].name
                                        );
                                    }
                                    if (GLBOutput == "Animation") {
                                        DialogueInput("/IMAGES FRAME_RATE", GLBFrameRate);
                                        DialogueInput(
                                            "/IMAGES ONLY_WINDOW",
                                            window_id,
                                            "EXPORT_3D",
                                            "ANIMATION",
                                            dir + "/" + data.entities_of_interest[j].name
                                        );
                                    }
                                }
                            }
                        }
                    }
                }
            }
            DialogueInput("/AC");
        }
    }
}

/**
 * When looping through the models that have been selected after selecting entries, then fiund the window number for the stated model_id
 */
function find_window_number_for_model() {
    SetCurrentModel(model_id);
    let n = GetNumberOf(WINDOW);
    window_id = -1;
    for (let m = 1; m <= n; m++) {
        let wm = GetWindowModels(m);
        for (var j = 0; j < wm.nm; j++) {
            if (wm.list[j] == model_id) {
                window_id = m;
                break;
            }
        }
    }
}

/**
 * @returns {Array} entryModelList
 * Finds out the models that have been selected by the user after a button press by searching through the selected WidgetItems and putting them into an array
 */
function find_entry_model_list() {
    let widgetItems = gui.Window1.lbEntity.WidgetItems();

    for (let i = 0; i < widgetItems.length; i++) {
        if (widgetItems[i].selected) {
            // Split up the WidgetItem text and find the model number
            let text = widgetItems[i].text;
            text = text.split(" |");
            text = text[0].split("M");
            text = text[1]; // model number of selected entry

            // Checks if the array already contains the model, if it does not then adds the model to the list
            if (!entryModelList.includes(text)) {
                entryModelList.push(text);
            }
        }
    }
    return entryModelList;
}

/**
 * Only the selected entities
 */
function only() {
    // Creates the entry model list to find out which models have been selected for use
    entryModelList = [];
    entryModelList = find_entry_model_list();

    // Loops through the models selected
    for (let x = 0; x < entryModelList.length; x++) {
        // Sets model_id to the model in the loop
        model_id = Number(entryModelList[x]);

        // Finds the window number for the model in the loop
        find_window_number_for_model();

        // Resest the window of the model in the loop, if no entries are selcted then returns
        if (!reset("full")) return;

        // Blanks all parts ready for action
        DialogueInput("/BLANK ALL");

        // Completes the blanking
        action("Only");
    }
}

/**
 * Highlight the selected entities
 */
function highlight() {
    // Creates the entry model list to find out which models have been selected for use
    entryModelList = [];
    entryModelList = find_entry_model_list();

    // Loops through the models selected
    for (let x = 0; x < entryModelList.length; x++) {
        // Sets model_id to the model in the loop
        model_id = Number(entryModelList[x]);

        // Finds the window number for the model in the loop
        find_window_number_for_model();

        // Resest the window of the model in the loop, if no entries are selcted then returns
        if (!reset("full")) return;

        // Sets all parts we don't want highlighting to 90% transparency and grey ready for action
        DialogueInput("/PROPERTIES TRANSPARENCY ALL", "90");
        DialogueInput("/PROPERTIES COLOUR ALL GREY");

        // Completes the highlighting
        action("Highlight");
    }
}

/**
 * Opens window to set GLB Export options
 */
function GLB_edit() {
    gui.WindowGLB.Show(false);
}

/**
 * GLB Export the selected entities when export button is pressed in GLB edit window
 */
function GLB() {
    GLBOutput = gui.WindowGLB.cbOutput.text;
    GLBFrameRate = gui.WindowGLB.txtFrameRate.text;
    dir = gui.WindowGLB.txtDir.text;

    if (dir == null || !File.IsDirectory(dir)) {
        Window.Message("", "Invalid file directory, cancelling GLB Export");
        return;
    }

    gui.WindowGLB.Hide();

    // Creates the entry model list to find out which models have been selected for use
    entryModelList = [];
    entryModelList = find_entry_model_list();

    // Loops through the models selected
    for (let x = 0; x < entryModelList.length; x++) {
        // Sets model_id to the model in the loop
        model_id = Number(entryModelList[x]);

        // Finds the window number for the model in the loop
        find_window_number_for_model();

        // Resest the window of the model in the loop, if no entries are selcted then returns
        if (!reset()) return;

        // Complets the GLB Export
        action("GLB");
        Unblank(PART, "ALL", window_id);
        DialogueInput("/AC");
    }
}

/**
 * When the GLB output is changed between Current Frame and Animation, enable Frame Rate option for Animation
 */
function GLB_output_change() {
    if (gui.WindowGLB.cbOutput.text == "Current Frame") {
        gui.WindowGLB.lblFrameRate.active = false;
        gui.WindowGLB.txtFrameRate.active = false;
    } else {
        gui.WindowGLB.lblFrameRate.active = true;
        gui.WindowGLB.txtFrameRate.active = true;
    }
}

/**
 * Enables and disables the GLB export button depending on a correct directory or not
 */
function update_export_button() {
    dir = gui.WindowGLB.txtDir.text;

    if (File.IsDirectory(dir)) {
        gui.WindowGLB.btnExport.active = true;
    } else {
        gui.WindowGLB.btnExport.active = false;
    }
}

/**
 * When the file selector button is pressed in GLB edit window, to select the location to save the GLB Exports to
 */
function GLB_dir() {
    let cancelText = gui.WindowGLB.txtDir.text;
    dir = Window.GetDirectory();
    if (dir == null) {
        WarningMessage("No directory selected");
        gui.WindowGLB.txtDir.text = cancelText;
    } else {
        gui.WindowGLB.txtDir.text = dir;
    }

    update_export_button();
}

/**
 * Closes GLB Export options window when cancel is pressed
 */
function GLB_cancel() {
    gui.WindowGLB.Hide();
}

/**
 * Opens up the clickhelp manual when the help button is pressed
 */
function workflow_manual() {
    OpenManual("d3plot", "entities-of-interest.html");
}