【创新实训 - 个人报告】06 - 向引擎注册插件

开始之前

在上文中,我们提到了几样基本工具之间的配合。
然而,基本工具也需要向引擎注册才能使用。在此基础上,我们需要给引擎写一些模块用来适配。

设计思路

最基础的工具类,告诉引擎这是一个工具:

// ================================================================================
// * Tool <SDUDOC Engine>
// --------------------------------------------------------------------------------
//   Designer: Lagomoro <Yongrui Wang>
//   From: SDU <Shandong University>
//   License: MIT license
// --------------------------------------------------------------------------------
//   Latest update:
//   2020/03/10 - Version 1.0.0
//     - Engine core
// ================================================================================

// ================================================================================
// * Tool
// --------------------------------------------------------------------------------
function Tool(){
  this.initialize.apply(this, arguments);
}
// --------------------------------------------------------------------------------
// * Enum
// --------------------------------------------------------------------------------
Tool.Type = {
  DOCUMENT: 0, HISTORY: 1, PLUGIN: 2, PAGE: 3
};
// --------------------------------------------------------------------------------
// * Property
// --------------------------------------------------------------------------------
Tool.prototype._id = "";
Tool.prototype._tooltip = "";
Tool.prototype._icon = "";
Tool.prototype._type = 0;
Tool.prototype._description = "";
Tool.prototype._callback = function(){};
// --------------------------------------------------------------------------------
// * Initialize
// --------------------------------------------------------------------------------
Tool.prototype.initialize = function(id, tooltip, icon, type, description, callback){
  this._id = id;
  this._tooltip = tooltip;
  this._icon = icon;
  this._type = type;
  this._description = description;
  this._callback = callback;
};
// --------------------------------------------------------------------------------
// * Getter & Setter
// --------------------------------------------------------------------------------
Object.defineProperty(Tool.prototype, 'id', {
  get: function() {
    return this._id;
  },
  configurable: true
});
Object.defineProperty(Tool.prototype, 'tooltip', {
  get: function() {
    return this._tooltip;
  },
  configurable: true
});
Object.defineProperty(Tool.prototype, 'icon', {
  get: function() {
    return this._icon;
  },
  configurable: true
});
Object.defineProperty(Tool.prototype, 'type', {
  get: function() {
    return this._type;
  },
  configurable: true
});
Object.defineProperty(Tool.prototype, 'description', {
  get: function() {
    return this._description;
  },
  configurable: true
});
Object.defineProperty(Tool.prototype, 'callback', {
  get: function() {
    return this._callback;
  },
  configurable: true
});
// ================================================================================

然后我们需要一个Manager来管理工具类:

// ================================================================================
// * ToolManager <SDUDOC Engine>
// --------------------------------------------------------------------------------
//   Designer: Lagomoro <Yongrui Wang>
//   From: SDU <Shandong University>
//   License: MIT license
// --------------------------------------------------------------------------------
//   Latest update:
//   2020/03/14 - Version 1.0.0
//     - Engine core
// ================================================================================

// ================================================================================
// * ToolManager
// --------------------------------------------------------------------------------
function ToolManager() {
  throw new Error('This is a static class');
}
// --------------------------------------------------------------------------------
// * Property
// --------------------------------------------------------------------------------
ToolManager._tools = [];
ToolManager._handlers = {};
ToolManager._current_plugin = null;
// --------------------------------------------------------------------------------
// * Initialize
// --------------------------------------------------------------------------------
ToolManager.initialize = function() {
  this.clear();
  this._setupEventHandlers();
};
ToolManager.clear = function() {
  this._current_plugin = this.getInitialPlugin().id;
};
ToolManager._setupEventHandlers = function(){
  MouseInput.addHandler(new Handler("ToolManager.leftClick", "left_click", false, this, (event) => {
    this._processHandler.call(this, event, "left_click")}));
  MouseInput.addHandler(new Handler("ToolManager.middleClick", "middle_click", false, this, (event) => {
    this._processHandler.call(this, event, "middle_click")}));
  MouseInput.addHandler(new Handler("ToolManager.rightClick", "right_click", false, this, (event) => {
    this._processHandler.call(this, event, "right_click")}));
  MouseInput.addHandler(new Handler("ToolManager.leftDoubleClick", "left_double_click", false, this, (event) => {
    this._processHandler.call(this, event, "left_double_click")}));
  MouseInput.addHandler(new Handler("ToolManager.middleDoubleClick", "middle_double_click", false, this, (event) => {
    this._processHandler.call(this, event, "middle_double_click")}));
  MouseInput.addHandler(new Handler("ToolManager.rightDoubleClick", "right_double_click", false, this, (event) => {
    this._processHandler.call(this, event, "right_double_click")}));
  MouseInput.addHandler(new Handler("ToolManager.leftDown", "left_down", false, this, (event) => {
    this._processHandler.call(this, event, "left_down")}));
  MouseInput.addHandler(new Handler("ToolManager.middleDown", "middle_down", false, this, (event) => {
    this._processHandler.call(this, event, "middle_down")}));
  MouseInput.addHandler(new Handler("ToolManager.rightDown", "right_down", false, this, (event) => {
    this._processHandler.call(this, event, "right_down")}));
  MouseInput.addHandler(new Handler("ToolManager.leftUp", "left_up", false, this, (event) => {
    this._processHandler.call(this, event, "left_up")}));
  MouseInput.addHandler(new Handler("ToolManager.middleUp", "middle_up", false, this, (event) => {
    this._processHandler.call(this, event, "middle_up")}));
  MouseInput.addHandler(new Handler("ToolManager.rightUp", "right_up", false, this, (event) => {
    this._processHandler.call(this, event, "right_up")}));

  MouseInput.addHandler(new Handler("ToolManager.mouseMove", "mousemove", false, this, (event) => {
    this._processHandler.call(this, event, "mousemove")}));
  MouseInput.addHandler(new Handler("ToolManager.mouseOver", "mouseover", false, this, (event) => {
    this._processHandler.call(this, event, "mouseover")}));
  MouseInput.addHandler(new Handler("ToolManager.mouseOut", "mouseout", false, this, (event) => {
    this._processHandler.call(this, event, "mouseout")}));
  MouseInput.addHandler(new Handler("ToolManager.wheel", "wheel", false, this, (event) => {
    this._processHandler.call(this, event, "wheel")}));

  Input.addHandler(new Handler("ToolManager.keyClick", "key_click", 'all', this, (event) => {
    this._processHandler.call(this, event, "key_click")}));
  Input.addHandler(new Handler("ToolManager.keyHold", "key_hold", 'all', this, (event) => {
    this._processHandler.call(this, event, "key_hold")}));
  Input.addHandler(new Handler("ToolManager.keyLongHold", "key_long_hold", 'all', this, (event) => {
    this._processHandler.call(this, event, "key_long_hold")}));
  Input.addHandler(new Handler("ToolManager.keyDown", "key_down", 'all', this, (event) => {
    this._processHandler.call(this, event, "key_down")}));
  Input.addHandler(new Handler("ToolManager.keyUp", "key_up", 'all', this, (event) => {
    this._processHandler.call(this, event, "key_up")}));
};
// --------------------------------------------------------------------------------
// * Functions
// --------------------------------------------------------------------------------
ToolManager._processHandler = function(event, type){
  if(!this._current_plugin) return;
  switch (type){
    case 'left_click':
    case 'middle_click':
    case 'right_click':
    case 'left_double_click':
    // case 'middle_double_click':
    // case 'right_double_click':
    case 'left_down':
    case 'middle_down':
    case 'right_down':
    case 'left_up':
    case 'middle_up':
    case 'right_up':
    case 'mousemove':
    case 'mouseover':
    case 'mouseout':
    case 'wheel':this.callMouseHandler(event, type);break;
    case 'key_click':
    case 'key_hold':
    case 'key_long_hold':
    case 'key_down':
    case 'key_up':this.callKeyHandler(event, type);break;
  }
}
ToolManager.callMouseHandler = function(event, type){
  for(let i in this._handlers){
    if(this._handlers[i].type === type &&
      (this._handlers[i].id.startsWith('_') || this._handlers[i].id.startsWith(this.getCurrentPlugin().id))){
      this._handlers[i].callback.call(this._handlers[i].owner, event);
    }
  }
}
ToolManager.callKeyHandler = function(event, type){
  for(let i in this._handlers){
    if(this._handlers[i].type === type && this._handlers[i].key_code === Input.getKeyCode(event.keyCode)
      && (this._handlers[i].id.startsWith('_') || this._handlers[i].id.startsWith(this.getCurrentPlugin().id))){
      this._handlers[i].callback.call(this._handlers[i].owner, event);
    }
  }
}
// --------------------------------------------------------------------------------
ToolManager.addTool = function(tool){
  this._tools.push(tool);
}
ToolManager.addHandler = function(handler){
  this._handlers[handler.id] = handler;
};
ToolManager.removeHandler = function(id){
  this._handlers.remove(id);
};
// --------------------------------------------------------------------------------
ToolManager.getToolList = function(type){
  let data = [];
  for(let i in this._tools){
    if(this._tools[i].type === type){
      let callback_function = this._tools[i].callback;
      data.push({
        id: this._tools[i].id,
        tooltip: this._tools[i].tooltip,
        icon: this._tools[i].icon,
        description: this._tools[i].description,
        callback: function(id){
          Engine.clearFactory()
          callback_function(id)
        }
      })
    }
  }
  return data;
}
ToolManager.getInitialPlugin = function(){
  let list = this.getToolList(Tool.Type.PLUGIN);
  if(list.length === 0) return {id: null};
  return list[0];
}
ToolManager.getCurrentPlugin = function(){
  let list = this.getToolList(Tool.Type.PLUGIN);
  for(let i in list){
    if(list[i].id === this._current_plugin){
      return list[i];
    }
  }
}
ToolManager.setCurrentPlugin = function(id){
  let list = this.getToolList(Tool.Type.PLUGIN);
  for(let i in list){
    if(list[i].id === id){
      this._current_plugin = list[i].id;
      Engine.owner.current_plugin = i;
    }
  }
  Graphics.refresh();
}
// ================================================================================

Manager将会注册所有工具用到的事件,并向工具进行分发。
这是由于不同工具他们监听的事件不一样,在切换工具时有不同的表现。例如:点工具在按下鼠标时生成点,移动工具进行移动。
如果我们不写一个Manager去管理,而是用if去判断,那就耦合度太高了,达不到插件化的效果。

由此,点工具只需要如此即可注册:

// ================================================================================
// * Register Plugin Tool
// ================================================================================
ToolManager.addTool(new Tool("dot", "点工具", "mdi-circle-medium", Tool.Type.PLUGIN, "", function(id){
  ToolManager.setCurrentPlugin(id);
}));
// --------------------------------------------------------------------------------
ToolManager.addHandler(new Handler("dot.onLeftClick", "left_click", false, DotFactory, function(event){
  if(DocumentManager.getCurrentPage() <= 0) return;
  let collide_list = CollideManager.getCollideList(Dot2D.TAG, 1);
  if(collide_list.length > 0) return;

  collide_list = CollideManager.getCollideList(Line2D.TAG, 2);
  if(collide_list.length === 2){
    DocumentManager.addElement(Dot2D.TAG, DotFactory.makeObject(DocumentManager.getCurrentPageId(),
      Dot2D.Type.INTERSECTION, collide_list[0], collide_list[1]));
  }else if(collide_list.length === 1){
    let dependent = LineFactory.getDependent(collide_list[0], new Point(event.layerX, event.layerY));
    DocumentManager.addElement(Dot2D.TAG, DotFactory.makeObject(DocumentManager.getCurrentPageId(),
      Dot2D.Type.DEPENDENT, collide_list[0], dependent));
  }else{
    let point = Graphics.getGridPoint(new Point(event.layerX, event.layerY));
    DocumentManager.addElement(Dot2D.TAG, DotFactory.makeObject(DocumentManager.getCurrentPageId(),
      Dot2D.Type.FREE, point.x, point.y));
  }
}));
ToolManager.addHandler(new Handler("dot.onRightClick", "right_click", false, DotFactory, function(event){
  if(DocumentManager.getCurrentPage() <= 0) return;
  let collide_list = CollideManager.getCollideList(Dot2D.TAG, 1);
  if(collide_list.length === 0) return;
  DocumentManager.deleteElement(Dot2D.TAG, collide_list[0]);
}));
ToolManager.addHandler(new Handler("dot.onMouseMove", "mousemove", false, DotFactory, function(event){
  Graphics.refresh();
}));
ToolManager.addHandler(new Handler("dot.onMouseOut", "mouseout", false, DotFactory, function(event){
  Graphics.refresh();
}));

接下来是渲染器,不同工具的渲染顺序、渲染内容都存在一定偏差。

// ================================================================================
// * Renderer <SDUDOC Engine>
// --------------------------------------------------------------------------------
//   Designer: Lagomoro <Yongrui Wang>
//   From: SDU <Shandong University>
//   License: MIT license
// --------------------------------------------------------------------------------
//   Latest update:
//   2020/03/17 - Version 1.0.0
//     - Engine core
// ================================================================================

// ================================================================================
// * Renderer
// --------------------------------------------------------------------------------
function Renderer(){
  this.initialize.apply(this, arguments);
}
// --------------------------------------------------------------------------------
// * Property
// --------------------------------------------------------------------------------
Renderer.prototype._id = "";
Renderer.prototype._z = 0;
Renderer.prototype._owner = null;
Renderer.prototype._render = function(){};
// --------------------------------------------------------------------------------
// * Initialize
// --------------------------------------------------------------------------------
Renderer.prototype.initialize = function(id, z, owner, render){
  this._id = id;
  this._z = z;
  this._owner = owner;
  this._render = render;
};
// --------------------------------------------------------------------------------
// * Getter & Setter
// --------------------------------------------------------------------------------
Object.defineProperty(Renderer.prototype, 'id', {
  get: function() {
    return this._id;
  },
  configurable: true
});
Object.defineProperty(Renderer.prototype, 'z', {
  get: function() {
    return this._z;
  },
  configurable: true
});
Object.defineProperty(Renderer.prototype, 'owner', {
  get: function() {
    return this._owner;
  },
  configurable: true
});
Object.defineProperty(Renderer.prototype, 'render', {
  get: function() {
    return this._render;
  },
  configurable: true
});
// ================================================================================

在Randerer指定z值来规定渲染顺序。
同样需要Manager去管理。

// ================================================================================
// * RenderManager <SDUDOC Engine>
// --------------------------------------------------------------------------------
//   Designer: Lagomoro <Yongrui Wang>
//   From: SDU <Shandong University>
//   License: MIT license
// --------------------------------------------------------------------------------
//   Latest update:
//   2020/03/15 - Version 1.0.0
//     - Engine core
// ================================================================================

// ================================================================================
// * RenderManager
// --------------------------------------------------------------------------------
function RenderManager() {
  throw new Error('This is a static class');
}
// --------------------------------------------------------------------------------
// * Property
// --------------------------------------------------------------------------------
RenderManager._renderers = {};
RenderManager._z_list = [];
// --------------------------------------------------------------------------------
// * Initialize
// --------------------------------------------------------------------------------
RenderManager.initialize = function() {
  this.clear();
  this._setupZList();
};
RenderManager.clear = function() {
  this._z_list = [];
};
RenderManager._setupZList = function() {
  let temp = [];
  for(let i in this._renderers){
    temp.push({id: this._renderers[i].id, z: this._renderers[i].z});
  }
  let min_id = 0;
  while(temp.length > 0){
    min_id = 0;
    for(let i = 0;i < temp.length; i++){
      if(temp[i].z < temp[min_id].z){
        min_id = i;
      }
    }
    this._z_list.push(temp.splice(min_id, 1)[0].id);
  }
};
// --------------------------------------------------------------------------------
// * Functions
// --------------------------------------------------------------------------------
RenderManager.addRenderer = function(renderer){
  this._renderers[renderer.id] = renderer;
};
RenderManager.removeRenderer = function(id){
  this._renderers.remove(id);
};
RenderManager.callRenderer = function(ctx){
  for(let i = 0; i < this._z_list.length; i++){
    if(this._renderers[this._z_list[i]].id.startsWith("!" + ToolManager.getCurrentPlugin().id)) {
      continue;
    }else if(this._renderers[this._z_list[i]].id.startsWith("!")){
      this._renderers[this._z_list[i]].render.call(this._renderers[this._z_list[i]].owner, ctx);
      continue;
    }
    if(this._renderers[this._z_list[i]].id.startsWith("_") ||
      this._renderers[this._z_list[i]].id.startsWith(ToolManager.getCurrentPlugin().id)){
      this._renderers[this._z_list[i]].render.call(this._renderers[this._z_list[i]].owner, ctx);
      continue;
    }
  }
}
// ================================================================================

最后注册,就大功告成。
至此一个完整插件就ok啦!

// --------------------------------------------------------------------------------
RenderManager.addRenderer(new Renderer("_dot.normal", 10, DotFactory, function(ctx){
  if(DocumentManager.getCurrentPage() <= 0) return;
  let collide_list = CollideManager.getCollideList(Dot2D.TAG, 1);
  let dots = SDUDocument.getCurrentPageElements(Dot2D.TAG);
  for(let i in dots){
    if(collide_list.indexOf(i) === -1){
      dots[i].render(ctx);
    }
  }
}));
RenderManager.addRenderer(new Renderer("!dot.collide", 11, DotFactory, function(ctx){
  if(DocumentManager.getCurrentPage() <= 0) return;
  let collide_list = CollideManager.getCollideList(Dot2D.TAG, 1);
  if(collide_list.length > 0){
    SDUDocument.getCurrentPageElement(Dot2D.TAG, collide_list[0]).render(ctx);
  }
}));
RenderManager.addRenderer(new Renderer("dot.collide", 11, DotFactory, function(ctx){
  if(DocumentManager.getCurrentPage() <= 0) return;
  let collide_list = CollideManager.getCollideList(Dot2D.TAG, 1);
  if(collide_list.length > 0){
    SDUDocument.getCurrentPageElement(Dot2D.TAG, collide_list[0]).renderCollide(ctx);
  }
}));
RenderManager.addRenderer(new Renderer("dot.line.collide", 9, DotFactory, function(ctx){
  if(DocumentManager.getCurrentPage() <= 0) return;
  let collide_list = CollideManager.getCollideList(Line2D.TAG, 2);
  if(collide_list.length > 0){
    for(let i = 0; i < collide_list.length; i++){
      SDUDocument.getCurrentPageElement(Line2D.TAG, collide_list[i]).renderCollide(ctx);
    }
  }
}));
// --------------------------------------------------------------------------------
RenderManager.addRenderer(new Renderer("dot.mouse", 100, DotFactory, function(ctx){
  if(DocumentManager.getCurrentPage() <= 0) return;
  let collide_list = CollideManager.getCollideList(Dot2D.TAG, 1);
  if(collide_list.length > 0) return;

  collide_list = CollideManager.getCollideList(Line2D.TAG, 2);
  if(collide_list.length === 2){
    let point = LineFactory.getIntersection(collide_list[0], collide_list[1]);
    point.fillSelf(ctx, 3, 'rgba(255, 255, 255, 0.5)');
    point.strokeSelf(ctx, 5, 2, 'rgba(0, 0, 255, 0.5)');
  }else if(collide_list.length === 1){
    let mouse_point = MouseInput.getMousePoint();
    if(mouse_point !== null){
      let point = LineFactory.getProjection(collide_list[0], mouse_point);
      point.fillSelf(ctx, 3, 'rgba(255, 255, 255, 0.5)');
      point.strokeSelf(ctx, 5, 2, 'rgba(0, 0, 255, 0.5)');
    }
  }else{
    let mouse_point = MouseInput.getMousePoint();
    if(mouse_point !== null){
      mouse_point.fillSelf(ctx, 3, 'rgba(255, 255, 255, 0.5)');
      mouse_point.strokeSelf(ctx, 5, 2, 'rgba(0, 0, 255, 0.5)');
    }
  }
}));
// ================================================================================

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值