/*
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.elementModel = el.elementModel;
this.toggleWebGlMode(canvas, value);
this.application.ninja.elementMediator.replaceElement(canvas, el);
break;
case "strokeMaterial":
m = Object.create(MaterialsModel.getMaterial(value));
if(m)
{
el.elementModel.shapeModel.GLGeomObj.setStrokeMaterial(m);
color = this.getMaterialColor(m);
if(color)
{
el.elementModel.shapeModel.GLGeomObj.setStrokeColor(color);
}
el.elementModel.shapeModel.GLGeomObj.buildBuffers();
el.elementModel.shapeModel.GLWorld.render();
}
break;
case "fillMaterial":
m = Object.create(MaterialsModel.getMaterial(value));
if(m)
{
el.elementModel.shapeModel.GLGeomObj.setFillMaterial(m);
color = this.getMaterialColor(m);
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.documentController.activeDocument.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 "FlatMaterial";
}
case "fillMaterial":
var fm = el.elementModel.shapeModel.GLGeomObj.getFillMaterial();
if(fm)
{
return fm.getName();
}
else
{
return "FlatMaterial";
}
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() !== "RadialGradientMaterial") )
{
gradientM = Object.create(MaterialsModel.getMaterial("RadialGradientMaterial"));
}
}
else
{
if( !m || (m.getName() !== "LinearGradientMaterial") )
{
gradientM = Object.create(MaterialsModel.getMaterial("LinearGradientMaterial"));
}
}
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() === "LinearGradientMaterial") || m.getName() === "RadialGradientMaterial") )
{
flatM = Object.create(MaterialsModel.getMaterial("FlatMaterial"));
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);
}
}
// Support for ink-bottle tool
if(color.strokeInfo)
{
this.setProperty(el, "strokeSize", color.strokeInfo.strokeSize + " " + color.strokeInfo.strokeUnits);
}
}
el.elementModel.shapeModel.GLWorld.render();
this.application.ninja.documentController.activeDocument.needsSave = true;
}
},
getStroke: {
value: function(el) {
// TODO - Need to figure out which border side user wants
var size = this.getShapeProperty(el, "strokeSize");
var color = this.getShapeProperty(el, "stroke");
return {stroke:color, strokeSize:size};
}
},
setStroke: {
value: function(el, stroke) {
el.elementModel.shapeModel.GLGeomObj.setStrokeColor(stroke.color.webGlColor);
var strokeWidth = this.GetValueInPixels(stroke.strokeSize, stroke.strokeUnits);
el.elementModel.shapeModel.GLGeomObj.setStrokeWidth(strokeWidth);
this.setShapeProperty(el, "stroke", stroke.color.webGlColor);
this.setShapeProperty(el, "strokeSize", stroke.strokeSize + " " + stroke.strokeUnits);
el.elementModel.shapeModel.GLGeomObj.buildBuffers();
el.elementModel.shapeModel.GLWorld.render();
}
},
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