/home/lindsay/xeolabs/xeogl-next/xeogl/examples/js/stories/annotationStory.js
API Docs for:

File: /home/lindsay/xeolabs/xeogl-next/xeogl/examples/js/stories/annotationStory.js

  1. /**
  2. An **AnnotationStory** is a {{#crossLink "Story"}}{{/crossLink}} that contains a list
  3. of {{#crossLink "Annotation"}}Annotations{{/crossLink}} accompanied by a panel of text containing links that activate them.
  4.  
  5. <a href="../../examples/#annotations_stories_tronTank"><img src="../../assets/images/screenshots/tronTankStory.jpg"></img></a>
  6.  
  7. * AnnotationStory text is provided as markdown.
  8. * Words in the text can be linked to xeogl storytelling functions, to fly the camera to Annotation vantage points, show labels etc.
  9.  
  10. ## Authoring Mode
  11.  
  12. * SHIFT-click to place an Annotation
  13. * ESC to clear
  14. * ENTER to dump
  15.  
  16. ## Examples
  17.  
  18. * [Tron Tank Program AnnotationStory](../../examples/#annotations_stories_tronTank)
  19.  
  20. ## Usage
  21.  
  22. ````javascript
  23. // Load a Tron Tank model from SceneJS format. Give the model an ID - this
  24. // gets prefixed to the IDs of it's Meshes.
  25.  
  26. var model = new xeogl.SceneJSModel({
  27. id: "tank",
  28. src: "models/scenejs/tronTank/tronTank.json"
  29. });
  30.  
  31. model.scene.camera.eye = [15, 20, -25];
  32.  
  33. // When the model has loaded, create a story with annotations
  34.  
  35. model.on("loaded", function () {
  36.  
  37. new xeogl.AnnotationStory({
  38. speaking: false, // Set true to have a voice announce each annotation
  39. authoring: true, // Set true to author the annotations
  40. text: [
  41. "# [Stories](../docs/classes/AnnotationStory.html) : Tron Tank Program",
  42. "This is a Light Tank from the 1982 Disney movie *Tron*.",
  43. "The [orange tracks](focusAnnotation(0)) on this tank indicate that ....",
  44. "![](./images/Clu_Program.png)",
  45. "The [cannon](focusAnnotation(1)) is the tank's main armament, which ....",
  46. "The [pilot hatch](focusAnnotation(2)) is where Clu enters and exits the tank.",
  47. "At the back of the tank is the [antenna](focusAnnotation(3)) through ....",
  48. "*\"I fight for the users!\" -- Clu*"
  49. ],
  50. annotations: [
  51. {
  52. primIndex: 204,
  53. bary: [0.05, 0.16, 0.79],
  54. occludable: true,
  55. glyph: "A",
  56. title: "Orange tracks",
  57. desc: "Indicates that the pilot is the rebel hacker, Clu",
  58. eye: [14.69, 17.89, -26.88],
  59. look: [5.35, 4.14, -15.44],
  60. up: [-0.09, 0.99, 0.11],
  61. pinShown: true,
  62. labelShown: true,
  63. mesh: "tank.entity2"
  64. },
  65. {
  66. primIndex: 468,
  67. bary: [0.05, 0.16, 0.79],
  68. occludable: true,
  69. glyph: "B",
  70. title: "Cannon",
  71. desc: "Fires chevron-shaped bolts of de-rezzing energy",
  72. eye: [-0.66, 20.84, -21.59],
  73. look: [-0.39, 6.84, -9.18],
  74. up: [0.01, 0.97, 0.24],
  75. pinShown: true,
  76. labelShown: true,
  77. mesh: "tank.entity9"
  78. },
  79. {
  80. primIndex: 216,
  81. bary: [0.05, 0.16, 0.79],
  82. occludable: true,
  83. glyph: "C",
  84. title: "Pilot hatch",
  85. desc: "Clu hops in and out of the tank program here",
  86. eye: [1.48, 11.79, -15.13],
  87. look: [1.62, 5.04, -9.14],
  88. up: [0.01, 0.97, 0.24],
  89. pinShown: true,
  90. labelShown: true,
  91. mesh: "tank.entity6"
  92. },
  93. {
  94. primIndex: 4464,
  95. bary: [0.05, 0.16, 0.79],
  96. occludable: true,
  97. glyph: "D",
  98. title: "Antenna",
  99. desc: "Links the tank program to the Master Control Program",
  100. eye: [13.63, 16.79, 13.87],
  101. look: [1.08, 7.72, 3.07],
  102. up: [0.08, 0.99, 0.07],
  103. pinShown: true,
  104. labelShown: true,
  105. mesh: "tank.entity9"
  106. }
  107. ]
  108. });
  109. });
  110. ````
  111.  
  112. @class AnnotationStory
  113. @module xeogl
  114. @submodule stories
  115. @constructor
  116. @param [scene] {Scene} Parent {{#crossLink "Scene"}}Scene{{/crossLink}} - creates this AnnotationStory in the default
  117. {{#crossLink "Scene"}}Scene{{/crossLink}} when omitted.
  118. @param [cfg] {*} Configs
  119. @param [cfg.id] {String} Optional ID, unique among all components in the parent {{#crossLink "Scene"}}Scene{{/crossLink}},
  120. generated automatically when omitted.
  121. @param [cfg.meta] {String:Object} Optional map of user-defined metadata to attach to this AnnotationStory.
  122.  
  123. @extends Story
  124. */
  125. {
  126.  
  127. var speak = (function () {
  128. if (undefined === SpeechSynthesisUtterance) {
  129. return;
  130. }
  131. var msg = new SpeechSynthesisUtterance();
  132. var voices = window.speechSynthesis.getVoices();
  133. msg.voice = voices[10]; // Note: some voices don't support altering params
  134. msg.voiceURI = 'native';
  135. msg.volume = 1.0; // 0 to 1
  136. msg.rate = 1.0; // 0.1 to 10
  137. msg.pitch = 1.0; //0 to 2
  138. msg.text = 'Hello World';
  139. msg.lang = 'en-US';
  140. return function (text) {
  141. msg.text = text;
  142. speechSynthesis.speak(msg);
  143. };
  144. })();
  145.  
  146. var getDummyText = (function () {
  147. const dummyText = [
  148. "Lorem ipsum dolor sit amet, consectetur adipiscing elit. Egone non intellego, quid sit don Graece, Latine voluptas?.",
  149. "An est aliquid per se ipsum flagitiosum, etiamsi nulla comitetur infamia?",
  150. "Cur igitur easdem res, inquam, Peripateticis dicentibus verbum nullum est, quod non intellegatur?",
  151. "Nec enim, omnes avaritias si aeque avaritias esse dixerimus, sequetur ut etiam aequas esse dicamus. Quid enim de amicitia statueris utilitatis causa expetenda vides.",
  152. "Quis animo aequo videt eum, quem inpure ac flagitiose putet vivere?",
  153. "Quod cum accidisset ut alter alterum necopinato videremus, surrexit statim. Quis non odit sordidos, vanos, leves, futtiles?",
  154. "Cupit enim dícere nihil posse ad beatam vitam deesse sapienti."
  155. ];
  156. var i = 0;
  157. return function () {
  158. var text = dummyText[i];
  159. if (++i >= dummyText.length) {
  160. i = 0;
  161. }
  162. return text;
  163. };
  164. })();
  165.  
  166. xeogl.AnnotationStory = class xeoglAnnotationsStory extends xeogl.Story {
  167.  
  168. init(cfg) {
  169.  
  170. super.init(xeogl._apply({
  171. actions: {
  172. focusAnnotation: function (i) {
  173. var annotation = this._annotations[i];
  174. if (!annotation) {
  175. return;
  176. }
  177. if (this._lastAnnotation) {
  178. this._lastAnnotation.labelShown = false;
  179. }
  180. annotation.labelShown = true;
  181. var self = this;
  182. this._cameraFlight.flyTo(annotation, function () {
  183. if (self._speaking) {
  184. speak(annotation.title);
  185. speak(annotation.desc);
  186. }
  187. });
  188. this._lastAnnotation = annotation;
  189. }
  190. }
  191. }, cfg));
  192.  
  193. var self = this;
  194.  
  195. this._speaking = !!cfg.speaking;
  196. this._authoring = !!cfg.authoring;
  197. this._annotations = [];
  198.  
  199. if (cfg.annotations) {
  200. var annotation;
  201. var annotations = cfg.annotations;
  202. for (var i = 0, len = annotations.length; i < len; i++) {
  203. annotation = new xeogl.Annotation(this, annotations[i]);
  204. //annotation.pinShown = true;
  205. annotation.labelShown = false;
  206. this._annotations.push(annotation);
  207. annotation.on("pinClicked", (function () {
  208. var i2 = i;
  209. return function () {
  210. self._actions.focusAnnotation.call(self, i2);
  211. };
  212. })());
  213. }
  214. }
  215.  
  216. this._cameraFlight = new xeogl.CameraFlightAnimation(this, {duration: 1});
  217. this._cameraControl = new xeogl.CameraControl(this);
  218.  
  219. //-------------------------------------------------------------------
  220. // Authoring mode
  221. //-------------------------------------------------------------------
  222.  
  223. if (this._authoring) {
  224.  
  225. var input = this.scene.input;
  226. var shiftDown = false;
  227.  
  228. // SHIFT enables clicks to create annotations
  229. // ESC clears annotations
  230. // ENTER generates JS code for the AnnotationStory in a new browser tab
  231.  
  232. input.on("keydown", function (keyCode) {
  233. switch (keyCode) {
  234. case this.KEY_SHIFT:
  235. shiftDown = true;
  236. self._cameraControl.mousePickMesh.active = false;
  237. break;
  238. case this.KEY_ESCAPE:
  239. self._clear();
  240. break;
  241. case this.KEY_ENTER:
  242. self._dump();
  243. break;
  244. }
  245. });
  246.  
  247. input.on("keyup", function (keyCode) {
  248. switch (keyCode) {
  249. case this.KEY_SHIFT:
  250. shiftDown = false;
  251. self._cameraControl.mousePickMesh.active = true;
  252. break;
  253. }
  254. });
  255.  
  256. // Click while SHIFT is down creates a new Annotation
  257.  
  258. this._onMouseClicked = input.on("mouseclicked", function (coords) {
  259.  
  260. if (!shiftDown) {
  261. return;
  262. }
  263.  
  264. var hit = self.scene.pick({
  265. canvasPos: coords,
  266. pickSurface: true
  267. });
  268.  
  269. if (hit) {
  270.  
  271. var mesh = hit.mesh;
  272. var camera = mesh.scene.camera;
  273.  
  274. var i = self._annotations.length;
  275. var num = i + 1;
  276. var glyph = "" + num;
  277.  
  278. var dummyText = getDummyText();
  279.  
  280. var annotation = new xeogl.Annotation(self.scene, {
  281. mesh: hit.mesh.id,
  282. primIndex: hit.primIndex,
  283. bary: hit.bary,
  284. glyph: glyph,
  285. title: "Annotation " + num,
  286. desc: dummyText,
  287. eye: camera.eye,
  288. look: camera.look,
  289. up: camera.up,
  290. pinShown: true,
  291. labelShown: true
  292. });
  293.  
  294. annotation.on("pinClicked", function () {
  295. self._actions.focusAnnotation.call(self, i);
  296. });
  297.  
  298. self._annotations.push(annotation);
  299.  
  300. var text = self.text;
  301. text.push("[Annotation " + glyph + "](focusAnnotation(" + i + ")) - " + dummyText);
  302. text.push("");
  303. self.text = text;
  304. }
  305. });
  306. }
  307. }
  308.  
  309. _clear() {
  310. for (var i = 0, len = this._annotations.length; i < len; i++) {
  311. this._annotations[i].destroy();
  312. }
  313. this._annotations = [];
  314. this.text = [];
  315. }
  316.  
  317. _dump() {
  318. var w = window.open("");
  319. w.document.write("<pre>" + this.js + "</pre>");
  320. }
  321.  
  322. getJSON() {
  323. var annotationJSON;
  324. var annotations = [];
  325. for (var i = 0, len = this._annotations.length; i < len; i++) {
  326. annotationJSON = this._annotations[i].json;
  327. delete annotationJSON["id"]; // Story manages IDs
  328. annotations.push(annotationJSON);
  329. }
  330. return {
  331. text: this.text.slice(),
  332. annotations: annotations,
  333. authoring: this._authoring,
  334. speaking: this._speaking
  335. };
  336. }
  337.  
  338. destroy() {
  339. super.destroy();
  340. this.scene.input.off(this._onMouseClicked);
  341. }
  342. };
  343. }
  344.