• Jump To … +
    boxes.js commlink.js connector.js graph.js gui.js main.js nodes.js tools.js utils.js view.js
  • nodes.js

  • ¶
    'use strict';
  • ¶

    Introduction

    In this file, the Node classes are defined, designed to hold state without any code directly related to rendering. For instance, methods might specify the height of a node without offering any way to concretely visualize that height.

    The Node types are as follows:

    • BaseNode: A rectangle with input/output ports.
    • LabelledNode : Extends BaseNode with a title and label state.
    • ImageNode: Extends LabelledNode with an attached image.
    • Viewport: A special resizable image node without a title or labels.
    
    
    
    class BaseNode {
  • ¶

    A Node has parameters and declares a style and in/out port styles

        constructor({ name = 'default',
                      type = 'untyped',
                      inputs=[],                     // Input/output parameter names
                      outputs=[],
                      params = {},                    // Parameter state
                      labels = {},
                      buttons = {},                   // Button state
                      param_modes = {},
                      pos = [0,0],                    // Geometry
                      width = 100,
                      smooth = 10,
                      port_gap_ratio = 0.25,          // Port settings
                      style     = { fill: 'Silver',   // Styles
                                    stroke : 'black',
                                    strokeWidth : 3 },
                      input_style  = { fill: 'white' },
                      output_style = { fill: 'DarkGoldenRod' }
                    } = {} ) {
    
            if ( (inputs.length + outputs.length) == 0 ) {
                throw 'The number of inputs and output must not be zero.';
            }
    
            this.name = name;
            this.type = type,
            this.inputs = inputs;
            this.outputs = outputs;
            this.params = params;
            this.labels = labels;
            this.buttons = buttons;
            this.param_modes = param_modes;
            this._locked_params = _.mapObject(this.params, // Parameters 'locked' by connections
                                              (k,v) => { return false});
    
            this.port_gap_ratio = port_gap_ratio;
    
            this.style = style ;
            this.input_style = input_style;
            this.output_style = output_style;
    
            this.geom = {
                left: pos[0], top: pos[1],
                width: width,
                port_radius : 7,
                rx: smooth, ry: smooth};
    
            this.header_heights = {};
        }
    
        header_height() { // Sums the entries in header_heights
            return _.reduce(Object.values(this.header_heights),
                            (a, b) => { return a+b }, 0);
        }
    
        lock_param(param, state=true) {
            this._locked_params[param]=state;
        }
    
    
        unlocked_params(pmode=undefined) {
            if (!(pmode)) {return [] }
            else if (Object.keys(pmode).length == 0) {
                return []
            }
            let unlocked = [];
            for (let param of Object.keys(this.params)) {
                if (pmode[param]=='untyped') {
                    continue }
                else if (!(this._locked_params[param])) {
                    unlocked.push(param);
                }
            }
            return unlocked
        }
    
        ports_height() {
            return this.maxrows() * this.port_spacing(); // Unscaled height of the ports
        }
    
        port_spacing() {
            return this.geom.width * this.port_gap_ratio;
        }
    
        port_position(port, port_type='input') {
            let width = this.geom.width;
            let ypos =   ( this.header_height() + this.port_spacing()/2.0
                           + this.row(port, port_type) * this.port_spacing());
            return [port_type == 'input' ? 0  : width, ypos];
        }
    
        row(port, type) {         // Given a port name, return the corresponding row
  • ¶
            if (type == 'input') {
                return this.inputs.indexOf(port) + this.outputs.length
            }
            else {
                return this.outputs.indexOf(port)
            }
        }
    
        maxrows() {
            return this.inputs.length + this.outputs.length
        }
    }
    
    
    class LabelledNode extends BaseNode {
    
        constructor({ title_opts = {size : 12,
                                    width_ratio : 0.8,
                                    top_padding_ratio : 0.6,
                                    fontFamily : 'Monospace'},
    
                      label_opts = {size: 12,
                                    fontFamily : 'Arial'}
                    } = {}) {
            super(arguments[0]);
    
            this.title_opts = title_opts;
            this.label_opts = label_opts;
    
            this.title_height = 0  // To be set by LabelledBox
        }
    }
    
    
    
    class ImageNode extends LabelledNode {
    
        constructor( {image_opts = { imdata: undefined,
                                     top:  0,
                                     left: 0,
                                     width : 80,
                                     height : 80,
                                     xres : 256,
                                     yres : 256,
                                     strokeWidth : 5,
                                     stroke : 'black'
                                   }
                     }= {}) {
            super(arguments[0]);
            this.image_opts = image_opts;
            this.image = undefined;  // Actual image state ( will be fabric.Image )
        }
    
        image_scaleX() {
            return this.image_opts.width / this.image_opts.xres;
        }
    
        image_scaleY() {
            return this.image_opts.height / this.image_opts.yres;
        }
    
        image_left() {
  • ¶

    Position depends on whether left/right ports are present

            if (this.inputs.length>0 && this.outputs.length==0) {
                return this.geom.port_radius/2.0
            }
            else if (this.inputs.length>0 && this.outputs.length>0) {
                return this.geom.port_radius/4.0
            }
        }
    }
    
    
    class Viewport extends ImageNode {
  • ¶
        constructor() {
            super(arguments[0]);
            this.image_opts.width =  100;
            this.image_opts.height = 100;
        }
    }
  • ¶

    Docs Index

    main.js : Toplevel entry point.
    nodes.js : Nodes hold semantic and visual state.
    graph.js : A Graph holds nodes and edges.
    commlink.js : Commlink links the graph to the server.
    utils.js : Simple set of utilities injected into underscore.
    view.js : The View manages graphical state..
    boxes.js : Boxes are the visual representation of nodes.
    tools.js : Tools respond to interactive events.
    connector.js : The connection tool has its own file.