chart/particlePlot.js

  1. import * as d3 from "d3";
  2. import dataTransform from "../dataTransform.js";
  3. import component from "../component.js";
  4. import { dispatch } from "../events.js";
  5. import { createScene } from "../base.js";
  6. /**
  7. * Reusable 3D Particle Plot Chart
  8. *
  9. * @module
  10. *
  11. * @example
  12. * let chartHolder = d3.select("#chartholder");
  13. *
  14. * let myData = [...];
  15. *
  16. * let myChart = d3.x3d.chart.particlePlot();
  17. *
  18. * chartHolder.datum(myData).call(myChart);
  19. *
  20. * @see https://datavizproject.com/data-type/3d-scatterplot/
  21. */
  22. export default function() {
  23. /* Default Properties */
  24. let width = 500;
  25. let height = 500;
  26. let dimensions = { x: 40, y: 40, z: 40 };
  27. let colors = ["orange"];
  28. let color;
  29. let classed = "d3X3dParticlePlot";
  30. let mappings;
  31. let debug = false;
  32. /* Scales */
  33. let xScale;
  34. let yScale;
  35. let zScale;
  36. let colorScale;
  37. /* Components */
  38. const viewpoint = component.viewpoint();
  39. const axis = component.axisThreePlane();
  40. const particles = component.particles();
  41. /**
  42. * Initialise Data and Scales
  43. *
  44. * @private
  45. * @param {Array} data - Chart data.
  46. */
  47. const init = function(data) {
  48. let newData = {};
  49. ['x', 'y', 'z', 'color'].forEach((dimension) => {
  50. let set = {
  51. key: dimension,
  52. values: []
  53. };
  54. data.values.forEach((d) => {
  55. let key = mappings[dimension];
  56. let value = d.values.find((v) => v.key === key).value;
  57. set.values.push({ key: key, value: value });
  58. });
  59. newData[dimension] = dataTransform(set).summary();
  60. });
  61. let extentX = newData.x.valueExtent;
  62. let extentY = newData.y.valueExtent;
  63. let extentZ = newData.z.valueExtent;
  64. let extentColor = newData.color.valueExtent;
  65. xScale = d3.scaleLinear()
  66. .domain(extentX)
  67. .range([0, dimensions.x]);
  68. yScale = d3.scaleLinear()
  69. .domain(extentY)
  70. .range([0, dimensions.y]);
  71. zScale = d3.scaleLinear()
  72. .domain(extentZ)
  73. .range([0, dimensions.z]);
  74. if (color) {
  75. colorScale = d3.scaleQuantize()
  76. .domain(extentColor)
  77. .range([color, color]);
  78. } else {
  79. colorScale = d3.scaleQuantize()
  80. .domain(extentColor)
  81. .range(colors);
  82. }
  83. };
  84. /**
  85. * Constructor
  86. *
  87. * @constructor
  88. * @alias particlePlot
  89. * @param {d3.selection} selection - The chart holder D3 selection.
  90. */
  91. const my = function(selection) {
  92. const layers = ["axis", "particles"];
  93. const scene = createScene(selection, layers, classed, width, height, debug);
  94. selection.each((data) => {
  95. init(data);
  96. // Add Viewpoint
  97. viewpoint.centerOfRotation([dimensions.x / 2, dimensions.y / 2, dimensions.z / 2]);
  98. scene.call(viewpoint);
  99. // Add Axis
  100. axis.xScale(xScale)
  101. .yScale(yScale)
  102. .zScale(zScale);
  103. scene.select(".axis")
  104. .call(axis);
  105. // Add Particles
  106. particles.xScale(xScale)
  107. .mappings(mappings)
  108. .yScale(yScale)
  109. .zScale(zScale)
  110. .colorScale(colorScale);
  111. scene.select(".particles")
  112. .datum(data)
  113. .call(particles);
  114. });
  115. };
  116. /**
  117. * Width Getter / Setter
  118. *
  119. * @param {number} _v - X3D canvas width in px.
  120. * @returns {*}
  121. */
  122. my.width = function(_v) {
  123. if (!arguments.length) return width;
  124. width = _v;
  125. return this;
  126. };
  127. /**
  128. * Height Getter / Setter
  129. *
  130. * @param {number} _v - X3D canvas height in px.
  131. * @returns {*}
  132. */
  133. my.height = function(_v) {
  134. if (!arguments.length) return height;
  135. height = _v;
  136. return this;
  137. };
  138. /**
  139. * Dimensions Getter / Setter
  140. *
  141. * @param {{x: number, y: number, z: number}} _v - 3D object dimensions.
  142. * @returns {*}
  143. */
  144. my.dimensions = function(_v) {
  145. if (!arguments.length) return dimensions;
  146. dimensions = _v;
  147. return this;
  148. };
  149. /**
  150. * X Scale Getter / Setter
  151. *
  152. * @param {d3.scale} _v - D3 scale.
  153. * @returns {*}
  154. */
  155. my.xScale = function(_v) {
  156. if (!arguments.length) return xScale;
  157. xScale = _v;
  158. return my;
  159. };
  160. /**
  161. * Y Scale Getter / Setter
  162. *
  163. * @param {d3.scale} _v - D3 scale.
  164. * @returns {*}
  165. */
  166. my.yScale = function(_v) {
  167. if (!arguments.length) return yScale;
  168. yScale = _v;
  169. return my;
  170. };
  171. /**
  172. * Z Scale Getter / Setter
  173. *
  174. * @param {d3.scale} _v - D3 scale.
  175. * @returns {*}
  176. */
  177. my.zScale = function(_v) {
  178. if (!arguments.length) return zScale;
  179. zScale = _v;
  180. return my;
  181. };
  182. /**
  183. * Color Scale Getter / Setter
  184. *
  185. * @param {d3.scale} _v - D3 color scale.
  186. * @returns {*}
  187. */
  188. my.colorScale = function(_v) {
  189. if (!arguments.length) return colorScale;
  190. colorScale = _v;
  191. return my;
  192. };
  193. /**
  194. * Color Getter / Setter
  195. *
  196. * @param {string} _v - Color (e.g. "red" or "#ff0000").
  197. * @returns {*}
  198. */
  199. my.color = function(_v) {
  200. if (!arguments.length) return color;
  201. color = _v;
  202. return my;
  203. };
  204. /**
  205. * Colors Getter / Setter
  206. *
  207. * @param {Array} _v - Array of colours used by color scale.
  208. * @returns {*}
  209. */
  210. my.colors = function(_v) {
  211. if (!arguments.length) return colors;
  212. colors = _v;
  213. return my;
  214. };
  215. /**
  216. * Mappings Getter / Setter
  217. *
  218. * @param {Object} _v - Map properties to colour etc.
  219. * @returns {*}
  220. */
  221. my.mappings = function(_v) {
  222. if (!arguments.length) return mappings;
  223. mappings = _v;
  224. return my;
  225. };
  226. /**
  227. * Debug Getter / Setter
  228. *
  229. * @param {boolean} _v - Show debug log and stats. True/False.
  230. * @returns {*}
  231. */
  232. my.debug = function(_v) {
  233. if (!arguments.length) return debug;
  234. debug = _v;
  235. return my;
  236. };
  237. /**
  238. * Dispatch On Getter
  239. *
  240. * @returns {*}
  241. */
  242. my.on = function() {
  243. let value = dispatch.on.apply(dispatch, arguments);
  244. return value === dispatch ? my : value;
  245. };
  246. return my;
  247. }