Empirical
selection.h
Go to the documentation of this file.
1 // This file is part of Empirical, https://github.com/devosoft/Empirical
2 // Copyright (C) Michigan State University, 2016-2017.
3 // Released under the MIT Software license; see doc/LICENSE
4 
5 #ifndef __SELECTION_H__
6 #define __SELECTION_H__
7 
8 #include "d3_init.h"
9 #include "utils.h"
10 #include "dataset.h"
11 
12 #include <iostream>
13 #include <string>
14 #include <typeinfo>
15 #include <map>
16 #include <array>
17 
18 #include "../../base/assert.h"
19 #include "../js_utils.h"
20 #include "../JSWrap.h"
21 
23 extern "C" {
24  extern int n_objects();
25 }
27 
28 namespace D3 {
29 
30  class Dataset;
31 
38  template <typename DERIVED>
39  class SelectionOrTransition : public D3_Base {
40  public:
42  SelectionOrTransition(int id) : D3_Base(id){;};
44 
47  DERIVED Select(std::string selector) const {
48  int new_id = NextD3ID();
49 
50  EM_ASM_ARGS({
51  var new_selection = js.objects[$0].select(Pointer_stringify($1));
52  js.objects[$2] = new_selection;
53  }, this->id, selector.c_str(), new_id);
54 
55  return DERIVED(new_id);
56  }
57 
60  DERIVED SelectAll(std::string selector) const {
61  int new_id = NextD3ID();
62 
63  EM_ASM_ARGS({
64  // console.log($0, js.objects[$0]);
65  var new_selection = js.objects[$0].selectAll(Pointer_stringify($1));
66  js.objects[$2] = new_selection;
67  }, this->id, selector.c_str(), new_id);
68 
69  return DERIVED(new_id);
70  }
71 
79  // TODO: Allow arguments
80  DERIVED& Call(std::string function){
81  EM_ASM_ARGS({
82  var func_string = Pointer_stringify($1);
83  if (typeof window[func_string] === "function") {
84  func_string = window[func_string];
85  } else if (typeof window["emp"][func_string] === "function") {
86  func_string = window["emp"][func_string];
87  } else if (typeof window["d3"][func_string] === "function") {
88  func_string = window["d3"][func_string];
89  }
90 
91  emp.__new_object = js.objects[$0].call(function(sel){return func_string($0);});
92  }, this->id, function.c_str());
93 
94  return *(static_cast<DERIVED *>(this));
95  }
96 
98 
99  template <typename T>
101  Call(T function){
102  uint32_t fun_id = emp::JSWrap(function, "", false);
103  EM_ASM_ARGS({
104  emp.__new_object = js.objects[$0].call(function(selection) {
105  return emp.Callback($1, $0);
106  });
107  }, this->id, fun_id);
108  emp::JSDelete(fun_id);
109 
110  return *(static_cast<DERIVED *>(this));
111  }
112 
114 
122  DERIVED Filter(std::string selector) const {
123 
124  int new_id = NextD3ID();
125  D3_CALLBACK_METHOD_1_ARG(filter, selector.c_str())
126  StoreNewObject(new_id);
127  return DERIVED(new_id);
128  }
129 
131 
132  template <typename T>
134  Filter(T selector) const {
135 
136  int new_id = NextD3ID();
137  D3_CALLBACK_METHOD_CPP_FUNCTION_1_ARG(filter, selector)
138  StoreNewObject(new_id);
139  return DERIVED(new_id);
140  }
141 
143 
147 
148  DERIVED& Each(std::string function){
149  D3_CALLBACK_METHOD_1_ARG(each, function.c_str())
150  return *(static_cast<DERIVED *>(this));
151  }
152 
154 
155  template <typename T>
157  Each(T function){
159  return *(static_cast<DERIVED *>(this));
160  }
161 
163 
166  void Remove(){
167  EM_ASM_ARGS({js.objects[$0].remove()},
168  this->id);
169  }
170 
171  DERIVED Merge(DERIVED & other) {
172  int new_id = NextD3ID();
173  // std::cout << "New id should be: " << new_id << std::endl;
174  EM_ASM_ARGS({
175  js.objects[$2] = js.objects[$0].merge(js.objects[$1]);
176  // console.log("Merged: ", js.objects[$2], $2);
177  }, this->id, other.GetID(), new_id);
178  return DERIVED(new_id);
179  }
180 
181  /**************************************************************************/
203  /* Version for strings
204  This will break if someone happens to use a string that
205  is identical to a function name
206  */
207 
208  DERIVED& SetAttr(std::string name, std::string value) {
209 
210  D3_CALLBACK_METHOD_2_ARGS(attr, name.c_str(), value.c_str())
211  return *(static_cast<DERIVED *>(this));
212  }
213 
215 
216  //This version handles values that are not functions or strings
217  //is_fundamental safely excludes lambdas
218  template <typename T>
219  typename std::enable_if<std::is_fundamental<T>::value, DERIVED&>::type
220  SetAttr(std::string name, T value) {
221 
222  EM_ASM_ARGS({js.objects[$0].attr(Pointer_stringify($1), $2)},
223  this->id, name.c_str(), value);
224  return *(static_cast<DERIVED *>(this));
225  }
226 
227  //This version handles values that are C++ functions and wraps them with JSWrap
228  //If a function is being used repeatedly, it may be more efficient to wrap it
229  //once and then pass the name as a string.
230  template <typename T>
232  SetAttr(std::string name, T value) {
233 
234  //This should probably be JSWrapOnce, but that breaks the visualization
235  uint32_t fun_id = emp::JSWrap(value, "", false);
236 
237  EM_ASM_ARGS({
238  js.objects[$0].attr(Pointer_stringify($1), function(d, i, k) {
239  return emp.Callback($2, d, i, k);
240  });
241  }, this->id, name.c_str(), fun_id);
242 
243  emp::JSDelete(fun_id);
244 
245  return *(static_cast<DERIVED *>(this));
246  }
247 
248  /* We also need a const char * version, because the template version will be a
249  better match for raw strings than the std::string version
250  */
251 
252  DERIVED& SetAttr(std::string name, const char * value) {
253 
254  D3_CALLBACK_METHOD_2_ARGS(attr, name.c_str(), value)
255  return *(static_cast<DERIVED *>(this));
256  }
257 
258  /* Version for containers */
259  template <typename T>
260  typename std::enable_if<T::value_type != "", DERIVED&>::type
261  SetAttr(std::string name, T value) {
263 
264  EM_ASM_ARGS({
265  js.objects[$0].attr(Pointer_stringify($1), emp_i.__incoming_array);
266  }, this->id, name.c_str());
267 
268  return *(static_cast<DERIVED *>(this));
269  }
270 
272 
283  //std::string version because std::strings are better
284  DERIVED& SetStyle(std::string name, std::string value, bool priority=false){
285  if (priority){
286  EM_ASM_ARGS({
287  var func_string = Pointer_stringify($2);
288  if (typeof window[func_string] === "function") {
289  func_string = window[func_string];
290  }
291  for (name in {d3:"d3", emp:"emp"}) {
292  if (typeof window[name][func_string] === "function") {
293  func_string = window[name][func_string];
294  }
295  }
296  js.objects[$0].style(Pointer_stringify($1), in_string, "important");
297  }, this->id, name.c_str(), value.c_str());
298  } else {
299  D3_CALLBACK_METHOD_2_ARGS(style, name.c_str(), value.c_str())
300  }
301  return *(static_cast<DERIVED *>(this));
302  }
303 
305 
306  //Const char * version so the generic template doesn't get greedy with
307  //string literals
308  DERIVED& SetStyle(std::string name, const char* value, bool priority=false){
309  if (priority){
310  EM_ASM_ARGS({
311  var func_string = Pointer_stringify($2);
312  if (typeof window[func_string] === "function") {
313  func_string = window[func_string];
314  }
315  for (name in {d3:"d3", emp:"emp"}) {
316  if (typeof window[name][func_string] === "function") {
317  func_string = window[name][func_string];
318  }
319  };
320  js.objects[$0].style(Pointer_stringify($1), in_string, "important");
321  }, this->id, name.c_str(), value);
322  } else {
323  D3_CALLBACK_METHOD_2_ARGS(style, name.c_str(), value)
324  }
325  return *(static_cast<DERIVED *>(this));
326  }
327 
328  //This version handles values that are C++ functions and wraps them with JSWrap
329  //If a function is being used repeatedly, it may be more efficient to wrap it
330  //once and then pass the name as a string.
331  template <typename T>
333  SetStyle(std::string name, T value) {
334  D3_CALLBACK_METHOD_CPP_FUNCTION_2_ARGS(style, name.c_str(), value);
335  return *(static_cast<DERIVED *>(this));
336  }
337 
338  //Generic template version
339  template <typename T>
340  typename std::enable_if<std::is_fundamental<T>::value, DERIVED&>::type
341  SetStyle(std::string name, T value, bool priority=false){
342  if (priority){
343  EM_ASM_ARGS({js.objects[$0].style(Pointer_stringify($1), $2, "important")},
344  this->id, name.c_str(), value);
345  }
346  else {
347  EM_ASM_ARGS({js.objects[$0].style(Pointer_stringify($1), $2)},
348  this->id, name.c_str(), value);
349  }
350  return *(static_cast<DERIVED *>(this));
351  }
352 
354 
357  DERIVED& SetText(std::string text){
358  D3_CALLBACK_METHOD_1_ARG(text, text.c_str())
359  return *(static_cast<DERIVED *>(this));
360  }
361 
363 
364  template <typename T>
366  SetText(T func){
368  return *(static_cast<DERIVED *>(this));
369  }
370 
372 
373  // This stuff isn't implemented the same for selections and transitions
374  // but we want it to all show up together in the docs
375  #ifdef DOXYGEN_RUNNING
376 
384  // std::string verison
385  DERIVED& SetProperty(std::string name, std::string value) {return *(static_cast<DERIVED *>(this));}
386 
392  DERIVED& SetHtml(std::string value) {return *(static_cast<DERIVED *>(this));}
393 
396  // Value can also be a function that takes bound data and returns a bool
400  DERIVED& SetClassed(std::string classname, bool value) {return *(static_cast<DERIVED *>(this));}
401 
402  #endif
403 
405 
406  /**************************************************************************/
424  std::string GetAttrString(std::string name) const {
426  char * buffer = (char *)EM_ASM_INT({
427  var text = js.objects[$0].attr(Pointer_stringify($1));
428  var buffer = Module._malloc(text.length+1);
429  Module.writeStringToMemory(text, buffer);
430  return buffer;
431  }, this->id, name.c_str());
432 
433  std::string result = std::string(buffer);
434  free(buffer);
435  return result;
436  }
437 
439  int GetAttrInt(std::string name) const {
440  return EM_ASM_INT({
441  return js.objects[$0].attr(Pointer_stringify($1));
442  }, this->id, name.c_str());
443  }
444 
446  double GetAttrDouble(std::string name) const {
447  return EM_ASM_DOUBLE({
448  return js.objects[$0].attr(Pointer_stringify($1));
449  }, this->id, name.c_str());
450  }
451 
453  std::string GetStyleString(std::string name) const {
454  char * buffer = (char *)EM_ASM_INT({
455  var text = js.objects[$0].style(Pointer_stringify($1));
456  var buffer = Module._malloc(text.length+1);
457  Module.writeStringToMemory(text, buffer);
458  return buffer;
459  }, this->id, name.c_str());
460 
461  std::string result = std::string(buffer);
462  free(buffer);
463  return result;
464  }
465 
467  int GetStyleInt(std::string name) const {
468  return EM_ASM_INT({
469  return js.objects[$0].style(Pointer_stringify($1));
470  }, this->id, name.c_str());
471  }
472 
474  double GetStyleDouble(std::string name) const {
475  return EM_ASM_DOUBLE({
476  return js.objects[$0].style(Pointer_stringify($1));
477  }, this->id, name.c_str());
478  }
479 
481  std::string GetText() const {
482 
483  char * buffer = (char *)EM_ASM_INT({
484  var text = js.objects[$0].text();
485  var buffer = Module._malloc(text.length+1);
486  Module.writeStringToMemory(text, buffer);
487  return buffer;
488  }, this->id);
489 
490  std::string result = std::string(buffer);
491  free(buffer);
492  return result;
493  }
494 
495  // GetHtml and GetProperty are implemented differently for transitions and selections
496  // These are placeholders for doxygen
497 
498  #ifdef DOXYGEN_RUNNING
499 
504  std::string GetHtml() {return "";}
505 
510  std::string GetPropertyString(std::string name) {return "";}
511 
516  int GetPropertyInt(std::string name) {return 0;}
517 
522  double GetPropertyDouble(std::string name) {return 0;}
523 
524  #endif
525 
527  bool Empty() const {
528  int empty = EM_ASM_INT({return Number(js.objects[$0].empty())},
529  this->id);
530  return (bool)empty;
531  }
532 
534  int Size() const {
535  return EM_ASM_INT({return js.objects[$0].size()},
536  this->id);
537  }
538 
540 
541  };
542 
547  class Transition : public SelectionOrTransition<Transition> {
548  public:
549 
550  /**************************************************************************/
559  Transition(){;};
561 
564 
570  //TODO: D3 supports passing a selection, but it's kind of a weird edge case
571  Transition NewTransition(std::string name="") const {
572  int new_id = NextD3ID();
573  EM_ASM_ARGS({
574  var transition = js.objects[$0].transition(Pointer_stringify($1));
575  js.objects[$2] = transition;
576  }, this->id, name.c_str(), new_id);
577 
578  return D3::Transition(new_id);
579  }
580 
581  #ifndef DOXYGEN_RUNNING
582 
583  /**************************************************************************/
600  Transition& On(std::string type, std::string listener="null", bool capture=false){
601 
602  // Check that the listener is valid
603  emp_assert(EM_ASM_INT({
604  var func_string = Pointer_stringify($0);
605  if (func_string == "null") {
606  return true;
607  }
608  if (typeof window[func_string] === "function") {
609  func_string = window[func_string];
610  }
611  for (name in {d3:"d3", emp:"emp"}) {
612  if (typeof window[name][func_string] === "function") {
613  func_string = window[name][func_string];
614  }
615  }
616  return (typeof func_string === "function");
617  }, listener.c_str()) \
618  && "String passed to On is not s Javascript function or null", listener);
619 
620  int new_id = NextD3ID();
621 
622  EM_ASM_ARGS({
623  var func_string = Pointer_stringify($2);
624  if (typeof window[func_string] === "function") {
625  func_string = window[func_string];
626  }
627  for (name in {d3:"d3", emp:"emp"}) {
628  if (typeof window[name][func_string] === "function") {
629  func_string = window[name][func_string];
630  }
631  }
632 
633  if (typeof func_string === "function") {
634  js.objects[$0].on(Pointer_stringify($1),
635  func_string,$3);
636  } else {
637  js.objects[$0].on(Pointer_stringify($1), null);
638  }
639 
640  }, this->id, type.c_str(), listener.c_str(), capture, new_id);
641 
642  return (*this);
643  }
644 
646 
648  template <typename T>
650  On(std::string type, T listener, bool capture=false){
651 
652  uint32_t fun_id = emp::JSWrap(listener, "", false);
653  int new_id = NextD3ID();
654 
655  EM_ASM_ARGS({
656  js.objects[$0].on(Pointer_stringify($1),
657  function(d, i){
658  js.objects[$4] = d3.select(this);
659  emp.Callback($2, d, i, $4);}, $3);
660  }, this->id, type.c_str(), fun_id, capture, new_id);
661 
662  emp::JSDelete(fun_id);
663  return (*this);
664  }
665 
667 
668  Transition& SetDuration(double time) {
669  EM_ASM_ARGS({
670  js.objects[$0].duration($1);
671  }, this->id, time);
672  return (*this);
673  }
674 
679  // std::string verison
680  Transition& SetProperty(std::string name, std::string value){
681  EM_ASM_ARGS({
682  var arg1 = Pointer_stringify($1); \
683  var func_string = Pointer_stringify($2);
684  if (typeof window[func_string] === "function") {
685  func_string = window[func_string];
686  }
687  for (name in {d3:"d3", emp:"emp"}) {
688  if (typeof window[name][func_string] === "function") {
689  func_string = window[name][func_string];
690  }
691  }
692  js.objects[$0].each("end", function(){
693  d3.select(this).property(arg1, func_string);
694  });
695  }, name.c_str(), value.c_str());
696  return *this;
697  }
698 
700 
701  // Const char * version so raw strings work
702  Transition& SetProperty(std::string name, const char* value){
703  EM_ASM_ARGS({
704  var arg1 = Pointer_stringify($1); \
705  var func_string = Pointer_stringify($2);
706  if (typeof window[func_string] === "function") {
707  func_string = window[func_string];
708  }
709  for (name in {d3:"d3", emp:"emp"}) {
710  if (typeof window[name][func_string] === "function") {
711  func_string = window[name][func_string];
712  }
713  }
714  js.objects[$0].each("end", function(){
715  d3.select(this).property(arg1, func_string);
716  });
717  }, name.c_str(), value);
718  return *this;
719  }
720 
721  //Generic template version
722  template <typename T>
723  typename std::enable_if<std::is_fundamental<T>::value, Transition&>::type
724  SetProperty(std::string name, T value){
725  EM_ASM_ARGS({
726  js.objects[$0].each("end", function() {
727  d3.select(this).property(Pointer_stringify($1), $2);
728  });
729  }, this->id, name.c_str());
730  return *this;
731  }
732 
733  //This version handles values that are C++ functions and wraps them with JSWrap
734  //If a function is being used repeatedly, it may be more efficient to wrap it
735  //once and then pass the name as a string.
736  template <typename T>
738  SetProperty(std::string name, T value) {
739  uint32_t fun_id = emp::JSWrap(value, "", false);
740  EM_ASM_ARGS({
741  js.objects[$0].each("end", function(){
742  d3.select(this).property(Pointer_stringify($1),
743  function(d, i, j) {
744  return emp.Callback($2, d, i, j);
745  });
746  });
747  }, this->id, name.c_str(), fun_id);
748  emp::JSDelete(fun_id);
749  return *this;
750  }
751 
753 
756  Transition& SetHtml(std::string value){
757  EM_ASM_ARGS({
758  var func_string = Pointer_stringify($1);
759  if (typeof window[func_string] === "function") {
760  func_string = window[func_string];
761  }
762  for (name in {d3:"d3", emp:"emp"}) {
763  if (typeof window[name][func_string] === "function") {
764  func_string = window[name][func_string];
765  }
766  }
767  js.objects[$0].each("end", function(){
768  d3.select(this).html(func_string);
769  });
770  }, this->id, value.c_str());
771  return *this;
772  }
773 
775 
776  template <typename T>
778  SetHtml(T func){
779  uint32_t fun_id = emp::JSWrap(func, "", false);
780  EM_ASM_ARGS({
781  js.objects[$0].each("end", function(){
782  d3.select(this).html(function(d, i, j) {
783  return emp.Callback($1, d, i, j);
784  });
785  });
786  }, this->id, fun_id);
787  emp::JSDelete(fun_id);
788  return *this;
789  }
790 
792 
795  // Value can also be a function that takes bound data and returns a bool
796  Transition& SetClassed(std::string classname, bool value) {
797  EM_ASM_ARGS({
798  js.objects[$0].each("end", function(){
799  d3.select(this).classed(Pointer_stringify($1), $2);
800  });
801  }, this->id, classname.c_str(), value);
802  return *this;
803  }
804 
806 
807  // Version for C++ function
808  template <typename T>
810  SetClassed(std::string, std::string classname, T func){
811  uint32_t fun_id = emp::JSWrap(func, "", false);
812  EM_ASM_ARGS({
813  js.objects[$0].each("end", function(){
814  d3.select(this).classed(Pointer_stringify($1),
815  function(d, i, j) {
816  return emp.Callback($2, d, i, j);
817  });
818  });
819  }, this->id, classname.c_str(), fun_id);
820  emp::JSDelete(fun_id);
821 
822  return *this;
823  }
824 
825  // Version that allows strings containing function names but warns on other strings
826  Transition& SetClassed(std::string classname, std::string value){
827  emp_assert(EM_ASM_INT({
828  var func_string = Pointer_stringify($0);
829  if (typeof window[func_string] === "function") {
830  func_string = window[func_string];
831  }
832  for (name in {d3:"d3", emp:"emp"}) {
833  if (typeof window[name][func_string] === "function") {
834  func_string = window[name][func_string];
835  }
836  }
837  return (typeof func_string === "function");
838  }, value.c_str()) && "String passed to SetClassed is not a Javascript function", value);
839 
840  EM_ASM_ARGS({
841  var arg1 = Pointer_stringify($1); \
842  var func_string = Pointer_stringify($2);
843  if (typeof window[func_string] === "function") {
844  func_string = window[func_string];
845  }
846  for (name in {d3:"d3", emp:"emp"}) {
847  if (typeof window[name][func_string] === "function") {
848  func_string = window[name][func_string];
849  }
850  }
851  js.objects[$0].each("end", function(){
852  d3.select(this).classed(arg1, func_string);
853  });
854  }, classname.c_str(), value.c_str());
855 
856  return *this;
857  }
858 
860 
863  /**************************************************************************/
881  std::string GetHtml() const {
883  char * buffer = (char *)EM_ASM_INT({
884  var text = d3.select(js.objects[$0]).html();
885  var buffer = Module._malloc(text.length+1);
886  Module.writeStringToMemory(text, buffer);
887  return buffer;
888  }, this->id);
889 
890  std::string result = std::string(buffer);
891  free(buffer);
892  return result;
893  }
894 
896  std::string GetPropertyString(std::string name) const {
897  char * buffer = (char *)EM_ASM_INT({
898  var text = d3.select(js.objects[$0]).property(Pointer_stringify($1));
899  var buffer = Module._malloc(text.length+1);
900  Module.writeStringToMemory(text, buffer);
901  return buffer;
902  }, this->id, name.c_str());
903 
904  std::string result = std::string(buffer);
905  free(buffer);
906  return result;
907  }
908 
910  int GetPropertyInt(std::string name) const {
911  return EM_ASM_INT({
912  return d3.select(js.objects[$0]).property(Pointer_stringify($1));
913  }, this->id, name.c_str());
914  }
915 
917  double GetPropertyDouble(std::string name) const {
918  return EM_ASM_DOUBLE({
919  return d3.select(js.objects[$0]).property(Pointer_stringify($1));
920  }, this->id, name.c_str());
921  }
922 
924 
925  #endif
926 
927  };
928 
936  class Selection : public SelectionOrTransition<Selection> {
937 
938  public:
939 
944 
947  EM_ASM_ARGS({js.objects[$0] = d3.selection();}, this->id);
948  };
949 
953  // that you already created in Javascript and added to js.objects.
955  };
956 
958  };
959 
960  // Selection& operator= (const Selection & other) {
961  // std::cout << "Calling assingment: " << this->id << " " << other.id << std::endl;
962  // this->id = other.id;
963  // return (*this);
964  // }
965 
969  Selection(std::string selector, bool all = false) {
970  if (all){
971  EM_ASM_ARGS({
972  js.objects[$0] = d3.selectAll(Pointer_stringify($1));
973  }, this->id, selector.c_str());
974  }
975  else {
976  EM_ASM_ARGS({
977  js.objects[$0] = d3.select(Pointer_stringify($1));
978  }, this->id, selector.c_str());
979  }
980  };
981 
984 
985 
987 
996 
998 
1006 
1007  //Option to pass loaded dataset stored in Javascript without translating to C++
1008  Selection Data(Dataset & values, std::string key=""){
1009  int update_id = NextD3ID();
1010 
1011  EM_ASM_ARGS({
1012  //We could make this slightly prettier with macros, but would
1013  //add an extra comparison
1014  var in_string = Pointer_stringify($1);
1015  var fn = window["emp"][in_string];
1016  if (typeof fn === "function"){
1017  var update_sel = js.objects[$0].data(js.objects[$2], fn);
1018  } else if (typeof window["d3"][in_string] === "function") {
1019  var update_sel = js.objects[$0].data(js.objects[$2],
1020  window["d3"][in_string]);
1021  } else if (typeof window[in_string] === "function") {
1022  var update_sel = js.objects[$0].data(js.objects[$2],
1023  window[in_string]);
1024  } else {
1025  var update_sel = js.objects[$0].data(js.objects[$2]);
1026  }
1027 
1028  js.objects[$3] = update_sel;
1029  },this->id, key.c_str(), values.GetID(), update_id);
1030 
1031  Selection update = Selection(update_id);
1032  return update;
1033  }
1034 
1036 
1037  // Accepts Dataset and C++ function as key
1038  template<typename T>
1040  Data(Dataset & values, T key){
1041  int update_id = NextD3ID();
1042  uint32_t fun_id = emp::JSWrap(key, "", false);
1043 
1044  EM_ASM_ARGS({
1045  var update_sel = js.objects[$0].data(js.objects[$2],
1046  function(d,i) {
1047  return emp.Callback($1, d, i);
1048  });
1049  js.objects[$3] = update_sel;
1050 
1051  }, this->id, fun_id, values.GetID(), update_id);
1052 
1053  emp::JSDelete(fun_id);
1054 
1055  Selection update = Selection(update_id);
1056  return update;
1057  }
1058 
1059  // template<typename C>
1060  // emp::sfinae_decoy<Selection, decltype(C::value_type::n_fields)>
1061  // Data(C values){
1062  // std::cout << "using the right one" << std::endl;
1063  // int update_id = NextD3ID();
1064  // emp::pass_array_to_javascript(values);
1065  //
1066  // EM_ASM_ARGS({
1067  // var update_sel = js.objects[$0].data(emp_i.__incoming_array);
1068  // js.objects[$1] = update_sel;
1069  //
1070  // }, this->id, update_id);
1071  //
1072  // Selection update = Selection(update_id);
1073  // return update;
1074  // }
1075 
1076 
1077  // Accepts string referring to Javascript function
1078  template<typename C, class = typename C::value_type>
1079  // typename std::enable_if<std::is_pod<typename C::value_type>::value, Selection>::type
1080  Selection Data(C values, std::string key=""){
1081  int update_id = NextD3ID();
1082 
1083 
1084  // std::cout << "In bind data: " << values[0].x0() << std::endl;
1086 
1087  EM_ASM_ARGS({
1088  var in_string = Pointer_stringify($1);
1089  var fn = window["emp"][in_string];
1090  if (typeof fn === "function"){
1091  var update_sel = js.objects[$0].data(emp_i.__incoming_array, fn);
1092  } else if (typeof window["d3"][in_string] === "function") {
1093  var update_sel = js.objects[$0].data(emp_i.__incoming_array,
1094  window["d3"][in_string]);
1095  } else if (typeof window[in_string] === "function") {
1096  var update_sel = js.objects[$0].data(emp_i.__incoming_array,
1097  window[in_string]);
1098  } else {
1099  var update_sel = js.objects[$0].data(emp_i.__incoming_array);
1100  }
1101 
1102  js.objects[$2] = update_sel;
1103  }, this->id, key.c_str(), update_id);
1104 
1105  Selection update = Selection(update_id);
1106  return update;
1107  }
1108 
1109  // Accepts C++ function as key
1110  template<typename C, class = typename C::value_type, typename T>
1112  Data(C values, T key){
1113  int update_id = NextD3ID();
1115  uint32_t fun_id = emp::JSWrap(key, "", false);
1116 
1117  EM_ASM_ARGS({
1118  var update_sel = js.objects[$0].data(emp_i.__incoming_array,
1119  function(d,i,k) {
1120  return emp.Callback($1, d, i, k);
1121  });
1122  js.objects[$2] = update_sel;
1123  }, this->id, fun_id, update_id);
1124 
1125  emp::JSDelete(fun_id);
1126 
1127  Selection update = Selection(update_id);
1128  return update;
1129  }
1130 
1132 
1133  Dataset GetData() const {
1134  int new_id = NextD3ID();
1135  EM_ASM_ARGS({
1136  js.objects[$1] = [js.objects[$0].data()];
1137  }, this->id, new_id);
1138  return Dataset(new_id);
1139  }
1140 
1146 
1147  Selection EnterAppend(std::string type){
1148 
1149  int new_id = NextD3ID();
1150 
1151  EM_ASM_ARGS({
1152  var append_selection = js.objects[$0].enter()
1153  .append(Pointer_stringify($1));
1154  js.objects[$2] = append_selection;
1155  }, this->id, type.c_str(), new_id);
1156 
1157  return Selection(new_id);
1158  }
1159 
1164  Selection EnterInsert(std::string name, std::string before=NULL){
1165  int new_id = NextD3ID();
1166 
1167  if (before.c_str()){
1168  EM_ASM_ARGS({
1169  var new_sel = js.objects[$0].enter().insert(Pointer_stringify($1),
1170  Pointer_stringify($2));
1171  js.objects[$3] = new_sel;
1172  }, this->id, name.c_str(), before.c_str(), new_id);
1173  } else {
1174  EM_ASM_ARGS({
1175  var new_sel = js.objects[$0].enter().insert(Pointer_stringify($1));
1176  js.objects[$2] = new_sel;
1177  }, this->id, name.c_str(), new_id);
1178  }
1179 
1180  return Selection(new_id);
1181  }
1182 
1189 
1190  int new_id = NextD3ID();
1191 
1192  EM_ASM_ARGS({
1193  var enter_selection = js.objects[$0].enter();
1194  js.objects[$1] = enter_selection;
1195  }, this->id, new_id);
1196 
1197  return Selection(new_id);
1198  }
1199 
1202 
1204  void ExitRemove(){
1205 
1206  int new_id = NextD3ID();
1207 
1208  EM_ASM_ARGS({
1209  var exit_selection = js.objects[$0].exit().remove();
1210  js.objects[$1] = exit_selection;
1211  }, this->id, new_id);
1212  }
1213 
1221 
1222  int new_id = NextD3ID();
1223 
1224  EM_ASM_ARGS({
1225  var exit_selection = js.objects[$0].exit();
1226  js.objects[$1] = exit_selection;
1227  }, this->id, new_id);
1228 
1229  return Selection(new_id);
1230  }
1231 
1233 
1234  #ifndef DOXYGEN_RUNNING
1235 
1236  /**************************************************************************/
1253  // std::string verison
1258  Selection& SetProperty(std::string name, std::string value){
1259  D3_CALLBACK_METHOD_2_ARGS(property, name.c_str(), value.c_str())
1260  return *this;
1261  }
1262 
1264 
1265  // Const char * version so raw strings work
1266  Selection& SetProperty(std::string name, const char* value){
1267  D3_CALLBACK_METHOD_2_ARGS(property, name.c_str(), value)
1268  return *this;
1269  }
1270 
1271  //Generic template version
1272  template <typename T>
1273  typename std::enable_if<std::is_fundamental<T>::value, Selection&>::type
1274  SetProperty(std::string name, T value){
1275  EM_ASM_ARGS({js.objects[$0].property(Pointer_stringify($1),
1276  $2)}, this->id, name.c_str());
1277  return *this;
1278  }
1279 
1280  //This version handles values that are C++ functions and wraps them with JSWrap
1281  //If a function is being used repeatedly, it may be more efficient to wrap it
1282  //once and then pass the name as a string.
1283  template <typename T>
1285  SetProperty(std::string name, T value) {
1286  D3_CALLBACK_METHOD_CPP_FUNCTION_2_ARGS(property, name.c_str(), value);
1287  return *this;
1288  }
1289 
1291 
1294  Selection& SetHtml(std::string value){
1295  D3_CALLBACK_METHOD_1_ARG(html, value.c_str())
1296  return *this;
1297  }
1298 
1300 
1301  template <typename T>
1303  SetHtml(T func){
1305  return *this;
1306  }
1307 
1309 
1312  // Value can also be a function that takes bound data and returns a bool
1313  Selection& SetClassed(std::string classname, bool value) {
1314  EM_ASM_ARGS({
1315  js.objects[$0].classed(Pointer_stringify($1), $2);
1316  }, this->id, classname.c_str(), value);
1317  return *this;
1318  }
1319 
1321 
1322  // Version for C++ function
1323  template <typename T>
1325  SetClassed(std::string, std::string classname, T func){
1326  D3_CALLBACK_METHOD_CPP_FUNCTION_2_ARGS(classed, classname.c_str(), func)
1327  return *this;
1328  }
1329 
1330  // Version that allows strings containing function names but warns on other strings
1331  Selection& SetClassed(std::string classname, std::string value){
1332  emp_assert(EM_ASM_INT({
1333  var func_string = Pointer_stringify($0);
1334  if (typeof window[func_string] === "function") {
1335  func_string = window[func_string];
1336  }
1337  for (name in {d3:"d3", emp:"emp"}) {
1338  if (typeof window[name][func_string] === "function") {
1339  func_string = window[name][func_string];
1340  }
1341  }
1342  return (typeof func_string === "function");
1343  }, value.c_str()) && "String passed to SetClassed is not a Javascript function", value);
1344  D3_CALLBACK_METHOD_2_ARGS(classed, classname.c_str(), value.c_str())
1345  return *this;
1346  }
1347 
1349 
1352  /**************************************************************************/
1370  std::string GetHtml() const {
1372  char * buffer = (char *)EM_ASM_INT({
1373  var text = js.objects[$0].html();
1374  var buffer = Module._malloc(text.length+1);
1375  Module.writeStringToMemory(text, buffer);
1376  return buffer;
1377  }, this->id);
1378 
1379  std::string result = std::string(buffer);
1380  free(buffer);
1381  return result;
1382  }
1383 
1385  std::string GetPropertyString(std::string name) const {
1386  char * buffer = (char *)EM_ASM_INT({
1387  var text = js.objects[$0].property(Pointer_stringify($1));
1388  var buffer = Module._malloc(text.length+1);
1389  Module.writeStringToMemory(text, buffer);
1390  return buffer;
1391  }, this->id, name.c_str());
1392 
1393  std::string result = std::string(buffer);
1394  free(buffer);
1395  return result;
1396  }
1397 
1399  int GetPropertyInt(std::string name) const {
1400  return EM_ASM_INT({
1401  return js.objects[$0].property(Pointer_stringify($1));
1402  }, this->id, name.c_str());
1403  }
1404 
1406  double GetPropertyDouble(std::string name) const {
1407  return EM_ASM_DOUBLE({
1408  return js.objects[$0].property(Pointer_stringify($1));
1409  }, this->id, name.c_str());
1410  }
1411 
1413 
1414  #endif
1415 
1417  Selection Append(std::string name){
1418  int new_id = NextD3ID();
1419 
1420  EM_ASM_ARGS({
1421  var new_selection = js.objects[$0].append(Pointer_stringify($1));
1422  js.objects[$2] = new_selection;
1423  }, this->id, name.c_str(), new_id);
1424  return Selection(new_id);
1425  }
1426 
1432  Selection Insert(std::string name, std::string before=NULL){
1433  int new_id = NextD3ID();
1434 
1435  if (before.c_str()){
1436  EM_ASM_ARGS({
1437  var new_sel = js.objects[$0].insert(Pointer_stringify($1),
1438  Pointer_stringify($2));
1439  js.objects[$3] = new_sel;
1440  }, this->id, name.c_str(), before.c_str(), new_id);
1441  } else {
1442  EM_ASM_ARGS({
1443  var new_sel = js.objects[$0].insert(Pointer_stringify($1));
1444  js.objects[$2] = new_sel;
1445  }, this->id, name.c_str(), new_id);
1446  }
1447  return Selection(new_id);
1448  }
1449 
1452  Transition MakeTransition(std::string name=""){
1453  int new_id = NextD3ID();
1454  EM_ASM_ARGS({
1455  var transition = js.objects[$0].transition(Pointer_stringify($1));
1456  js.objects[$2] = transition;
1457  }, this->id, name.c_str(), new_id);
1458 
1459  return Transition(new_id);
1460  }
1461 
1463  int new_id = NextD3ID();
1464  EM_ASM_ARGS({
1465  var transition = js.objects[$0].transition(js.objects[$1]);
1466  js.objects[$2] = transition;
1467  }, this->id, t.GetID(), new_id);
1468 
1469  return Transition(new_id);
1470  }
1471 
1472 
1474  Selection& Interrupt(std::string name=""){
1475  EM_ASM_ARGS({
1476  js.objects[$0].interrupt(Pointer_stringify($1));
1477  }, this->id, name.c_str());
1478  return *this;
1479  }
1480 
1486  Selection& Move(int x, int y) {
1487  EM_ASM_ARGS({
1488  js.objects[$0].attr("transform", "translate("+$1+","+$2+")");
1489  }, this->id, x, y);
1490  return *this;
1491  }
1492 
1497  Selection& Rotate(int degrees) {
1498  EM_ASM_ARGS({
1499  js.objects[$0].attr("transform", "rotate("+$1+")");
1500  }, this->id, degrees);
1501  return *this;
1502  }
1503 
1506  EM_ASM_ARGS({js.objects[$0].order()},
1507  this->id);
1508  return *this;
1509  }
1510 
1512  EM_ASM_ARGS({js.objects[$0].raise();}, this->id);
1513  return *this;
1514  }
1515 
1517  EM_ASM_ARGS({js.objects[$0].lower();}, this->id);
1518  return *this;
1519  }
1520 
1521 
1533  Selection& On(std::string type, std::string listener="null", bool capture=false){
1534 
1535  // Check that the listener is valid
1536  emp_assert(EM_ASM_INT({
1537  var func_string = Pointer_stringify($0);
1538  if (func_string == "null") {
1539  return true;
1540  }
1541  if (typeof window[func_string] === "function") {
1542  func_string = window[func_string];
1543  }
1544  for (name in {d3:"d3", emp:"emp"}) {
1545  if (typeof window[name][func_string] === "function") {
1546  func_string = window[name][func_string];
1547  }
1548  }
1549  return (typeof func_string === "function");
1550  }, listener.c_str()) \
1551  && "String passed to On is not s Javascript function or null", listener);
1552 
1553  int new_id = NextD3ID();
1554 
1555  EM_ASM_ARGS({
1556  var func_string = Pointer_stringify($2);
1557  if (typeof window[func_string] === "function") {
1558  func_string = window[func_string];
1559  }
1560  for (name in {d3:"d3", emp:"emp"}) {
1561  if (typeof window[name][func_string] === "function") {
1562  func_string = window[name][func_string];
1563  }
1564  }
1565 
1566  if (typeof func_string === "function") {
1567  js.objects[$0].on(Pointer_stringify($1),
1568  function(d, i){
1569  js.objects[$4] = d3.select(this);
1570  func_string(d, i, $4);},
1571  $3);
1572  } else {
1573  js.objects[$0].on(Pointer_stringify($1), null);
1574  }
1575 
1576  }, this->id, type.c_str(), listener.c_str(), capture, new_id);
1577 
1578  return (*this);
1579  }
1580 
1582 
1584  template <typename T>
1586  On(std::string type, T listener, bool capture=false){
1587 
1588  uint32_t fun_id = emp::JSWrap(listener, "", false);
1589  int new_id = NextD3ID();
1590 
1591  EM_ASM_ARGS({
1592  js.objects[$0].on(Pointer_stringify($1),
1593  function(){
1594  js.objects[$4] = d3.select(this);
1595  emp.Callback($2, d, i, $4);}, $3);
1596  }, this->id, type.c_str(), fun_id, capture, new_id);
1597 
1598  emp::JSDelete(fun_id);
1599  return (*this);
1600  }
1601 
1603 
1613  Selection& Sort(std::string comparator = "ascending"){
1614  D3_CALLBACK_METHOD_1_ARG(sort, comparator.c_str())
1615  return (*this);
1616  }
1617 
1619 
1620  template <typename T>
1622  Sort(T comparator){
1624  return (*this);
1625  }
1626 
1628 
1629 
1631 
1632  //Set the tool tip up for this selection.
1633  //This function exists in case you want to bind the tooltip to an
1634  //event other than mouseover/out
1635  void SetupToolTip(ToolTip & tip) {
1636  EM_ASM_ARGS({
1637  js.objects[$0].call(js.objects[$1]);
1638  }, this->id, tip.GetID());
1639  }
1640 
1641  //Tell tooltip to appear on mouseover and dissapear on mouseout
1642  void BindToolTipMouseover(ToolTip & tip) {
1643  EM_ASM_ARGS({
1644  js.objects[$0].on("mouseover", js.objects[$1].show)
1645  .on("mouseout", js.objects[$1].hide);
1646  }, this->id, tip.GetID());
1647  }
1648 
1650 
1652  void AddToolTip(ToolTip & tip) {
1653  SetupToolTip(tip);
1654  BindToolTipMouseover(tip);
1655  }
1656 
1657  //TODO:
1658  //
1659  //GetClassed()
1660 
1661  //Datum() //requires callbacks
1662 
1663  //Node() //Is the node a selection? Do we even need this?
1664  };
1665 
1666 
1667 
1670  Selection Select(std::string selector) {
1671  return Selection(selector);
1672  }
1673 
1676  Selection SelectAll(std::string selector) {
1677  return Selection(selector, true);
1678  }
1679 
1682  template<typename T>
1683  Selection ShapesFromData(T values, std::string shape){
1684  Selection s = Select("svg").SelectAll(shape).Data(values);
1685  s.EnterAppend(shape);
1686  return s;
1687  }
1688 
1692  template<typename T>
1693  Selection ShapesFromData(T values, std::string shape, Selection & svg){
1694  Selection s = svg.SelectAll(shape).Data(values);
1695  s.EnterAppend(shape);
1696  return s;
1697  }
1698 
1699 }
1700 #endif
Selection()
Default constructor - constructs empty selection.
Definition: selection.h:946
DERIVED Merge(DERIVED &other)
Definition: selection.h:171
int GetPropertyInt(std::string name)
Definition: selection.h:516
DERIVED SelectAll(std::string selector) const
Definition: selection.h:60
Selection Enter()
Definition: selection.h:1188
void Sort(emp::vector< T > &v, Ts...args)
A quick shortcut for sorting a vector.
Definition: vector_utils.h:94
std::string GetStyleString(std::string name) const
Get the value of this object&#39;s [name] style when it&#39;s a string.
Definition: selection.h:453
bool Empty() const
Returns true if there are no elements in this selection (or all elements are null) ...
Definition: selection.h:527
SelectionOrTransition()
Definition: selection.h:41
REAL_TYPE sfinae_decoy
Definition: meta.h:93
Definition: dataset.h:23
DERIVED & Call(std::string function)
Definition: selection.h:80
SelectionOrTransition(const SelectionOrTransition< DERIVED > &s)
Definition: selection.h:43
Transition MakeTransition(Transition &t)
Definition: selection.h:1462
Selection(const Selection &s)
Definition: selection.h:957
Selection & Sort(std::string comparator="ascending")
Definition: selection.h:1613
DERIVED & SetStyle(std::string name, std::string value, bool priority=false)
Definition: selection.h:284
#define D3_CALLBACK_METHOD_CPP_FUNCTION_1_ARG(FUNC, CPP_FUN)
Definition: utils.h:132
Definition: d3_init.h:43
DERIVED & SetText(std::string text)
Definition: selection.h:357
void Remove()
Definition: selection.h:166
std::string GetHtml()
Definition: selection.h:504
DERIVED & SetAttr(std::string name, std::string value)
Definition: selection.h:208
Create a tooltup using the d3.tip Javascript library.
Definition: d3_init.h:106
#define D3_CALLBACK_METHOD_2_ARGS(FUNC, ARG1, ARG2)
Definition: utils.h:101
int id
Definition: d3_init.h:45
Definition: selection.h:547
void StoreNewObject(int id)
Definition: utils.h:142
double GetPropertyDouble(std::string name)
Definition: selection.h:522
Selection ShapesFromData(T values, std::string shape)
Definition: selection.h:1683
Selection & On(std::string type, std::string listener="null", bool capture=false)
Definition: selection.h:1533
DERIVED & SetProperty(std::string name, std::string value)
Definition: selection.h:385
DERIVED & SetHtml(std::string value)
Definition: selection.h:392
int GetAttrInt(std::string name) const
Get the value of this object&#39;s [name] attribute when it&#39;s an int.
Definition: selection.h:439
Selection EnterAppend(std::string type)
Definition: selection.h:1147
DERIVED Filter(std::string selector) const
Definition: selection.h:122
Definition: selection.h:39
Transition MakeTransition(std::string name="")
Definition: selection.h:1452
Definition: selection.h:936
Selection & Lower()
Definition: selection.h:1516
int NextD3ID()
Definition: d3_init.h:31
std::string GetAttrString(std::string name) const
Get the value of this object&#39;s [name] attribute when it&#39;s a string.
Definition: selection.h:425
Selection & Order()
Change the order of elements in the document to match their order in this selection.
Definition: selection.h:1505
double GetAttrDouble(std::string name) const
Get the value of this object&#39;s [name] attribute when it&#39;s a double.
Definition: selection.h:446
std::string GetText() const
Get this object&#39;s text.
Definition: selection.h:481
Dataset GetData() const
Definition: selection.h:1133
void AddToolTip(ToolTip &tip)
Add the ToolTip [tip] to the current selection.
Definition: selection.h:1652
DERIVED & SetClassed(std::string classname, bool value)
Definition: selection.h:400
If we are in emscripten, make sure to include the header.
Definition: array.h:37
Selection & Raise()
Definition: selection.h:1511
Selection EnterInsert(std::string name, std::string before=NULL)
Definition: selection.h:1164
Selection(int id)
Definition: selection.h:954
#define emp_assert(...)
Definition: assert.h:199
typename internal::ip_sort< T >::result sort
Definition: IntPack.h:193
Transition(int id)
Advanced: Construct new transition pointing to the [id]th element in js.objects.
Definition: selection.h:563
Definition: axis.h:20
int GetStyleInt(std::string name) const
Get the value of this object&#39;s [name] style when it&#39;s an int.
Definition: selection.h:467
std::string GetPropertyString(std::string name)
Definition: selection.h:510
DERIVED & Each(std::string function)
Definition: selection.h:148
Selection & Move(int x, int y)
Definition: selection.h:1486
SelectionOrTransition(int id)
Definition: selection.h:42
Selection(std::string selector, bool all=false)
Definition: selection.h:969
Selection Insert(std::string name, std::string before=NULL)
Definition: selection.h:1432
#define D3_CALLBACK_METHOD_1_ARG(FUNC, ARG1)
Definition: utils.h:115
Selection & Rotate(int degrees)
Definition: selection.h:1497
DERIVED Select(std::string selector) const
Definition: selection.h:47
Selection & Interrupt(std::string name="")
Interrupt the transition with the name [name] on the current selection.
Definition: selection.h:1474
void ExitRemove()
Selection must have an exit selection (i.e. have just had data bound to it).
Definition: selection.h:1204
Selection Exit()
Definition: selection.h:1220
Selection Data(Dataset &values, std::string key="")
Definition: selection.h:1008
Selection Append(std::string name)
Append DOM element(s) of the type specified by [name] to this selection.
Definition: selection.h:1417
double GetStyleDouble(std::string name) const
Get the value of this object&#39;s [name] style when it&#39;s a double.
Definition: selection.h:474
Tools to maintain data in D3.
int GetID() const
Definition: d3_init.h:96
void pass_array_to_javascript(C values)
Definition: js_utils.h:212
int Size() const
Returns number of elements in this selection.
Definition: selection.h:534
~Selection()
Destructor.
Definition: selection.h:983
#define D3_CALLBACK_METHOD_CPP_FUNCTION_2_ARGS(FUNC, ARG1, CPP_FUN)
Definition: utils.h:120
Transition NewTransition(std::string name="") const
Definition: selection.h:571