Source: render/kekule.render.base.js

  1. /**
  2. * @fileoverview
  3. * Base (abstract) class of 2D or 3D molecule renderers.
  4. * @author Partridge Jiang
  5. */
  6. /*
  7. * requires /lan/classes.js
  8. * requires /core/kekule.common.js
  9. * requires /render/kekule.render.utils.js
  10. */
  11. "use strict";
  12. /**
  13. * Namespace for renderer system.
  14. * @namespace
  15. */
  16. Kekule.Render = {};
  17. /**
  18. * Enumeration of coord systems (chemObject, context or screen).
  19. * @class
  20. */
  21. Kekule.Render.CoordSystem = {
  22. /** ChemObject system, used to mark the inner structure of chem objects. 2D or 3D. */
  23. CHEM: 0,
  24. /** Context system, used for draw bridges. 2D or 3D. */
  25. CONTEXT: 1,
  26. /** Screen system, 2D, usually in pixel. */
  27. SCREEN: 2
  28. };
  29. /*
  30. * Enumeration of interaction states of chem object (node or connector).
  31. * @class
  32. * @deprecated
  33. */
  34. //Kekule.Render.InteractState = {
  35. /** Normal state. */
  36. //NORMAL: 'normal',
  37. /** Mouse hover above. */
  38. //HOVER: 'hover',
  39. /** Object is selected. */
  40. //ACTIVE: 'active'
  41. //};
  42. /**
  43. * Enumeration of renderer types: 2D or 3D.
  44. * @enum
  45. */
  46. Kekule.Render.RendererType = {
  47. /** 2D renderer. */
  48. R2D: 2,
  49. /** 3D renderer. */
  50. R3D: 3
  51. };
  52. /**
  53. * The position of coord point in object box.
  54. * @enum
  55. */
  56. Kekule.Render.CoordPos = {
  57. /** Center point of object. */
  58. CENTER: 0,
  59. /** Top left point of object, usually used in blocks of 2D context. */
  60. CORNER_TL: 21,
  61. /** Default value, same as CENTER. */
  62. DEFAULT: 0
  63. };
  64. /**
  65. * C is the most common element in organic molecule. So the label of C will be ignored in bond-line formula.
  66. * @constant
  67. */
  68. Kekule.Render.DEF_ATOM_ATOMIC_NUM = 6;
  69. /**
  70. * Enumeration of molecule display type, condensed formula or bond-line formula.
  71. * @enum
  72. */
  73. Kekule.Render.MoleculeDisplayType = {
  74. /** bond-line formula */
  75. SKELETAL: 1,
  76. /** Condensed formula */
  77. CONDENSED: 2,
  78. DEFAULT: 1
  79. };
  80. Kekule.Render.Molecule2DDisplayType = Kekule.Render.MoleculeDisplayType;
  81. /**
  82. * Enumeration of node label (usually atom label) display mode (especially in 2D renderer).
  83. * @enum
  84. */
  85. Kekule.Render.NodeLabelDisplayMode = {
  86. /** Label is hidden */
  87. HIDDEN: -1,
  88. /** Label should be shown */
  89. SHOWN: 1,
  90. /** Whether show label is decided by the display type of molecule */
  91. SMART: 0,
  92. /** Default is SMART */
  93. DEFAULT: 0
  94. }
  95. /**
  96. * Enumeration of hydrongen display strategy (espcially in 2D renderer).
  97. * @enum
  98. */
  99. Kekule.Render.HydrogenDisplayLevel = {
  100. /** No hydrongen is displayed */
  101. NONE: 0,
  102. /** Only display explicit hydrogens. */
  103. EXPLICIT: 1,
  104. /** Display explicit hydrogens only when the count is not the same as implicit. */
  105. UNMATCHED_EXPLICIT: 2,
  106. /** Display all hydrogens, whether explicit or implicit ones. */
  107. ALL: 10,
  108. /** Default is EXPLICIT. */
  109. DEFAULT: 1
  110. };
  111. /**
  112. * Enumeration of direction of text (especially rich text label).
  113. * @enum
  114. */
  115. Kekule.Render.TextDirection = {
  116. /** @ignore */
  117. DEFAULT: 0,
  118. /** Left to right. */
  119. LTR: 1,
  120. /** Right to left. */
  121. RTL: 3,
  122. /** Top to bottom. */
  123. TTB: 2,
  124. /** Bottom to top */
  125. BTT: 4,
  126. /** Inherit from parent setting */
  127. INHERIT: 10
  128. /* Decide the direction by environment around. Useful when draw label in ctab. */
  129. /*SMART: 20*/
  130. };
  131. /**
  132. * Enumeration of horizontal / vertical alignment of text.
  133. * @enum
  134. */
  135. Kekule.Render.TextAlign = {
  136. /*BASELINE: 0, //??????*/
  137. DEFAULT: 0,
  138. LEFT: 1,
  139. RIGHT: 2,
  140. TOP: 3,
  141. BOTTOM: 4,
  142. CENTER: 5,
  143. LEADING: 10,
  144. TRAILING: 11,
  145. /**
  146. * LEADING and TRAILING are related align mode, related to text direction.
  147. * This function returns absolute align value due to textDirection.
  148. * @returns {Int}
  149. */
  150. getAbsAlign: function(textAlign, textDirection)
  151. {
  152. var result = textAlign;
  153. var TA = Kekule.Render.TextAlign;
  154. var TD = Kekule.Render.TextDirection;
  155. if ((textAlign === TA.LEADING) || (textAlign === TA.TRAILING))
  156. {
  157. var isLeading = textAlign == TA.LEADING;
  158. switch (textDirection)
  159. {
  160. case TD.RTL: result = isLeading? TA.RIGHT: TA.LEFT; break;
  161. case TD.TTB: result = isLeading? TA.TOP: TA.BOTTOM; break;
  162. case TD.BTT: result = isLeading? TA.BOTTOM: TA.TOP; break;
  163. case TD.LTR:
  164. default:
  165. result = isLeading? TA.LEFT: TA.RIGHT; break;
  166. }
  167. }
  168. return result;
  169. }
  170. };
  171. /**
  172. * Enumeration of alignment types of box in horizontal direction.
  173. * @enum
  174. */
  175. Kekule.Render.BoxXAlignment = {
  176. LEFT: 0,
  177. RIGHT: 1,
  178. CENTER: 2
  179. };
  180. /**
  181. * Enumeration of alignment types of box in vertical direction.
  182. * @enum
  183. */
  184. Kekule.Render.BoxYAlignment = {
  185. TOP: 0,
  186. BOTTOM: 1,
  187. CENTER: 2
  188. };
  189. /**
  190. * Enumeration of alignment mode of a rich text box.
  191. * @enum
  192. */
  193. Kekule.Render.TextBoxAlignmentMode = {
  194. /** Alignment based on the whole text box */
  195. BOX: 0,
  196. /** Alignment based on the childmost anchor item of rich text */
  197. ANCHOR: 1
  198. };
  199. /**
  200. * Enumeration of types of rendering a bond line.
  201. * @enum
  202. */
  203. Kekule.Render.BondRenderType = {
  204. /** Usual single bond, draw in a thin line */
  205. SINGLE: 0,
  206. /** Usual double bond, draw in thin double line */
  207. DOUBLE: 1,
  208. /** Usual triple bond, draw in thin triple line */
  209. TRIPLE: 2,
  210. /* Usual quad bond, draw in thin quad line */
  211. QUAD: 3,
  212. /** Dashed bond line */
  213. DASHED: 4,
  214. /** Dashed double line */
  215. DASHED_DOUBLE: 5,
  216. /** Dashed triple line */
  217. DASHED_TRIPLE: 6,
  218. /** A sold and a dashed line, usually used for aromatic bond */
  219. SOLID_DASH: 7,
  220. /** A line with a arrow in the end, usually for coordinate-bond */
  221. ARROWED: 8,
  222. /** A line with a arrow in the head, usually for coordinate-bond */
  223. ARROWED_INV: 9,
  224. /** A hashed line */
  225. HASHED: 10,
  226. /** A bold line, usually for bond above paper */
  227. BOLD: 11,
  228. /** A bold and a normal line, usually for double bond above paper */
  229. BOLD_DOUBLE: 12,
  230. /** A bold and two normal line, usually for triple bond above paper */
  231. BOLD_TRIPLE: 13,
  232. /* A bold and three normal line, usually for quad bond above paper */
  233. BOLD_QUAD: 14,
  234. /** A bold and a dash line, usually for aromatic bond above paper */
  235. BOLD_DASH: 16,
  236. /** A solid wedge triangle from atom 1 to atom 2, usually for wedge up bond */
  237. WEDGED_SOLID: 20,
  238. /** A solid wedge triangle from atom 2 to atom 1, usually for wedge up bond */
  239. WEDGED_SOLID_INV: 21,
  240. /** A hollow wedge triangle from atom 1 to atom 2, usually for wedge up bond */
  241. WEDGED_HOLLOW: 22,
  242. /** A hollow wedge triangle from atom 2 to atom 1, usually for wedge up bond */
  243. WEDGED_HOLLOW_INV: 23,
  244. /** A hased wedge triangle from atom 1 to atom 2, usually for wedge down bond */
  245. WEDGED_HASHED: 24,
  246. /** A hased wedge triangle from atom 2 to atom 1, usually for wedge down bond */
  247. WEDGED_HASHED_INV: 25,
  248. /** A bold rectangle, indicating a bond near the observer. Usually connected with wedged bonds. */
  249. WEDGED_SOLID_BOTH: 26,
  250. /** A bold hollow rectangle, indicating a bond near the observer. Usually connected with wedged bonds. */
  251. WEDGED_HOLLOW_BOTH: 27,
  252. /** A wavy line, usually used for bond with uncertain stereo */
  253. WAVY: 30,
  254. /** A cross double bond, means an uncertain E or Z stereo */
  255. SCISSORS_DOUBLE: 40
  256. };
  257. /**
  258. * Enumeration of types to render a charge on atom.
  259. * @enum
  260. */
  261. Kekule.Render.ChargeMarkRenderType = {
  262. /** Number + symbol, such as 2+, 3- */
  263. NUM_WITH_SYMBOL: 1,
  264. DEFAULT: 1,
  265. /** Only symbol, such as ++, = */
  266. //SYMBOL_ONLY: 2,
  267. /** Surrond with a circle to emphasis, the circle will only be draw when charge = +1/-1 */
  268. CIRCLE_AROUND: 3
  269. };
  270. /**
  271. * Enumeration of graphic quality levels to render objects in 3D.
  272. * @enum
  273. */
  274. Kekule.Render.Render3DGraphicQuality = {
  275. EXTREME_LOW: 1,
  276. LOW: 2,
  277. MEDIUM: 3,
  278. HIGH: 4,
  279. EXTREME_HIGH: 5
  280. };
  281. /**
  282. * Enumeration of types to render a molecule in 3D.
  283. * @enum
  284. */
  285. Kekule.Render.Molecule3DDisplayType = {
  286. /** Wire frame */
  287. WIRE: 31,
  288. /** Sticks */
  289. STICKS: 32,
  290. /** Ball and stick */
  291. BALL_STICK: 33,
  292. /** Space fill */
  293. SPACE_FILL: 34,
  294. /** Default is ball and stick */
  295. DEFAULT: 33
  296. };
  297. /**
  298. * Enumeration of types to render a bond in 3D.
  299. * @enum
  300. */
  301. Kekule.Render.Bond3DRenderMode = {
  302. /** do not render bond. */
  303. NONE: 0,
  304. /** One wire is used to represent one bond (multiple or not). */
  305. WIRE: 1,
  306. /** Multiple wires are used for multiple bond. */
  307. MULTI_WIRE: 2,
  308. /** One cylinder is used to represent one bond (multiple or not). */
  309. CYLINDER: 3,
  310. /** Use multiple cylinders for multiple bond. */
  311. MULTI_CYLINDER: 4,
  312. /**
  313. * Check if connector / bond should be draw in lines.
  314. * @param {Int} mode
  315. * @returns {Bool}
  316. */
  317. isWireMode: function(mode)
  318. {
  319. var M = Kekule.Render.Bond3DRenderMode;
  320. return (mode === M.WIRE) || (mode === M.MULTI_WIRE);
  321. },
  322. /**
  323. * Check if connector / bond should be draw in cylinders.
  324. * @param {Int} mode
  325. * @returns {Bool}
  326. */
  327. isCylinderMode: function(mode)
  328. {
  329. var M = Kekule.Render.Bond3DRenderMode;
  330. return (mode === M.CYLINDER) || (mode === M.MULTI_CYLINDER);
  331. }
  332. };
  333. /**
  334. * Enumeration of types to decide how a bond is splitted in 3D render.
  335. * @enum
  336. */
  337. Kekule.Render.Bond3DSpliceMode = {
  338. /** Bond draw as a whole, not split. */
  339. UNSPLIT: 1,
  340. /** Split from the middle, as two line with the same length. */
  341. MID_SPLIT: 2,
  342. /** Split, a biger atom gains biger part of bond */
  343. WEIGHTING_SPLIT: 3
  344. };
  345. /**
  346. * Enumeration of types to draw a connector (bond) in 3D render.
  347. * @enum
  348. */
  349. Kekule.Render.Bond3DRenderType = {
  350. /** Just one line or cylinder, used for most bonds. */
  351. SINGLE: 1,
  352. /** Double lines or cylinders, used for double bond. */
  353. DOUBLE: 2,
  354. /** Triple lines or cylinders, used for triple bond. */
  355. TRIPLE: 3,
  356. /** Dash line or cylinder, usually used for H-bond. */
  357. DASH: -1,
  358. /** One solid and a dash line or cylinder, used for aromatic bond. */
  359. SOLID_DASH: -2
  360. };
  361. /**
  362. * Enumeration of types to draw a node (atom) in 3D render.
  363. * @enum
  364. */
  365. Kekule.Render.Node3DRenderMode = {
  366. /** Do not render explicit atom, used in WIRE display mode. */
  367. NONE: 0,
  368. /** Render atom as ball, used in BALL_STICK display mode. */
  369. BALL: 1,
  370. /** Render atom as a huge ball, according to Vdw radius, used in SPACE_FILL display mode. */
  371. SPACE: 2
  372. ///** Render as small ball at the end of bond, used in STICKS display mode */
  373. //SMALL_CAP: 3
  374. };
  375. /**
  376. * Enumeration of shape types to describle meta shape info.
  377. * @enum
  378. */
  379. Kekule.Render.MetaShapeType = {
  380. // 2D shapes
  381. /** A single point on context. Can be determinated by a single coord ({[coord]}). */
  382. POINT: 0,
  383. /** A circle on context. Can be determinated by a single coord and a radius. ({[coord], radius}) */
  384. CIRCLE: 1,
  385. /** A line on context, determinated by two coords and a width property ({[coord1, coord2], width). */
  386. LINE: 2,
  387. /** A rectangle on context, determinated by two coords ({[coord1, coord2]}). */
  388. RECT: 3,
  389. /** An arc on context, determinated by a single coord and radius, startAngle, endAngle, anticlockwise. */
  390. ARC: 5,
  391. /** Unclosed polyline, determinated by a set of coords ({[coord1, coord2, coord3, ... }). */
  392. POLYLINE: 11,
  393. /** Polygon, determinated by a set of coords ({[coord1, coord2, coord3, ... }). */
  394. POLYGON: 10,
  395. // 3D shapes
  396. /** A shpere on 3D context. Can be determinated by a single coord and a radius. ({[coord], radius}) */
  397. SPHERE: 101,
  398. /** Cylinder in 3D context. Can be determinated by two coords and a radius. ({[coord1, coord2], radius}) */
  399. CYLINDER: 102,
  400. /**
  401. * A complex shape composited of a series of child shapes.
  402. * In implementation, an array of meta shapes will map to this type.
  403. */
  404. COMPOSITE: -1
  405. };
  406. // Alias of MetaShapeType
  407. Kekule.Render.BoundShapeType = Kekule.Render.MetaShapeType;
  408. /**
  409. * Enumeration of types of updating a object.
  410. * @enum
  411. */
  412. Kekule.Render.ObjectUpdateType = {
  413. /** Modify a existing object. */
  414. MODIFY: 0,
  415. /** Add a new object. */
  416. ADD: 1,
  417. /** Remove a existing object. */
  418. REMOVE: 2,
  419. /** Clear whole object. */
  420. CLEAR: 3
  421. };
  422. /**
  423. * Base class for different types of concrete renderers.
  424. * @class
  425. * @augments ObjectEx
  426. * @param {Kekule.ChemObject} chemObj Object to be drawn.
  427. * @param {Object} drawBridge Concrete draw bridge to do the actual draw job.
  428. * //@param {Object} renderConfigs Configuration for rendering.
  429. * // This property should be an instance of {@link Kekule.Render.Render2DConfigs} or {@link Kekule.Render.Render3DConfigs}.
  430. * @param {Kekule.ObjectEx} parent Parent object of this renderer, usually another renderer or an instance of {@link Kekule.Render.ChemObjPainter}, or null.
  431. *
  432. * @property {Kekule.ChemObject} chemObj Object to be drawn, read only.
  433. * @property {Object} drawBridge Concrete draw bridge to do the actual draw job.
  434. * //@property {Object} renderConfigs Configuration for rendering.
  435. * // This property should be an instance of {@link Kekule.Render.Render2DConfigs} or {@link Kekule.Render.Render3DConfigs}
  436. * //@property {Object} renderCache Stores params (center coord, options, matrix...) on last draw process.
  437. * @property {Kekule.ObjectEx} parent Parent object of this renderer, usually another renderer or an instance of {@link Kekule.Render.ChemObjPainter}.
  438. * @property {Kekule.Render.AbstractRenderer} parentRenderer Parent renderer that calls this one.
  439. *
  440. * @property {Bool} canModifyTargetObj If set to true, renderer may change the rendered object (e.g., add charge markers, change block sizes...).
  441. * This property can inherit value from parent.
  442. * @property {Object} redirectContext If this property is set, renderer will draw on this one instead if the context in drawXXX methods.
  443. * This property is used by editor. User should utilize this property with caution.
  444. */
  445. /**
  446. * Invoked when a basic object (node, connector, glyph...) is drawn, updated or removed.
  447. * event param of it has fields: {obj, parentObj, boundInfo, updateType}
  448. * where boundInfo provides the bound box information of this object on context. It has the following fields:
  449. * {
  450. * context: drawing context object
  451. * obj: drawn object
  452. * parentObj: parent of drawn object
  453. * boundInfo: a hash containing info of bound, including fields:
  454. * {
  455. * shapeType: value from {@link Kekule.Render.MetaShapeType} or {@link Kekule.Render.Meta3DShapeType}.
  456. * coords: [Array of coords]
  457. * }
  458. * updateType: add, modify or remove
  459. * }
  460. * boundInfo may also be a array for complex situation (such as multicenter bond): [boundInfo1, boundInfo2, boundInfo3...].
  461. * Note that in removed event, boundInfo may be null.
  462. * @name Kekule.Render.AbstractRenderer#updateBasicDrawObject
  463. * @event
  464. */
  465. /**
  466. * Invoked when whole chem object (molecule, reaction...) is prepared to be drawn in context.
  467. * event param of it has two fields: {context, obj}
  468. * @name Kekule.Render.AbstractRenderer#prepareDrawing
  469. * @event
  470. */
  471. /**
  472. * Invoked when whole chem object (molecule, reaction...) is drawn in context.
  473. * event param of it has two fields: {context, obj}
  474. * @name Kekule.Render.AbstractRenderer#draw
  475. * @event
  476. */
  477. /**
  478. * Invoked when whole chem object (molecule, reaction...) is cleared from context.
  479. * event param of it has two fields: {context, obj}
  480. * NOTE: this event is not well implemented and may be buggy.
  481. * @name Kekule.Render.AbstractRenderer#clear
  482. * @event
  483. */
  484. Kekule.Render.AbstractRenderer = Class.create(ObjectEx,
  485. /** @lends Kekule.Render.AbstractRenderer# */
  486. {
  487. /** @private */
  488. CLASS_NAME: 'Kekule.Render.AbstractRenderer',
  489. /** @private */
  490. RENDER_CACHE_FIELD: '__$renderCache$__',
  491. /** @constructs */
  492. initialize: function($super, chemObj, drawBridge, /*renderConfigs,*/ parent)
  493. {
  494. $super();
  495. this.setPropValue('chemObj', chemObj, true); // since we have no setChemObj method, use this instead
  496. /*
  497. if (renderConfigs)
  498. this.setRenderConfigs(renderConfigs);
  499. */
  500. if (parent)
  501. this.setParent(parent);
  502. this.setDrawBridge(drawBridge);
  503. this.setBubbleEvent(true); // allow event bubble
  504. this._suspendUpdateStatus = 0; // used internal
  505. this._suspendUpdateInfos = [];
  506. },
  507. finalize: function($super)
  508. {
  509. //console.log('release renderer', this.getClassName());
  510. this.setPropValue('chemObj', null, true);
  511. this.setDrawBridge(null);
  512. $super();
  513. },
  514. /** @private */
  515. initProperties: function()
  516. {
  517. this.defineProp('chemObj', {'dataType': 'Kekule.ChemObject', 'serializable': false, 'setter': null}); // readonly
  518. /*
  519. this.defineProp('baseCoord', {'dataType': DataType.HASH, 'serializable': false,
  520. 'getter': function()
  521. {
  522. var result = this.getPropStoreFieldValue('baseCoord');
  523. if (!result)
  524. result = this.getAutoBaseCoord();
  525. return result;
  526. }
  527. });
  528. */
  529. this.defineProp('drawBridge', {'dataType': DataType.OBJECT, 'serializable': false});
  530. //this.defineProp('renderConfigs', {'dataType': DataType.OBJECT, 'serializable': false});
  531. //this.defineProp('renderCache', {'dataType': DataType.OBJECT, 'serializable': false});
  532. this.defineProp('parent', {'dateType': DataType.OBJECT, 'serializable': false});
  533. this.defineProp('parentRenderer',
  534. {
  535. 'dateType': 'Kekule.Render.AbstractRenderer', 'serializable': false,
  536. 'getter': function()
  537. {
  538. var p = this.getParent();
  539. return (p instanceof Kekule.Render.AbstractRenderer)? p: null;
  540. },
  541. 'setter': function(value)
  542. {
  543. this.setParent(value);
  544. }
  545. });
  546. this.defineProp('redirectContext', {'dataType': DataType.OBJECT, 'serializable': false});
  547. this.defineProp('canModifyTargetObj', {'dataType': DataType.BOOL, 'serializable': false,
  548. 'getter': function()
  549. {
  550. var result = this.getPropStoreFieldValue('canModifyTargetObj');
  551. if (Kekule.ObjUtils.isUnset(result))
  552. {
  553. var p = this.getParent();
  554. if (p && p.getCanModifyTargetObj)
  555. result = p.getCanModifyTargetObj();
  556. }
  557. return result;
  558. }
  559. });
  560. this.defineEvent('clear');
  561. this.defineEvent('updateBasicDrawObject');
  562. },
  563. /**
  564. * Check if current renderer is the topmost one (without parent renderer, but maybe has parent painter).
  565. */
  566. isRootRenderer: function()
  567. {
  568. return !this.getParentRenderer();
  569. },
  570. /** @private */
  571. getHigherLevelObj: function()
  572. {
  573. return this.getParent();
  574. },
  575. /**
  576. * Report the type (2D or 3D) of this renderer.
  577. * @returns {Int} Value from {@link Kekule.Render.RendererType}.
  578. */
  579. getRendererType: function()
  580. {
  581. return Kekule.Render.RendererType.R2D; // default is 2D renderer
  582. },
  583. /**
  584. * Report coord mode of this renderer.
  585. * @returns {Int} Value from {@link Kekule.CoordMode}.
  586. */
  587. getCoordMode: function()
  588. {
  589. var rType = this.getRendererType();
  590. return (rType === Kekule.Render.RendererType.R3D)? Kekule.CoordMode.COORD3D: Kekule.CoordMode.COORD2D;
  591. },
  592. /**
  593. * Returns draw params (center coord, options, matrix...) on last draw process on context.
  594. * @returns {Hash}
  595. */
  596. getRenderCache: function(context)
  597. {
  598. var result = this.getExtraProp(context, this.RENDER_CACHE_FIELD);
  599. if (!result)
  600. {
  601. result = {};
  602. this.setExtraProp(context, this.RENDER_CACHE_FIELD, result);
  603. }
  604. return result;
  605. /*
  606. var result = this._cache; //[this.RENDER_CACHE_FIELD];
  607. if (!result)
  608. {
  609. this._cache = {'field': 'value'};
  610. result = this._cache;
  611. console.log('initial render cache', this.getClassName());
  612. }
  613. return result;
  614. */
  615. },
  616. /**
  617. * A method that will be called before draw().
  618. * Some preparation job can be done here.
  619. * Note that only the root renderer will call this method.
  620. * @private
  621. */
  622. beginDraw: function(context, baseCoord, options)
  623. {
  624. var b = this.getDrawBridge();
  625. if (b.prepareContext)
  626. b.prepareContext(context);
  627. },
  628. /**
  629. * A method that will be called after draw().
  630. * Note that only the root renderer will call this method.
  631. * @private
  632. */
  633. endDraw: function(context, baseCoord, options)
  634. {
  635. // some draw bridge (such as Three.js need to call render method to do actual draw job.
  636. var b = this.getDrawBridge();
  637. if (b.renderContext)
  638. b.renderContext(context);
  639. },
  640. /** @private */
  641. updateDrawInfoInCache: function(chemObj, context, baseCoord, options, realDrawOptions)
  642. {
  643. var p = this.getRenderCache(context);
  644. if (context)
  645. p.context = context;
  646. if (baseCoord)
  647. p.baseCoord = baseCoord;
  648. if (chemObj)
  649. p.chemObj = chemObj;
  650. if (options)
  651. p.options = options;
  652. if (realDrawOptions)
  653. p.realDrawOptions = realDrawOptions;
  654. },
  655. /** @private */
  656. _isCurrChemObjNeedToBeDrawn: function(partialDrawObjs, context)
  657. {
  658. var selfObj = this.getChemObj();
  659. for (var i = 0, l = partialDrawObjs.length; i < l; ++i)
  660. {
  661. var obj = partialDrawObjs[i];
  662. if ((obj === selfObj) || (obj.isChildOf(selfObj)))
  663. return true;
  664. }
  665. return false;
  666. },
  667. /**
  668. * Auto calculate draw context coord by coord of chem obj. When no baseCoord is provided in draw method,
  669. * this result may be used instead.
  670. * @param {Hash} drawOptions
  671. * @returns {Hash}
  672. */
  673. getAutoBaseCoord: function(drawOptions)
  674. {
  675. return this.doGetAutoBaseCoord(drawOptions);
  676. },
  677. /**
  678. * Do actual work of getAutoBaseCoord.
  679. * Descendants need to override this method.
  680. * @param {Hash} drawOptions
  681. * @returns {Hash}
  682. * @private
  683. */
  684. doGetAutoBaseCoord: function(drawOptions)
  685. {
  686. return null;
  687. },
  688. /** @ignore */
  689. getCachedDrawOptions: function(context)
  690. {
  691. return this.getRenderCache(context).options;
  692. },
  693. /** @ignore */
  694. getCachedDrawnElem: function(context)
  695. {
  696. return this.getRenderCache(context).drawnElem;
  697. },
  698. /**
  699. * Draw an instance of ChemObject to context.
  700. * The actual job is done in doDraw method. Descendants should override doDraw.
  701. * @param {Object} context Context to be drawn, such as Canvas, SVG, VML and so on.
  702. * @param {Hash} baseCoord Base coord to draw this object, can be null to use coord of chemObj itself.
  703. * This coord is based on context.
  704. * @param {Hash} options Draw options, such as draw rectangle, draw style, zoom and so on.
  705. * Different renderer may requires different option params.
  706. * In options hash object, there may be one special array field: partialDrawObjs. If this field is set,
  707. * then only chem objects in this array will be actually drawn.
  708. * @returns {Object} Drawn element on context (such as SVG) or null on direct context (such as canvas).
  709. */
  710. draw: function(context, baseCoord, options)
  711. {
  712. //console.log('[Draw]', this.getClassName(), this.getChemObj().getId? this.getChemObj().getId(): null);
  713. /*
  714. var p = this.getRenderCache(context);
  715. p.context = context;
  716. p.baseCoord = baseCoord;
  717. p.chemObj = this.getChemObj();
  718. */
  719. //console.log('baseDraw', baseCoord, p);
  720. //this.updateDrawInfoInCache(this.getChemObj(), context, baseCoord, options);
  721. try
  722. {
  723. this.__isDrawing = true; // flag avoid duplicated draw
  724. var ops = {};
  725. // actual draw options should also inherited from parent renderer
  726. var parentOps;
  727. var parent = this.getParentRenderer();
  728. if (parent)
  729. {
  730. var parentOps = parent.getRenderCache().options;
  731. //console.log('parent', this.getClassName(), parentOps);
  732. /*
  733. if (parentOps)
  734. ops = Object.create(parentOps);
  735. */
  736. }
  737. if (parentOps)
  738. {
  739. ops = Object.create(parentOps);
  740. ops = Object.extend(ops, options); // self options should override parent one
  741. }
  742. else
  743. ops = Object.create(options || null);
  744. var chemObj = this.getChemObj();
  745. var partialDrawObjs = ops.partialDrawObjs;
  746. /*
  747. if ((this instanceof Kekule.Render.Ctab2DRenderer))
  748. console.log(this.getClassName(), partialDrawObjs, !partialDrawObjs || this._isCurrChemObjNeedToBeDrawn(partialDrawObjs, context));
  749. */
  750. if (partialDrawObjs && (!this._isCurrChemObjNeedToBeDrawn(partialDrawObjs, context)))
  751. {
  752. //console.log('no need partial draw', this.getClassName(), chemObj, partialDrawObjs);
  753. return null;
  754. }
  755. /*
  756. else if (partialDrawObjs)
  757. console.log('partial draw objects', this.getClassName(), partialDrawObjs);
  758. */
  759. //p.options = ops;
  760. var renderOptionsGetter = (this.getRendererType() === Kekule.Render.RendererType.R3D) ?
  761. 'getRender3DOptions' : 'getRenderOptions';
  762. var localOps = chemObj[renderOptionsGetter] ? chemObj[renderOptionsGetter]() : null;
  763. renderOptionsGetter = (this.getRendererType() === Kekule.Render.RendererType.R3D) ?
  764. 'getOverriddenRender3DOptions' : 'getOverriddenRenderOptions';
  765. var localOverrideOps = chemObj[renderOptionsGetter] ? chemObj[renderOptionsGetter]() : null;
  766. ops = Kekule.Render.RenderOptionUtils.mergeRenderOptions(localOps || {}, ops);
  767. this.getRenderCache().options = ops;
  768. ops = Kekule.Render.RenderOptionUtils.mergeRenderOptions(localOverrideOps || {}, ops);
  769. //console.log('draw ops', this.getClassName(), localOps, ops);
  770. this.updateDrawInfoInCache(this.getChemObj(), context, baseCoord, options, ops);
  771. var isRoot = this.isRootRenderer();
  772. this.invokeEvent('prepareDrawing', {'context': context, 'obj': this.getChemObj()});
  773. //console.log('DRAW', isRoot);
  774. if (isRoot)
  775. this.beginDraw(context, baseCoord, ops);
  776. var result = this.doDraw(context, baseCoord, ops);
  777. this.getRenderCache(context).drawnElem = result;
  778. if (isRoot)
  779. this.endDraw(context, baseCoord, ops);
  780. this.invokeEvent('draw', {'context': context, 'obj': this.getChemObj()});
  781. }
  782. finally
  783. {
  784. this.__isDrawing = false;
  785. }
  786. return result;
  787. },
  788. /**
  789. * Do actual work of {@link Kekule.Render.AbstractRenderer.draw}.
  790. * @param {Object} context Context to be drawn, such as Canvas, SVG, VML and so on.
  791. * @param {Hash} baseCoord Coord on context to draw the center of chemObj.
  792. * @param {Hash} options Actual draw options, such as draw rectangle, draw style and so on.
  793. * @returns {Object} Drawn element on context (such as SVG) or null on direct context (such as canvas).
  794. * @private
  795. */
  796. doDraw: function(context, baseCoord, options)
  797. {
  798. // do nothing here
  799. return this.doDrawSelf(context, baseCoord, options);
  800. },
  801. /**
  802. * Do actual work of draw self object (without children).
  803. * @param {Object} context Context to be drawn, such as Canvas, SVG, VML and so on.
  804. * @param {Hash} baseCoord Coord on context to draw the center of chemObj.
  805. * @param {Hash} options Actual draw options, such as draw rectangle, draw style and so on.
  806. * @returns {Object} Drawn element on context (such as SVG) or null on direct context (such as canvas).
  807. * @private
  808. */
  809. doDrawSelf: function(context, baseCoord, options)
  810. {
  811. // do nothing here
  812. return null;
  813. },
  814. /**
  815. * Redraw previous object on context with same draw options. Should not be called before draw.
  816. * @param {Object} context
  817. */
  818. redraw: function(context)
  819. {
  820. var isRoot = this.isRootRenderer();
  821. //console.log('[Redraw]', isRoot, this.getClassName(), this.getChemObj().getId? this.getChemObj().getId(): null);
  822. //console.log('REDRAW', this.getClassName(), isRoot);
  823. if (isRoot)
  824. {
  825. var cache = this.getRenderCache(context) || {};
  826. this.beginDraw(context, cache.baseCoord, cache.options);
  827. }
  828. var result = this.doRedraw(context);
  829. this.getRenderCache(context).drawnElem = result;
  830. if (isRoot)
  831. this.endDraw(context, cache.baseCoord, cache.options);
  832. return result;
  833. },
  834. /**
  835. * Do actual work of {@link Kekule.Render.AbstractRenderer.redraw}. Descendants may override this method.
  836. * @param {Object} context
  837. * @private
  838. */
  839. doRedraw: function(context)
  840. {
  841. // A default implementation. Descendants can override this to provide a more efficient one.
  842. var p = this.getRenderCache(context);
  843. //this.clear(context);
  844. //return this.doDraw(context, p.baseCoord, p.realDrawOptions);
  845. return this.draw(context, p.baseCoord, p.options);
  846. },
  847. /**
  848. * Whether current renderer can modify elements drawn on context.
  849. * @param {Object} context
  850. * @returns {Bool}
  851. */
  852. canModifyGraphic: function(context)
  853. {
  854. var b = this.getDrawBridge();
  855. return b.canModifyGraphic? b.canModifyGraphic(context): false;
  856. },
  857. /**
  858. * Call this method before a series of rendered element updating job (for instance, call update method)
  859. * to avoid unnecessary redraw.
  860. */
  861. beginUpdateRenderer: function()
  862. {
  863. ++this._suspendUpdateStatus;
  864. },
  865. /**
  866. * Call this method after a series of rendered element updateing job,
  867. * notify the renderer to redraw the context.
  868. */
  869. endUpdateRenderer: function()
  870. {
  871. --this._suspendUpdateStatus;
  872. if (this._suspendUpdateStatus <= 0)
  873. this._suspendUpdateStatus = 0;
  874. if (!this.isUpdatingRenderer())
  875. {
  876. this.doEndUpdateRenderer();
  877. }
  878. },
  879. /** @private */
  880. doEndUpdateRenderer: function()
  881. {
  882. this.updateEx(this._suspendUpdateInfos);
  883. this._suspendUpdateInfos = [];
  884. },
  885. /**
  886. * Check if beginUpdateRenderer is called and endUpdateRenderer is not called yet.
  887. * @returns {Bool}
  888. */
  889. isUpdatingRenderer: function()
  890. {
  891. return this._suspendUpdateStatus > 0;
  892. },
  893. /**
  894. * Do a update job according to info provided by updateItems. Must be called after draw.
  895. * @param {Array} updateInfos Each item has format: {context, items: [{updateType, updatedObjDetails: [{obj, propNames}]}]}
  896. * @returns {Bool}
  897. */
  898. updateEx: function(updateInfos)
  899. {
  900. var canModify;
  901. var result = true;
  902. if (!this.isUpdatingRenderer())
  903. {
  904. canModify = this.canModifyGraphic(context);
  905. if (canModify)
  906. {
  907. //result = this.doUpdate(context, updatedObjs, updateType);
  908. for (var i = 0, l = updateInfos.length; i < l; ++i)
  909. {
  910. var info = updateInfos[i];
  911. var context = info.context;
  912. for (var j = 0, k = info.items.length; j < k; ++j)
  913. {
  914. var item = info.items[j];
  915. result = result && this.doUpdate(context, item.updatedObjDetails, item.updateType);
  916. if (!result) // update individual failed
  917. break;
  918. }
  919. if (!result)
  920. break;
  921. }
  922. }
  923. //if (!result) // can not update by self, call parent or repaint the whole context
  924. else // can not modify graphic, call parent to update the whole context
  925. {
  926. var isRoot = this.isRootRenderer();
  927. if (isRoot)
  928. {
  929. //console.log('update root', this.getClassName());
  930. var contexts = [];
  931. for (var i = 0, l = updateInfos.length; i < l; ++i)
  932. {
  933. var info = updateInfos[i];
  934. var context = info.context;
  935. Kekule.ArrayUtils.pushUnique(contexts, context);
  936. }
  937. for (var i = 0, l = contexts.length; i < l; ++i)
  938. {
  939. this.getDrawBridge().clearContext(context);
  940. var cache = this.getRenderCache(context);
  941. //console.log('draw root once', this.getClassName());
  942. this.draw(context, cache.baseCoord, cache.options);
  943. }
  944. return true;
  945. }
  946. else
  947. {
  948. var p = this.getParentRenderer();
  949. return p.updateEx(updateInfos);
  950. }
  951. }
  952. return result;
  953. }
  954. else // updating, suspend
  955. {
  956. if (!this._suspendUpdateInfos)
  957. this._suspendUpdateInfos = [];
  958. this._mergeRendererUpdateInfo(this._suspendUpdateInfos, updateInfos);
  959. return true;
  960. }
  961. },
  962. /**
  963. * Merge src into dest
  964. * @private
  965. */
  966. _mergeRendererUpdateInfo: function(dest, src)
  967. {
  968. for (var i = 0, l = src.length; i < l; ++i)
  969. {
  970. var info = src[i];
  971. var index = this._indexOfContextInRendererUpdateInfos(info.context, dest);
  972. if (index < 0)
  973. dest.push(info);
  974. else
  975. {
  976. var old = dest[index];
  977. // TODO: updateType not yet merged
  978. old.items = old.items.concat(info.items);
  979. }
  980. }
  981. return dest;
  982. },
  983. /** @private */
  984. _indexOfContextInRendererUpdateInfos: function(context, infos)
  985. {
  986. for (var i = 0, l = infos.length; i < l; ++i)
  987. {
  988. var info = infos[i];
  989. if (info.context === context)
  990. return i;
  991. }
  992. return -1;
  993. },
  994. /**
  995. * Update a child object inside chemObj. Must be called after draw.
  996. * @param {Object} context
  997. * @param {Variant} updatedObjDetails Object detail containing field {obj, propNames} or array of details.
  998. * @param {Int} updateType Value from {@link Kekule.Render.ObjectUpdateType}
  999. * @returns {Bool}
  1000. */
  1001. update: function(context, updatedObjDetails, updateType)
  1002. {
  1003. /*
  1004. var result = false;
  1005. if (this.canModifyGraphic(context))
  1006. {
  1007. result = this.doUpdate(context, updatedObjs, updateType);
  1008. }
  1009. if (!result) // can not update by self, call parent or repaint the whole context
  1010. {
  1011. if (this.isRootRenderer())
  1012. {
  1013. this.getDrawBridge().clearContext(context);
  1014. var cache = this.getRenderCache(context);
  1015. this.draw(context, cache.baseCoord, cache.options);
  1016. return true;
  1017. }
  1018. else
  1019. {
  1020. var p = this.getParentRenderer();
  1021. return p.update(context, updatedObjs, updateType);
  1022. }
  1023. }
  1024. return result;
  1025. */
  1026. var objDetails = [];
  1027. for (var i = 0, l = updatedObjDetails.length; i < l; ++i)
  1028. {
  1029. var obj = updatedObjDetails[i].obj;
  1030. if (this.isChemObjRenderedBySelf(context, obj))
  1031. objDetails.push(updatedObjDetails[i]);
  1032. }
  1033. if (objDetails.length)
  1034. return this.updateEx([{'context': context, items: [{'updateType': updateType, 'updatedObjDetails': objDetails/*updatedObjs*/}]}]);
  1035. else
  1036. return true;
  1037. },
  1038. /**
  1039. * Do actual work of update. Descendants may override this.
  1040. * @param {Object} context
  1041. * @param {Array} updatedObjDetails Object detail containing field {obj, propNames} or array of details.
  1042. * @param {Int} updateType Value from {@link Kekule.Render.ObjectUpdateType}
  1043. * @returns {Bool}
  1044. * @private
  1045. */
  1046. doUpdate: function(context, updateObjDetails, updateType)
  1047. {
  1048. //console.log('do update', this.getClassName(), updateObjDetails);
  1049. return this.doUpdateSelf(context, updateObjDetails, updateType);
  1050. },
  1051. /**
  1052. * Do actual work of update self (without children). Descendants should override this.
  1053. * @param {Object} context
  1054. * @param {Array} updatedObjDetails Object detail containing field {obj, propNames} or array of details.
  1055. * @param {Int} updateType Value from {@link Kekule.Render.ObjectUpdateType}
  1056. * @returns {Bool}
  1057. * @private
  1058. */
  1059. doUpdateSelf: function(context, updatedObjDetails, updateType)
  1060. {
  1061. //console.log('[doUpdateSelf]', this.getClassName(), updatedObjDetails);
  1062. var r = false;
  1063. if (this.canModifyGraphic(context))
  1064. {
  1065. /* // TODO: now has bugs, disable it
  1066. // work well now? 2014-06-06
  1067. */
  1068. /*
  1069. if (updatedObjs.indexOf(this.getChemObj()))
  1070. {
  1071. if (updateType === Kekule.Render.ObjectUpdateType.CLEAR)
  1072. return this.doClear(context);
  1073. else
  1074. {
  1075. //console.log('update by redraw', this.getClassName(), updatedObjs);
  1076. // simpliest method to update is to redraw the whole chemObj
  1077. this.doClear(context);
  1078. var p = this.getRenderCache(context);
  1079. return this.draw(context, p.baseCoord, p.options);
  1080. }
  1081. }
  1082. */
  1083. var redrawSelf = false;
  1084. var chemObj = this.getChemObj();
  1085. for (var i = 0, l = updatedObjDetails.length; i < l; ++i)
  1086. {
  1087. var detail = updatedObjDetails[i];
  1088. if (detail.obj === chemObj)
  1089. {
  1090. //console.log('update self detail', this.getClassName(), detail.obj.getId());
  1091. redrawSelf = true;
  1092. break;
  1093. }
  1094. }
  1095. if (redrawSelf)
  1096. {
  1097. if (updateType === Kekule.Render.ObjectUpdateType.CLEAR)
  1098. return this.doClear(context);
  1099. else
  1100. {
  1101. //console.log('<update by redraw>', this.getClassName(), updatedObjDetails);
  1102. // simpliest method to update is to redraw the whole chemObj
  1103. this.doClear(context);
  1104. var p = this.getRenderCache(context);
  1105. return this.draw(context, p.baseCoord, p.options);
  1106. }
  1107. }
  1108. /**/
  1109. /*
  1110. var T = Kekule.Render.ObjectUpdateType;
  1111. switch (updateType)
  1112. {
  1113. case T.ADD:
  1114. r = this.doAddNew(context, updatedObjs);
  1115. break;
  1116. case T.MODIFY:
  1117. r = this.doModify(context, updatedObjs);
  1118. break;
  1119. case T.REMOVE:
  1120. r = this.doRemove(context, updatedObjs);
  1121. break;
  1122. case T.CLEAR:
  1123. r = this.doClear(context);
  1124. this.invokeEvent('clear', {'context': context, 'obj': this.getChemObj()});
  1125. break;
  1126. }
  1127. */
  1128. }
  1129. return r;
  1130. },
  1131. /** @ignore */
  1132. _extractObjsOfUpdateObjDetails: function(updatedObjDetails)
  1133. {
  1134. return Kekule.Render.UpdateObjUtils._extractObjsOfUpdateObjDetails(updatedObjDetails);
  1135. },
  1136. /** @ignore */
  1137. _createUpdateObjDetailsFromObjs: function(updatedObjs)
  1138. {
  1139. return Kekule.Render.UpdateObjUtils._createUpdateObjDetailsFromObjs(updatedObjs);
  1140. },
  1141. /**
  1142. * Add a new child object to chemObj. Must be called after draw.
  1143. * @param {Object} context
  1144. * @param {Variant} updatedObjs
  1145. * @returns {Bool} Whether the actual add job is done.
  1146. * @private
  1147. */
  1148. addNew: function(context, updatedObjs)
  1149. {
  1150. var details = this._createUpdateObjDetailsFromObjs(updatedObjs);
  1151. return this.update(context, details, Kekule.Render.ObjectUpdateType.ADD);
  1152. },
  1153. /*
  1154. * Do actual work of addNew. Descendants should override this.
  1155. * This function should return true after actual work done. Otherwise false should be returned.
  1156. * @param {Object} context
  1157. * @param {Variant} updatedObjs
  1158. * @returns {Bool}
  1159. * @private
  1160. * @deprecated
  1161. */
  1162. /*
  1163. doAddNew: function(context, updatedObjs)
  1164. {
  1165. return false;
  1166. },
  1167. */
  1168. /**
  1169. * Modify chemObj or a child object inside chemObj. Must be called after draw.
  1170. * @param {Object} context
  1171. * @param {Variant} updatedObjs
  1172. * @returns {Bool} Whether the actual modify job is done.
  1173. * @private
  1174. */
  1175. modify: function(context, updatedObjDetails)
  1176. {
  1177. return this.update(context, updatedObjDetails, Kekule.Render.ObjectUpdateType.MODIFY);
  1178. },
  1179. /*
  1180. * Do actual work of modify. Descendants should override this.
  1181. * This function should return true after actual work done. Otherwise false should be returned.
  1182. * @param {Object} context
  1183. * @param {Variant} updatedObjs
  1184. * @returns {Bool}
  1185. * @private
  1186. * @deprecated
  1187. */
  1188. /*
  1189. doModify: function(context, updatedObjs)
  1190. {
  1191. return false;
  1192. },
  1193. */
  1194. /**
  1195. * Remove a child object inside chemObj and update the rendering. Must be called after draw.
  1196. * @param {Object} context
  1197. * @param {Variant} removedObjs
  1198. * @returns {Bool} Whether the actual remove job is done.
  1199. * @private
  1200. */
  1201. remove: function(context, removedObjs)
  1202. {
  1203. var details = this._createUpdateObjDetailsFromObjs(removedObjs);
  1204. return this.update(context, details, Kekule.Render.ObjectUpdateType.REMOVE);
  1205. },
  1206. /*
  1207. * Do actual work of remove. Descendants should override this.
  1208. * This function should return true after actual work done. Otherwise false should be returned.
  1209. * @param {Object} context
  1210. * @param {Variant} removedObjs
  1211. * @returns {Bool}
  1212. * @private
  1213. * @deprecated
  1214. */
  1215. /*
  1216. doRemove: function(context, removedObjs)
  1217. {
  1218. return false;
  1219. },
  1220. */
  1221. /**
  1222. * Clear whole chemObj on context.
  1223. * @param {Object} context
  1224. * @returns {Bool} Whether the actual clear job is done.
  1225. */
  1226. clear: function(context)
  1227. {
  1228. //console.log('[Clear]', this.getClassName(), this.getChemObj().getId? this.getChemObj().getId(): null);
  1229. //return this.update(context, Kekule.Render.UpdateObjUtils._createUpdateObjDetailsFromObjs([this.getChemObj()]), Kekule.Render.ObjectUpdateType.CLEAR);
  1230. var result = this.doClear(context);
  1231. this.invokeEvent('clear', {'context': context, 'obj': this.getChemObj()});
  1232. },
  1233. /**
  1234. * Do actual job of clear.
  1235. * This function should return true after actual work done. Otherwise false should be returned.
  1236. * @param {Object} context
  1237. * @returns {Bool}
  1238. */
  1239. doClear: function(context)
  1240. {
  1241. if (this.canModifyGraphic())
  1242. {
  1243. return this.doClearSelf(context);
  1244. }
  1245. else
  1246. return false;
  1247. },
  1248. /**
  1249. * Do actual job of clear self (without children). Descendants should override this method.
  1250. * This function should return true after actual work done. Otherwise false should be returned.
  1251. * @param {Object} context
  1252. * @returns {Bool}
  1253. */
  1254. doClearSelf: function(context)
  1255. {
  1256. if (this.canModifyGraphic())
  1257. {
  1258. //console.log('clear', this.getClassName());
  1259. var drawnElem = this.getRenderCache(context).drawnElem;
  1260. //console.log('clear', drawnElem);
  1261. if (drawnElem)
  1262. {
  1263. try
  1264. {
  1265. this.getDrawBridge().removeDrawnElem(context, drawnElem);
  1266. }
  1267. catch(e) // avoid error when drawnElem is already removed from context
  1268. {
  1269. //console.log('clear error', this.getClassName(), drawnElem);
  1270. }
  1271. }
  1272. this.getRenderCache(context).drawnElem = null;
  1273. }
  1274. else
  1275. return false;
  1276. },
  1277. /**
  1278. * Estimate the bound box around current chemObj (in chem coord system).
  1279. * @param {Object} context
  1280. * @param {Object} options
  1281. * @param {Bool} allowCoordBorrow
  1282. * @returns {Hash} A 2D or 3D box, in chemObj's coord system.
  1283. */
  1284. estimateObjBox: function(context, options, allowCoordBorrow)
  1285. {
  1286. var box = this.doEstimateObjBox(context, options, allowCoordBorrow);
  1287. //console.log('get box', this.getClassName(), box);
  1288. // if box has some field which is undefined or null, set it to 0
  1289. if (box)
  1290. box = this._fillBoxDefaultValue(box, this.getRendererType());
  1291. return box;
  1292. },
  1293. /**
  1294. * Do actual work of {@link Kekule.Render.AbstractRenderer.estimateObjBox}.
  1295. * @param {Object} context
  1296. * @param {Object} options
  1297. * @param {Bool} allowCoordBorrow
  1298. * @returns {Hash} A 2D or 3D box.
  1299. * @private
  1300. */
  1301. doEstimateObjBox: function(context, options, allowCoordBorrow)
  1302. {
  1303. return this.doEstimateSelfObjBox(context, options, allowCoordBorrow);
  1304. },
  1305. /**
  1306. * Calculate the containing box of only this object (without children).
  1307. * Descendants may override this method.
  1308. * @param {Object} context
  1309. * @param {Object} options
  1310. * @param {Bool} allowCoordBorrow
  1311. * @returns {Hash} A 2D or 3D box.
  1312. * @private
  1313. */
  1314. doEstimateSelfObjBox: function(context, options, allowCoordBorrow)
  1315. {
  1316. return null;
  1317. },
  1318. /**
  1319. * Estimate the bound box need to render current chemObj (in context coord system).
  1320. * Note: this method should not be called outside draw(). Otherwise the result may be unreliable or even no result can be returned.
  1321. * @param {Object} context
  1322. * @param {Hash} baseCoord Center coord in context to draw object. Can be null.
  1323. * @param {Object} options
  1324. * @param {Bool} allowCoordBorrow
  1325. * @returns {Hash} A 2D or 3D box, in context's coord system.
  1326. */
  1327. estimateRenderBox: function(context, baseCoord, options, allowCoordBorrow)
  1328. {
  1329. var box = this.doEstimateRenderBox(context, baseCoord, options, allowCoordBorrow);
  1330. // if box has some field which is undefined or null, set it to 0
  1331. if (box)
  1332. box = this._fillBoxDefaultValue(box, this.getRendererType());
  1333. return box;
  1334. },
  1335. /**
  1336. * Do actual work of {@link Kekule.Render.AbstractRenderer.estimateRenderBox}.
  1337. * @param {Object} context
  1338. * @param {Hash} baseCoord Center coord in context to draw object. Can be null.
  1339. * @param {Object} options
  1340. * @param {Bool} allowCoordBorrow
  1341. * @returns {Hash} A 2D or 3D box.
  1342. * @private
  1343. */
  1344. doEstimateRenderBox: function(context, baseCoord, options, allowCoordBorrow)
  1345. {
  1346. // do nothing here
  1347. return null;
  1348. },
  1349. /** @private */
  1350. _fillBoxDefaultValue: function(box, rendererType)
  1351. {
  1352. if (!box)
  1353. box = {};
  1354. var is3D = rendererType === Kekule.Render.RendererType.R3D;
  1355. var r = {
  1356. 'x1': box.x1 || 0,
  1357. 'x2': box.x2 || 0,
  1358. 'y1': box.y1 || 0,
  1359. 'y2': box.y2 || 0
  1360. };
  1361. if (is3D)
  1362. {
  1363. r.z1 = box.z1 || 0;
  1364. r.z2 = box.z2 || 0;
  1365. }
  1366. return r;
  1367. },
  1368. /**
  1369. * Transform a context based coord to inner coord basd on chemObj coord system.
  1370. * @param {Object} context
  1371. * @param {Kekule.ChemObject} chemObj
  1372. * @param {Hash} coord
  1373. * @returns {Hash}
  1374. */
  1375. transformCoordToObj: function(context, chemObj, coord)
  1376. {
  1377. return this.doTransformCoordToObj(context, chemObj, coord);
  1378. },
  1379. /** @private */
  1380. doTransformCoordToObj: function(context, chemObj, coord)
  1381. {
  1382. return coord;
  1383. },
  1384. /**
  1385. * Transform a chemObj based inner coord to context based one.
  1386. * @param {Object} context
  1387. * @param {Kekule.ChemObject} chemObj
  1388. * @param {Hash} coord
  1389. * @returns {Hash}
  1390. */
  1391. transformCoordToContext: function(context, chemObj, coord)
  1392. {
  1393. return this.doTransformCoordToContext(context, chemObj, coord);
  1394. },
  1395. /** @private */
  1396. doTransformCoordToContext: function(context, chemObj, coord)
  1397. {
  1398. return coord;
  1399. },
  1400. /**
  1401. * Transform a context based coord to screen based one (usually in pixel).
  1402. * @param {Object} context
  1403. * @param {Hash} coord
  1404. * @return {Hash}
  1405. */
  1406. transformContextCoordToScreen: function(context, coord)
  1407. {
  1408. return this.doTransformContextCoordToScreen(context, coord);
  1409. },
  1410. /** @private */
  1411. doTransformContextCoordToScreen: function(context, coord)
  1412. {
  1413. var b = this.getDrawBridge();
  1414. return (b && b.transformContextCoordToScreen)? b.transformContextCoordToScreen(context, coord): coord;
  1415. },
  1416. /**
  1417. * Should be called when a basic object (node, connector, glyph...) is drawn on context.
  1418. * @param {Object} obj
  1419. * @param {Object} boundInfo
  1420. * @private
  1421. */
  1422. basicDrawObjectUpdated: function(context, obj, parentObj, boundInfo, updateType)
  1423. {
  1424. /*
  1425. if (!boundInfo)
  1426. console.log(arguments.callee.caller.toString());
  1427. */
  1428. this.invokeEvent('updateBasicDrawObject', {'context': context, 'obj': obj, 'parentObj': parentObj, 'boundInfo': boundInfo, 'updateType': updateType});
  1429. },
  1430. /**
  1431. * Indicate whether a chemObj (including childObj) is rendered by this renderer, or should be rendered by this renderer.
  1432. * Descendants may override this method.
  1433. * @param {Object} context
  1434. * @param {Object} obj
  1435. * @returns {boolean}
  1436. */
  1437. isChemObjRenderedBySelf: function(context, obj)
  1438. {
  1439. return (obj === this.getRenderCache(context).chemObj);
  1440. },
  1441. /**
  1442. * Indicate whether a chemObj (including childObj) is rendered directly by this renderer (not by child renderers).
  1443. * Descendants may override this method.
  1444. * @param {Object} context
  1445. * @param {Object} obj
  1446. * @returns {boolean}
  1447. */
  1448. isChemObjRenderedDirectlyBySelf: function(context, obj)
  1449. {
  1450. if (obj && this.getRenderCache(context).chemObj)
  1451. return (obj === this.getRenderCache(context).chemObj);
  1452. else
  1453. return false;
  1454. },
  1455. /** @private */
  1456. createBoundInfo: function(boundType, coords, additionalInfos)
  1457. {
  1458. return Kekule.Render.MetaShapeUtils.createShapeInfo(boundType, coords, additionalInfos);
  1459. },
  1460. /** @private */
  1461. createPointBoundInfo: function(coord)
  1462. {
  1463. return this.createBoundInfo(Kekule.Render.BoundShapeType.POINT, [coord]);
  1464. },
  1465. /** @private */
  1466. createCircleBoundInfo: function(coord, radius)
  1467. {
  1468. return this.createBoundInfo(Kekule.Render.BoundShapeType.CIRCLE, [coord], {'radius': radius});
  1469. },
  1470. /** @private */
  1471. createArcBoundInfo: function(coord, radius, startAngle, endAngle, anticlockwise, width)
  1472. {
  1473. return this.createBoundInfo(Kekule.Render.BoundShapeType.ARC, [coord],
  1474. {'radius': radius, 'startAngle': startAngle, 'endAngle': endAngle, 'anticlockwise': anticlockwise, 'width': width});
  1475. },
  1476. /** @private */
  1477. createLineBoundInfo: function(coord1, coord2, width)
  1478. {
  1479. return this.createBoundInfo(Kekule.Render.BoundShapeType.LINE, [coord1, coord2], {'width': width});
  1480. },
  1481. /** @private */
  1482. createRectBoundInfo: function(coord1, coord2)
  1483. {
  1484. return this.createBoundInfo(Kekule.Render.BoundShapeType.RECT, [coord1, coord2]);
  1485. },
  1486. /** @private */
  1487. createSphereBoundInfo: function(coord, radius)
  1488. {
  1489. return this.createBoundInfo(Kekule.Render.BoundShapeType.SPHERE, [coord], {'radius': radius});
  1490. },
  1491. /** @private */
  1492. createCylinderBoundInfo: function(coord1, coord2, radius)
  1493. {
  1494. return this.createBoundInfo(Kekule.Render.BoundShapeType.CYLINDER, [coord1, coord2], {'radius': radius});
  1495. }
  1496. });
  1497. Kekule.ClassDefineUtils.addExtraObjMapSupport(Kekule.Render.AbstractRenderer);
  1498. /**
  1499. * A dummy renderer that does nothing.
  1500. * @class
  1501. * @augments Kekule.Render.AbstractRenderer
  1502. */
  1503. Kekule.Render.DummyRenderer = Class.create(Kekule.Render.AbstractRenderer,
  1504. /** @lends Kekule.Render.DummyRenderer# */
  1505. {
  1506. /** @private */
  1507. CLASS_NAME: 'Kekule.Render.DummyRenderer',
  1508. /** @ignore */
  1509. draw: function(context, baseCoord, options)
  1510. {
  1511. return;
  1512. },
  1513. /** @ignore */
  1514. redraw: function(context)
  1515. {
  1516. return;
  1517. }
  1518. });
  1519. /**
  1520. * A base renderer class to draw object togather with its children.
  1521. * @class
  1522. * @augments Kekule.Render.AbstractRenderer
  1523. */
  1524. Kekule.Render.CompositeRenderer = Class.create(Kekule.Render.AbstractRenderer,
  1525. /** @lends Kekule.Render.CompositeRenderer# */
  1526. {
  1527. /** @private */
  1528. CLASS_NAME: 'Kekule.Render.CompositeRenderer',
  1529. /** @private */
  1530. initProperties: function()
  1531. {
  1532. this.defineProp('targetChildObjs', {
  1533. 'dataType': DataType.ARRAY,
  1534. 'serializable': false
  1535. });
  1536. this.defineProp('childRendererMap', {
  1537. 'dataType': DataType.OBJECT,
  1538. 'serializable': false,
  1539. 'getter': function()
  1540. {
  1541. var result = this.getPropStoreFieldValue('childRendererMap');
  1542. if (!result)
  1543. {
  1544. result = new Kekule.MapEx(true); // non-weak map, as we should store the renderers
  1545. this.setPropStoreFieldValue('childRendererMap', result);
  1546. }
  1547. return result;
  1548. }
  1549. });
  1550. },
  1551. /** @ignore */
  1552. finalize: function($super)
  1553. {
  1554. this.reset();
  1555. $super();
  1556. },
  1557. /** @ignore */
  1558. doEstimateObjBox: function($super, context, options, allowCoordBorrow)
  1559. {
  1560. var result = $super(context, options, allowCoordBorrow);
  1561. var renderers = this.prepareChildRenderers();
  1562. var BU = Kekule.BoxUtils;
  1563. for (var i = 0, l = renderers.length; i < l; ++i)
  1564. {
  1565. var r = renderers[i];
  1566. if (r)
  1567. {
  1568. var b = r.estimateObjBox(context, options, allowCoordBorrow);
  1569. if (b)
  1570. {
  1571. if (!result)
  1572. result = BU.clone(b); //Object.extend({}, b);
  1573. else
  1574. result = BU.getContainerBox(result, b);
  1575. }
  1576. }
  1577. }
  1578. return result;
  1579. },
  1580. /** @ignore */
  1581. isChemObjRenderedBySelf: function($super, context, obj)
  1582. {
  1583. var result = $super(context, obj);
  1584. //console.log('check rendered by self', obj.getClassName(), this.getClassName(), result);
  1585. if (!result)
  1586. {
  1587. var childRenderers = this.getChildRenderers();
  1588. for (var i = 0, l = childRenderers.length; i < l; ++i)
  1589. {
  1590. var r = childRenderers[i];
  1591. if (r.isChemObjRenderedBySelf(context, obj))
  1592. return true;
  1593. }
  1594. }
  1595. if (!result)
  1596. {
  1597. this.refreshChildObjs();
  1598. var objs = this.getTargetChildObjs();
  1599. result = (objs && objs.indexOf(obj) >= 0);
  1600. //console.log('here', this.getClassName(), obj.getClassName(), result);
  1601. }
  1602. return result;
  1603. },
  1604. /** @ignore */
  1605. isChemObjRenderedDirectlyBySelf: function($super, context, obj)
  1606. {
  1607. return $super(context, obj);
  1608. },
  1609. /** @ignore */
  1610. doSetRedirectContext: function($super, value)
  1611. {
  1612. $super(value);
  1613. // if has child renderers, set redirect context as well
  1614. var childRenderers = this.getChildRenderers();
  1615. if (childRenderers && childRenderers.length)
  1616. {
  1617. for (var i = 0, l = childRenderers.length; i < l; ++i)
  1618. {
  1619. childRenderers[i].setRedirectContext(value);
  1620. }
  1621. }
  1622. },
  1623. /**
  1624. * Returns all children of this.getChemObj(). Descendants must override this method.
  1625. * If no children is found, null should be returned.
  1626. * @returns {Array}
  1627. * @private
  1628. */
  1629. getChildObjs: function()
  1630. {
  1631. var chemObj = this.getChemObj();
  1632. if (chemObj && chemObj.getAttachedMarkers)
  1633. return [].concat(chemObj.getAttachedMarkers() || []);
  1634. else
  1635. return [];
  1636. },
  1637. /**
  1638. * Prepare all child objects to be drawn.
  1639. * @private
  1640. */
  1641. prepareChildObjs: function()
  1642. {
  1643. var childObjs = this.getTargetChildObjs();
  1644. if (childObjs) // already prepared
  1645. return childObjs;
  1646. this.setTargetChildObjs(this.getChildObjs());
  1647. return this.getTargetChildObjs();
  1648. },
  1649. /** @private */
  1650. refreshChildObjs: function()
  1651. {
  1652. this.setTargetChildObjs(null);
  1653. this.prepareChildObjs();
  1654. /*
  1655. if (this.getTargetChildObjs().length)
  1656. console.log('refresh child', this.getClassName(), this.getTargetChildObjs());
  1657. */
  1658. },
  1659. /** @private */
  1660. getChildRenderers: function()
  1661. {
  1662. return this.getChildRendererMap().getValues();
  1663. },
  1664. /** @private */
  1665. getRendererForChild: function(childObj, canCreate)
  1666. {
  1667. var renderSelector = (this.getRendererType() === Kekule.Render.RendererType.R3D)?
  1668. Kekule.Render.get3DRendererClass: Kekule.Render.get2DRendererClass;
  1669. var rendererMap = this.getChildRendererMap();
  1670. var result = rendererMap.get(childObj);
  1671. if (!result && canCreate)
  1672. {
  1673. var c = renderSelector(childObj) || Kekule.Render.DummyRenderer; // dummy renderer, do nothing
  1674. var result = c? new c(childObj, this.getDrawBridge(), /*this.getRenderConfigs(),*/ this): null; // renderer may be null for some unregistered objects
  1675. rendererMap.set(childObj, result);
  1676. result.setRedirectContext(this.getRedirectContext());
  1677. }
  1678. return result;
  1679. },
  1680. /**
  1681. * Prepare renders to draw child objects.
  1682. * @private
  1683. */
  1684. prepareChildRenderers: function()
  1685. {
  1686. var rendererMap = this.getChildRendererMap();
  1687. var childObjs = this.prepareChildObjs() || [];
  1688. // remove unneed renderers
  1689. var oldRenderedObjs = rendererMap.getKeys();
  1690. for (var i = 0, l = oldRenderedObjs.length; i < l; ++i)
  1691. {
  1692. var obj = oldRenderedObjs[i];
  1693. if (childObjs.indexOf(obj) < 0)
  1694. rendererMap.remove(obj);
  1695. }
  1696. // add new renderers if needed
  1697. for (var i = 0, l = childObjs.length; i < l; ++i)
  1698. {
  1699. var childObj = childObjs[i];
  1700. this.getRendererForChild(childObj, true);
  1701. }
  1702. //return childRenderers;
  1703. return rendererMap.getValues();
  1704. },
  1705. /**
  1706. * Release all child renderer instance.
  1707. * @private
  1708. */
  1709. releaseChildRenderers: function()
  1710. {
  1711. var rendererMap = this.getChildRendererMap();
  1712. var childRenderers = rendererMap.getValues();
  1713. if (!childRenderers)
  1714. return;
  1715. for (var i = 0, l = childRenderers.length; i < l; ++i)
  1716. {
  1717. childRenderers[i].finalize();
  1718. }
  1719. rendererMap.clear();
  1720. },
  1721. /** @private */
  1722. hasChildRenderers: function()
  1723. {
  1724. var childRenderers = this.getChildRendererMap().getValues();
  1725. var result = childRenderers && childRenderers.length;
  1726. return result;
  1727. },
  1728. /**
  1729. * Prepare child objects and renderers, a must have step before draw.
  1730. * @private
  1731. */
  1732. prepare: function()
  1733. {
  1734. this.prepareChildObjs();
  1735. this.prepareChildRenderers();
  1736. },
  1737. /**
  1738. * Set renderer to initialized state, clear childObjs and childRenderers.
  1739. * @private
  1740. */
  1741. reset: function()
  1742. {
  1743. //console.log('reset', this.getClassName());
  1744. this.setTargetChildObjs(null);
  1745. this.releaseChildRenderers();
  1746. },
  1747. /**
  1748. * Whether the whole renderer (and its children) should be wholely repainted even in partial draw mode.
  1749. * Descendants may override this.
  1750. * @returns {Bool}
  1751. * @private
  1752. */
  1753. _needWholelyDraw: function(partialDrawObjs, context)
  1754. {
  1755. var selfObj = this.getChemObj();
  1756. return !partialDrawObjs || partialDrawObjs.indexOf(selfObj) >= 0;
  1757. },
  1758. /** @private */
  1759. doDraw: function($super, context, baseCoord, options)
  1760. {
  1761. //this.reset();
  1762. /*
  1763. this.setTargetChildObjs(null); // refresh child objects first
  1764. this.prepare();
  1765. //console.log('draw', this.getClassName(), options.partialDrawObjs, baseCoord);
  1766. */
  1767. this.refreshChildObjs(); // refresh child objects first
  1768. this.prepareChildRenderers(); // refresh renderer list
  1769. var op = Object.create(options);
  1770. if (options.partialDrawObjs && this._needWholelyDraw(options.partialDrawObjs, context))
  1771. op.partialDrawObjs = null; // if self need to be draw, all child renderers should be repainted as well
  1772. //if (!this.hasChildRenderers())
  1773. if (!this.getTargetChildObjs().length)
  1774. return $super(context, baseCoord, op);
  1775. else // then draw each child objects by child renderers
  1776. {
  1777. //console.log('do draw self', this.getClassName());
  1778. var selfElem = this.doDrawSelf(context, baseCoord, op);
  1779. var group = this.doDrawChildren(context, baseCoord, op);
  1780. // self
  1781. if (selfElem)
  1782. this.addToDrawGroup(selfElem, group);
  1783. return group;
  1784. }
  1785. },
  1786. /** @private */
  1787. doDrawChildren: function(context, baseCoord, options)
  1788. {
  1789. var group = this.createDrawGroup(context);
  1790. var childRenderers = this.getChildRenderers();
  1791. var ops = Object.create(options);
  1792. this.getRenderCache(context).childDrawOptions = ops;
  1793. for (var i = 0, l = childRenderers.length; i < l; ++i)
  1794. {
  1795. var r = childRenderers[i];
  1796. var baseCoord = null;
  1797. var elem = r.draw(context, baseCoord, ops);
  1798. if (group && elem)
  1799. this.addToDrawGroup(elem, group);
  1800. }
  1801. //console.log('draw children', this.getClassName(), group, childRenderers.length, this.getTargetChildObjs());
  1802. return group;
  1803. },
  1804. /** @private */
  1805. doClear: function($super, context)
  1806. {
  1807. $super(context);
  1808. if (this.hasChildRenderers())
  1809. {
  1810. this.doClearChildren(context);
  1811. }
  1812. return true;
  1813. },
  1814. /** @private */
  1815. doClearChildren: function(context)
  1816. {
  1817. var childRenderers = this.getChildRendererMap().getValues();
  1818. for (var i = 0, l = childRenderers.length; i < l; ++i)
  1819. {
  1820. if (childRenderers[i])
  1821. {
  1822. childRenderers[i].clear(context);
  1823. }
  1824. }
  1825. },
  1826. /** @private */
  1827. doUpdate: function($super, context, updateObjDetails, updateType)
  1828. {
  1829. this.refreshChildObjs(); // refresh child objects first
  1830. //this.prepare();
  1831. // update self
  1832. $super(context, updateObjDetails, updateType);
  1833. //if (this.hasChildRenderers())
  1834. if (this.getTargetChildObjs().length)
  1835. {
  1836. //console.log('do update children of ', this.getClassName());
  1837. this.doUpdateChildren(context, updateObjDetails, updateType);
  1838. }
  1839. return true;
  1840. },
  1841. /** @private */
  1842. doUpdateChildren: function(context, updateObjDetails, updateType)
  1843. {
  1844. var updatedObjs = Kekule.Render.UpdateObjUtils._extractObjsOfUpdateObjDetails(updateObjDetails);
  1845. //console.log('update Objs', this.getClassName(), updatedObjs);
  1846. var directChildren = this.getTargetChildObjs() || [];
  1847. var childRendererMap = this.getChildRendererMap();
  1848. var objs = Kekule.ArrayUtils.toArray(updatedObjs);
  1849. var objsMap = new Kekule.MapEx(false);
  1850. var renderers = [];
  1851. var redrawRoot = false;
  1852. for (var i = 0, l = objs.length; i < l; ++i)
  1853. {
  1854. var obj = objs[i];
  1855. if (this.isChemObjRenderedDirectlyBySelf(context, obj)) // need redraw self
  1856. {
  1857. redrawRoot = true;
  1858. }
  1859. }
  1860. if (redrawRoot)
  1861. {
  1862. this.doClear(context);
  1863. this.redraw(context);
  1864. return true;
  1865. }
  1866. for (var i = 0, l = objs.length; i < l; ++i)
  1867. {
  1868. var obj = objs[i];
  1869. /* // TODO: now has bugs, disable it currently
  1870. if (this.isChemObjRenderedDirectlyBySelf(context, obj)) // the root object it self updated, need re-render self
  1871. {
  1872. //console.log('do redraw');
  1873. redrawRoot = true;
  1874. //return true;
  1875. }
  1876. */
  1877. var renderer = childRendererMap.get(obj);
  1878. if (renderer) // is direct child and has a corresponding renderer
  1879. {
  1880. // check update type, if updateType is remove, just remove the renderer
  1881. if (updateType === Kekule.Render.ObjectUpdateType.REMOVE)
  1882. {
  1883. renderer.clear();
  1884. childRendererMap.remove(obj);
  1885. }
  1886. else
  1887. {
  1888. Kekule.ArrayUtils.pushUnique(renderers, renderer);
  1889. olds = [obj];
  1890. objsMap.set(renderer, olds);
  1891. }
  1892. }
  1893. else
  1894. {
  1895. var rs = this._getRenderersForChildObj(context, obj);
  1896. if (!rs.length)
  1897. {
  1898. if (directChildren.indexOf(obj) >= 0) // still can not find
  1899. {
  1900. var r = this.getRendererForChild(obj, true);
  1901. var drawnResult = r.draw(context, null, this.getRenderCache(context).childDrawOptions);
  1902. if (drawnResult)
  1903. {
  1904. var drawnElem = this.getCachedDrawnElem(context);
  1905. if (drawnElem)
  1906. this.addToDrawGroup(drawnResult, drawnElem);
  1907. }
  1908. }
  1909. }
  1910. for (var j = 0, k = rs.length; j < k; ++j)
  1911. {
  1912. var renderer = rs[j];
  1913. var olds = objsMap.get(renderer);
  1914. if (!olds)
  1915. {
  1916. renderers.push(renderer);
  1917. olds = [];
  1918. objsMap.set(renderer, olds);
  1919. }
  1920. olds.push(obj);
  1921. }
  1922. }
  1923. }
  1924. // apply update in each renderer
  1925. var result = true;
  1926. for (var i = 0, l = renderers.length; i < l; ++i)
  1927. {
  1928. var renderer = renderers[i];
  1929. var o = objsMap.get(renderer);
  1930. var details = Kekule.Render.UpdateObjUtils._createUpdateObjDetailsFromObjs(o);
  1931. //console.log('child renderer update', renderer.getClassName(), details);
  1932. var r = renderer.update(context, details, updateType);
  1933. result = result && r;
  1934. }
  1935. return result;
  1936. },
  1937. /** @private */
  1938. _getRenderersForChildObj: function(context, childObj)
  1939. {
  1940. var result = [];
  1941. var childRenderers = this.getChildRenderers();
  1942. for (var i = 0, l = childRenderers.length; i < l; ++i)
  1943. {
  1944. var r = childRenderers[i];
  1945. if (r.isChemObjRenderedBySelf(context, childObj))
  1946. result.push(r);
  1947. }
  1948. return result;
  1949. }
  1950. });
  1951. /**
  1952. * 2D renderer factory.
  1953. * @class
  1954. */
  1955. Kekule.Render.Renderer2DFactory = Kekule.FactoryUtils.createSimpleFactory(Kekule.FactoryUtils.MATCH_BY_CLASS);
  1956. /**
  1957. * Returns a suitable 2D renderer class for chemObj
  1958. * @param {Object} chemObj
  1959. * @returns {Kekule.Render.AbstractRenderer}
  1960. * @function
  1961. */
  1962. Kekule.Render.get2DRendererClass = function(chemObj)
  1963. {
  1964. var aClass = (chemObj instanceof ObjectEx)? chemObj.getClass(): chemObj;
  1965. return Kekule.Render.Renderer2DFactory.getClass(aClass);
  1966. };
  1967. /**
  1968. * 3D renderer factory.
  1969. * @class
  1970. */
  1971. Kekule.Render.Renderer3DFactory = Kekule.FactoryUtils.createSimpleFactory(Kekule.FactoryUtils.MATCH_BY_CLASS);
  1972. /**
  1973. * Returns a suitable 3D renderer class for chemObj
  1974. * @param {Object} chemObj
  1975. * @returns {Kekule.Render.AbstractRenderer}
  1976. * @function
  1977. */
  1978. Kekule.Render.get3DRendererClass = function(chemObj)
  1979. {
  1980. var aClass = (chemObj instanceof ObjectEx)? chemObj.getClass(): chemObj;
  1981. return Kekule.Render.Renderer3DFactory.getClass(aClass);
  1982. };
  1983. /**
  1984. * Implemtation of 2D/3D draw bridge manager.
  1985. * @class
  1986. * @ignore
  1987. */
  1988. Kekule.Render.DrawBridgeManager = Class.create({
  1989. /** @private */
  1990. CLASS_NAME: 'Kekule.Render.DrawBridgeManager',
  1991. /** @ignore */
  1992. initialize: function()
  1993. {
  1994. this._items = [];
  1995. this._preferredItem = null;
  1996. },
  1997. /** @private */
  1998. _indexOfBridgeClass: function(bridgeClass)
  1999. {
  2000. for (var i = 0, l = this._items.length; i < l; ++i)
  2001. {
  2002. var item = this._items[i];
  2003. if (item.bridgeClass === bridgeClass)
  2004. return i;
  2005. }
  2006. return -1;
  2007. },
  2008. /** @private */
  2009. _sortItems: function()
  2010. {
  2011. this._items.sort(
  2012. function(item1, item2)
  2013. {
  2014. return (item1.priorityLevel || 0) - (item2.priorityLevel || 0);
  2015. }
  2016. )
  2017. },
  2018. /** @private */
  2019. _reselectPreferred: function()
  2020. {
  2021. this._sortItems();
  2022. for (var i = this._items.length - 1; i >= 0; --i)
  2023. {
  2024. var item = this._items[i];
  2025. if (item.isSupported)
  2026. {
  2027. this._preferredItem = item;
  2028. return item;
  2029. }
  2030. }
  2031. this._preferredItem = null;
  2032. return null;
  2033. },
  2034. /**
  2035. * Register a bridge.
  2036. * @param {Class} bridgeClass
  2037. * @param {Int} priorityLevel
  2038. * @returns {Object}
  2039. * @ignore
  2040. */
  2041. register: function(bridgeClass, priorityLevel)
  2042. {
  2043. if (!priorityLevel)
  2044. priorityLevel = 0;
  2045. var index = this._indexOfBridgeClass(bridgeClass);
  2046. var item;
  2047. if (index >= 0)
  2048. {
  2049. item = this._items[index];
  2050. item.priorityLevel = priorityLevel;
  2051. }
  2052. else
  2053. {
  2054. item = {'bridgeClass': bridgeClass, 'priorityLevel': priorityLevel};
  2055. item.isSupported = bridgeClass.isSupported? bridgeClass.isSupported(): false;
  2056. this._items.push(item);
  2057. }
  2058. this._sortItems();
  2059. if ((!this._preferredItem) || (this._preferredItem.priorityLevel < priorityLevel))
  2060. {
  2061. if (item.isSupported) // if isSupported method not exists, assure it always not be supported
  2062. this._preferredItem = item;
  2063. }
  2064. return item;
  2065. },
  2066. /**
  2067. * Unregister a bridge.
  2068. * @param {Class} bridgeClass
  2069. * @returns {Object}
  2070. * @ignore
  2071. */
  2072. unregister: function(bridgeClass)
  2073. {
  2074. var item = null;
  2075. var i = this._indexOfBridgeClass(bridgeClass);
  2076. if (i >= 0)
  2077. {
  2078. item = this._items[i];
  2079. this._items.splice(i, 1);
  2080. if (item === this._preferredItem)
  2081. this._reselectPreferred();
  2082. }
  2083. return item;
  2084. },
  2085. /**
  2086. * Gets most suitable bridge class in current environment.
  2087. * @returns {Class}
  2088. * @ignore
  2089. */
  2090. getPreferredBridgeClass: function()
  2091. {
  2092. return (this._preferredItem)? this._preferredItem.bridgeClass: null;
  2093. },
  2094. /**
  2095. * Returns instance of preferred bridge in current environment.
  2096. * @returns {Object}
  2097. * @ignore
  2098. */
  2099. getPreferredBridgeInstance: function()
  2100. {
  2101. var c = this.getPreferredBridgeClass();
  2102. if (c)
  2103. {
  2104. /*
  2105. if (!c.getInstance) // class has not been singletoned
  2106. Kekule.ClassUtils.makeSingleton(c);
  2107. return c.getInstance()
  2108. */
  2109. return new c();
  2110. }
  2111. else
  2112. return null;
  2113. }
  2114. });
  2115. /**
  2116. * Draw bridge manager for 2D rendering.
  2117. * @object
  2118. */
  2119. Kekule.Render.DrawBridge2DMananger = new Kekule.Render.DrawBridgeManager();
  2120. /**
  2121. * Draw bridge manager for 3D rendering.
  2122. * @object
  2123. */
  2124. Kekule.Render.DrawBridge3DMananger = new Kekule.Render.DrawBridgeManager();