"use strict";
var _ = require("lodash"),
COLOR = require("color"),
Condicio = require("condicio"),
Matrix = require("gl-matrix"),
Node = require("./Node.js"),
jQuery = require("jquery"),
Effect = require("./Effect.js"),
TransparencyEffect = require("./TransparencyEffect.js"),
LightEffect = require("./LightEffect.js"),
ParamValidation = require("../ParamValidation.js"),
Utility = require("../Utility.js"),
CvtForJson = require("../CvtForJson.js"),
ClipEffect = require("./Effects.js").ClipEffect,
DraggableEffect = require("./Effects.js").DraggableEffect,
Style = require("./Style.js");
/**
* @class View3D.Dragger
* 拖拽器
* 拖拽器可用于创建可拖拽效果接口从而实现拖拽模型的效果
*/
function Dragger(objectID, callbackID, callback) {
this.objectID = objectID;
this.callbackID = callbackID;
this.callback = callback;
};
Dragger.prototype = _.create(Dragger.prototype, {
constructor: Dragger
});
/**
* @hide
* 启用dragger
*
* @param {Session} session 连接实例
*/
Dragger.prototype.on = function(session){
session.registerCallback(this.callbackID, this.callback, "Dragger", true);
};
/**
* @hide
* 结束Dragger
*
* @param {Session} session 连接实例
*/
Dragger.prototype.off = function(session){
session.unregisterCallback(this.callbackID);
};
/**
* @class View3D.EffectFactory
* 提供创建不同{@link View3D.Effect}的接口
*
* 应用不应该构建,应该通过{@link View3D}直接获取
*/
function EffectFactory(session, objectID) {
this.session = session;
this.objectID = objectID;
};
/**
* 创建变色效果
*
* @param {Node[]} nodes 要改变颜色的节点数组
* @param {Color} color 目标颜色,颜色中的alpha值无效
*
* @return {View3D.Effect}
*/
EffectFactory.prototype.createColorChangeEffect = function (nodes, color) {
ParamValidation.checkIsTypeArray(nodes, Node, "The type of nodes must be 'Node Array' and valid!");
Condicio.checkArgument(nodes.length !== 0, "The number of nodes to highlight must`t be NULL");
//Condicio.checkIsType(color, COLOR, "The type of color must be 'Color'");
var nodeIDs = CvtForJson.cvtNodeArray(nodes);
var effectID = Utility.genGUID();
var params = {
effectID: effectID,
nodeIDs: nodeIDs,
color: color.rgbaArray()
};
this.session.request(this.objectID, "createColorChangeEffect", params);
return new Effect(effectID);
};
/**
* 创建隐藏节点效果
*
* @param {Node[]} nodes 目标节点集合
*
* @return {View3D.Effect}
*/
EffectFactory.prototype.createHideEffect = function (nodes) {
ParamValidation.checkIsTypeArray(nodes, Node, "The type of nodes must be 'Node Array' and valid");
Condicio.checkArgument(nodes.length !== 0, "The number of target-nodes must`t be NULL!");
var nodeIDs = CvtForJson.cvtNodeArray(nodes);
var effectID = Utility.genGUID();
var params = {
effectID: effectID,
nodeIDs: nodeIDs,
};
this.session.request(this.objectID, "createHideEffect", params);
return new Effect(effectID);
};
/**
* 创建剖切效果
*
* 整个场景(view)暂时仅支持六个剖切(对应的剖切号0~5)
*
* @param {Number} no 剖切号(0~5),不能存在相同的剖切号
* @param {vec3} pos 剖切面位置
* @param {vec3} dir 剖切方向,以pos为点以dir为法线构造剖切面。以该剖切面为边界,dir方向一侧的模型正常显示,另一侧的模型则被剖切不显示
* @param {Node[]} destNodes 要剖切的节点数组(数组中的节点必须存在于场景中)
* @param {Object} ctrlParam 剖切控制器参数
* @param {Number} ctrlParam.scale 剖切控制器矩形大小,这是一个像素值
* @param {boolean} [ctrlParam.showClipCtrl = true] 是否显示剖切控制器
* @param {Color} [ctrlParam.color = COLOR('rgb(0, 254, 255)').alpha(0.2)] 剖切控制器矩形的颜色,颜色中的alpha值有效
* @param {boolean} [ctrlParam.showBorder = false] 是否显示剖切控制器矩形的边框
* @param {Function} ctrlParam.pickHandler 剖切控制器点击回调
* @param {Error} ctrlParam.pickHandler.error 剖切控制器点击回调参数,内部异常,如果没有异常则为null
* @param {vec3} ctrlParam.pickHandler.position 剖切控制器点击回调参数,当前剖切面位置
* @param {vec3} ctrlParam.pickHandler.direction 剖切控制器点击回调参数,所点选的方向控制器所代表的方向
*
* @return {View3D.Effect}
*/
EffectFactory.prototype.createClipEffect = function (no, pos, dir, destNodes, ctrlParam) {
Condicio.checkIsNumber(no, "The clip no must be Number!");
ParamValidation.checkIsVec3(pos, "The type of clip-face-position must be 'vec3' and valid!");
ParamValidation.checkIsVec3(dir, "The type of clip-direction must be 'vec3' and valid!");
Condicio.checkArgument(Matrix.vec3.length(dir) !== 0, "The clip-direction must be a vector of length not 0");
ParamValidation.checkIsTypeArray(destNodes, Node, "The type of destNodes must be 'Node Array' and valid!");
Condicio.checkArgument(destNodes.length !== 0, "The number of destNodes must`t be NULL!");
Condicio.checkIsObject(ctrlParam, "The type of clip-controller must be 'Object'!");
Condicio.checkIsNumber(ctrlParam.scale, "The type of clip-matrix-size must be Number!");
Condicio.checkArgument(ctrlParam.scale > 0, "The ctrlParam.scale must >0!");
Condicio.checkIsFunction(ctrlParam.pickHandler, "The type of clip-controller-pickHandler must be 'Function'!");
ctrlParam.color = ctrlParam.color || COLOR('rgb(0, 254, 255)').alpha(0.2);
ctrlParam.showBorder = ctrlParam.showBorder || false;
if(Condicio.isUndefined(ctrlParam.showClipCtrl))
ctrlParam.showClipCtrl = true;
Condicio.checkIsBoolean(ctrlParam.showClipCtrl, "The showClipCtrl must be 'boolean'!");
Condicio.checkIsBoolean(ctrlParam.showBorder, "The showBorder must be 'boolean'!");
//Condicio.checkIsType(ctrlParam.color, COLOR, "The type of color must be 'Color'");
var nodeIDs = CvtForJson.cvtNodeArray(destNodes);
var pickHandlerID = Utility.genGUID();
var pickHandlerWrapper = function (error, result) {
if (!error) {
var vec3pos = result.pos;
var vec3dir = result.dir;
ctrlParam.pickHandler(error, CvtForJson.cvtToVec3(vec3pos), CvtForJson.cvtToVec3(vec3dir));
}
else
ctrlParam.pickHandler(error, null, null);
};
var effectID = Utility.genGUID();
var params = {
effectID: effectID,
no: no,
pos: CvtForJson.cvtVec3(pos),
dir: CvtForJson.cvtVec3(dir),
nodeIDs: nodeIDs,
ctrlParam: {
scale: ctrlParam.scale,
color: ctrlParam.color.rgbaArray(),
showBorder: ctrlParam.showBorder,
showClipCtrl: ctrlParam.showClipCtrl
},
pickHandlerID: pickHandlerID
};
this.session.request(this.objectID, "createClipEffect", params);
return new ClipEffect(effectID, pickHandlerID, pickHandlerWrapper);
};
/**
* 创建node边框效果
*
* @param {Object} option
* @param {Node[]} option.nodes 创建边框的目标节点(节点必须存在于场景中)
* @param {Color} [option.color = (255, 0, 0)] 创建边框的颜色(备注:颜色中的alpha值无效)
* @param {Number} [option.width = 5] 创建边框的宽度
*
* @return {view3D.Effect}
*/
EffectFactory.prototype.createOutlineEffect = function(option){
if(Condicio.isUndefined(option.color))
option.color = COLOR('rgb(255, 0, 0)').alpha(1);
if(Condicio.isUndefined(option.width))
option.width = 5;
ParamValidation.checkIsTypeArray(option.nodes, Node, "The type of nodes must be 'Node Array' and valid");
Condicio.checkArgument(0 != option.nodes.length, "The number of nodes must`t be NULL!");
//Condicio.checkIsType(option.color, COLOR, "The type of color must be 'Color'");
Condicio.checkIsNumber(option.width, "The type of width must be Number!");
Condicio.checkArgument(option.width > 0, "The width must > 0!");
var nodeIDs = CvtForJson.cvtNodeArray(option.nodes);
var effectID = Utility.genGUID();
var params = {
effectID: effectID,
nodeIDs: nodeIDs,
color: option.color.rgbaArray(),
width: option.width
};
this.session.request(this.objectID, "createOutlineEffect", params);
return new Effect(effectID);
};
/**
* 创建透明节点效果
*
* @param {Object} options
* @param {Node[]} options.destNodes 目标节点集合
* @param {Number} [options.transparencyValue = 1] 透明度(范围0~1),0为全透明,1为不透明(值:0<=transparencyValue<=1)
*
* @return {View3D.TransparencyEffect}
*/
EffectFactory.prototype.createTransparencyEffect = function (options) {
if(Condicio.isUndefined(options.transparencyValue))
options.transparencyValue = 1.0;
ParamValidation.checkIsTypeArray(options.destNodes, Node, "The type of destNodes must be 'Node Array' and valid");
Condicio.checkArgument(0 != options.destNodes.length, "The number of destNodes must`t be NULL!");
Condicio.checkIsNumber(options.transparencyValue, "The type of transparencyValue must be 'number'!");
Condicio.checkArgument(options.transparencyValue >= 0 && options.transparencyValue <= 1, "The transparencyValue must: >= 0 && <= 1 !");
var nodeIDs = CvtForJson.cvtNodeArray(options.destNodes);
var effectID = Utility.genGUID();
var params = {
effectID: effectID,
nodeIDs: nodeIDs,
transparencyValue: options.transparencyValue
};
this.session.request(this.objectID, "createTransparencyEffect", params);
return new TransparencyEffect(this.session, effectID);
};
/**
* 创建开口效果
*
* @param {Node[]} nodes 应用此开口效果的节点数组(目前节点只支持Drawing加载出的Node)
* @param {vec3[]} openingBoundary 多边形各个点坐标(开口的区域依赖于传入的点序,点依次和下一个点之间构成开口区域的一条边,
最后一个点和第一个构成一条边)
* @return {View3D.Effect}
*/
EffectFactory.prototype.createOpeningEffect = function (nodes, openingBoundary) {
ParamValidation.checkIsTypeArray(nodes, Node, "The type of nodes must be 'Node Array' and valid!");
ParamValidation.checkIsVec3Array(openingBoundary, "The type of openPts must be 'vec3 Array' and valid!");
Condicio.checkArgument(openingBoundary.length > 2, "The size of openPts must greater than 2");
var nodeIDs = CvtForJson.cvtNodeArray(nodes);
var ptsNumberArray = CvtForJson.cvtVec3Array(openingBoundary);
var effectID = Utility.genGUID();
var params = {
effectID: effectID,
nodeIDs: nodeIDs,
openingBoundary: ptsNumberArray
};
this.session.request(this.objectID, "createOpeningEffect", params);
return new Effect(effectID);
};
/**
* 创建位移拖拽器,拖拽该拖拽器可以实现位置移动
* 该拖拽器可用于创建可拖拽效果
*
* @param {vec3} pos 拖拽控制器的初始位置
* @param {Function} dragCallback 拖拽回调
* @param {Error} dragCallback.error 表示拖拽中出现的错误
* @param {Number} dragCallback.type 表示当前拖拽回调的类型:1-一次拖拽完成;2-拖拽过程中
* @param {vec3} dragCallback.pos 表示当前拖拽器所在位置
*
* @return {View3D.Dragger}
*/
EffectFactory.prototype.createAxisTranslationDragger = function (pos, dragCallback) {
ParamValidation.checkIsVec3(pos, "The type of pos must be 'vec3' and valid!");
Condicio.checkIsFunction(dragCallback, "The type of dragCallback must be 'Function'!");
var draggerID = Utility.genGUID();
var posArray = CvtForJson.cvtVec3(pos);
var dragCallbackID = Utility.genGUID();
var dragCallbackWrapper = function (error, result) {
if (!error) {
var type = result.type;
var vec3pos = result.pos;
dragCallback(error, type, CvtForJson.cvtToVec3(vec3pos));
}
else
dragCallback(error, null, null);
};
var params = {
draggerID: draggerID,
pos: posArray,
dragCallbackID: dragCallbackID
};
this.session.request(this.objectID, "createAxisTranslationDragger", params);
return new Dragger(draggerID, dragCallbackID, dragCallbackWrapper);
};
/**
* 创建单方向位移拖拽器,拖拽该拖拽器可以实现位置移动
* 该拖拽器可用于创建可拖拽效果
*
* @param {Object} option 创建拖拽器的参数
* @param {vec3} option.pos 拖拽控制器的初始位置
* @param {vec3} option.direction 拖拽控制器的拖拽方向
* @param {Object} [option.dragDistance] 可拖拽移动的距离:单位:毫米,取值[0, +∞)(以初始位置为起点,基于拖拽方向可向前向后拖拽的距离)
* @param {Number} option.dragDistance.forward 向前可拖拽的距离
* @param {Number} option.dragDistance.backward 向后可拖拽的距离
* @param {Color} option.color 拖拽控制器的颜色(颜色中的alpha值无效)
* @param {Function} option.dragCallback 拖拽回调
* @param {Error} option.dragCallback.error 表示拖拽中出现的错误
* @param {Number} option.dragCallback.type 表示当前拖拽回调的类型:1-一次拖拽完成; 2-拖拽过程中
* @param {vec3} option.dragCallback.pos 表示当前拖拽器所在位置
*
* @return {View3D.Dragger}
*/
EffectFactory.prototype.create1DTranslationDragger = function (option) {
ParamValidation.checkIsVec3(option.pos, "The type of option.pos must be 'vec3' and valid!");
ParamValidation.checkIsVec3(option.direction, "The type of option.direction must be 'vec3' and valid!");
Condicio.checkArgument(Matrix.vec3.length(option.direction) > 0, "The type of option.direction must be 'vec3' and valid!");
//Condicio.checkIsType(option.color, COLOR, "The type of option.color must be 'Color'");
Condicio.checkIsFunction(option.dragCallback, "The type of option.dragCallback must be 'Function'!");
var dragDistance;
if(Condicio.isUndefined(option.dragDistance)){
dragDistance = {
forward : -1,
backward : -1
}
}
else{
Condicio.checkArgument(option.dragDistance.forward >= 0 && option.dragDistance.backward >= 0, "The option.draggerableDistance must >= 0!");
dragDistance = option.dragDistance;
}
var draggerID = Utility.genGUID();
var posArray = CvtForJson.cvtVec3(option.pos);
var dirArray = CvtForJson.cvtVec3(option.direction);
var colorArray = option.color.rgbaArray();
var dragCallbackID = Utility.genGUID();
var dragCallbackWrapper = function (error, result) {
if (!error) {
var type = result.type;
var vec3pos = result.pos;
option.dragCallback(error, type, CvtForJson.cvtToVec3(vec3pos));
}
else
option.dragCallback(error, null, null);
};
var params = {
draggerID: draggerID,
pos: posArray,
direction: dirArray,
color: colorArray,
dragDistance: dragDistance,
dragCallbackID: dragCallbackID
};
this.session.request(this.objectID, "create1DTranslationDragger", params);
return new Dragger(draggerID, dragCallbackID, dragCallbackWrapper);
};
/**
* 创建可拖拽效果
* 创建时需要传入一个拖拽器,可以实现节点被这个拖拽的效果
*
* @param {Node[]} nodes 应用可拖拽效果的节点集合,只有可变换的(transformable)节点才可以应用此效果
* 可变换节点的来源有:1.使用createTransformation接口创建的节点
* 2. 使用接口loadModel加载进来的自定义模型,用户非常确认此模型或者模型中的一部分是可变换的
3. 使用接口loadOGFModel加载进来的模型。4. 使用接口loadPGFModel加载进来的模型
* @param {Dragger} dragger 拖拽器
*
* @return {View3D.Effect}
*/
EffectFactory.prototype.createDraggableEffect = function (nodes, dragger) {
ParamValidation.checkIsTypeArray(nodes, Node, "The type of nodes must be 'Node Array' and valid!");
Condicio.checkIsType(dragger, Dragger, "The type of dragger must be 'Dragger'!");
var nodeIDs = CvtForJson.cvtNodeArray(nodes);
var effectID = Utility.genGUID();
var params = {
effectID: effectID,
nodeIDs: nodeIDs,
draggerID: dragger.objectID
};
this.session.request(this.objectID, "createDraggableEffect", params);
return new DraggableEffect(effectID, dragger);
};
/**
* 释放效果资源
*
* @param {View3D.Effect[]} effects 待释放的效果资源集合
*/
EffectFactory.prototype.releaseEffect = function (effects) {
ParamValidation.checkIsTypeArray(effects, Effect, "The type of effects must be 'Effect Array' and valid");
var objectIDs = [];
for (var i = 0; i < effects.length; ++i) {
objectIDs.push(effects[i].objectID);
}
this.session.request(this.objectID, "releaseEffect", { objectIDs: objectIDs });
};
var MOVING_MODE = {
NoMoving: 0,
FollowingCamera: 1
};
/**
* 创建光源
*
* @param {Object} option 创建光照参数
* @param {Object} option.lightStyle 光照基本属性,参照View3D.Style.LightStyle的定义
* @param {Number} option.lightNo 光源编号
* @param {Object} option.movingMode 光源移动方式
* @param {Number} option.movingMode.mode 光源移动时,选择移动方式MOVING_MODE
* { NoMoving: 0,FollowingCamera: 1}
* @param {Number} [option.movingMode.radian=π/4] <a href="https://fulongtech.atlassian.net/wiki/spaces/RenderingTech/pages/75956914">参数相关详情请参考</a>
* @param {vec3} [option.movingMode.translate=(0,0,0)] <a href="https://fulongtech.atlassian.net/wiki/spaces/RenderingTech/pages/75956914">参数相关详情请参考</a>
*
* @return {View3D.LightEffect}
*/
EffectFactory.prototype.createLightEffect = function (option){
Condicio.checkIsObject(option, "The type of option must be 'Object'!");
var newStyle = jQuery.extend({}, Style.LightStyle, option.lightStyle);
ParamValidation.checkIsLightStyle(newStyle);
Condicio.checkNotUndefined(option.lightNo, "option.lightNo is undefined!");
Condicio.checkIsNumber(option.lightNo, "The type of option.lightNo must be Number!");
Condicio.checkArgument((option.lightNo >= 0 && option.lightNo <8 ), "The value of option.lightNo is not valid");
newStyle.lightNo = option.lightNo;
Condicio.checkNotUndefined(option.movingMode, "option.movingMode is undefined!");
Condicio.checkIsObject(option.movingMode, "The type of option.movingMode must be 'Object'!");
option.movingMode.mode = option.movingMode.mode || MOVING_MODE.NoMoving;
var movingMode = {
mode: option.movingMode.mode
}
if(movingMode.mode == MOVING_MODE.FollowingCamera) {
movingMode.radian = option.movingMode.radian || 0.785;
Condicio.checkIsNumber(movingMode.radian, "The type of options.movingMode.radian must be Number!");
movingMode.translate = option.movingMode.translate || Matrix.vec3.fromValues(0,0,0);
ParamValidation.checkIsVec3(movingMode.translate, "The type of options.movingMode.translate must be 'vec3' and valid!");
movingMode.translate = CvtForJson.cvtVec3(movingMode.translate);
}
var effectID = Utility.genGUID();
var lightStyle = {
diffuse: newStyle.diffuse.rgbaArray(),
specular: newStyle.specular.rgbaArray(),
ambient: newStyle.ambient.rgbaArray(),
position: CvtForJson.cvtVec4(newStyle.position),
spotCutoff: newStyle.spotCutoff,
range: newStyle.range,
direction: CvtForJson.cvtVec3(newStyle.direction)
}
var params = {
effectID: effectID,
lightNo: newStyle.lightNo,
lightStyle:lightStyle,
movingMode: movingMode,
};
this.session.request(this.objectID, "createLightEffect", params);
return new LightEffect(this.session, effectID);
};
module.exports = EffectFactory;