/*
This file contains proprietary software owned by Motorola Mobility, Inc.
No rights, expressed or implied, whatsoever to this software are provided by Motorola Mobility, Inc. hereunder.
(c) Copyright 2011 Motorola Mobility, Inc. All Rights Reserved.
*/
var Montage = require("montage/core/core").Montage,
CanvasController = require("js/controllers/elements/canvas-controller").CanvasController,
njModule = require("js/lib/NJUtils"),
World = require("js/lib/drawing/world").World,
MaterialsModel = require("js/models/materials-model").MaterialsModel;
exports.ShapesController = Montage.create(CanvasController, {
setProperty: {
value: function(el, p, value, eventType, source) {
var val = parseInt(value),
canvas,
m,
color;
switch(p) {
case "strokeSize":
this.setShapeProperty(el, "strokeSize", value);
var strokeInfo = njModule.NJUtils.getValueAndUnits(value);
val = this.GetValueInPixels(strokeInfo[0], strokeInfo[1]);
// TODO - For now, just handle Line, Rectangle and Oval. Eventually, move this into each class's
// setStrokeWidth code like SubPath and BrushStroke do.
var geomType = el.elementModel.shapeModel.GLGeomObj.geomType();
if( (geomType > 0) && (geomType < 4) )
{
// Changing stroke size should grow/shrink the shape from the center.
var delta = ~~(val - el.elementModel.shapeModel.GLGeomObj.getStrokeWidth()),
l = this.application.ninja.elementMediator.getProperty(el, "left", parseInt),
t = this.application.ninja.elementMediator.getProperty(el, "top", parseInt),
w = this.application.ninja.elementMediator.getProperty(el, "width", parseInt),
h = this.application.ninja.elementMediator.getProperty(el, "height", parseInt);
if(geomType === 3)
{
var slope = el.elementModel.shapeModel.slope;
// set the dimensions
if(slope === "horizontal")
{
h = Math.max(val, 1);
t -= ~~(delta/2);
}
else if(slope === "vertical")
{
w = Math.max(val, 1);
l -= ~~(delta/2);
}
else
{
var oldXAdj = el.elementModel.shapeModel.GLGeomObj.getXAdj();
var oldYAdj = el.elementModel.shapeModel.GLGeomObj.getYAdj();
var theta = Math.atan(el.elementModel.shapeModel.slope);
var xAdj = Math.abs((val/2)*Math.sin(theta));
var yAdj = Math.abs((val/2)*Math.cos(theta));
var dX = ~~(xAdj*2 - oldXAdj*2);
var dY = ~~(yAdj*2 - oldYAdj*2);
l -= dX;
t -= dY;
w += dX*2;
h += dY*2;
el.elementModel.shapeModel.GLGeomObj.setXAdj(xAdj);
el.elementModel.shapeModel.GLGeomObj.setYAdj(yAdj);
}
}
else
{
l -= ~~(delta/2);
t -= ~~(delta/2);
w += delta;
h += delta;
}
this.application.ninja.elementMediator.setProperties([{element:el, properties:{left: l + "px", top: t + "px", width: w + "px", height:h + "px"}}], eventType, source);
}
el.elementModel.shapeModel.GLGeomObj.setStrokeWidth(val);
el.elementModel.shapeModel.GLGeomObj.buildBuffers();
el.elementModel.shapeModel.GLWorld.render();
break;
case "innerRadius":
this.setShapeProperty(el, "innerRadius", value);
el.elementModel.shapeModel.GLGeomObj.setInnerRadius(val/100);
el.elementModel.shapeModel.GLGeomObj.buildBuffers();
el.elementModel.shapeModel.GLWorld.render();
break;
case "tlRadius":
this.setShapeProperty(el, "tlRadius", value);
el.elementModel.shapeModel.GLGeomObj.setTLRadius(val);
el.elementModel.shapeModel.GLGeomObj.buildBuffers();
el.elementModel.shapeModel.GLWorld.render();
break;
case "trRadius":
this.setShapeProperty(el, "trRadius", value);
el.elementModel.shapeModel.GLGeomObj.setTRRadius(val);
el.elementModel.shapeModel.GLGeomObj.buildBuffers();
el.elementModel.shapeModel.GLWorld.render();
break;
case "blRadius":
this.setShapeProperty(el, "blRadius", value);
el.elementModel.shapeModel.GLGeomObj.setBLRadius(val);
el.elementModel.shapeModel.GLGeomObj.buildBuffers();
el.elementModel.shapeModel.GLWorld.render();
break;
case "brRadius":
this.setShapeProperty(el, "brRadius", value);
el.elementModel.shapeModel.GLGeomObj.setBRRadius(val);
el.elementModel.shapeModel.GLGeomObj.buildBuffers();
el.elementModel.shapeModel.GLWorld.render();
break;
case "width":
el.elementModel.shapeModel.GLGeomObj.setWidth(val);
CanvasController.setProperty(el, p, value);
el.elementModel.shapeModel.GLWorld.setViewportFromCanvas(el);
el.elementModel.shapeModel.GLGeomObj.buildBuffers();
el.elementModel.shapeModel.GLWorld.render();
break;
case "height":
el.elementModel.shapeModel.GLGeomObj.setHeight(val);
CanvasController.setProperty(el, p, value);
el.elementModel.shapeModel.GLWorld.setViewportFromCanvas(el);
el.elementModel.shapeModel.GLGeomObj.buildBuffers();
el.elementModel.shapeModel.GLWorld.render();
break;
case "useWebGl":
canvas = njModule.NJUtils.make("canvas", el.className, this.application.ninja.currentDocument);
canvas.setAttribute("data-RDGE-id", njModule.NJUtils.generateRandom());
canvas.width = el.width;
canvas.height = el.height;
canvas._model = el.elementModel;
this.toggleWebGlMode(canvas, value);
this.application.ninja.elementMediator.replaceElement(canvas, el);
break;
case "strokeMaterial":
// skip shape types that don't support WebGL
if(!el.elementModel.shapeModel.useWebGl) {
return;
}
m = Object.create(MaterialsModel.getMaterial(value));
if(m)
{
el.elementModel.shapeModel.GLGeomObj.setStrokeMaterial(m);
color = this.getMaterialColor(value);
if(color)
{
el.elementModel.shapeModel.GLGeomObj.setStrokeColor(color);
}
el.elementModel.shapeModel.GLGeomObj.buildBuffers();
el.elementModel.shapeModel.GLWorld.render();
}
break;
case "fillMaterial":
// skip shape types that don't support WebGL or fill color
if(!el.elementModel.shapeModel.GLGeomObj.canFill || !el.elementModel.shapeModel.useWebGl) {
return;
}
m = Object.create(MaterialsModel.getMaterial(value));
if(m)
{
el.elementModel.shapeModel.GLGeomObj.setFillMaterial(m);
color = this.getMaterialColor(value);
if(color)
{
el.elementModel.shapeModel.GLGeomObj.setFillColor(color);
}
el.elementModel.shapeModel.GLGeomObj.buildBuffers();
el.elementModel.shapeModel.GLWorld.render();
}
break;
case "editStrokeMaterial":
NJevent("showMaterialPopup",{materialId : this.getProperty(el, "strokeMaterial"), useSelection: true, whichMaterial: 'stroke'});
break;
case "editFillMaterial":
NJevent("showMaterialPopup",{materialId : this.getProperty(el, "fillMaterial"), useSelection: true, whichMaterial: 'fill'});
break;
case "animate":
if(value)
{
el.elementModel.shapeModel.animate = true;
el.elementModel.shapeModel.GLWorld._previewAnimation = true;
el.elementModel.shapeModel.GLWorld.restartRenderLoop();
}
else
{
el.elementModel.shapeModel.animate = false;
el.elementModel.shapeModel.GLWorld._previewAnimation = false;
el.elementModel.shapeModel.GLWorld._canvas.task.stop();
}
break;
case "strokeHardness":
this.setShapeProperty(el, "strokeHardness", value);
el.elementModel.shapeModel.GLGeomObj.setStrokeHardness(val);
el.elementModel.shapeModel.GLWorld.render();
break;
case "strokeSmoothing":
this.setShapeProperty(el, "strokeSmoothing", value);
el.elementModel.shapeModel.GLGeomObj.setSmoothingAmount(val);
el.elementModel.shapeModel.GLWorld.render();
break;
case "doSmoothing":
this.setShapeProperty(el, "doSmoothing", value);
el.elementModel.shapeModel.GLGeomObj.setDoSmoothing(value);
el.elementModel.shapeModel.GLWorld.render();
break;
case "isCalligraphic":
this.setShapeProperty(el, "isCalligraphic", value);
el.elementModel.shapeModel.GLGeomObj.setStrokeUseCalligraphic(value);
el.elementModel.shapeModel.GLWorld.render();
break;
case "strokeAngle":
this.setShapeProperty(el, "strokeAngle", value);
el.elementModel.shapeModel.GLGeomObj.setStrokeAngle(Math.PI * val/180);
el.elementModel.shapeModel.GLWorld.render();
break;
default:
CanvasController.setProperty(el, p, value);
}
this.application.ninja.currentDocument.model.needsSave = true;
}
},
getProperty: {
value: function(el, p) {
switch(p) {
case "strokeSize":
case "innerRadius":
case "tlRadius":
case "trRadius":
case "blRadius":
case "brRadius":
case "useWebGl":
case "animate":
return this.getShapeProperty(el, p);
case "border":
return this.getColor(el, false);
case "background":
return this.getColor(el, true);
case "strokeHardness":
if (el.elementModel && el.elementModel.shapeModel){
return el.elementModel.shapeModel.GLGeomObj.getStrokeHardness();
} else {
return null;
}
break;
case "doSmoothing":
if (el.elementModel && el.elementModel.shapeModel){
return el.elementModel.shapeModel.GLGeomObj.getDoSmoothing();
} else {
return null;
}
break;
case "strokeSmoothing":
if (el.elementModel && el.elementModel.shapeModel){
return el.elementModel.shapeModel.GLGeomObj.getSmoothingAmount();
} else {
return null;
}
break;
case "isCalligraphic":
if (el.elementModel && el.elementModel.shapeModel){
return el.elementModel.shapeModel.GLGeomObj.getStrokeUseCalligraphic();
} else {
return null;
}
break;
case "strokeAngle":
if (el.elementModel && el.elementModel.shapeModel){
return 180*el.elementModel.shapeModel.GLGeomObj.getStrokeAngle()/Math.PI;
} else {
return null;
}
break;
case "strokeMaterial":
var sm = el.elementModel.shapeModel.GLGeomObj.getStrokeMaterial();
if(sm)
{
return sm.getName();
}
else
{
return "Flat";
}
case "fillMaterial":
var fm = el.elementModel.shapeModel.GLGeomObj.getFillMaterial();
if(fm)
{
return fm.getName();
}
else
{
return "Flat";
}
default:
return CanvasController.getProperty(el, p);
}
}
},
getShapeProperty: {
value: function(el, prop) {
if(el.elementModel && el.elementModel.shapeModel)
{
return el.elementModel.shapeModel[prop];
}
else
{
console.log("No shapeModel, one should have been created already");
return null;
}
}
},
setShapeProperty: {
value: function(el, prop, value) {
if(el.elementModel && el.elementModel.shapeModel)
{
el.elementModel.shapeModel[prop] = value;
}
else
{
console.log("No shapeModel, one should have been created already");
}
}
},
GetValueInPixels: {
value: function(value, units, h)
{
switch(units)
{
case "px":
{
return value;
}
case "pt":
{
return ~~(value*4/3);
}
case "%":
{
if(h)
{
return ~~(value/100*h);
}
else
{
console.warn("Can't use % for a line's stroke size, using 10 for the value.");
return 10;
}
}
}
}
},
CapWorldPercentFromValue: {
value: function(value, units, h)
{
return Math.min(this.GetWorldPercentFromValue(value, units, h), 2);
}
},
GetWorldPercentFromValue: {
value: function(value, units, h)
{
switch(units)
{
case "pt":
{
value = Math.round(value*4/3);
return 4*value/h;
}
case "px":
{
return 4*value/h;
}
case "%":
{
// Our calculations in GLWorld use 2 = 100%, so our calculations would usually be value/50,
// but in order to get values other than 0, 1, and 2, we need to multiply by 10, round that value,
// and then divide by 50*10 again.
// 100*10 = 1000/500 = 2
// 20*10 = 200/500 = 0.4
// 50*10 = 500/500 = 1
return Math.round(value*10)/500;
}
default:
{
console.warn("Unhandled units " + units);
}
}
}
},
//--------------------------------------------------------------------------------------------------------
// Routines to get/set color properties
getColor: {
value: function(el, isFill) {
if(isFill)
{
// Properties Panel asks for fill color even for shapes that only have strokes
// Check that shape object supports fills
if(el.elementModel.shapeModel.GLGeomObj.canFill)
{
return this.application.ninja.colorController.colorModel.webGlToColor(el.elementModel.shapeModel.GLGeomObj.getFillColor());
}
else
{
return null;
}
}
else
{
return this.application.ninja.colorController.colorModel.webGlToColor(el.elementModel.shapeModel.GLGeomObj.getStrokeColor());
}
}
},
_setGradientMaterial: {
value: function(el, gradientMode, isFill) {
var m,
gradientM;
if(isFill)
{
m = el.elementModel.shapeModel.GLGeomObj.getFillMaterial();
}
else
{
m = el.elementModel.shapeModel.GLGeomObj.getStrokeMaterial();
}
if(gradientMode === "radial")
{
if( !m || (m.getName() !== "Radial Gradient") )
{
gradientM = Object.create(MaterialsModel.getMaterial("Radial Gradient"));
}
}
else
{
if( !m || (m.getName() !== "Linear Gradient") )
{
gradientM = Object.create(MaterialsModel.getMaterial("Linear Gradient"));
}
}
if(gradientM)
{
if(isFill)
{
el.elementModel.shapeModel.GLGeomObj.setFillMaterial(gradientM);
}
else
{
el.elementModel.shapeModel.GLGeomObj.setStrokeMaterial(gradientM);
}
el.elementModel.shapeModel.GLGeomObj.buildBuffers();
}
}
},
_setFlatMaterial: {
value: function(el, isFill) {
var m,
flatM;
if(isFill)
{
m = el.elementModel.shapeModel.GLGeomObj.getFillMaterial();
}
else
{
m = el.elementModel.shapeModel.GLGeomObj.getStrokeMaterial();
}
if(!m || ((m.getName() === "Linear Gradient") || m.getName() === "Radial Gradient") )
{
// Uber Material also supports solid colors, so don't change from Uber to Flat Material
if(m && (m.getName() === "Uber")) { return; }
flatM = Object.create(MaterialsModel.getMaterial("Flat"));
if(flatM)
{
if(isFill)
{
el.elementModel.shapeModel.GLGeomObj.setFillMaterial(flatM);
}
else
{
el.elementModel.shapeModel.GLGeomObj.setStrokeMaterial(flatM);
}
el.elementModel.shapeModel.GLGeomObj.buildBuffers();
}
}
}
},
setColor: {
value: function(el, color, isFill) {
var mode = color.mode,
webGl;
if(isFill)
{
// skip shape types that don't have fill color
if(el.elementModel.shapeModel.GLGeomObj.canFill)
{
if(mode)
{
switch (mode) {
case 'nocolor':
el.elementModel.shapeModel.GLGeomObj.setFillColor(null);
break;
case 'gradient':
if(el.elementModel.shapeModel.useWebGl)
{
this._setGradientMaterial(el, color.color.gradientMode, isFill);
}
el.elementModel.shapeModel.GLGeomObj.setFillColor({gradientMode:color.color.gradientMode, color:color.color.stops});
break;
default:
if(el.elementModel.shapeModel.useWebGl)
{
this._setFlatMaterial(el, isFill);
}
webGl = this.application.ninja.colorController.colorModel.colorToWebGl(color.color);
el.elementModel.shapeModel.GLGeomObj.setFillColor(webGl);
}
}
}
else
{
return;
}
}
else
{
if(mode)
{
switch (mode) {
case 'nocolor':
el.elementModel.shapeModel.GLGeomObj.setStrokeColor(null);
break;
case 'gradient':
if(el.elementModel.shapeModel.useWebGl)
{
this._setGradientMaterial(el, color.color.gradientMode, isFill);
}
el.elementModel.shapeModel.GLGeomObj.setStrokeColor({gradientMode:color.color.gradientMode, color:color.color.stops});
break;
default:
if(el.elementModel.shapeModel.useWebGl)
{
this._setFlatMaterial(el, isFill);
}
webGl = this.application.ninja.colorController.colorModel.colorToWebGl(color.color);
el.elementModel.shapeModel.GLGeomObj.setStrokeColor(webGl);
}
}
}
el.elementModel.shapeModel.GLWorld.render();
this.application.ninja.currentDocument.model.needsSave = true;
}
},
getStroke: {
value: function(el, stroke) {
var strokeInfo = {},
color,
strokeWidth,
strokeSize;
if(stroke.colorInfo) {
strokeInfo.colorInfo = {};
color = this.getColor(el, false);
if(color && color.color) {
strokeInfo.colorInfo.mode = color.mode;
strokeInfo.colorInfo.color = color.color;
} else {
strokeInfo.colorInfo.mode = "nocolor";
strokeInfo.colorInfo.color = null;
}
}
if(stroke.shapeInfo) {
strokeInfo.shapeInfo = {};
strokeWidth = this.getProperty(el, "strokeSize");
if(strokeWidth) {
strokeSize = njModule.NJUtils.getValueAndUnits(strokeWidth);
strokeInfo.shapeInfo.strokeSize = strokeSize[0];
strokeInfo.shapeInfo.strokeUnits = strokeSize[1];
}
}
if(stroke.webGLInfo) {
strokeInfo.webGLInfo = {};
if(this.getShapeProperty(el, "useWebGl")) {
strokeInfo.webGLInfo.material = this.getProperty(el, "strokeMaterial");
} else {
strokeInfo.webGLInfo.material = null;
}
}
return strokeInfo;
}
},
setStroke: {
value: function(el, stroke, eventType, source) {
if(stroke.shapeInfo) {
this.setProperty(el, "strokeSize", stroke.shapeInfo.strokeSize + " " + stroke.shapeInfo.strokeUnits, eventType, source);
}
var m;
if(stroke.webGLInfo) {
m = stroke.webGLInfo.material;
this.setProperty(el, "strokeMaterial", m);
if((m === "Linear Gradient") || (m === "Radial Gradient")) {
// Just use the default gradient material values
return;
}
}
if(stroke.colorInfo) {
if(el.elementModel.shapeModel.useWebGl) {
m = el.elementModel.shapeModel.GLGeomObj.getStrokeMaterial().getName();
if( ((stroke.colorInfo.mode === "gradient") && (m !== "Linear Gradient") && (m !== "Radial Gradient")) ||
((stroke.colorInfo.mode !== "gradient") && ((m === "Linear Gradient") || (m === "Radial Gradient"))))
{
return;
} else {
this.setColor(el, stroke.colorInfo, false);
}
} else {
this.setColor(el, stroke.colorInfo, false);
}
}
}
},
getFill: {
value: function(el, fill) {
var fillInfo = {},
color;
if(fill.colorInfo) {
fillInfo.colorInfo = {};
color = this.getColor(el, true);
if(color && color.color) {
fillInfo.colorInfo.mode = color.mode;
fillInfo.colorInfo.color = color.color;
} else {
fillInfo.colorInfo.mode = "nocolor";
fillInfo.colorInfo.color = null;
}
}
if(fill.webGLInfo) {
fillInfo.webGLInfo = {};
if(this.getShapeProperty(el, "useWebGl")) {
fillInfo.webGLInfo.material = this.getProperty(el, "fillMaterial");
} else {
fillInfo.webGLInfo.material = null;
}
}
return fillInfo;
}
},
setFill: {
value: function(el, fill) {
var m;
if(fill.webGLInfo) {
m = fill.webGLInfo.material;
this.setProperty(el, "fillMaterial", m);
if((m === "Linear Gradient") || (m === "Radial Gradient")) {
// Just use the default gradient material values
return;
}
}
if(fill.colorInfo) {
if(el.elementModel.shapeModel.useWebGl) {
m = el.elementModel.shapeModel.GLGeomObj.getFillMaterial().getName();
if( ((fill.colorInfo.mode === "gradient") && (m !== "Linear Gradient") && (m !== "Radial Gradient")) ||
((fill.colorInfo.mode !== "gradient") && ((m === "Linear Gradient") || (m === "Radial Gradient"))))
{
return;
} else {
this.setColor(el, fill.colorInfo, true);
}
} else {
this.setColor(el, fill.colorInfo, true);
}
}
}
},
DisplayMaterials: {
value: function (cb)
{
var optionItem = document.createElement("option");
optionItem.value = 0;
optionItem.innerText = "Default";
cb.appendChild(optionItem);
var materials = this.application.ninja.appModel.materials;
var len = materials.length;
var i;
for (i = 0; i < len; i++)
{
var current = materials[i];
optionItem = document.createElement("option");
optionItem.value = i+1;
optionItem.innerText = current.getName();
cb.appendChild(optionItem);
}
}
},
isElementAShape: {
value: function(el)
{
return (el.elementModel && el.elementModel.isShape);
}
},
toggleWebGlMode: {
value: function(el, useWebGl)
{
if(useWebGl)
{
this.convertToWebGlWorld(el);
}
else
{
this.convertTo2DWorld(el);
}
}
},
convertToWebGlWorld: {
value: function(el)
{
if(el.elementModel.shapeModel.useWebGl)
{
return;
}
var world,
worldData = el.elementModel.shapeModel.GLWorld.exportJSON();
if(worldData)
{
worldData = this.flip3DSense (worldData );
world = new World(el, true);
el.elementModel.shapeModel.GLWorld = world;
el.elementModel.shapeModel.useWebGl = true;
world.importJSON(worldData);
el.elementModel.shapeModel.GLGeomObj = world.getGeomRoot();
}
}
},
convertTo2DWorld: {
value: function(el)
{
if(!el.elementModel.shapeModel.useWebGl)
{
return;
}
var world,
worldData = el.elementModel.shapeModel.GLWorld.exportJSON();
if(worldData)
{
worldData = this.flip3DSense (worldData );
world = new World(el, false);
el.elementModel.shapeModel.GLWorld = world;
el.elementModel.shapeModel.useWebGl = false;
world.importJSON(worldData);
el.elementModel.shapeModel.GLGeomObj = world.getGeomRoot();
}
}
},
flip3DSense: {
value: function( importStr )
{
var jObj;
var index = importStr.indexOf( ';' );
if ((importStr[0] === 'v') && (index < 24))
{
// JSON format. separate the version info from the JSON info
//var vStr = importStr.substr( 0, index+1 );
var jStr = importStr.substr( index+1 );
jObj = JSON.parse( jStr );
jObj.webGL = !jObj.webGL;
if(jObj.children)
{
var nKids = jObj.children.length;
for (var i=0; i