当前位置: 首页 > 工具软件 > ClayGL > 使用案例 >

2021SC@SDUSC山东大学软件学院软件工程应用与实践——claygl(源代码分析13)

龚睿
2023-12-01

2021SC@SDUSC

目录

一.clay.compositor. Graph类概述

二.clay.compositor. Graph类的作用

三.clay.compositor. Graph类源码分析

 1.new Graph()

2.addNode(node)

3.after(name, action, context opt)

4.before(name, action, context opt)

5.dirty()

6.error(action, context opt)

7.getNodeByName(name) → {clay.compositor.CompositorNode}

8.has(name, action) → {boolean}

9.off(action, context opt)

10.on(name, action, context opt)

11.once(name, action, context opt)

12.removeNode(node)

13.success(action, context opt)

14.trigger(name)

15.update()


一.clay.compositor. Graph类概述

    clay.compositor. Graph类可以构造一个图,并为图添加节点

二.clay.compositor. Graph类的作用

    clay.compositor. Graph类有构造一个图,为图添加节点,设置触发事件,移除触发事件获取节点,移除节点,更新链接等功能

三.clay.compositor. Graph类源码分析

 1.new Graph()

    该函数是Graph的构造函数,继承自clay.core.Base,能够构造一个Graph对象。

    无参数。

    其源代码为:

var Graph = Base.extend(function () {
    return /** @lends clay.compositor.Graph# */ {
        /**
         * @type {Array.<clay.compositor.CompositorNode>}
         */
        nodes: []
    };
}

    从源代码中可以看出,Graph类有1个成员变量nodes,类型为数组Array<clay.compositor.CompositorNode>,表示图的节点;同时,Graph继承了Base的number成员变量。

2.addNode(node)

    该函数的作用是为图添加一个节点。

    参数为node,类型为clay.compositor.CompositorNode,表示为图添加的节点。

    其源代码为:

addNode: function (node) {
        if (this.nodes.indexOf(node) >= 0) {
            return;
        }
        this.nodes.push(node);
        this._dirty = true;
    }

    从源代码中可以看出,该函数首先判断本对象的nodes数组中是否存在形式参数node,如果存在则返回,如果不存在则加入node对象,并设置本对象的_dirty为true,表示已经修改过。

3.after(name, action, context opt)

    该函数的作用是设置事件触发后的动作,继承自Base。

    参数为name,类型为string,表示事件触发后的动作的名称,action,类型为function,表示事件触发后的动作的函数,context,类型为object,表示事件触发后的动作的内容。

    其源代码为:

after: function(name, action, context) {
        if (!name || !action) {
            return;
        }
        name = 'after' + name;
        return this.on(name, action, context);
    }

    从源代码中可以看出,该函数首先判断name和action是否存在,若其中一个不存在,则直接返回,若存在,则在name前加入字符串‘after’,并调用本对象的on(name, action, context)方法,从而实现事件触发后的动作

4.before(name, action, context opt)

    该函数的作用是设置事件触发前的动作,继承自Base。

    参数为name,类型为string,表示事件触发前的动作的名称,action,类型为function,表示事件触发前的动作的函数,context,类型为object,表示事件触发前的动作的内容。

    其源代码为:

before: function(name, action, context) {
        if (!name || !action) {
            return;
        }
        name = 'before' + name;
        return this.on(name, action, context);
    }

    从源代码中可以看出,该函数首先判断name和action是否存在,若其中一个不存在,则直接返回,若存在,则在name前加入字符串‘before’,并调用本对象的on(name, action, context)方法,从而实现事件触发前的动作。

5.dirty()

    该函数的作用是标记Graph对象已经被修改过。

    无参数。

    其源代码为:

dirty: function () {
        this._dirty = true;
    }

    该函数将本对象的成员变量_dirty设置为true,标记Graph对象已经修改过。

6.error(action, context opt)

    该函数的作用是设置事件发生错误触发的动作,继承自Base。

    action,类型为function,表示事件发生错误触发的函数,context,类型为object,表示事件发生错误触发的内容。

    其源代码如下:

error: function(action, context) {
        return this.once('error', action, context);
    }

   从源代码中可以看出,该函数直接调用本对象的once方法,name为‘error’,另外两个参数为方法的形参,从而实现错误的处理。

7.getNodeByName(name) → {clay.compositor.CompositorNode}

    该函数的作用是通过name名称获取Node节点。

    无参数。

    其源代码为:

getNodeByName: function (name) {
        for (var i = 0; i < this.nodes.length; i++) {
            if (this.nodes[i].name === name) {
                return this.nodes[i];
            }
        }
    }

    从源代码中可以看出,该函数对本对象的nodes数组进行遍历,寻找数组中名称为name的对象,并返回该对象,从而实现通过名称找到相应的节点。

8.has(name, action) → {boolean}

    该函数的作用是判断是否注册事件驱动程序,继承自Base。

    参数为name,类型为string,表示事件的名称,action,类型为function,表示事件执行的方法。

    其源代码如下:

has: function(name, action) {
        var handlers = this.__handlers__;
        if (! handlers ||
            ! handlers[name]) {
            return false;
        }
        var hdls = handlers[name];
        for (var i = 0; i < hdls.length; i++) {
            if (hdls[i].action === action) {
                return true;
            }
        }
    }

    从源代码可以看出,首先将本对象的__handlers__成员变量赋值给新建的对象handlers,判断handlers是否存在和handlers中是否存在name的对象,若其中一个条件不满足,则返回false。接着,将handlers中名为name的对象赋给新变量hdls,对hdls进行遍历,若hdls中某一对象的action与形参的action相同,则返回true,从而判断name对象是否有action方法,进而判断是否注册事件驱动程序。

9.off(action, context opt)

    该函数的作用是移除事件监听器,继承自Base。

    参数有action,类型为function,表示所移除事件监听器的动作方法,context,类型为object,表示所移除事件监听器的内容。

    其源代码如下:

off: function(name, action) {
        var handlers = this.__handlers__ || (this.__handlers__={});
        if (!action) {
            handlers[name] = [];
            return;
        }
        if (handlers[name]) {
            var hdls = handlers[name];
            var retains = [];
            for (var i = 0; i < hdls.length; i++) {
                if (action && hdls[i].action !== action) {
                    retains.push(hdls[i]);
                }
            }
            handlers[name] = retains;
        }
        return this;
    }

    从源代码中可以看出,off方法接收了name和action这两个形式参数,name表示事件监听器的name,action表示事件监听器的action。首先,若本对象的__handlers__成员变量若不为空,则将其赋值给新创建的局部变量handlers,否则将handlers设置为空字典。

    判断action形参,若action为空,则设置handlers中键为name的值为空数组,并返回。判断handlers,若其键为name的值不为空,即action不为空,则创建局部变量hdls,并将handlers[name]赋值给它。继续创建名为retains的空数组。对hdls进行遍历,若action不为空并且hdls中不存在与action一致的action值,则将hdls中符合该特性的action放入retains数组中,并将retains数组重新赋给handlers中键为name的值,通过这一方法,即可删除本对象中的action方法,从而移除事件监听器。最后返回本对象即可。

10.on(name, action, context opt)

    该函数能够注册事件处理程序,继承自Base。

    参数有name,类型为string,表示事件处理程序的名称,action,类型为function,表示事件处理程序的动作方法,context,类型为object,表示事件处理程序的内容。

    其源代码如下:

on: function(name, action, context) {
        if (!name || !action) {
            return;
        }
        var handlers = this.__handlers__ || (this.__handlers__={});
        if (!handlers[name]) {
            handlers[name] = [];
        }
        else {
            if (this.has(name, action)) {
                return;
            }
        }
        var handler = new Handler(action, context || this);
        handlers[name].push(handler);
        return this;
    }

    从源代码中可以看出,接收的形参有三个,分别为name,action,context,分别表示事件处理程序的名称,动作方法,内容。首先,该函数判断时间处理程序的名称和动作方法是否为空,若其中一个为空,则返回。

    接着,若本对象的__handlers__成员变量若不为空,则将其赋值给新创建的局部变量handlers,否则将handlers设置为空字典。

    然后,判断handlers键为name的值是否为空,如果为空则将其初始化为空数组,否则,调用has函数,判断相应name和action的事件处理程序是否已经存在,若存在则返回。

    最后,采用形参action和context创建一个Handler对象,并赋值给新建的handler局部变量,若context为空,则将本对象作为context。将handler加入到handlers中键为name的数组中即可,从而完成时间处理程序的注册。最后返回本对象。

11.once(name, action, context opt)

    该函数的作用是注册一个事件,该事件仅执行一次便移除,继承自Base。

    参数有name,类型为string,表示事件的名称,action,类型为function,表示事件的动作方法,context,类型为object,表示事件的内容。

    其源代码如下:

once: function(name, action, context) {
        if (!name || !action) {
            return;
        }
        var self = this;
        function wrapper() {
            self.off(name, wrapper);
            action.apply(this, arguments);
        }
        return this.on(name, wrapper, context);
    }

    从源代码中可以看出,接收的形参有三个,分别为name,action,context,分别表示事件处理程序的名称,动作方法,内容。首先,该函数判断时间处理程序的名称和动作方法是否为空,若其中一个为空,则返回。

    接着,创建一个局部变量self,并将本对象赋值给self。执行一个函数wrapper,对self变量调用off方法,移除wrapper事件监听器,接着,通过接收的参数,调用action的apply方法,执行action,从而实现执行该action方法,但执行后即移除该事件,从而实现仅执行一次该方法的作用。最后返回本对象的on函数的返回值,参数为name,wrapper和context。

12.removeNode(node)

    该函数的作用是移除Graph中指定的Node节点。

    参数为node,类型为clay.compositor.CompositorNode或string,表示要移除的节点或节点的名称。

    其源代码为:

removeNode: function (node) {
        if (typeof node === 'string') {
            node = this.getNodeByName(node);
        }
        var idx = this.nodes.indexOf(node);
        if (idx >= 0) {
            this.nodes.splice(idx, 1);
            this._dirty = true;
        }
    }

    从源代码中可以看出,首先判断node的类型,如果类型为string,则调用getNodeByName方法,获取nodes数组中指定的node,如果为Node类型,则直接获取该Node。接着,调用nodes数组的indexof方法,获取node的索引,如果索引存在,则根据索引去除该节点,并设置_dirty为true,表示已修改。

13.success(action, context opt)

    该函数的作用是注册success事件处理程序,继承自Base。

    参数为action,类型为function,表示success事件处理程序的动作方法发,context,类型为object,表示success事件处理程序的内容。

    其源代码如下:

success: function(action, context) {
        return this.once('success', action, context);
    }

    从源代码可以看出,该函数调用本对象的once方法,once方法能够注册时间处理程序。传入的参数有name,action和context,name固定为'success',最终实现success事件处理程序的注册。

14.trigger(name)

    该函数的作用是触发事件,继承自Base。

    参数有name,类型为string,表示触发事件的名称,继承自Base。

     其源代码如下:

trigger: function(name) {
        if (!this.hasOwnProperty('__handlers__')) {
            return;
        }
        if (!this.__handlers__.hasOwnProperty(name)) {
            return;
        }
        var hdls = this.__handlers__[name];
        var l = hdls.length, i = -1, args = arguments;
        // Optimize advise from backbone
        switch (args.length) {
            case 1:
                while (++i < l) {
                    hdls[i].action.call(hdls[i].context);
                }
                return;
            case 2:
                while (++i < l) {
                    hdls[i].action.call(hdls[i].context, args[1]);
                }
                return;
            case 3:
                while (++i < l) {
                    hdls[i].action.call(hdls[i].context, args[1], args[2]);
                }
                return;
            case 4:
                while (++i < l) {
                    hdls[i].action.call(hdls[i].context, args[1], args[2], args[3]);
                }
                return;
            case 5:
                while (++i < l) {
                    hdls[i].action.call(hdls[i].context, args[1], args[2], args[3], args[4]);
                }
                return;
            default:
                while (++i < l) {
                    hdls[i].action.apply(hdls[i].context, Array.prototype.slice.call(args, 1));
                }
                return;
        }
    }

    从源代码可以看出,首先判断本对象是否有成员变量__handlers__,若无则返回,若有,则判断__handlers__中是否有键为name的值,若其值为空,则返回。

    若以上两个条件均满足,则创建一个局部变量hdls,将本对象中__handlers__中键为name的值赋给它。获取hdls的长度赋给局部变量l,创建i为-1,args为参数arguments。

    判断args参数的长度,若为1,则调用hdls中第一个对象的action方法,参数为hdls第一个对象的内容;若为2,则调用hdls中第一个对象的action方法,参数为hdls第一个对象的内容和args[1];若为3,则调用hdls中第一个对象的action方法,参数为hdls第一个对象的内容和args[1],args[2];若为4,则调用hdls中第一个对象的action方法,参数为hdls第一个对象的内容和args[1],args[2],args[3];若为5,则调用hdls中第一个对象的action方法,参数为hdls第一个对象的内容和args[1],args[2],args[3],args[4];否则,调用hdls中第一个对象的action方法,参数为hdls第一个对象的内容和所有的args参数。可见,该函数能够调用hdls中的方法,即__handlers__的方法,并以context和arguments参数来运行,从而实现事件的出发。

15.update()

    该函数的作用是更新图的链接。

    无参数。

    其源代码为:

update: function () {
        for (var i = 0; i < this.nodes.length; i++) {
            this.nodes[i].clear();
        }
        // Traverse all the nodes and build the graph
        for (var i = 0; i < this.nodes.length; i++) {
            var node = this.nodes[i];
            if (!node.inputs) {
                continue;
            }
            for (var inputName in node.inputs) {
                if (!node.inputs[inputName]) {
                    continue;
                }
                if (node.pass && !node.pass.material.isUniformEnabled(inputName)) {
                    console.warn('Pin '  + node.name + '.' + inputName + ' not used.');
                    continue;
                }
                var fromPinInfo = node.inputs[inputName];
                var fromPin = this.findPin(fromPinInfo);
                if (fromPin) {
                    node.link(inputName, fromPin.node, fromPin.pin);
                }
                else {
                    if (typeof fromPinInfo === 'string') {
                        console.warn('Node ' + fromPinInfo + ' not exist');
                    }
                    else {
                        console.warn('Pin of ' + fromPinInfo.node + '.' + fromPinInfo.pin + ' not exist');
                    }
                }
            }
        }
    }

    从源代码中可以看出,首先对nodes数组进行遍历,并对每一个节点对象调用clear方法进行清除。接着,继续对nodes数组进行遍历,获取每一个node节点,如果node节点的inputs对象为空,则跳过该节点,否则对node节点的inputs数组中的对象进行遍历,如果inputs中的对象为空,则跳过,否则判断node的pass是否存在和pass的材料是否没有inputName,如果是,则未使用,打印相应的消息提醒并跳过。接着获取刚才inputs中的节点赋值给fromPinInfo,调用findPin方法找到fromPinInfo中的Pin赋值给fromPin,如果fromPin存在,调用node的link方法对其进行更新链接,如果Pin不存在,则判断fromPinInfo是否为string类型,是的话则打印fromPinInfo不存在Node,否则打印fromPinInfo的node中无Pin,表示未更新。从而实现图的更新

 类似资料: