Home Reference Source

src/js/molecules/equation.js

import Atom from '../prototypes/atom'
import GlobalVariables from '../globalvariables'

/**
 * This class creates the Equation atom.
 */
export default class Equation extends Atom {
    
    /**
     * The constructor function.
     * @param {object} values An array of values passed in which will be assigned to the class as this.x
     */ 
    constructor(values){
        super(values)
        
        this.addIO('output', 'result', this, 'number', 0)
        
        /**
         * This atom's name
         * @type {string}
         */
        this.name = 'Equation'
        
        /**
         * This atom's type
         * @type {string}
         */
        this.atomType = 'Equation'
        
        /**
         * Evaluate the equation adding and removing inputs as needed
         */ 
        this.value = 0
        /** 
         * A description of this atom
         * @type {string}
         */
        this.description = "Defines a mathematical equation. Edit the output field to add or remove inputs."
        
        /**
         * This atom's height as drawn on the screen
         */
        this.height
        /**
         * The index number of the currently selected option
         * @type {number}
         */
        this.currentEquation = "x + y"
        
        this.setValues(values)
        this.addAndRemoveInputs()
        this.setValues(values) //Set values again to load input values which were saved
        
        
    }
    
    /**
     * Draw the Bill of material atom which has a BOM icon.
     */ 
    draw() {
        
        super.draw("rect")
        
        let pixelsX = GlobalVariables.widthToPixels(this.x)
        let pixelsY = GlobalVariables.heightToPixels(this.y)
        let pixelsRadius = GlobalVariables.widthToPixels(this.radius)
        /**
        * Relates height to radius
        * @type {number}
        */
        this.height = pixelsRadius

        GlobalVariables.c.beginPath()
        GlobalVariables.c.fillStyle = '#484848'
        GlobalVariables.c.font = `${pixelsRadius/1.5}px Work Sans Bold`
        
        GlobalVariables.c.fillText('X + Y', pixelsX - pixelsRadius/1.2, pixelsY+this.height/3)
        GlobalVariables.c.fill()
        GlobalVariables.c.closePath()
        
    }
    
    /**
     * Add and remove inputs as needed from the atom
     */ 
    addAndRemoveInputs(){
        //Find all the letters in this equation
        var re = /[a-zA-Z]/g
        const variables = this.currentEquation.match(re)
        
        //Remove any inputs which are not needed
        const deleteExtraInputs = () => {
            this.inputs.forEach( input => {
                if( !variables.includes(input.name) ){
                    this.removeIO('input', input.name, this)
                    deleteExtraInputs() //This needs to be called recursively to make sure all the inputs are deleted
                }
            })
        }
        deleteExtraInputs()
        
        //Add any inputs which are needed
        for (var variable in variables){
            if(!this.inputs.some(input => input.Name === variables[variable])){
                this.addIO('input', variables[variable], this, 'number', 1)
            }
        }
    }
    
    /**
     * Evaluate the equation
     */ 
    evaluateEquation(){
        //Substitute numbers into the string
        var substitutedEquation = this.currentEquation
        this.name = this.currentEquation
        
        //Find all the letters in this equation
        var re = /[a-zA-Z]/g
        const variables = this.currentEquation.match(re)
        for (var variable in variables){
            for (var i= 0; i<this.inputs.length; i++){
                if (this.inputs[i].name == variables[variable]) {
                    substitutedEquation = substitutedEquation.replace(this.inputs[i].name, this.findIOValue(this.inputs[i].name))
                }
            }
        }
        return GlobalVariables.limitedEvaluate(substitutedEquation)
    }
    
    /**
     * Evaluate the equation adding and removing inputs as needed
     */ 
    updateValue(){
        try{
            
            this.addAndRemoveInputs()
            
            if(this.inputs.every(x => x.ready)){
                
                this.decreaseToProcessCountByOne()
                
                //Evaluate the equation
                this.value = this.evaluateEquation()
                
                this.output.setValue(this.value)
                this.output.ready = true
            }
            
            //Updates the inputs
            if(this.selected){
                this.updateSidebar()
            }
        }catch(err){
            console.warn(err)
            this.setAlert(err)
        }
    }
    
    /**
     * Sets all the input and output values to match their associated atoms.
     */ 
    loadTree(){
        this.inputs.forEach(input => {
            input.loadTree()
        })
        
        this.value = this.evaluateEquation()
        this.output.value = this.value
        
        return this.value
    }
    
    /**
     * Add the equation choice to the object which is saved for this molecule
     */
    serialize(){
        var superSerialObject = super.serialize()
        
        //Write the current equation to the serialized object
        superSerialObject.currentEquation = this.currentEquation
        
        return superSerialObject
    }
    
    /**
     * Add a dropdown to choose the equation type to the sidebar.
     */
    updateSidebar(){
        //Update the side bar to make it possible to change the molecule name
        
        var valueList = super.updateSidebar()
        
        this.createEditableValueListItem(valueList,this,"currentEquation", "output=", false, (newEquation)=>{this.setEquation(newEquation)})
        
    }
    
    /**
     * Set the current equation to be a new value.
     */
    setEquation(newEquation){
        this.currentEquation = newEquation.trim() //remove leading and trailing whitespace
        this.updateValue()
    }

    /**
     * Send the value of this atom to the 3D display. Used to display the number
     */ 
    sendToRender(){
        //Send code to jotcad to render
        try{
            const values = {op: "text", value:this.output.getValue(), writePath: this.path }
            const {answer} = window.ask(values)
            answer.then( () => {
                GlobalVariables.writeToDisplay(this.path)
            })
        }
        catch(err){
            this.setAlert(err)
        }

    }
}