/* 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.GLGeomObj.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.GLGeomObj.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") ) { 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.colorInfo) { this.setColor(el, stroke.colorInfo, false); } if(stroke.shapeInfo) { this.setProperty(el, "strokeSize", stroke.shapeInfo.strokeSize + " " + stroke.shapeInfo.strokeUnits, eventType, source); } if(stroke.webGLInfo) { this.setProperty(el, "strokeMaterial", stroke.webGLInfo.material); } } }, 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) { if(fill.colorInfo) { this.setColor(el, fill.colorInfo, true); } if(fill.webGLInfo) { this.setProperty(el, "fillMaterial", fill.webGLInfo.material); } } }, 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