post_strength_check.js

// module: TRUE

/** Model ID for workflow model
 *  @type {number} */
let model_id = Workflow.ModelIdFromIndex(0);
let unit_system = Workflow.ModelUnitSystemFromIndex(0);

/** Model information for workflow model */
let info = GetModelInfo(model_id);

/** Window ID for workflow model
 *  @type {number} */
let win_id = -1;

/** Previous state
 *  @type {number} */
let prev_st;

/** Current state
 *  @type {number} */
let curr_state;

/** Previous part
 *  @type {number} */
let prev_pt;

/** List of yielding parts
 * @type {number[]} */
let list_yielding = [];

/** List of non-yielding parts
 * @type {number[]} */
let list_non_yielding = [];

/** Object with yielding & non_yielding lists */
let test_yielding;

/** D3PLOT plot component
 *  @type {number} */
let plot_comp = 1;

/** D3PLOT plot type
 *  @type {number} */
let plot_typ = 1;

/** No user defined contour levels
 * @type {boolean} */
let auto_contour = true;

/** Calculate maximum contour values
 * @type {boolean} */
let calc_max = true;

/** Workflow initialisation
 * @type {boolean} */
let initialised = false;

/** Properties filename
 * @type {string} */
let props_filename = null;

/** Exit script without changing properties
 * @type {boolean} */
let exit_without_props = false;

import { SavePropFile, LoadPropFile, DelPropFile } from "../../modules/d3plot_properties.js";

import * as guiWdw from "post_strength_check_gui_raw.js";
guiWdw.wdwStrengthCheck.Show(false); /* Show the first window */

/**
 * Main callback function to update the yielding parts displayed
 */
export function update_plot()
{
    SetCurrentModel(model_id);

    win_id = calc_window_id();

    auto_contour = !(guiWdw.userDefCB.pushed);

    if (auto_contour) /* if automatic contour is used, maximum value calculation is irrelevant */
    {
        guiWdw.maxValCB.active = false;
        guiWdw.maxValLabel.active = false;
        calc_max = false;
    }
    else /* ungrey maximum value label & tickbox */
    {
        guiWdw.maxValCB.active = true;
        guiWdw.maxValLabel.active = true;
        calc_max = guiWdw.maxValCB.pushed;
    }

    /* Do nothing if there's no ZTF file */
    if (info.ztf_name == "Not defined")
    {
        ErrorMessage("There is no ZTF file in the model directory!");
        let answer = Window.Error("Strength Check Workflow Error", "There is no ZTF file in the model directory. Yielding parts cannot be evaluated. \nExiting workflow...");
        if (answer == Window.OK) exit_strength_check();
    }

    /* Update total number of states in model */
    guiWdw.stateTotal.text = `/ ${info.num_states}`;

    /** yielding parts display mode
     *  @type {number} */
    let mode = 1;

    switch (guiWdw.displayMode.selectedItem.text.substring(0, 1))
    {
        case "1": mode = 1; break;
        case "2": mode = 2; break;
        case "3": mode = 3; break;

        default: mode = 1;
    }

    switch (guiWdw.plotComp.selectedItem.text.substring(0, 4))
    {
        case "YUTF": plot_comp = 1; break;
        case "YUTP": plot_comp = 2; break;

        default: plot_comp = 1;
    }

    switch (guiWdw.plotTyp.selectedItem.text.substring(0, 2))
    {
        case "SI": plot_typ = 1; break;
        case "CT": plot_typ = 2; break;

        default: plot_typ = 1;
    }

    if (isNaN(parseInt(guiWdw.stateTB.text)))
    {
        WarningMessage(`${guiWdw.stateTB.text} is not a number. Reverted to previous state.`);
        guiWdw.stateTB.text = `${prev_st}`;
        return;
    }

    if (prev_st != parseInt(guiWdw.stateTB.text)) //Need to detect changes to state from main animation control buttons. Issue raised. Already logged?
    {
        curr_state = parseInt(guiWdw.stateTB.text);
        guiWdw.stateTB.text = `${curr_state}`;

        if (curr_state < 1 || curr_state > info.num_states)
        {
            WarningMessage(`State ${curr_state} is not a valid state between 1 to ${info.num_states}. Reverted to previous state.`);
            guiWdw.stateTB.text = `${prev_st}`;
            return;
        }

        prev_st = curr_state;

        /* Go to concerned state */
        SetCurrentState(curr_state);
        DialogueInputNoEcho(`/CM ${model_id}`);
        DialogueInputNoEcho(`/STATE ${curr_state}`);

        /* Update yielding and non-yielding part lists */
        check_parts_yielding(); /* Uses the GetConditionParts() JS API function */

        /* Set to last yielding part (or 0 if no yielding parts) if previous partTB.text exceeds current list_yielding.length */
        if (parseInt(guiWdw.partTB.text) > list_yielding.length)
            guiWdw.partTB.text = `${list_yielding.length}`;

        /* If previous state has no yielding parts but current state has yielding parts, default to part 1 */
        if (parseInt(guiWdw.partTB.text) == 0 && list_yielding.length != 0)
            guiWdw.partTB.text = "1";

        /* Update total number of yielding parts in state */
        guiWdw.partTotal.text = `/ ${list_yielding.length}`;
    }

    DialogueInputNoEcho("/PROPERTIES DISPLAY_MODE DEFAULT"); /* Reset display mode to default */

    /* Give warning message & plot accordingly when there are no yielding parts for state concerned */
    if (list_yielding.length == 0)
    {
        /* Update GUI: Grey looping through yielding parts */
        loop_buttons(false);

        WarningMessage(`There are no yielding parts in state ${curr_state}`);
        if (mode == 3)
        {
            Unblank(MODEL, "ALL", win_id);
            plot_component(true);
        }
        else if (mode == 1)
        {
            DialogueInputNoEcho("/PROPERTIES DISPLAY_MODE PART ALL", "SH");
            DialogueInputNoEcho("/PROPERTIES COLOUR ALL", "GREY");
            DialogueInputNoEcho("/PROPERTIES TRANSPARENCY ALL", "50");
            Unblank(MODEL, "ALL", win_id);
            plot_component(true);
        }
        else if (mode == 2)
        {
            Blank(MODEL, "ALL", win_id);
            plot_component(true);
        }
        return;
    }

    /* Reset colours to default */
    DialogueInputNoEcho("/PROPERTIES COLOUR ALL", "DEFAULT");
    /* Reset transparency to default */
    DialogueInputNoEcho("/PROPERTIES TRANSPARENCY DEFAULT");
    /* Unblank everything */
    Unblank(MODEL, "ALL", win_id);

    /* If partTB.text is not a number, revert to previous part */
    if (isNaN(parseInt(guiWdw.partTB.text)))
    {
        WarningMessage(`${guiWdw.partTB.text} is not a number. Reverted to previous part index.`);
        guiWdw.partTB.text = `${prev_pt}`;
        return;
    }

    /** Yielding index of part to draw (for mode 2)
     *  @type {number} */
    let index = parseInt(guiWdw.partTB.text);

    /* If index is not a valid index, revert to previous part */
    if (index < 1 || index > list_yielding.length)
    {
        WarningMessage(`${index} is not a valid part index between 1 to ${list_yielding.length}. Reverted to previous part index.`);
        guiWdw.partTB.text = `${prev_pt}`;
        return;
    }

    guiWdw.partTB.text = `${index}`;
    prev_pt = index;

    if (mode == 3) /* Do a normal plot of component plot_comp */
    {
        /* Update GUI: Grey looping through yielding parts */
        loop_buttons(false);

        /* Do CT/SI plot accordingly */
        plot_component(auto_contour);
    }
    else if (mode == 1) /* Draw model with non-yielding parts transparent-grey. */
    {
        /* Update GUI: Grey looping through yielding parts */
        loop_buttons(false);

        /* Set non-yielding parts to transparent-grey */
        parts_grey_transparent();

        /* Do CT/SI plot accordingly */
        plot_component(auto_contour);
    }
    else if (mode == 2) /* Do a plot of component plot_comp for each yielding part in state */
    {
        /* Blank everything */
        Blank(MODEL, "ALL", win_id);

        /* Update GUI: Ungrey looping through yielding parts */
        loop_buttons(true);

        /* Unblank the part concerned */
        Unblank(PART, list_yielding[index - 1], win_id);

        /* Do CT/SI plot accordingly */
        plot_component(auto_contour);
    }
    DialogueInputNoEcho("Quit");

    if (list_yielding.length == 1)
        Message(`${list_yielding.length} yielding parts in state ${curr_state}`);
    else
        Message(`${list_yielding.length} yielding parts in state ${curr_state}`);

    if (mode == 2) /* If cycling through yielding parts */
        Message(`Plotting yielding part ${index} of ${list_yielding.length}: PART ${GetLabel(PART, list_yielding[index - 1])}`);
}

/**
 * Callback function to update yielding & non-yielding part lists
 */
function check_parts_yielding()
{
    /** Number of parts in model
     *  @type {number} */
    let n = 0;

    n = GetNumberOf(PART);
    if (n == 0)
    {
        Message("No PARTs in model.");
        return;
    }

    /* Reset arrays */
    list_yielding = [];
    list_non_yielding = [];

    test_yielding = GetConditionParts(YUTF, 1.0, GTEQ);
    list_yielding = test_yielding.pass_list;
    list_non_yielding = test_yielding.fail_list;

    n = test_yielding.passed + test_yielding.failed;

    Message(`Checked ${n} PARTs for yielding status: ${list_yielding.length} yielding & ${list_non_yielding.length} non-yielding`);
}

/**
 * Callback function to update GUI: Ungrey buttons forlooping through yielding parts
 * @param {boolean} option Switch looping buttons on/off
 */
function loop_buttons(option)
{
    guiWdw.yptLabel.active = option;
    guiWdw.firstPart.active = option;
    guiWdw.prevPart.active = option;
    guiWdw.partTB.active = option;
    guiWdw.partTotal.active = option;
    guiWdw.nextPart.active = option;
    guiWdw.lastPart.active = option;
}

/**
 * Callback function to do plot of plot_comp in plot_typ
 * @param {boolean} plot_without If true, plot without user defined contour levels
 */
function plot_component(plot_without)
{
    /** Maximum contour plot value
     *  @type {number} */
    let max = 0;

    /** Number contour plot levels
     *  @type {number} */
    let level = 13;

    if (plot_comp == 1)
        DialogueInputNoEcho("/COMPONENT", "YIELD_UTILISATION_FACTOR");
    else if (plot_comp == 2)
        DialogueInputNoEcho("/COMPONENT", "YIELD_UTILISATION_PERCENTAGE");

    /* Change integration point to MAX_ALL!!! */
    DialogueInputNoEcho("/STRESS_CONTROL SURFACE MAX");

    /* Set current window for plotting */
    DialogueInputNoEcho(`/CW ${win_id}`);

    if (plot_without) /* plot_without means using automatic contour levels */
    {
        /* Just do an automatic contour plot */
        DialogueInputNoEcho("/CONTOUR AUTOMATIC_ALL");
        Message("Plotting without user defined contour levels.");
        if (plot_typ == 1)
            DialogueInputNoEcho("/SI GO");
        else if (plot_typ == 2)
            DialogueInputNoEcho("/CT");
        return;
    }
    else if (!calc_max) /* when user-defined contour levels are used with max contour level set to 1.3 or 130 */
    {
        Message("Plotting with user defined contour levels but maximum contour value is not calculated.");
        level = 13;
        DialogueInputNoEcho(`/CONTOUR NUMBER_OF_LEVELS ${level}`);
        if (plot_comp == 1)
            DialogueInputNoEcho("/CONTOUR USER_DEFINED", "0.0", "0.1", "0.2", "0.3", "0.4", "0.5", "0.6", "0.7", "0.8", "0.9", "1.0", "1.1", "1.2", "1.3");
        else if (plot_comp == 2)
            DialogueInputNoEcho("/CONTOUR USER_DEFINED", "0.0", "10", "20", "30", "40", "50", "60", "70", "80", "90", "100", "110", "120", "130");
    }
    else /* when user-defined contour levels are used and maximum contour level is calculated */
    {
        /* Do an automatic contour plot first */
        DialogueInputNoEcho("/CONTOUR AUTOMATIC_ALL");
        if (plot_typ == 1)
            DialogueInputNoEcho("/SI GO");
        else if (plot_typ == 2)
            DialogueInputNoEcho("/CT");

        //- GetContourLimit() doesn't work properly (for mode 1) without SI/CT plot update above

        //I see flickering (probably due to "/CT" call in parts_grey_transparent & here) but it might be unavoidable?

        /* Get maximum scalar contour value. Note: If null for maximum of both scalar 1 & scalar 2 components, max remains 0 */
        if (GetContourLimit(MAX, "SCALAR_1") != null)
            max = GetContourLimit(MAX, "SCALAR_1"); /* Get max for scalar 1 component */
        else if (GetContourLimit(MAX, "SCALAR_2") != null)
            max = GetContourLimit(MAX, "SCALAR_2"); /* Get max for scalar 2 component */

        if (max < 0.1) /* Any number less than this will be out-of-range for setting contour levels */
        {
        /* No need to modify contour bar levels & colours */
            return;
        }

        /* Set contour levels & limits. Note: max will always be >1.0 */
        if ((plot_comp == 1 && max <= 1.1) || (plot_comp == 2 && max <= 110))
        {
            level = 11;
            DialogueInputNoEcho(`/CONTOUR NUMBER_OF_LEVELS ${level}`);
            if (plot_comp == 1)
                DialogueInputNoEcho("/CONTOUR USER_DEFINED", "0.0", "0.1", "0.2", "0.3", "0.4", "0.5", "0.6", "0.7", "0.8", "0.9", "1.0", `${max}`);
            else if (plot_comp == 2)
                DialogueInputNoEcho("/CONTOUR USER_DEFINED", "0.0", "10", "20", "30", "40", "50", "60", "70", "80", "90", "100", `${max}`);
        }
        else if ((plot_comp == 1 && max <= 1.2) || (plot_comp == 2 && max <= 120))
        {
            level = 12;
            DialogueInputNoEcho(`/CONTOUR NUMBER_OF_LEVELS ${level}`);
            if (plot_comp == 1)
                DialogueInputNoEcho("/CONTOUR USER_DEFINED", "0.0", "0.1", "0.2", "0.3", "0.4", "0.5", "0.6", "0.7", "0.8", "0.9", "1.0", "1.1", `${max}`);
            else if (plot_comp == 2)
                DialogueInputNoEcho("/CONTOUR USER_DEFINED", "0.0", "10", "20", "30", "40", "50", "60", "70", "80", "90", "100", "110", `${max}`);
        }
        else
        {
            level = 13;
            DialogueInputNoEcho(`/CONTOUR NUMBER_OF_LEVELS ${level}`);
            if (plot_comp == 1)
                DialogueInputNoEcho("/CONTOUR USER_DEFINED", "0.0", "0.1", "0.2", "0.3", "0.4", "0.5", "0.6", "0.7", "0.8", "0.9", "1.0", "1.1", "1.2", `${max}`);
            else if (plot_comp == 2)
                DialogueInputNoEcho("/CONTOUR USER_DEFINED", "0.0", "10", "20", "30", "40", "50", "60", "70", "80", "90", "100", "110", "120", `${max}`);
        }
    }

    /* Set contour bar colours. Note: level will always be >=11 */
    DialogueInputNoEcho("/CONTOUR COLOURS", "1", "BLUE");
    DialogueInputNoEcho("/CONTOUR COLOURS", "2", "CYAN/BLUE");
    DialogueInputNoEcho("/CONTOUR COLOURS", "3", "LIGHT_BLUE");
    DialogueInputNoEcho("/CONTOUR COLOURS", "4", "CYAN");
    DialogueInputNoEcho("/CONTOUR COLOURS", "5", "GREEN/CYAN");
    DialogueInputNoEcho("/CONTOUR COLOURS", "6", "GREEN");
    DialogueInputNoEcho("/CONTOUR COLOURS", "7", "YELLOW/GREEN");
    DialogueInputNoEcho("/CONTOUR COLOURS", "8", "YELLOW");
    DialogueInputNoEcho("/CONTOUR COLOURS", "9", "RED/ORANGE");
    DialogueInputNoEcho("/CONTOUR COLOURS", "10", "DARK_ORANGE");
    DialogueInputNoEcho("/CONTOUR COLOURS", "11", "RED");
    if (level >= 12) DialogueInputNoEcho("/CONTOUR COLOURS", "12", "RED/MAGENTA");
    if (level == 13) DialogueInputNoEcho("/CONTOUR COLOURS", "13", "MAGENTA");

    /* Refresh contour plot */
    DialogueInputNoEcho("/REDRAW");
}

/**
 * Callback function to change non-yielding PARTs to grey_transparent (uses internal indices)
 */
function parts_grey_transparent()
{
    /* Blank yielding parts */
    Blank(PART, list_yielding, win_id);

    /* Refresh graphics */
    DialogueInputNoEcho("/REDRAW");

    /* Set display mode to SH */
    DialogueInputNoEcho("/PROPERTIES DISPLAY_MODE PART AV", "SH");
    /* Set colour to grey */
    DialogueInputNoEcho("/PROPERTIES COLOUR PART AV", "GREY");
    /* Set transparency to 50 */
    DialogueInputNoEcho("/PROPERTIES TRANSPARENCY PART AV", "50");

    /* Unblank yielding parts */
    Unblank(PART, list_yielding, win_id);
}

/**
 * Callback function to calculate the window_id for model_id
 * @returns {number} Window ID
 */
function calc_window_id()
{
    /** Number of windows in D3PLOT
     *  @type {number} */
    let n = GetNumberOf(WINDOW);

    /** Window ID corresponding to model_id
     *  @type {number} */
    let window_id = -1;

    /** Number of models in window_id
     *  @type {number[]} */
    let nm = [1, 1];

    for (let i = 1; i <= n; i++)
    {
        /** Model numbers in window i */
        let wm = GetWindowModels(i);

        for (let j = 0; j < wm.nm; j++)
        {
            if (wm.list[j] == model_id)
            {
                window_id = i;
                nm[0] = wm.nm;
                nm[1] = j + 1;
                break;
            }
        }
    }

    if (nm[0] > 1)
    {
        WarningMessage(`Model ${model_id} is model ${nm[1]}/${nm[0]} in window ${window_id}. This workflow does not work properly for windows with more than 1 model.`);
        exit_without_props = true;
        exit_strength_check();
    }

    if (window_id == -1) /* Aka. if the window containing the model has been deleted, but not the model [Window -> Close Window -> Leave Model] */
    {
        WarningMessage("Unable to find model window");
        exit_without_props = true;
        exit_strength_check();
    }
    return window_id;
}

/**
 * Callback function corresponding to the firstState button
 */
export function first_state()
{
    /** Update stateTB with this state
     *  @type {number} */
    let state = 1;
    guiWdw.stateTB.text = `${state}`;

    update_plot();
}

/**
 * Callback function corresponding to the prevState button
 */
export function prev_state()
{
    /** Update stateTB with this state
     *  @type {number} */
    let state = parseInt(guiWdw.stateTB.text);
    if (state - 1 >= 1)
        state -= 1;
    else
    {
        WarningMessage("This is already the first state.");
        return;
    }
    guiWdw.stateTB.text = `${state}`;

    update_plot();
}

/**
 * Callback function corresponding to the nextState button
 */
export function next_state()
{
    /** Update stateTB with this state
     *  @type {number} */
    let state = parseInt(guiWdw.stateTB.text);
    if (state + 1 <= info.num_states)
        state += 1;
    else
    {
        WarningMessage("This is already the last state.");
        return;
    }

    guiWdw.stateTB.text = `${state}`;

    update_plot();
}

/**
 * Callback function corresponding to the finalState button
 */
export function last_state()
{
    /** Update stateTB with this state
     *  @type {number} */
    let state = info.num_states;
    guiWdw.stateTB.text = `${state}`;

    /* Save properties for restoration later */
    if (!initialised)
    {
        props_filename = SavePropFile(model_id);
        initialised = true;
    }

    update_plot();
}

/**
 * Callback function corresponding to the firstPart button
 */
export function first_part()
{
    /** Update partTB with this yielding part
     *  @type {number} */
    let part = 1;
    guiWdw.partTB.text = `${part}`;

    update_plot();
}

/**
 * Callback function corresponding to the prevPart button
 */
export function prev_part()
{
    /** Update partTB with this yielding part
     *  @type {number} */
    let part = parseInt(guiWdw.partTB.text);
    if (part - 1 >= 1)
        part -= 1;
    else
    {
        WarningMessage("This is already the first part.");
        return;
    }
    guiWdw.partTB.text = `${part}`;

    update_plot();
}

/**
 * Callback function corresponding to the nextPart button
 */
export function next_part()
{
    /** Update partTB with this yielding part
     *  @type {number} */
    let part = parseInt(guiWdw.partTB.text);
    if (part + 1 <= list_yielding.length)
        part += 1;
    else
    {
        WarningMessage("This is already the last part.");
        return;
    }
    guiWdw.partTB.text = `${part}`;

    update_plot();
}

/**
 * Callback function corresponding to the finalPart button
 */
export function last_part()
{
    /** Update partTB with this yielding part
     *  @type {number} */
    let part = list_yielding.length;
    guiWdw.partTB.text = `${part}`;

    update_plot();
}

/**
 * Callback function to open workflow manual
 */
export function workflow_manual()
{
    OpenManual("d3plot", "strength-check.html");
}

/**
 * Callback function to give message on exit of workflow
 */
export function exit_strength_check()
{
    if (info.ztf_name == "Not defined" || exit_without_props)
        DelPropFile(props_filename); /* Delete temporary properties file */
    else
        LoadPropFile(model_id, props_filename);

    Message("Exited post_strength_check.js");
    Exit();
}