component/axisThreePlane.js

import * as d3 from "d3";
import componentAxis from "./axis.js";

/**
 * Reusable 3D Multi Plane Axis Component
 *
 * @module
 */
export default function() {

	/* Default Properties */
	let dimensions = { x: 40, y: 40, z: 40 };
	let colors = ["blue", "red", "green"];
	let classed = "d3X3dAxisThreePlane";
	let labelPosition = "proximal";

	/* Scales */
	let xScale;
	let yScale;
	let zScale;

	/* Components */
	const xzAxis = componentAxis();
	const yzAxis = componentAxis();
	const yxAxis = componentAxis();
	const zxAxis = componentAxis();

	/**
	 * Constructor
	 *
	 * @constructor
	 * @alias axisThreePlane
	 * @param {d3.selection} selection - The chart holder D3 selection.
	 */
	const my = function(selection) {
		selection.each(function() {

			const element = d3.select(this)
				.classed(classed, true);

			const layers = ["xzAxis", "yzAxis", "yxAxis", "zxAxis"];
			element.selectAll("group")
				.data(layers)
				.enter()
				.append("Group")
				.attr("class", (d) => d);

			xzAxis.scale(xScale)
				.direction("x")
				.tickDirection("z")
				.tickSize(zScale.range()[1] - zScale.range()[0])
				.color(colors[0])
				.labelPosition(labelPosition);

			yzAxis.scale(yScale)
				.direction("y")
				.tickDirection("z")
				.tickSize(zScale.range()[1] - zScale.range()[0])
				.color(colors[1])
				.labelPosition(labelPosition);

			yxAxis.scale(yScale)
				.direction("y")
				.tickDirection("x")
				.tickSize(xScale.range()[1] - xScale.range()[0])
				.color(colors[1])
				.labelPosition(labelPosition);

			zxAxis.scale(zScale)
				.direction("z")
				.tickDirection("x")
				.tickSize(xScale.range()[1] - xScale.range()[0])
				.color(colors[2])
				.labelPosition(labelPosition);

			// We only want 2 sets of labels on the y axis if they are in distal position.
			if (labelPosition === "proximal") {
				// Make the tickFormat return nothing (i.e hide text).
				yxAxis.tickFormat((d) => "");
			} else {
				// Make the yxAxis tickFormat match the yzAxis.
				yxAxis.tickFormat(yzAxis.tickFormat());
			}

			element.select(".xzAxis")
				.call(xzAxis);

			element.select(".yzAxis")
				.call(yzAxis);

			element.select(".yxAxis")
				.call(yxAxis);

			element.select(".zxAxis")
				.call(zxAxis);
		});
	};

	/**
	 * Dimensions Getter / Setter
	 *
	 * @param {{x: number, y: number, z: number}} _v - 3D object dimensions.
	 * @returns {*}
	 */
	my.dimensions = function(_v) {
		if (!arguments.length) return dimensions;
		dimensions = _v;
		return this;
	};

	/**
	 * X Scale Getter / Setter
	 *
	 * @param {d3.scale} _v - D3 scale.
	 * @returns {*}
	 */
	my.xScale = function(_v) {
		if (!arguments.length) return xScale;
		xScale = _v;
		return my;
	};

	/**
	 * Y Scale Getter / Setter
	 *
	 * @param {d3.scale} _v - D3 scale.
	 * @returns {*}
	 */
	my.yScale = function(_v) {
		if (!arguments.length) return yScale;
		yScale = _v;
		return my;
	};

	/**
	 * Z Scale Getter / Setter
	 *
	 * @param {d3.scale} _v - D3 scale.
	 * @returns {*}
	 */
	my.zScale = function(_v) {
		if (!arguments.length) return zScale;
		zScale = _v;
		return my;
	};

	/**
	 * Colors Getter / Setter
	 *
	 * @param {Array} _v - Array of colours used by color scale.
	 * @returns {*}
	 */
	my.colors = function(_v) {
		if (!arguments.length) return colors;
		colors = _v;
		return my;
	};

	/**
	 * Label Position Getter / Setter
	 *
	 * @param {string} _v - Position ("proximal" or "distal")
	 * @returns {*}
	 */
	my.labelPosition = function(_v) {
		if (!arguments.length) return labelPosition;
		labelPosition = _v;
		return my;
	};

	return my;
}