Giter VIP home page Giter VIP logo

clojurefx's People

Contributors

web-flow avatar zilti avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

clojurefx's Issues

Status check (because GitHub disabled private messaging...)

This isn't an issue, but since GitHub disabled private messaging, I was mainly just wondering if anything new was planned for ClojureFX. I myself haven't been able to work on it since early December until today. Let me know what I can do to contribute.

Clojure FXML

so I'm of the camp that I want to build my UIs as I have been accustomed to do with enterprise tools i.e. a UI builder and specifically SceneBuilder.

So I'm hoping to have Clojure load the FXML and then in a clojure idiomatic manner interact with the UI components e.g. add more elements to a list, setup action handlers to actions specified in the FXML etc.

Is this possible now with ClojureFX ?

thanks

Charles

I am rather new both to github and clojure

I made some changes to your code(btw thanks for this) and I wondered what you thought about them.

First , extremely arbitrary change, I renamed fx to make(I think that the names should be more generic reflecting what they do).

I changed the construct node so that we could use a :cons key. :cons stands for constructor, I thought it might be nice to do things like this...

(make image :cons ["http://static.giantbomb.com/uploads/original/0/26/9780-itsahim.JPG" true] :height 100 :width 1000)

It can be passed a single argument or an array of args.

I would like to be able to contribute, I am a neophyte but I will be trying to use javafx a good bit...

How do you launch a clojurefx created app as a standalone app?

I experimented with creating the first JavaFX getting started tutorials using clojurefx, and it runs fine within the context of the REPL. But when I try to create a launchable application using a -main function, it runs and then immediately quits without an error. It seems that the event loop immediately stops after showing the stage. How would I go about creating a launchable clojurefx app?

ComboBox implementation + CellFactory questions

Hey! It's been a long time since I've contributed/posted here.

I'm trying to add a ComboBox control to the scene. So far I've successfully done it like so:

(def my-obs-list
  (FXCollections/observableList
    [(Rectangle. 10 10 (. Color RED  ))
     (Rectangle. 10 10 (. Color GREEN))
     (Rectangle. 10 10 (. Color BLUE ))]))
(def-fx combo-box-1 combo-box
  :items my-obs-list)
(conj-fx! rt combo-box-1)

However, there are problems with this implementation that I won't detail here. Sure enough, I went to the JavaFX API and apparently it says that _"Putting nodes into the items list is strongly not recommended. This is because the default cell factory simply inserts Node items directly into the cell, including in the ComboBox 'button' area too. Because the scenegraph only allows for Nodes to be in one place at a time, this means that when an item is selected it becomes removed from the ComboBox list, and becomes visible in the button area. ... [T]he recommended approach, rather than inserting Node instances into the items list, is to put the relevant information into the ComboBox, and then provide a custom cell factory[:]"_

 ComboBox<Color> cmb = new ComboBox<Color>();
 cmb.getItems().addAll(
     Color.RED,
     Color.GREEN,
     Color.BLUE);
 cmb.setCellFactory(new Callback<ListView<Color>, ListCell<Color>>() {
     @Override public ListCell<Color> call(ListView<Color> p) {
         return new ListCell<Color>() {
             private final Rectangle rectangle;
             { 
                 setContentDisplay(ContentDisplay.GRAPHIC_ONLY); 
                 rectangle = new Rectangle(10, 10);
             }

             @Override protected void updateItem(Color item, boolean empty) {
                 super.updateItem(item, empty);

                 if (item == null || empty) {
                     setGraphic(null);
                 } else {
                     rectangle.setFill(item);
                     setGraphic(rectangle);
                 }
            }
       };
   }
});

Now here's where it gets tricky for me. I'm not exactly sure how to implement this in Clojure. I have a vague idea that I need to start with something like:

(set-fx! combo-box-1 :cell-factory
  (proxy [Callback] []
    (call [p]
    ...

But that's where I'm not sure what to do. There's an override method within the return statement which is part of the ListCell class ("updateItem"), and I'm not sure how to go about implementing it in Clojure. Honestly, my Java experience is extremely limited to the point that, as of today, I barely knew what an interface meant or what a callback was, but I've at least managed to figure out things like those somewhat. Even so, I'll need some more help to get this ComboBox up and running. Who knew it would be so difficult (for me)?

Thanks for any help you can give!

_EDIT:_*
Maybe I don't even need to use a CellFactory and I can just use normal Clojure functions (working on JavaFX controls) instead?

Camelcase beyond 1 capital letter

I just wanted to make you aware of one thing.

This:

(bind-property! rt :gridLinesVisible (atom true))

nil

works perfectly fine. But

(bind-property! rt :grid-lines-visible (atom true))

#<IllegalArgumentException java.lang.IllegalArgumentException: No matching method found: bind for class java.lang.IllegalArgumentException>

doesn't work; I don't think it camelcases properly using the "camel" private function in clojurefx.core, so Java treats it like it's not a valid property.

That said, this works just fine (grid-pane -> GridPane):

(deffx rt grid-pane
  :hgap 10
  :vgap 10
  :padding (Insets. 25 25 25 25)) ; :alignment CENTER

Scene constructor failing in 0.0.12

I am unable to invoke the scene constructor,
also "lein test" is failing the scene test:

FAIL "Constructor detection" at (core_test.clj:25)
Expected: javafx.scene.Scene
Actual: java.lang.IllegalArgumentException

No matching field found: valueOf for class javafx.scene.input.MouseButton"

The problem is here:

(defmethod preprocess-event javafx.scene.input.MouseEvent [e]
  (-> (prep-event-map e
                     :button (-> (.getButton e) .valueOf str/lower-case keyword)

.valueOf returns enum constant from the string, while we are looking for the string from the enum, so probably .toString should be used instead.

(.show stg) - ClassCastException for javafx.stage.Stage

_---Problem---_

After doing all the necessary require and import statements, I try to set up the root, scene, and stage like this. It worked great before the latest commit. Now there are a few errors with (.show stg) like so:

(deffx rt grid-pane
  :hgap 10
  :vgap 10
  :padding (Insets. 25 25 25 25))

-> 'user/rt

(deffx scn scene
  :width 800
  :height 600
  :root rt)

-> 'user/scn

(deffx stg stage
  :title "Testing"
  :scene scn)

-> 'user/stg

(run-now (.show stg))

-> IllegalArgumentException No matching field found: show for class java.lang.ClassCastException  clojure.lang.Reflector.getInstanceField (Reflector.java:271)

(.show stg)

-> IllegalArgumentException No matching field found: show for class java.lang.ClassCastException  clojure.lang.Reflector.getInstanceField (Reflector.java:271)

_---Source of problem---_

There are two blocks of code in the latest commits that cause this problem. The first is the Constructor Tools (clojurefx.core, line 320) block (specifically the construct and construct-node macros, methods, and multimethods). The second is the fx macro (clojurefx.core, line 377). I'm not sure how to fix this.

How to create construct-node methods correctly and/or more efficiently?

Based on the issue we talked about recently - fx vs. Java's new operator - I'd like to eliminate new altogether from my code using fx. But to do that, there need to be ClojureFX constructors defined for each object, right? Or is there an easier way, just like you do programmatically for methods using method-fetcher? Could there possibly be a constructor-fetcher that programmatically gets the constructors for a given object type, which fx then uses in place of a bunch of construct-node methods?

Also, the construct-node method for LinearGradient isn't working for me. I input the following:

(fx linear-gradient
  :start-x 0.0 :start-y 1.0
  :end-x 1.0 :end-y 0.0
  :proportional true
  :cycle-method (. CycleMethod NO_CYCLE)
  :stops  [(new Stop 0    (. Color (web "#f8bd55")))
           (new Stop 0.14 (. Color (web "#c0fe56")))
           (new Stop 0.28 (. Color (web "#5dfbc1")))
           (new Stop 0.43 (. Color (web "#64c2f8")))
           (new Stop 0.57 (. Color (web "#be4af7")))
           (new Stop 0.71 (. Color (web "#ed5fc2")))
           (new Stop 0.85 (. Color (web "#ef504c")))
           (new Stop 1    (. Color (web "#f2660f")))])

And I just get the following error:

-> #<CompilerException java.lang.IllegalArgumentException: No matching ctor found for class javafx.scene.paint.LinearGradient>

I tried creating a construct-node method for Stop based on the same code base:

(defmethod construct-node javafx.scene.paint.Stop [c {:keys [offset color]}]
  (constructor-helper c [offset color]))

-> (fx stop :offset 0.0 :color (. Color (web "#f8bd55")))

But this yields the same error.

Enhancement: wrap-arg multimethod not very flexible or intelligent

In order for us to know how to wrap an argument, we need to know the property name and the class that contains it.

(defmulti wrap-arg "Autoboxing-like behaviour for arguments for ClojureFX nodes." 
    (fn [arg# clazz] [(key arg#) clazz]))

(defmethod wrap-arg :default [arg# clazz] (val arg#))

And this would be an example:

(defn list->observable [l]
   (javafx.collections.FXCollections/observableArrayList l))

(defmethod wrap-arg [:data javafx.scene.chart.PieChart] [arg# clazz]
    (list->observable (val arg#)))

So that, when we call fx to build the component:

(fx pie-chart :data [(javafx.scene.chart.PieChart$Data. "Pie1" 3)
                              (javafx.scene.chart.PieChart$Data. "Pie2" 9)])

;by the way, PieChart$Data could also be added to the pkgs to make it nicer

fx* is going to be called it in a generic way as:

...
(doseq [arg# args#] ;; Apply arguments
               (if (contains? methods# (key arg#))
                 (((key arg#) methods#) obj# (wrap-arg arg# (type obj#)))))
...

IllegalArgumentExceptions / no matching ctor for several classes

_This relates somewhat to the creating construct-node methods issue._

As in the LinearGradient IllegalArgumentException issue, this works fine:

_(See note about get-prop-fx at the end of the comment for info)_

(def kv-x
  (new KeyValue (get-prop-fx stpn scale-x) 2))

But this doesn't:

(deffx kv-x key-value
  :target (get-prop-fx stpn scale-x)
  :end-value 2.0)

But the weird thing is that I already followed the resolution to the NullPointerException issue and added the KeyValue class in core/pkgs and also created a construct-node multimethod implementation for it like so:

(defmethod construct-node javafx.animation.KeyValue [c {:keys [target end-value]}]
  (constructor-helper c [target end-value]))

But no luck. I just get this error just like the LinearGradient IllegalArgumentException issue:

java.lang.IllegalArgumentException: No matching ctor found for class javafx.animation.KeyValue

This is an issue for the KeyValue, KeyFrame, and AnimationTimer classes, and I'm sure will be an issue for other classes in the future, too, as I explore them all.

Also, just a question - if there is no construct-node multimethod implementation for a given class/object, then is the object by default instantiated without any arguments, and then the key-value pairs are progressively added in via Java set methods (setXValue, setColor, etc.)? If so, that would explain the IllegalArgumentExceptions with only certain classes/objects and not others. The ones that have IllegalArgumentExceptions are the classes/objects that must have arguments in the constructor.

_Note about get-prop-fx:
get-prop-fx is a function I created to get the property of something. (getfx circle1 :radius) -> circle1.getRadius() but (get-prop-fx circle1 :radius) -> circle1.radiusProperty()
_

(remove!) and (remove-all!) functions? (Enhancements)

I'm currently using a function add! that does (obviously) the opposite of what remove! would do:

(defn add! [parent child]
  (swap-content! parent #(conj % child)))
-> #'core/add!

(add! rt big-circle) ; an already-defined big circle appears on the scene
-> true

remove! would remove a child node from its parent node. It could work like so:

(defn remove! [parent child]
; working code here
)
->#'core/remove!

(remove! rt big-circle) ; the big circle is removed and disappears
-> true

And finally, remove-all! would remove all children nodes from their parent node.

(defn remove-all! [parent]
; working code here, maybe some looping constructs
)
->#'core/remove-all!

(remove-all! rt) ; the scene is cleared
-> true

No matching method found, "setOnaction", for javafx.stage.Button

The issue is this:

(deffx hide-button button
  :text "Hide this window")

-> 'user/hide-button

(set-listener! hide-button
  :onAction [_] (core/run-now (.hide stg)))

-> <IllegalArgumentException java.lang.IllegalArgumentException: No matching method found: setOnaction for class javafx.scene.control.Button>

I think this may have something to do with the camel function again. It should say setOnAction, not setOnaction.

Listener and Event questions

Currently I'm trying to set up an EventHandler for a ComboBox object named combo-box-1. I've done it before with a Button object (login-btn) like so:

(set-listener! login-btn
  :on-action [_] (set-fx! login-btn :text-fill (. Color FIREBRICK))

For the ComboBox, I want a more specific type of event, specifically, a DragOver event (or perhaps something else - I'm just experimenting). I'm trying to do it like so, but it's not working:

(set-listener! combo-box-1
  :on-drag-over [_] (set-fx! combo-box-1
                    :items my-obs-list-2)) 

How can I set this up? I'm not too familiar with this. Thanks!

_EDIT:_
I decided to play around with some things and discovered that the following works quite nicely, even though it's not idiomatic to ClojureFX. I did create a function for wrapping the EventHandler setup:

(defn event-handling [function]
  (proxy [EventHandler] []
    (handle [event] function)))
(defn my-event-handler-function [event]
  (if (= (get-fx event :code) (. KeyCode ENTER)) 
      (do (if (= (get-fx stage-title :fill) (. Color RED))
                 (set-fx! stage-title :fill (. Color BLACK))
                 (set-fx! stage-title :fill (. Color RED)))
                 (.consume event))))
(set-fx! combo-box-1 :on-key-pressed
  (event-handling my-event-handler-function))

Maybe this can somehow be used like so into the EventHandler wrapper for ClojureFX:

(set-fx! combo-box-1 :on-key-pressed
  (fx event-handler :handler my-event-handler-function))

KeyValue and KeyFrame error - NullPointerException

When I try to construct a KeyValue using Java interop, it works great:

_(By the way, get-prop-fx is a function I created to get the property of something. (getfx circle1 :radius) -> circle1.getRadius() but (get-prop-fx circle1 :radius) -> circle1.radiusProperty()...)_

(new KeyValue
  (get-prop-fx (eval (symbol (str "circle" 1))) :translate-x)
  (* (. Math random) 800))

-> #<KeyValue KeyValue [target=DoubleProperty [bean: Circle[centerX=0.0, centerY=0.0, radius=150.0, fill=0xffffff0d, ...

But when I try to use the deffx function, it throws a NullPointerException:

(deffx key-value1 key-value
  :target (get-prop-fx (eval (symbol (str "circle" 1))) :translate-x)
  :end-value (* (. Math random) 800))

-> CompilerException java.lang.NullPointerException

Maybe it's because I'm trying to do get-prop-fx inside of deffx, which creates problems with the JavaFX threads?

Anonymous (deffx) function instead of (new ObjectType arguments)

I find myself having to do a lot of (new ObjectType arguments) constructors:

(deffx path1 path)
(. add (getfx path1 elements)
  (new MoveTo 20 20)
  (new CubicCurveTo 380 0   380 120 200 120)
  (new CubicCurveTo 0   120 0   240 380 240))

However, I think it would be more idiomatic to ClojureFX and much easier to read to use a deffx anonymously (with no associated symbol) like so:

(deffx path1 path)
(. add (getfx path1 elements)
  (deffx move-to ; no symbol associated, just the JavaFX object type
    :x 20 :y 20)
  (deffx cubic-curve-to
    :control-x-1 380 :control-y-1 0  
    :control-x-2 380 :control-y-2 120
    :x 200 :y 120)
  (deffx cubic-curve-to
    :control-x-1 0 :control-y-1 120  
    :control-x-2 0 :control-y-2 240
    :x 380 :y 240))

(getfx) function?

In the other Clojure wrapper for JavaFX I was using, I used Java interop directly to get properties of JavaFX objects. Is there a function in ClojureFX similar to deffx that uses key-value pairs to get properties? Like so:

(getfx btn :text)

-> "This is the button's text"

(getfx scn :width)

-> 800

Alternatively - and I don't know if this is possible - perhaps one could call the object directly like so:

(btn :text)

-> "This is the button's text"

Is there anything that does this without having to call Java directly (which is the whole point of a wrapper)?

GridPane swap-content! multimethod (line 440) - "Not on FX application thread"

Before a recent commit, the GridPane method of the swap-content! multimethod didn't exist specifically. I guess (def-simple-swapper javafx.scene.layout.Pane .getChildren .setAll) might have been used in place of it, but I'm not sure. In either case, the new GridPane method of the swap-content! multimethod doesn't work correctly, even though most of the others (for instance, for Stage, Scene, etc.) do. I'm using a function I created to add a child node to a parent node, which uses swap-content! like so:

(defn add! [parent child]
  (swap-content! parent #(conj % child)))

I most commonly use it to add JavaFX objects to the root like so:

(add! rt my-text)

But for some reason, when rt is a GridPane and I don't comment out the new GridPane method of swap-content!, I get the following error:

llegalStateException Not on FX application thread; currentThread = nREPL-worker-0  com.sun.javafx.tk.Toolkit.checkFxUserThread (Toolkit.java:209)

I'm not sure why this is. Maybe the GridPane method of swap-content! isn't using run-now or run-later?

Also, just a random question. If the def-simple-swappers work with swap-content!, then why worry about the complexity of creating specific methods for each object? I've used the def-simple-swappers indirectly with all kinds of objects and I've commented out most of the new methods of swap-content!, and it all still works great. Is there something I'm missing here?

P.S. - I love the page you created here: http://zilti.github.io/clojurefx/ . Very informative!

LinearGradient IllegalArgumentException

This works fine:

(new LinearGradient
  0.0 1.0 1.0 0.0 true
  (. CycleMethod NO_CYCLE)
  (into-array Stop
    [(new Stop 0    (. Color (web "#f8bd55")))
     (new Stop 0.14 (. Color (web "#c0fe56")))
     (new Stop 0.28 (. Color (web "#5dfbc1")))
     (new Stop 0.43 (. Color (web "#64c2f8")))
     (new Stop 0.57 (. Color (web "#be4af7")))
     (new Stop 0.71 (. Color (web "#ed5fc2")))
     (new Stop 0.85 (. Color (web "#ef504c")))
     (new Stop 1    (. Color (web "#f2660f")))]))

-> #<LinearGradient linear-gradient(from 0.0% 100.0% to 100.0% 0.0%, 0xf8bd55ff 0.0%, 0xc0fe56ff 14.000000000000002%, ...

But using the deffx equivalent does not:

(deffx background linear-gradient
  :start-x 0.0
  :start-y 1.0
  :end-x   1.0
  :end-y   0.0
  :proportional true
  :cycle-method (. CycleMethod NO_CYCLE)
  :stops (into-array Stop
          [(new Stop 0    (. Color (web "#f8bd55")))
           (new Stop 0.14 (. Color (web "#c0fe56")))
           (new Stop 0.28 (. Color (web "#5dfbc1")))
           (new Stop 0.43 (. Color (web "#64c2f8")))
           (new Stop 0.57 (. Color (web "#be4af7")))
           (new Stop 0.71 (. Color (web "#ed5fc2")))
           (new Stop 0.85 (. Color (web "#ef504c")))
           (new Stop 1    (. Color (web "#f2660f")))]))

-> <CompilerException java.lang.IllegalArgumentException: No matching ctor found for class javafx.scene.paint.LinearGradient

Any solutions?

swap-content! for Group class

This is a minor addition.

I was trying to work with the Group class but realized that there was no method in swap-content for it. So I added this to the list of def-simple-swapper:

(def-simple-swapper javafx.scene.Group .getChildren .setAll)

This seems to work well so far.

Keys for deffx: Not just :listener but also :handle (Enhancement)

I've been using the :listener key in deffx for a few simple things - for instance, changing the text of a button on a given event, etc. But the :listener key only associates an EventListener to a given object, for instance, a Button. I've been working through some JavaFX exercises and one of them calls for the creation of an AnimationTimer like so:

timer = new AnimationTimer() {
            @Override
            public void handle(long n) {
                txt.setText(i.toString());
            }
};

But there is currently no idiomatic ClojureFX way to do this. It would be nice if this were possible:

(defn my-handle-func [] (bind-property! txt :text (str i)))
(deffx timer animation-timer
  :handle my-handle-func)

I've also been needing to use an EventHandler instantiated like so:

 EventHandler onFinished = new EventHandler<ActionEvent>() {
            public void handle(ActionEvent t) {
                 stack.setTranslateX(java.lang.Math.random()*200-100);
            }
};

It would be nice if this were possible in ClojureFX:

(deffx event-h event-handler
  :handle (fn [] (bind-property! stackpn :translate-x (* (. Math random) (- 200 100)))))

fx-remove! not working with GridPane swap-content*! multimethod

Note that I have standardized some of the function names for my own use (deffx -> def-fx; fx-remove! -> remove-fx!; setfx! -> set-fx!, etc.).

_fx-remove! works very well when I comment out the GridPane swap-content_! multimethod.*

;-----Root-----
(def-fx rt grid-pane
  :hgap 10
  :vgap 10
  :padding (Insets. 25 25 25 25)
  :alignment (. Pos CENTER))
;-----Scene/Window-----
(def-fx scn scene
  :width 800
  :height 600
  :root rt)
;-----Stage/Window-surround-----
(def-fx stg stage
  :title "Testing"
  :scene scn)
(do-fx (.show stg))
;-----Scene title-----
(def-fx stage-title text
  :text "Welcome to the tester."
  :font (Font. "Myriad Pro Light" 30))
(set-grid-index! stage-title 1 0)
(conj-fx! rt stage-title)

Note that the last return value is:

#<GridPane Grid hgap=10.0, vgap=10.0, alignment=CENTER>

Then, as a test, I do the following:

(remove-fx! rt stage-title)

Instead of the Text object disappearing, I get the following return value, the same one as before:

#<GridPane Grid hgap=10.0, vgap=10.0, alignment=CENTER>

remove-all-fx!, however, works very well, even with the GridPane swap-content*! multimethod.

bind-property! with multiple properties as arguments?

Just an enhancement suggestion/question.

Currently I'm doing this:

(bind-property! scn :width (atom 300))
(bind-property! scn :width (atom 600))

Would it be possible to make the function capable of taking in multiple properties as arguments to bind-property! kind of like deffx?:

(deffx scn scene
  :width 800
  :height 600
  :root rt)

It would work like this:

(bind-property! scn :width (atom 300) :height (atom 600))

A cursory look tells me that you can just change the code in bind-property! from [obj prop at] to include an ampersand & to account for more arguments, then do some sort of loop construct over the atoms.

layoutX and layoutY properties not mutable?; ClojureFX mutability generally

For some reason, I can't seem to set the layoutX or layoutY properties of a ListView object, which means I can't reposition it once I conj it to the root node. I've tried this:

(set-fx! my-list-view :layout-y 200)

The first time I do this, an exception is thrown (IllegalStateException Not on FX application thread), and the second time, nil is returned but nothing is actually changed (at least visually, because get-fx returns the updated value of layoutX or layoutY).

I've also tried:

(core/run-later (.setLayoutY my-list-view 200))

and:

(core/run-now (.setLayoutY my-list-view 200))

but no luck.

The only thing that seems to work is just using remove-fx! to remove the ListView from the root and creating a new one with an updated layout value, but that seems unnecessary.

Even so, perhaps this issue is a force for good, because perhaps it can be an impetus towards developing a more functional approach. Although it could potentially be slower, one alternative is, instead of changing the value of a given property, to instead traverse the properties of a node/object and return a new node with a different value associated with that key, like so:

(update-fx my-list-view :pref-width 220)

It would have no side effects and would instead simply return the same node/object but with a different value associated with pref-width. Then it could be conj!ed on to its parent node as necessary.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.