pre_intrusion_contour_plot.js

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

let workflowDefinitionFilename = Workflow.WorkflowDefinitionFilename();

// The following variables are global as they are passed into multiple functions
let a; // node 1 object
let b; // node 2 object
let c; // node 3 object
let xx; // x vector x direction
let xy; // x vector y direction
let xz; // x vector z direction
let yx; // y vector x direction
let yy; // y vector y direction
let yz; // y vector z direction
let zx; // z vector x direction
let zy; // z vector y direction
let zz; // z vector z direction
let m; // the model that is selected in the set up function

set_up();

/* Show the first window */
gui.Window1.keepOnTop = true; // Makes the GUI stay on top, otherwise when you move the graphics window it gets hidden behind it and you can't see it
if (gui) gui.Window1.Show(false);

function set_up() {
    // Selecting the model to use if there are multiple ones open in PRIMER
    gui.model = Model.Select("Select the model to use");
    if (gui.model == null) {
        Window.Information("", "You need to select a model before you can use this workflow");
        Exit();
    }
    m = gui.model;

    // Reading in any previously saved data
    let numModels = Workflow.NumberOfModels();
    for (let i = 0; i < numModels; i++) {
        let modelID = Workflow.ModelIdFromIndex(i);
        if (modelID != gui.model.number) {
            continue;
        }
        /** @type {UserData} */
        let userData = Workflow.ModelUserDataFromIndex(i);
        try {
            // @ts-ignore
            gui.Window1.txtParts.text = userData.parts;
            // @ts-ignore
            gui.Window1.txtN1.text = userData.node1;
            // @ts-ignore
            gui.Window1.txtN2.text = userData.node2;
            // @ts-ignore
            gui.Window1.txtN3.text = userData.node3;
        } catch (e) {
            ErrorMessage("Error reading saved data");
        }
    }

    // setting up callback functions
    gui.Window1.btnSelect.onClick = select;
    gui.Window1.txtN1.onChange = coordinate_system;
    gui.Window1.txtN2.onChange = coordinate_system;
    gui.Window1.txtN3.onChange = coordinate_system;
    gui.Window1.btnSelectN1.onClick = select_n1;
    gui.Window1.btnSelectN2.onClick = select_n2;
    gui.Window1.btnSelectN3.onClick = select_n3;
    gui.Window1.btnSaveToFile.onClick = save_to_file;
    gui.Window1.btnSaveToModel.onClick = save_to_model;
    gui.Window1.helpBtn.onClick = workflow_manual;
}

/**
 * Select the parts when clicked
 */
function select() {
    /* Select parts and populate text box. Assume there is just one model loaded */

    if (!m) {
        Window.Message("", "You need to read a model into PRIMER");
        return;
    }

    let f = AllocateFlag();
    let selection = Part.Select(f, "Pick parts of interest", m, false);
    if (selection == null) {
        ReturnFlag(f);
        return;
    }

    let text = "";

    let p = Part.First(m);
    let first = true;

    while (p) {
        if (p.Flagged(f)) {
            if (first) {
                text += p.label;
                first = false;
            } else {
                text += " " + p.label;
            }
        }

        p = p.Next();
    }

    ReturnFlag(f);

    gui.Window1.txtParts.text = text;
}

/**
 * Select node 1 when pick is clicked
 */
function select_n1() {
    let n1 = Node.Pick("Pick node1", m);
    if (!n1) return;
    gui.Window1.txtN1.text = n1.label;
    coordinate_system();
}

/**
 * Select node 2 when pick is clicked
 */
function select_n2() {
    let n2 = Node.Pick("Pick node2", m);
    if (!n2) return;
    gui.Window1.txtN2.text = n2.label;
    coordinate_system();
}

/**
 * Select node 3 when pick is clicked
 */
function select_n3() {
    let n3 = Node.Pick("Pick node3", m);
    if (!n3) return;
    gui.Window1.txtN3.text = n3.label;
    coordinate_system();
}

/**
 * Calculates coordinate system ready for it be drawn
 */
function coordinate_system() {
    // Exits the function if any of the nodes does not contain a number or any text at all
    if (gui.Window1.txtN1.text == "") {
        return;
    }
    if (gui.Window1.txtN2.text == "") {
        return;
    }
    if (gui.Window1.txtN3.text == "") {
        return;
    }
    if (isNaN(gui.Window1.txtN1.text)) {
        return;
    }
    if (isNaN(gui.Window1.txtN2.text)) {
        return;
    }
    if (isNaN(gui.Window1.txtN3.text)) {
        return;
    }

    a = Node.GetFromID(m, parseInt(gui.Window1.txtN1.text)); // node 1
    b = Node.GetFromID(m, parseInt(gui.Window1.txtN2.text)); // node 2
    c = Node.GetFromID(m, parseInt(gui.Window1.txtN3.text)); // node 3

    if (a == null || b == null || c == null) {
        WarningMessage("One of the nodes you entered does not exist");
        return;
    }

    // X vector is b-a
    xx = b.x - a.x;
    xy = b.y - a.y;
    xz = b.z - a.z;

    // B vector is c-a
    let bx = c.x - a.x;
    let by = c.y - a.y;
    let bz = c.z - a.z;

    // To get the Z vector we need to cross the X vector with the B vector
    // z cross product vector
    zx = xy * bz - by * xz;
    zy = bx * xz - xx * bz;
    zz = xx * by - bx * xy;

    // To get the Y cross product vector you then need to cross the Z and the X vectors.
    yx = zy * xz - xy * zz;
    yy = xx * zz - zx * xz;
    yz = zx * xy - xx * zy;

    // Find the magnitude of the vectors
    let lengthx = Math.sqrt(xx * xx + xy * xy + xz * xz);
    let lengthy = Math.sqrt(yx * yx + yy * yy + yz * yz);
    let lengthz = Math.sqrt(zx * zx + zy * zy + zz * zz);

    // Normalise the vectors
    xx /= lengthx; // This is shorthand for xx = xx / lengthx
    xy /= lengthx;
    xz /= lengthx;
    yx /= lengthy;
    yy /= lengthy;
    yz /= lengthy;
    zx /= lengthz;
    zy /= lengthz;
    zz /= lengthz;

    // Make the lines the size you want
    let size = 250;
    xx = xx * size;
    xy = xy * size;
    xz = xz * size;
    yx = yx * size;
    yy = yy * size;
    yz = yz * size;
    zx = zx * size;
    zy = zy * size;
    zz = zz * size;

    // how PRIMER calculates coordinate system, un-comment this to check if own method is correct.
    // we can't use this PRIMER method because it saves an entity to the model which we don't want.
    //let cs = new CoordinateSystem(m, CoordinateSystem.NODES, 200, parseInt(gui.Window1.txtN1.text), parseInt(gui.Window1.txtN2.text), parseInt(gui.Window1.txtN3.text), 0, 1, "Test csys");
    //cs.Sketch();

    do_draw();
    Graphics.DrawingFunction(do_draw);
    View.Redraw();
}

/**
 * Draws coordinate geometry based on the selected nodes
 */
function do_draw() {
    Graphics.Start();

    Graphics.DepthTest(false); // Turning depth testing off may be used to ensure that an item (e.g. a line or text) is always drawn in front of parts and will not be obscured.

    // Setting up details for sketching. The sketch colour ensures colour matches theme of PRIMER so you can always see it.
    Graphics.LineWidth(1);
    Graphics.LineColour(Colour.SKETCH);
    Graphics.LineStyle(Graphics.SOLID_LINE);
    Graphics.TextColour(Colour.SKETCH);
    Graphics.TextSize(10);

    // X Line
    Graphics.Line(a.x, a.y, a.z, a.x + xx, a.y + xy, a.z + xz);
    Graphics.MoveTo(a.x + xx / 2, a.y + xy / 2, a.z + xz / 2);
    Graphics.Text("X");

    // Z Line
    Graphics.Line(a.x, a.y, a.z, a.x + zx, a.y + zy, a.z + zz);
    Graphics.MoveTo(a.x + zx / 2, a.y + zy / 2, a.z + zz / 2);
    Graphics.Text("Z");

    // Y Line
    Graphics.Line(a.x, a.y, a.z, a.x + yx, a.y + yy, a.z + yz);
    Graphics.MoveTo(a.x + yx / 2, a.y + yy / 2, a.z + yz / 2);
    Graphics.Text("Y");

    Graphics.Finish();
}

/**
 * Saves the data to a file
 */
function save_to_file() {
    /* Get data to write to file */

    let userData = get_user_data();

    /* Ask the user where to write it */

    let outputFilename = Window.GetFile(".json", true);

    if (outputFilename == null) return;

    /* API call to write workflow file */

    Workflow.WriteToFile(userData, outputFilename, workflowDefinitionFilename);

    Message("Written workflow file to " + outputFilename);
}

/**
 * Saves the data to the model
 */
function save_to_model() {
    /* Get data to write to file */

    let userData = get_user_data();

    /* Ask the user which model to write it to */

    let model = Model.Select("Select the model to write to");

    if (model == null) return;

    /* API call to write workflow to a model */

    Workflow.WriteToModel(userData, model, workflowDefinitionFilename);

    Message("Workflow data added to post *END data. You need to write out the model from the main Model->Write menu to save the additions.");
}

/**
 * The user data object
 * @returns {Object} UserData
 */
function get_user_data() {
    return {
        parts: gui.Window1.txtParts.text,
        node1: parseInt(gui.Window1.txtN1.text),
        node2: parseInt(gui.Window1.txtN2.text),
        node3: parseInt(gui.Window1.txtN3.text)
    };
}

/**
 * Opens up the clickhelp manual when the help button is pressed
 */
function workflow_manual() {
    OpenManual("primer", "intrusion-contour-plot.html");
}

/**
 * The user data object in the workflow file
 * @typedef {Object} UserData
 */