Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Create additional D3.js symbols

Tags:

d3.js

D3 already features a bunch of symbols, but I'd like to add a custom one. So that I could for example just call d3.svg.symbol().type('custom') in my code.

like image 825
zabbarob Avatar asked Aug 15 '14 18:08

zabbarob


1 Answers

This cannot be done directly since the array of symbol definitions is not accessible from the API.

You can see in the source code HERE that the symbol definitions are stored in a d3.map called d3_svg_symbols. The only part of this map that gets exposed to the public API is the array of keys. This is done by calling the .keys() method of the map, HERE.

d3.svg.symbolTypes = d3_svg_symbols.keys();

The definitions themselves are never exposed, and so you cannot add definitions directly as you had hoped.

You can, however, construct a workaround without too much difficulty. One way would be to create a map of your custom symbols, and create a function based on the existing one for the built-in symbols. For example:

// DEFINE A COUPLE OF CUSTOM SYMBOLS
var customSymbolTypes = d3.map({
  'custom-symbol-1': function(size) {
    // returns a path-data string
  },
  'custom-symbol-2': function(size) {
    // returns a path-data string
  }
});


// CREATE A CUSTOM SYMBOL FUNCTION MIRRORING THE BUILT-IN FUNCTIONALITY
d3.svg.customSymbol = function() {
  var type,
      size = 64; // SET DEFAULT SIZE
  function symbol(d,i) {
    // GET THE SYMBOL FROM THE CUSTOM MAP
    return customSymbolTypes.get(type.call(this,d,i))(size.call(this,d,i));
  }
  // DEFINE GETTER/SETTER FUNCTIONS FOR SIZE AND TYPE
  symbol.type = function(_) {
    if (!arguments.length) return type;
    type = d3.functor(_);
    return symbol;
  };
  symbol.size = function(_) {
    if (!arguments.length) return size;
    size = d3.functor(_);
    return symbol;
  };
  return symbol;
};

Then, you could create a function to check if a symbol is in the list of built-in symbols, and if it's not, assume it is a custom symbol:

function getSymbol(type, size) {
  // SIZE DEFAULTS TO 64 IF NOT GIVEN
  size = size || 64;
  // IF TYPE IS ONE OF THE BUILT-IN TYPES, CALL THE BUILT-IN FUNCTION
  if (d3.svg.symbolTypes.indexOf(type) !== -1) {
    return d3.svg.symbol().type(type).size(size)();
  } 
  // OTHERWISE, CALL THE CUSTOM SYMBOL FUNCTION
  else {
    return d3.svg.customSymbol().type(type).size(size)();
  }
}

HERE is a demo of this method in action.

I'll admit it seems kind of crazy to have to re-implement the whole symbol function like that. It might be worth a feature request on the github page asking to be able to provide a custom map of symbol definitions that could be read into the built-in method. Anyway, I hope that helps for now.

like image 118
jshanley Avatar answered Oct 06 '22 03:10

jshanley