"use strict";
var _ = require("lodash");
var Node = require("./Node.js"),
Interaction = require("./Interaction.js"),
Utility = require("../Utility.js"),
CvtForJson = require("../CvtForJson.js"),
PointPickInteraction = require("./PointPickInteraction.js"),
MouseHoverInteraction = require("./MouseHoverInteraction.js"),
CompositeInteraction = require("./CompositeInteraction.js"),
ParamValidation = require("../ParamValidation.js"),
Style = require("./Style.js"),
GLM = require("gl-matrix"),
COLOR = require("color"),
Condicio = require("condicio"),
jQuery = require("jquery"),
Tracker = require("./Tracker.js"),
LineBasedRectTracker = require("./LineBasedRectTracker.js"),
cvtToNodeArray = require("./CvtToNodeArray"),
PickFilter = require("./Filter.js");
/**
* @class View3D.InteractionFactory
* 提供可创建不同{@link Interaction}的接口
*
* 提供可创建不同的{@link PickFilter}接口
*
* 提供可创建不同的{@link Tracker}接口
*
* 应用不应该构建,应该通过{@link View3D}直接获取
*/
function InteractionFactory(session, objectID) {
this.session = session;
this.objectID = objectID;
};
//解析点击结果
var analysePicked = function (picked, session) {
var pickedArray = [];
for (var i = 0; i < picked.length; ++i) {
var pick = {};
pick.position = CvtForJson.cvtToVec3(picked[i].position);
pick.normal = CvtForJson.cvtToVec3(picked[i].normal);
pick.nodePath = cvtToNodeArray(picked[i].nodePath, session);
pickedArray.push(pick);
}
return pickedArray;
};
/**
* 创建鼠标左键/右键点选操作交互
* 需要注意的点是:单击pickHandler只会回复一次交互结果;双击的情况下会先回一次单击交互结果再回一次双击交互结果
* @localdoc
* @param {Function} pickHandler 当选择了一个点,则触发此回调
* @param {Error} pickHandler.err 交互过程中发生错误传出错误
* @param {Object} pickHandler.msg 交互结果信息
* @param {Number} pickHandler.msg.button 标识鼠标左键/右键与触发类型 0-左键单击 2-右键单击 3-左键双击
* @param {vec2} pickHandler.msg.screenPosition 鼠标点击位置屏幕坐标
* @param {Object[]} pickHandler.msg.picked 选中节点信息,按照被选中节点离屏幕的距离从近到远排序,如果没有选中节点,则返回空数组
* @param {Node[]} pickHandler.msg.picked.nodePath 选中的节点路径,即从被选中的节点到根节点的所有节点按序排列
* @param {vec3} pickHandler.msg.picked.position 点击到节点上的位置
* @param {vec3} pickHandler.msg.picked.normal 点击到位置的法向
* @param {Object} [option] 创建鼠标点击交互的参数
* @param {PickFilter} [option.pickFilter] 点击结果过滤器
* @param {Tracker} [option.tracker] 交互过程中附带的tracker
*
* @return {View3D.Interaction}
*/
InteractionFactory.prototype.createPointPickInteraction = function (pickHandler, option) {
Condicio.checkIsFunction(pickHandler, "The type of pickHandler-callback must be 'Function'!");
var pickFilterID = "";
var trackerID = "";
if (!Condicio.isUndefined(option) && !Condicio.isUndefined(option.pickFilter))
pickFilterID = option.pickFilter.objectID;
if (!Condicio.isUndefined(option) && !Condicio.isUndefined(option.tracker))
trackerID = option.tracker.objectID;
var _this = this;
var pickHandlerWrapper = function (error, result) {
var picks = result.picked;
//解析点击结果
var picked = analysePicked(picks, _this.session);
var msg = new Object();
// 屏幕坐标
msg.screenPos = CvtForJson.cvtToVec2(result.screenPos);
// 选中节点信息
msg.picked = picked;
// 点击的鼠标键位
msg.button = result.button;
pickHandler(error, msg);
}
var pickHandlerID = Utility.genGUID();
var interactionID = Utility.genGUID();
var params = {
interactionID: interactionID,
pickHandlerID: pickHandlerID
};
if (pickFilterID !== "")
params.pickFilterID = pickFilterID;
if (trackerID !== "")
params.trackerID = trackerID;
this.session.request(this.objectID, "createPointPickInteraction", params);
return new PointPickInteraction(interactionID, pickHandlerID, pickHandlerWrapper);
};
/**
* 释放交互资源
*
* @param {Interaction[]} interactions 待释放的交互资源集合
*/
InteractionFactory.prototype.releaseInteraction = function (interactions) {
ParamValidation.checkIsTypeArray(interactions, Interaction, "The type of Resources waiting release must be 'Interaction Array'");
var objectIDs = [];
for (var i = 0; i < interactions.length; ++i) {
objectIDs.push(interactions[i].objectID);
}
this.session.request(this.objectID, "releaseInteraction", { objectIDs: objectIDs });
};
/**
* 创建鼠标悬停操作交互
*
* @param {Function} hoverHandler 当鼠标悬停或悬停结束都会触发此回调
* @param {Error} hoverHandler.err 交互过程中失败
* @param {Object} hoverHandler.msg 鼠标悬停交互结果信息
* @param {Number} hoverHandler.msg.mouseHoverState 0:标识鼠标开始悬停,1:标识鼠标结束悬停
* @param {Vec2} hoverHandler.msg.screenPosition 鼠标悬停时的屏幕坐标,结束悬停时坐标值无效
* @param {Object[]} hoverHandler.msg.picked 参照{@link #createPointPickInteraction} picked的定义
* @param {Object} [option] 创建鼠标点击交互的参数
* @param {PickFilter} [option.pickFilter] 点击结果过滤器
* @param {Tracker} [option.tracker] 交互过程中附带的tracker
* @param {Number} [option.hoverTime = 300] 鼠标悬停需要的时间(毫秒)
*
* @return {View3D.Interaction}
*/
InteractionFactory.prototype.createMouseHoverInteraction = function (hoverHandler, option) {
Condicio.checkIsFunction(hoverHandler, "The type of hoverHandler-callback must be 'Function'!");
var pickFilterID = "";
var trackerID = "";
if (!Condicio.isUndefined(option) && !Condicio.isUndefined(option.pickFilter))
pickFilterID = option.pickFilter.objectID;
if (!Condicio.isUndefined(option) && !Condicio.isUndefined(option.tracker))
trackerID = option.tracker.objectID;
var hoverTime = 300;
if (!Condicio.isUndefined(option) && !Condicio.isUndefined(option.hoverTime))
hoverTime = option.hoverTime;
Condicio.checkArgument(hoverTime > 0, "param hoverTime must greater than 0!");
var _this = this;
var hoverHandlerWrapper = function (err, result) {
var mouseHoverState = result.mouseHoverState;
var screenPos = result.screenPos;
var picks = result.picked;
var pickedArray= analysePicked(picks, _this.session);
var screenPosVec2 = new GLM.vec2.fromValues(screenPos[0], screenPos[1]);
var msg = {
screenPos : screenPosVec2,
picked : pickedArray,
mouseHoverState : mouseHoverState
}
hoverHandler(err, msg);
};
var hoverHandlerID = Utility.genGUID();
var interactionID = Utility.genGUID();
var params = {
interactionID: interactionID,
hoverHandlerID: hoverHandlerID,
hoverTime: hoverTime
};
if (pickFilterID !== "")
params.pickFilterID = pickFilterID;
if (trackerID !== "")
params.trackerID = trackerID;
this.session.request(this.objectID, "createMouseHoverInteraction", params);
return new MouseHoverInteraction(interactionID, hoverHandlerID, hoverHandlerWrapper);
};
/**
* 创建混合式交互操作,满足可以开启多个交互的操作
*
* @param {Interaction[]} interactions 交互的集合
*
* @return {View3D.Interaction}
*/
InteractionFactory.prototype.createCompositeInteraction = function (interactions) {
ParamValidation.checkIsTypeArray(interactions, Interaction, "The type of interactions must be 'Interaction Array' and valid");
Condicio.checkArgument(interactions.length != 0, "The number of interactions must`t be NULL!");
var interactionIDs = [];
for (var i = 0; i < interactions.length; ++i) {
interactionIDs.push(interactions[i].objectID);
}
var interactionID = Utility.genGUID();
var params = {
interactionID: interactionID,
subInteractionIDs: interactionIDs
};
this.session.request(this.objectID, "createCompositeInteraction", params);
return new CompositeInteraction(interactionID, interactions);
};
/**
* 创建距离点的皮筋,包括动态变化的线和文字
*
* @param {vec3} startPt 皮筋的起点
* @param {Object} [style] 绘制属性
* @param {Object} [style.lineStyle] 线属性,参照View3D.Style.LineStyle的定义
* @param {Object} [style.textFrameStyle] 文字属性,参照View3D.Style.TextFrameStyle的定义
*
* @return {View3D.Tracker}
*/
InteractionFactory.prototype.createDistanceToPointTracker = function (startPt, style) {
var defaultStyle = {
lineStyle: Style.LineStyle,
textFrameStyle: Style.TextFrameStyle
};
var newStyle = jQuery.extend(true, {}, defaultStyle, style);
ParamValidation.checkIsVec3(startPt, "The type of startPt must be 'vec3'!");
ParamValidation.checkIsLineStyle(newStyle.lineStyle);
ParamValidation.checkIsTextFrameStyle(newStyle.textFrameStyle);
newStyle.lineStyle.color = newStyle.lineStyle.color.rgbaArray();
newStyle.textFrameStyle.text.color = newStyle.textFrameStyle.text.color.rgbaArray();
newStyle.textFrameStyle.border.color = newStyle.textFrameStyle.border.color.rgbaArray();
newStyle.textFrameStyle.text.backdropColor = newStyle.textFrameStyle.text.backdropColor.rgbaArray();
newStyle.textFrameStyle.fillColor = newStyle.textFrameStyle.fillColor.rgbaArray();
var startPtArray = CvtForJson.cvtVec3(startPt);
var trackerID = Utility.genGUID();
var params = {
trackerID: trackerID,
startPoint: startPtArray,
style: newStyle
};
this.session.request(this.objectID, "createDistanceToPointTracker", params);
return new Tracker(trackerID);
};
/**
* 创建距离一个面的皮筋(跟随鼠标的一端会有一个点),包括动态变化的线和文字
*
* @param {Object} plane 面
* @param {vec3} plane.point 面上一个点
* @param {vec3} plane.normal 面的法线(垂直于面)
* @param {Object} [style] 绘制属性
* @param {Object} [style.lineStyle] 线属性,参照View3D.Style.LineStyle的定义
* @param {Object} [style.textFrameStyle] 文字属性,参照View3D.Style.TextFrameStyle的定义
* @param {Object} [style.pointStyle] 点属性,参照View3D.Style.PointStyle
*
* @return {View3D.Tracker}
*/
InteractionFactory.prototype.createDistanceToPlaneTracker = function (plane, style) {
var defaultStyle = {
lineStyle: Style.LineStyle,
textFrameStyle: Style.TextFrameStyle,
pointStyle: Style.PointStyle
};
var newStyle = jQuery.extend(true, {}, defaultStyle, style);
ParamValidation.checkIsVec3(plane.point, "The type of plane.point must be 'vec3'!");
ParamValidation.checkIsVec3(plane.normal, "The type of plane.normal must be 'vec3'!");
Condicio.checkArgument(GLM.vec3.length(plane.normal) > 0, "The plane.normal must be valid!");
ParamValidation.checkIsLineStyle(newStyle.lineStyle);
ParamValidation.checkIsTextFrameStyle(newStyle.textFrameStyle);
ParamValidation.checkIsPointStyle(newStyle.pointStyle);
newStyle.lineStyle.color = newStyle.lineStyle.color.rgbaArray();
newStyle.textFrameStyle.text.color = newStyle.textFrameStyle.text.color.rgbaArray();
newStyle.textFrameStyle.border.color = newStyle.textFrameStyle.border.color.rgbaArray();
newStyle.textFrameStyle.text.backdropColor = newStyle.textFrameStyle.text.backdropColor.rgbaArray();
newStyle.textFrameStyle.fillColor = newStyle.textFrameStyle.fillColor.rgbaArray();
newStyle.pointStyle.color = newStyle.pointStyle.color.rgbaArray();
var plane = {
point: CvtForJson.cvtVec3(plane.point),
normal: CvtForJson.cvtVec3(plane.normal)
};
var trackerID = Utility.genGUID();
var params = {
trackerID: trackerID,
plane: plane,
style: newStyle
};
this.session.request(this.objectID, "createDistanceToPlaneTracker", params);
return new Tracker(trackerID);
};
/**
* 创建跟随鼠标移动的小方框Tracker
*
* @return {View3D.Tracker}
*/
InteractionFactory.prototype.createFollowRectTracker = function () {
var trackerID = Utility.genGUID();
var params = {
trackerID: trackerID
};
this.session.request(this.objectID, "createFollowRectTracker", params);
return new Tracker(trackerID);
};
/**
* 创建复合Tracker
*
* @param {Tracker[]} trackers tracker的集合
*
* @return {View3D.Tracker}
*/
InteractionFactory.prototype.createCompositeTracker = function (trackers) {
ParamValidation.checkIsTypeArray(trackers, Tracker, "The type of trackers must be 'Tracker Array' and valid");
Condicio.checkArgument(trackers.length != 0, "The number of trackers must`t be NULL!");
var trackerIDs = [];
for (var i = 0; i < trackers.length; ++i) {
trackerIDs.push(trackers[i].objectID);
}
var trackerID = Utility.genGUID();
var params = {
trackerID: trackerID,
subTrackerIDs: trackerIDs
};
this.session.request(this.objectID, "createCompositeTracker", params);
return new Tracker(trackerID);
};
/**
* 创建垂直距离水平距离Tracker
*
* @param {vec3} startPt 皮筋的起点
* @param {Object} [style] 绘制属性
* @param {Object} [style.lineStyle] 线属性,参照View3D.Style.LineStyle的定义
* @param {Object} [style.textFrameStyle] 文字属性,参照View3D.Style.TextFrameStyle的定义
*
* @return {View3D.Tracker}
*/
InteractionFactory.prototype.createVHDistanceTracker = function (startPt, style) {
var defaultStyle = {
lineStyle: Style.LineStyle,
textFrameStyle: Style.TextFrameStyle
};
var newStyle = jQuery.extend(true, {}, defaultStyle, style);
ParamValidation.checkIsVec3(startPt);
ParamValidation.checkIsLineStyle(newStyle.lineStyle);
ParamValidation.checkIsTextFrameStyle(newStyle.textFrameStyle);
newStyle.lineStyle.color = newStyle.lineStyle.color.rgbaArray();
newStyle.textFrameStyle.text.color = newStyle.textFrameStyle.text.color.rgbaArray();
newStyle.textFrameStyle.border.color = newStyle.textFrameStyle.border.color.rgbaArray();
newStyle.textFrameStyle.text.backdropColor = newStyle.textFrameStyle.text.backdropColor.rgbaArray();
newStyle.textFrameStyle.fillColor = newStyle.textFrameStyle.fillColor.rgbaArray();
var startPtArray = CvtForJson.cvtVec3(startPt);
var trackerID = Utility.genGUID();
var params = {
trackerID: trackerID,
startPoint: startPtArray,
style: newStyle
};
this.session.request(this.objectID, "createVHDistanceTracker", params);
return new Tracker(trackerID);
};
/**
* 创建绘制矩形Tracker
* 把一个线段沿垂直线段方向拉伸形成一个矩形
*
* @param {Object} baseLine 矩形的起始边
* @param {vec3} baseLine.startPt 矩形起始边的起点
* @param {vec3} baseLine.endPt 矩形起始边的终点
* @param {Object} [style] 绘制属性
* @param {Object} [style.lineStyle] 线属性,参照View3D.Style.LineStyle的定义
* @param {Object} [style.textFrameStyle] 文字属性,参照View3D.Style.TextFrameStyle的定义
*
* @return {View3D.LineBasedRectTracker}
*/
InteractionFactory.prototype.createLineBasedRectTracker = function (baseLine, style) {
Condicio.checkIsObject(baseLine, "The type of baseLine must be 'Object'!");
ParamValidation.checkIsVec3(baseLine.startPt);
ParamValidation.checkIsVec3(baseLine.endPt);
var defaultStyle = {
lineStyle: Style.LineStyle,
textFrameStyle: Style.TextFrameStyle
};
var newStyle = jQuery.extend(true, {}, defaultStyle, style);
ParamValidation.checkIsLineStyle(newStyle.lineStyle);
ParamValidation.checkIsTextFrameStyle(newStyle.textFrameStyle);
newStyle.lineStyle.color = newStyle.lineStyle.color.rgbaArray();
newStyle.textFrameStyle.text.color = newStyle.textFrameStyle.text.color.rgbaArray();
newStyle.textFrameStyle.text.backdropColor = newStyle.textFrameStyle.text.backdropColor.rgbaArray();
newStyle.textFrameStyle.border.color = newStyle.textFrameStyle.border.color.rgbaArray();
newStyle.textFrameStyle.fillColor = newStyle.textFrameStyle.fillColor.rgbaArray();
var startPtArray = CvtForJson.cvtVec3(baseLine.startPt);
var endPtArray = CvtForJson.cvtVec3(baseLine.endPt);
var trackerID = Utility.genGUID();
var params = {
trackerID: trackerID,
lineStartPoint: startPtArray,
lineEndPoint: endPtArray,
style: newStyle
};
this.session.request(this.objectID, "createLineBasedRectTracker", params);
return new LineBasedRectTracker(trackerID);
};
/**
* 创建过滤刨切的点击过滤器
*
* @return {View3D.PickFilter}
*/
InteractionFactory.prototype.createClipedPickFilter = function () {
var pickFilterID = Utility.genGUID();
this.session.request(this.objectID, "createClipedPickFilter", { pickFilterID: pickFilterID });
return new PickFilter(pickFilterID);
};
/**
* 创建过滤掉点选结果中除第一个之外的其他结果的过滤器
* 如果点选结果本身就是空的则不执行该过滤
*
* @return {View3D.PickFilter}
*/
InteractionFactory.prototype.createFrontResultFilter = function () {
var pickFilterID = Utility.genGUID();
this.session.request(this.objectID, "createFrontResultFilter", { pickFilterID: pickFilterID });
return new PickFilter(pickFilterID);
};
/**
* 创建过滤器链
* 过滤器链中的过滤器会依次起作用
*
* @param {PickFilter[]} filters 子过滤器
*
* @return {View3D.PickFilter}
*/
InteractionFactory.prototype.createChainFilter = function (filters) {
var pickFilterID = Utility.genGUID();
ParamValidation.checkIsTypeArray(filters, PickFilter, "The type of filters must be 'PickFilter Array' and valid");
Condicio.checkArgument(filters.length != 0, "The number of filters must`t be NULL!");
var filterIDs = [];
for (var i = 0; i < filters.length; ++i) {
filterIDs.push(filters[i].objectID);
}
var params = {
pickFilterID: pickFilterID,
subFilterIDs: filterIDs
};
this.session.request(this.objectID, "createChainFilter", params);
return new PickFilter(pickFilterID);
};
/**
* 创建过滤掉没有命名的过滤器
* 没有命名是指该点击结果的NodePath中所有的节点都没有命名
*
* @return {View3D.PickFilter}
*/
InteractionFactory.prototype.createNamedNodeFilter = function () {
var pickFilterID = Utility.genGUID();
this.session.request(this.objectID, "createNamedNodeFilter", { pickFilterID: pickFilterID });
return new PickFilter(pickFilterID);
};
/**
* 创建节点路径中包含特定节点的过滤器
* 当一个点击结果的NodePath中包含指定的节点则该点击结果不被该过滤器过滤掉
*
* @param {Node[]} includeNodes 指定的节点
*
* @return {View3D.PickFilter}
*/
InteractionFactory.prototype.createIncludeNodeFilter = function (includeNodes) {
var nodeIDArray = CvtForJson.cvtNodeArray(includeNodes);
var pickFilterID = Utility.genGUID();
var params = {
pickFilterID: pickFilterID,
includeNodes: nodeIDArray
};
this.session.request(this.objectID, "createIncludeNodeFilter", params);
return new PickFilter(pickFilterID);
};
/**
* 创建过滤掉透明节点的过滤器
* 在点击结果中过滤掉透明度低于transparencyValue的节点
*
* @param {Object} options
* @param {Number} [options.transparencyValue=0.5] 透明度(范围0~1),0为全透明,1为不透明(值:0<=transparencyValue<=1)
*
* @return {View3D.PickFilter}
*/
InteractionFactory.prototype.createTransparencyNodeFilter = function (options) {
if(Condicio.isUndefined(options))
options = {};
if(Condicio.isUndefined(options.transparencyValue))
options.transparencyValue = 0.5;
Condicio.checkIsNumber(options.transparencyValue, "The type of options.transparencyValue must be 'number'!");
Condicio.checkArgument(options.transparencyValue >= 0 && options.transparencyValue <= 1, "The transparencyValue must: >= 0 && <= 1 !");
var pickFilterID = Utility.genGUID();
var params = {
pickFilterID: pickFilterID,
transparencyValue: options.transparencyValue
};
this.session.request(this.objectID, "createTransparencyNodeFilter", params);
return new PickFilter(pickFilterID);
};
function vctRgbaArray(newStyle) {
newStyle.lineStyle.color = newStyle.lineStyle.color.rgbaArray();
newStyle.textFrameStyle.text.color = newStyle.textFrameStyle.text.color.rgbaArray();
newStyle.textFrameStyle.border.color = newStyle.textFrameStyle.border.color.rgbaArray();
newStyle.textFrameStyle.text.backdropColor = newStyle.textFrameStyle.text.backdropColor.rgbaArray();
newStyle.textFrameStyle.fillColor = newStyle.textFrameStyle.fillColor.rgbaArray();
};
/**
* 创建规则圆形的皮筋和该圆形直径的皮筋,包括动态变化的线和文字 (圆形皮筋与圆形直径皮筋只平行于xy平面,以startPt的z轴为准)
*
* @param {vec3} startPt 皮筋的起点
* @param {Object} [style] 绘制属性
* @param {Object} [style.lineStyle] 线属性,参照View3D.Style.LineStyle的定义
* @param {Object} [style.textFrameStyle] 文字属性,参照View3D.Style.TextFrameStyle的定义
* @param {Number} [style.pointNum] 组成圆形皮筋的点数量(整数 3-65536)(默认值30)
*
* @return {View3D.Tracker}
*/
InteractionFactory.prototype.createCircularTracker = function (startPt, style) {
var defaultStyle = {
lineStyle: Style.LineStyle,
textFrameStyle: Style.TextFrameStyle
};
var newStyle = jQuery.extend(true, {}, defaultStyle, style);
ParamValidation.checkIsVec3(startPt, "The type of startPt must be 'vec3'!");
ParamValidation.checkIsLineStyle(newStyle.lineStyle);
ParamValidation.checkIsTextFrameStyle(newStyle.textFrameStyle);
if(style.pointNum === undefined) { style.pointNum = 30; }
Condicio.checkIsNumber(style.pointNum, "option.pointNum must be a number");
Condicio.checkArgument(style.pointNum >= 3 && style.pointNum <= 65536 && style.pointNum % 1 === 0, "option.pointNum must be a positive integer with values from 3 to 65536");
vctRgbaArray(newStyle);
var startPtArray = CvtForJson.cvtVec3(startPt);
var trackerID = Utility.genGUID();
var params = {
trackerID: trackerID,
startPoint: startPtArray,
style: newStyle,
pointNum: style.pointNum
};
this.session.request(this.objectID, "createCircularTracker", params);
return new Tracker(trackerID);
};
module.exports = InteractionFactory;