UNPKG

kekule

Version:

Open source JavaScript toolkit for chemoinformatics

489 lines (464 loc) 16.5 kB
/** * @fileoverview * This file contains classes to draw ChemSpace objects in 2D or 3D context. * Usually the user do not need to call renderer classes to draw, but use painter instances instead. * @author Partridge Jiang */ /* * requires /core/kekule.common.js * requires /utils/kekule.utils.js * requires /render/kekule.render.base.js * requires /render/kekule.render.configs.js * //requires /render/kekule.render.boundInfoRecorder.js */ (function(){ /** * Painter used by user to draw a chem object. * * @augments ObjectEx * * @param {Int} renderType Display in 2D or 3D. Value from {@link Kekule.Render.RendererType}. * @param {Kekule.ChemObject} chemObj Object to be drawn. * @param {Object} drawBridge A object that implements the actual draw job. * //@param {Hash} options Options to draw object. * @param {Object} renderConfigs Global configuration for rendering. * This property should be an instance of {@link Kekule.Render.Render2DConfigs} or {@link Kekule.Render.Render3DConfigs}. * Set this param to null to use default configs. * * @property {Int} renderType Display in 2D or 3D. Value from {@link Kekule.Render.RendererType}. * @property {Kekule.ChemObject} chemObj Object to be drawn. * @property {Object} drawBridge A object that implements the actual draw job. Read only. * @property {Object} renderConfigs Configuration for rendering. * This property should be an instance of {@link Kekule.Render.Render2DConfigs} or {@link Kekule.Render.Render3DConfigs} * @propety {Kekule.Render.AbstractRenderer} renderer Renderer to draw the chem object. * * @property {Bool} canModifyTargetObj If set to true, renderer may change the rendered object (e.g., add charge markers, change block sizes...). * @class */ Kekule.Render.ChemObjPainter = Class.create(ObjectEx, /** @lends Kekule.Render.ChemObjPainter# */ { /** @private */ CLASS_NAME: 'Kekule.Render.ChemObjPainter', /** @constructs */ initialize: function(/*$super, */renderType, chemObj, drawBridge, renderConfigs) { this.tryApplySuper('initialize') /* $super() */; var rtype = renderType || Kekule.Render.RendererType.R2D; this.setPropStoreFieldValue('renderType', rtype); this.setPropStoreFieldValue('chemObj', chemObj); this.setPropStoreFieldValue('drawBridge', drawBridge || ((rtype === Kekule.Render.RendererType.R3D)? Kekule.Render.DrawBridge3DMananger.getPreferredBridgeInstance(): Kekule.Render.DrawBridge2DMananger.getPreferredBridgeInstance())); if (renderConfigs) this.setPropStoreFieldValue('renderConfigs', renderConfigs); /* else { this.setPropStoreFieldValue('renderConfigs', (rtype === Kekule.Render.RendererType.R3D)? Kekule.Render.Render3DConfigs.getInstance(): Kekule.Render.Render2DConfigs.getInstance()); } */ //this.setPropStoreFieldValue('boundInfoRecorder', new Kekule.Render.BoundInfoRecorder(this)); }, finalize: function(/*$super*/) { if (this.getRenderer()) this.getRenderer().finalize(); this.tryApplySuper('finalize') /* $super() */; }, /** @private */ initProperties: function() { this.defineProp('renderType', {'dataType': DataType.INT, 'serializable': false, 'setter': null}); this.defineProp('chemObj', {'dataType': 'Kekule.ChemObject', 'serializable': false, 'setter': null}); this.defineProp('drawBridge', {'dataType': DataType.OBJECT, 'serializable': false, 'setter': null}); this.defineProp('renderConfigs', {'dataType': DataType.OBJECT, 'serializable': false, 'getter': function() { var result = this.getPropStoreFieldValue('renderConfigs'); if (!result) result = (this.getRenderType() === Kekule.Render.RendererType.R3D)? Kekule.Render.Render3DConfigs.getInstance(): Kekule.Render.Render2DConfigs.getInstance(); return result; } }); this.defineProp('renderer', {'dateType': 'Kekule.Render.AbstractRenderer', 'serializable': false, 'setter': null}); this.defineProp('canModifyTargetObj', {'dataType': DataType.BOOL}); // private property // private object to record all bound infos this.defineProp('boundInfoRecorder', {'dataType': 'Kekule.Render.BoundInfoRecorder', 'serializable': false, 'setter': null, 'getter': function() { var r = this.getRenderer(); return r && r.getBoundInfoRecorder(); } }); }, /** @private */ getDrawOptionFromConfigs: function(renderConfigs) { var U = Kekule.Render.RenderOptionUtils; return U.convertConfigsToPlainHash(renderConfigs); }, /** @private */ prepareRenderer: function() { if (this.getRenderer()) // renderer already set return this.getRenderer(); var func = (this.getRenderType() === Kekule.Render.RendererType.R3D)? Kekule.Render.get3DRendererClass: Kekule.Render.get2DRendererClass; var c = func(this.getChemObj()); //console.log('prepare renderer',c); if (c) { var r = new c(this.getChemObj(), this.getDrawBridge(), /*this.getRenderConfigs(),*/ this); this.setPropStoreFieldValue('renderer', r); //r.setBoundInfoRecorder(this.getBoundInfoRecorder()); return r; } else // can not find suitable renderer { Kekule.error(/*Kekule.ErrorMsg.CANNOT_FIND_SUITABLE_RENDERER_FOR_OBJ*/Kekule.$L('ErrorMsg.CANNOT_FIND_SUITABLE_RENDERER_FOR_OBJ')); return null; } }, /** * Create an suitable context for drawing. * @param {Element} parentElem HTML element, context will be append to it. * @param {Int} width * @param {Int} height * @returns {Object} */ createContext: function(parentElem, width, height) { var b = this.getDrawBridge(); return b.createContext(parentElem, width, height); }, /** @private */ _convertContextBoxToScreen: function(context, box) { if (!box) return {}; var bridge = this.getDrawBridge(); var coord1 = bridge.transformContextCoordToScreen(context, {'x': box.x1, 'y': box.y1}); var coord2 = bridge.transformContextCoordToScreen(context, {'x': box.x2, 'y': box.y2}); return Kekule.BoxUtils.createBox(coord1, coord2); }, /** * Estimate the bound box around current chemObj (in chem coord system). * @param {Object} context * @param {Object} options * @param {Bool} allowCoordBorrow * @returns {Hash} A 2D or 3D box, in chemObj's coord system. */ estimateObjBox: function(context, options, allowCoordBorrow) { var ops = this._generateDrawOptions(options); this.prepareRenderer(); return this.getRenderer().estimateObjBox(context, options, allowCoordBorrow); }, /** * Estimate the bound box need to render current chemObj (in context coord system). * @param {Object} context * @param {Hash} baseCoord Center coord in context to draw object. Can be null. * @param {Object} options * @param {Bool} allowCoordBorrow * @returns {Hash} A 2D or 3D box, in context's coord system. */ estimateRenderBox: function(context, baseCoord, options, allowCoordBorrow) { var ops = this._generateDrawOptions(options); this.prepareRenderer(); var renderer = this.getRenderer(); return renderer.estimateRenderBox(context, baseCoord, ops, allowCoordBorrow); }, /** * Estimate the bound box need to render current chemObj (in screen coord system). * @param {Object} context * @param {Hash} baseCoord Center coord in context to draw object. Can be null. * @param {Object} options * @param {Bool} allowCoordBorrow * @returns {Hash} A 2D or 3D box, in context's coord system. */ estimateScreenBox: function(context, baseCoord, options, allowCoordBorrow) { var box = this.estimateRenderBox(context, baseCoord, options, allowCoordBorrow); return box? this._convertContextBoxToScreen(context, box): null; }, /* * Calculate the precise drawing box of chemObj, in context coord. * This method must be called after draw. * @param {Object} context * @returns {Hash} */ /* calcActualRenderBox: function(context) { return this.getBoundInfoRecorder().getContainerBox(context); }, */ /* * Calculate the precise drawing box of chemObj, in screen coord. * This method must be called after draw. * @param {Object} context * @returns {Hash} */ /* calcActualScreenBox: function(context) { var box = this.calcActualRenderBox(context); return this._convertContextBoxToScreen(context, box); }, */ /** @private */ _generateDrawOptions: function(inputOptions) { var configs = this.getRenderConfigs(); var ops = configs? this.getDrawOptionFromConfigs(configs): {}; ops = Object.extend(ops, inputOptions); return ops; }, /** * Draw an instance of ChemObject to context. * The actual job is done in doDraw method. Descendants should override doDraw. * @param {Object} context Context to be drawn, such as Canvas, SVG, VML and so on. * @param {Hash} baseCoord Coord of center to draw this object, can be null. This coord is based on context. * @param {Hash} options Draw options, such as draw rectangle, draw style, zoom and so on. * Different coordMode may requires different option params. */ draw: function(context, baseCoord, options) { if (!this.getChemObj()) return null; this.prepareRenderer(); //this.getBoundInfoRecorder().clear(context); /* var configs = this.getRenderConfigs(); var ops = configs? this.getDrawOptionFromConfigs(configs): {}; ops = Object.extend(ops, options); */ var ops = this._generateDrawOptions(options); var r = this.getRenderer(); //console.log('painter draw', r.getClassName(), r); var result = r.draw(context, baseCoord, ops); return result; }, /** * Redraw previous object on context with same draw options. Should not be called before draw. * @param {Object} context */ redraw: function(context) { //this.getBoundInfoRecorder().clear(context); return this.getRenderer().redraw(context); }, /* * Calculate prefered input transform options (but not the final transform params). * @param {Object} context * @param {Hash} baseCoord * @param {Hash} drawOptions * @param {Hash} objBox * @returns {Hash} */ /* calcPreferedTransformOptions: function(context, baseCoord, drawOptions, objBox) { var r = this.getRenderer(); if (r) return r.calcPreferedTransformOptions(context, baseCoord, drawOptions, objBox); else { return null; } }, */ /** * Get the actual transform params used by current root renderer. * @param {Object} context * @param {Hash} baseCoord * @param {Hash} drawOptions * @returns {hash} * @ignore */ getActualRenderTransformParams: function(context) { var r = this.getRenderer(); if (r) { return r.getRenderFinalTransformParams(context); } else return null; }, /** @ignore */ getActualInitialRenderTransformOptions: function(context) { var r = this.getRenderer(); if (r) { return r.getRenderInitialTransformOptions(context); } else return null; }, /** * Repaint with only geometry options (translate/zoom/rotate) changes (without the modification of chemobj or draw color, molecule type...). * In 3D mode sometimes this repainting can be achieved by the modify of camera position (without recalc the position of node and connectors) * so that the speed may enhance greatly. * @param {Object} context * @param {Hash} newOptions */ changeGeometryOptions: function(context, baseCoord, newOptions) { var r = this.getRenderer(); if (r && r.changeGeometryOptions) { var ops = this._generateDrawOptions(newOptions); return r.changeGeometryOptions(context, baseCoord, ops); } else { this.clearContext(context); return this.draw(context, baseCoord, newOptions); } }, /** * Check if changeGeometryOptions is availble to current renderer. * @returns {Bool} */ supportGeometryOptionChange: function() { var r = this.getRenderer(); return r && r.changeGeometryOptions; }, /** * Call this method before a series of rendered element updating job (for instance, call update method) * to avoid unnecessary redraw. */ beginUpdatePainter: function() { return this.getRenderer().beginUpdateRenderer(); }, /** * Call this method after a series of rendered element updateing job, * notify the painter to redraw the context. */ endUpdatePainter: function() { return this.getRenderer().endUpdateRenderer(); }, /** * Update a child object inside chemObj. Must be called after draw. * @param {Object} context * @param {Variant} updatedObjs Object or Array * @param {Int} updateType Value from {@link Kekule.Render.ObjectUpdateType} */ update: function(context, updatedObjs, updateType) { return this.getRenderer().update(context, updatedObjs, updateType); }, /** * Add a new child object to chemObj. Must be called after draw. * @param {Object} context * @param {Variant} updatedObjs * @returns {Bool} Whether the actual add job is done. * @private */ addNew: function(context, updatedObjs) { return this.getRenderer().addNew(context, updatedObjs); }, /** * Modify chemObj or a child object inside chemObj. Must be called after draw. * @param {Object} context * @param {Variant} updatedObjs * @returns {Bool} Whether the actual modify job is done. * @private */ modify: function(context, updatedObjs) { return this.getRenderer().modify(context, updatedObjs); }, /** * Remove a child object inside chemObj and update the rendering. Must be called after draw. * @param {Object} context * @param {Variant} removedObjs * @returns {Bool} Whether the actual remove job is done. * @private */ remove: function(context, removedObjs) { return this.getRenderer().remove(context, removedObjs); }, /** * Clear whole chemObj on context. * @param {Object} context * @returns {Bool} Whether the actual clear job is done. */ clear: function(context) { var renderer = this.getRenderer(); if (renderer) renderer.clear(context); return this; }, /** * Clear the painted objects in whole context. * @param {Object} context */ clearContext: function(context) { return this.getDrawBridge().clearContext(context); } }); /** * Painter used by user to draw a chem object in 2D. * * @augments Kekule.Render.ChemObjPainter * * @param {Kekule.ChemObject} chemObj Object to be drawn. * @param {Object} drawBridge A object that implements the actual draw job. * @param {Hash} options Options to draw object. * @param {Object} renderConfigs Global configuration for rendering. * This property should be an instance of {@link Kekule.Render.Render2DConfigs} or {@link Kekule.Render.Render3DConfigs}. * Set this param to null to use default configs. * * @class */ Kekule.Render.ChemObjPainter2D = Class.create(Kekule.Render.ChemObjPainter, /** @lends Kekule.Render.ChemObjPainter2D# */ { /** @private */ CLASS_NAME: 'Kekule.Render.ChemObjPainter2D', /** @constructs */ initialize: function(/*$super, */chemObj, drawBridge, renderConfigs) { this.tryApplySuper('initialize', [Kekule.Render.RendererType.R2D, chemObj, drawBridge, renderConfigs]) /* $super(Kekule.Render.RendererType.R2D, chemObj, drawBridge, renderConfigs) */; } }); /** * Painter used by user to draw a chem object in 3D. * * @augments Kekule.Render.ChemObjPainter * * @param {Kekule.ChemObject} chemObj Object to be drawn. * @param {Object} drawBridge A object that implements the actual draw job. * @param {Hash} options Options to draw object. * @param {Object} renderConfigs Global configuration for rendering. * This property should be an instance of {@link Kekule.Render.Render2DConfigs} or {@link Kekule.Render.Render3DConfigs}. * Set this param to null to use default configs. * * @class */ Kekule.Render.ChemObjPainter3D = Class.create(Kekule.Render.ChemObjPainter, /** @lends Kekule.Render.ChemObjPainter3D# */ { /** @private */ CLASS_NAME: 'Kekule.Render.ChemObjPainter3D', /** @constructs */ initialize: function(/*$super, */chemObj, drawBridge, renderConfigs) { this.tryApplySuper('initialize', [Kekule.Render.RendererType.R3D, chemObj, drawBridge, renderConfigs]) /* $super(Kekule.Render.RendererType.R3D, chemObj, drawBridge, renderConfigs) */; } }); })();