Giter Club home page Giter Club logo

gamma's People

Contributors

gitter-badger avatar jaredly avatar jlongster avatar kovasb avatar rogerallen avatar sgrove 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  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  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

gamma's Issues

Consider naming programs

It would be nice to know what program is causing an error in the console, perhaps we can encourage/require (p/program {:name ... :fragment-shader ... :vertex-shader ...}) to give saner error messages?

Assert argument types

Just lost more time than I care to admit to: (g/swizzle :xyz quaternion) where the message was Uncaught Error: Doesn't support name: {:tag :variable, :name "aRotation", :type :vec4, :storage :attribute}.

Gamma asserting types for all of the base operations should make it much more enjoyable to use.

Inferring floats for shader output

This shader:

(def program-source
  (p/program
   {:vertex-shader   {(g/gl-position) (-> u-p-matrix
                                          (g/* u-mv-matrix)
                                          (g/* (g/vec4 a-position 1)))
                      v-texture-coord a-texture-coord
                      v-light-weighting (g/if u-use-lighting
                                          (let [transformed-normal          (g/* u-n-matrix a-vertex-normal)
                                                directional-light-weighting (g/max (g/dot transformed-normal u-lighting-direction) 0.0)]
                                            (g/* directional-light-weighting
                                                 (g/+ u-ambient-color u-directional-color)))
                                          (g/vec3 1 1 1))}})}))

is output as:

uniform mat3 uNMatrix;
uniform mat4 uMVMatrix;
uniform mat4 uPMatrix;
uniform vec3 uAmbientColor;
uniform vec3 uDirectionalColor;
uniform vec3 uLightingDirection;
attribute vec3 aVertexNormal;
attribute vec3 aVertexPosition;
attribute vec2 aTextureCoord;
uniform bool uUseLighting;
varying mediump vec2 vTextureCoord;
varying mediump vec3 vLightWeighting;
void main(void){
vec3 v90;
if(uUseLighting){
  (v90 = (max(dot((uNMatrix * aVertexNormal), uLightingDirection), 0) * (uAmbientColor + uDirectionalColor)));}
  else {
    (v90 = vec3(1, 1, 1));}
(gl_Position = ((uPMatrix * uMVMatrix) * vec4(aVertexPosition, 1)));
(vTextureCoord = aTextureCoord);
(vLightWeighting = v90);
}

And will fail because the 0.0 is output as 0, and according to this max requires a float. There also doesn't seem to be any casting functions to manually work around this for the time being.

Bad GLSL from shared term in conditional

Hi, I found this failure case. If a term appears multiple times within one branch of an if, the shared term gets emitted without an assignment to a variable.

(require '[gamma.api :as g])
(require '[gamma.program :as p])

(def ans2
  (let [g-elem (g/* 5 5)
        sh {(g/gl-position) (g/vec4 (g/if (g/== 0 0)
                                          (g/+ g-elem g-elem)
                                          0.0))}]
    (:glsl (p/shader sh {:float :highp}))))

(println ans2)
precision highp float;

void main(void){
float v387;
float v394;
if((0.0 == 0.0)){
  (5.0 * 5.0);                       <----- note missing assignment
  (v387 = (v394 + v394));}
  else {
    (v387 = 0.0);}
(gl_Position = vec4(v387));
}

I tried to track it down in the code but got lost in the compiler.

Add :attribute-count to program data

Right now I have to filter :inputs to figure out how many attributes to enable/disable, also useful for general tooling. Probably useful to have :uniform-count and :varying-count as well for parity.

Tips for readable Gamma output

It looks like it'll be a long road to get human-readable output from gamma. For example, at a glance, can you tell if these two shaders are equivalent?

    precision mediump float;

    varying vec2 vTextureCoord;
    varying vec3 vTransformedNormal;
    varying vec4 vPosition;

    uniform bool uUseColorMap;
    uniform bool uUseSpecularMap;
    uniform bool uUseLighting;

    uniform vec3 uAmbientColor;

    uniform vec3 uPointLightingLocation;
    uniform vec3 uPointLightingSpecularColor;
    uniform vec3 uPointLightingDiffuseColor;

    uniform sampler2D uColorMapSampler;
    uniform sampler2D uSpecularMapSampler;


    void main(void) {
        vec3 lightWeighting;
        if (!uUseLighting) {
            lightWeighting = vec3(1.0, 1.0, 1.0);
        } else {
            vec3 lightDirection = normalize(uPointLightingLocation - vPosition.xyz);
            vec3 normal = normalize(vTransformedNormal);

            float specularLightWeighting = 0.0;
            float shininess = 32.0;
            if (uUseSpecularMap) {
                shininess = texture2D(uSpecularMapSampler, vec2(vTextureCoord.s, vTextureCoord.t)).r * 255.0;
            }
            if (shininess < 255.0) {
                vec3 eyeDirection = normalize(-vPosition.xyz);
                vec3 reflectionDirection = reflect(-lightDirection, normal);

                specularLightWeighting = pow(max(dot(reflectionDirection, eyeDirection), 0.0), shininess);
            }

            float diffuseLightWeighting = max(dot(normal, lightDirection), 0.0);
            lightWeighting = uAmbientColor
                + uPointLightingSpecularColor * specularLightWeighting
                + uPointLightingDiffuseColor * diffuseLightWeighting;
        }

        vec4 fragmentColor;
        if (uUseColorMap) {
            fragmentColor = texture2D(uColorMapSampler, vec2(vTextureCoord.s, vTextureCoord.t));
        } else {
            fragmentColor = vec4(1.0, 1.0, 1.0, 1.0);
        }
        gl_FragColor = vec4(fragmentColor.rgb * lightWeighting, fragmentColor.a);
    }
precision mediump float;
uniform vec3 uPointLightingSpecularColor;
uniform vec3 uPointLightingLocation;
varying mediump vec3 vTransformedNormal;
varying highp vec4 vPosition;
uniform sampler2D uColorMapSampler;
uniform vec3 uAmbientColor;
varying mediump vec2 vTextureCoord;
uniform sampler2D uSpecularMapSampler;
void main(void){
vec3 v1848;
vec3 v1850;
vec3 v1849;
vec2 v1839;
(v1839 = vec2(vTextureCoord.st));
(v1848 = vPosition.xyz);
(v1849 = normalize(vTransformedNormal));
(v1850 = normalize((uPointLightingLocation - v1848)));
(gl_FragColor = vec4(
  (texture2D(uColorMapSampler, v1839).rgb * (uAmbientColor + ((uPointLightingSpecularColor * pow(
    max(dot(reflect((-1.0 * v1850), v1849), normalize((-1.0 * v1848))), 0.0),
    (texture2D(uSpecularMapSampler, v1839).r * 255.0))) + (uPointLightingSpecularColor * max(
    dot(v1849, v1850),
    0.0))))),
  1.0));
}

Ultimately the gamma-source shader isn't so bad, but as people are learning (and learning to trust) gamma, it's likely they'll want to compare some hand-written glsl with the output, to get a sense of things. Could certainly be a big issue with onboarding new developers.

There will probably be lots of ways to "pretty-print" the shaders, but here are a two I've thought of:

  1. Group the inputs into uniforms, attributes, and varying with one empty line between each group
  2. For each group, either arrange them 1.) alphabetically, or 2.) (possibly more useful) by the order they appear in the body of the shader.
  3. Being able to give locals names so they show up when pretty-printing the source of a gamma shader, with the understanding that this is only a debugging tool e.g.:
(let [st (g/local :st (g/vec2 (g/swizzle v-texture-coord :st)))
       diffuse-light-weighting (g/local :diffuseLightWeighting (.....))]
  ....)

This would probably be enough for me to quickly piece together whether the above shaders were equivalent, which would probably tell me why my current example is not working.

should error on too many arguments to vec4

I spent a while chasing a bug which turned out to be me thinking a vec4 was a vec3, and it would have tipped me off a lot sooner if (g/vec4 (g/vec4 1.0) 1.0) threw an error (currently it just silently ignores the extra argument)

indicating negation

Want a constructor fn in gamma.api to indicate negation.

Currently we'd have to do something like (g/* -1.0 x)

Whereas we'd want something like (g/neg x)

Support statements at top level, for e.g. discard

As it looks gamma only supports assignments at the top level, since the "ast" function in program.cljs maps to g/set k v over the top level dictionary. The discard statement is needed for proper transparency sorting, as described in https://www.khronos.org/opengl/wiki/Transparency_Sorting .

I resorted to putting a magic vector vec4(0.0, 1.0, 2.0, 3.0) and then doing a search/replace on the glsl string to insert a discard later, but that's not very robust to changes in the gamma compiler. Is there a better temporary hack? And is support for imperative statements expected in the future or completely out of scope?

Warn/Error on identical input variables

This one is tough, and may not be easily solvable, but I just lost a lot of time to copy/pasting some code without updating the id...

(def a-position
  (g/attribute "aVertexPosition" :vec3))

(def a-offset
  (g/attribute "aVertexPosition" :vec3))

I thought my shader (which used both a-position and a-offset) took two inputs, but obviously only took one - didn't realize it until I looked at the generated glsl and then the program inputs.

Not sure how to fix this one, but seems likely to be a source of confusion and pain down the road.

Add round() support

Not sure how you feel about this one, since it's not in the spec, but perhaps extremely common, base functions like this should be put in place to encourage higher-level library interop/reuse, rather than everyone implementing their own floor(a + 0.5) function?

How to specify "precision mediump float;" in fragment shaders?

I'm getting the error:

Uncaught Error: failed to compile fragment-shader:ERROR: 0:5: '' : No precision specified for (float)

from glsl:

uniform sampler2D uSampler;
varying mediump vec2 vTextureCoord;
varying mediump vec3 vLightWeighting;
void main(void){
vec4 v118;
(v118 = texture2D(uSampler, vec2(vTextureCoord.st)));
(gl_FragColor = vec4((v118.rgb * vLightWeighting), v118.a));
}

from gamma:

(def v-texture-coord
  (g/varying "vTextureCoord" :vec2 :mediump))

(def v-light-weighting
  (g/varying "vLightWeighting" :vec3 :mediump))

(def u-sampler
  (g/uniform "uSampler" :sampler2D))

(def program source
(p/program
{:fragment-shader (let [texture-color (g/texture2D u-sampler (g/vec2 (g/swizzle v-texture-coord :st)))
                           rgb           (g/* (g/swizzle texture-color :rgb) v-light-weighting)
                           a             (g/swizzle texture-color :a)]
                       {(g/gl-frag-color) (g/vec4 rgb a)})})

While trying to produce the equivalent of:

    precision mediump float;

    varying vec2 vTextureCoord;
    varying vec3 vLightWeighting;

    uniform sampler2D uSampler;

    void main(void) {
        vec4 textureColor = texture2D(uSampler, vec2(vTextureCoord.s, vTextureCoord.t));
        gl_FragColor = vec4(textureColor.rgb * vLightWeighting, textureColor.a);
    }

Invalid output

(def vertex-position (g/attribute "a_VertexPosition" :vec2))
(def vertex-color (g/attribute "a_VertexColor" :vec4))
(def vtx-time (g/uniform "u_Time" :vec2))
(def v-color (g/varying "vColor" :vec4 :mediump))

(def vertex-shader {v-color         vertex-color
                    (g/gl-position) (g/+
                                     (g/vec4 (g/+ (g/* (g/swizzle vertex-position :x) (g/swizzle vtx-time :y))
                                                  (g/* (g/swizzle vertex-position :y) (g/swizzle vtx-time :x)))
                                             (g/- (g/* (g/swizzle vertex-position :y) (g/swizzle vtx-time :y))
                                                  (g/* (g/swizzle vertex-position :x) (g/swizzle vtx-time :x)))
                                             (g/swizzle vertex-position :z) 0)
                                     (g/vec4 vertex-position 0 1))})

Outputs:

attribute vec4 a_VertexColor;
attribute vec2 a_VertexPosition;
uniform vec2 u_Time;
varying mediump vec4 vColor;
void main(void){
[:nil {:source-id {:tag :id, :id 4572}, :id {:tag :id, :id 4608}}][:nil {:source-id {:tag :id, :id 4573}, :id {:tag :id, :id 4609}}][:nil {:source-id {:tag :id, :id 4574}, :id {:tag :id, :id 4610}}]
vColor = a_VertexColor;
gl_Position = vec4(
  a_VertexPosition.x * u_Time.y + a_VertexPosition.y * u_Time.x,
  a_VertexPosition.y * u_Time.y - a_VertexPosition.x * u_Time.x,
  a_VertexPosition.z,
  0) + vec4(, , );
}

(notice the clojure literals)

Either removing the v-color entry produces valid output:

(def vertex-shader {(g/gl-position) (g/+
                                     (g/vec4 (g/+ (g/* (g/swizzle vertex-position :x) (g/swizzle vtx-time :y))
                                                  (g/* (g/swizzle vertex-position :y) (g/swizzle vtx-time :x)))
                                             (g/- (g/* (g/swizzle vertex-position :y) (g/swizzle vtx-time :y))
                                                  (g/* (g/swizzle vertex-position :x) (g/swizzle vtx-time :x)))
                                             (g/swizzle vertex-position :z) 0)
                                     (g/vec4 vertex-position 0 1))})

=>

attribute vec2 a_VertexPosition;
uniform vec2 u_Time;
void main(void){

gl_Position = vec4(
  a_VertexPosition.x * u_Time.y + a_VertexPosition.y * u_Time.x,
  a_VertexPosition.y * u_Time.y - a_VertexPosition.x * u_Time.x,
  a_VertexPosition.z,
  0) + vec4(a_VertexPosition, 0, 1);
}

Or changing one of the x or y (but not z or w) ops:

(def vertex-shader {v-color         vertex-color
                    (g/gl-position) (g/+
                                     (g/vec4 1
                                              ;; Could also change the g/- call below
                                             (g/- (g/* (g/swizzle vertex-position :y) (g/swizzle vtx-time :y))
                                                  (g/* (g/swizzle vertex-position :x) (g/swizzle vtx-time :x)))
                                             (g/swizzle vertex-position :z) 0)
                                     (g/vec4 vertex-position 0 1))})

=>

attribute vec4 a_VertexColor;
attribute vec2 a_VertexPosition;
uniform vec2 u_Time;
varying mediump vec4 vColor;
void main(void){

vColor = a_VertexColor;
gl_Position = vec4(
  1,
  a_VertexPosition.y * u_Time.y - a_VertexPosition.x * u_Time.x,
  a_VertexPosition.z,
  0) + vec4(a_VertexPosition, 0, 1);
}

I don't get any warnings, so it's a bit hard to track down what I'm doing wrong.

Refactor compiler

The compiler is too complicated for people to contribute / fix their bug.

A - Document how stuff works
B - Add compiler tests for key transformations
C - Refactor
D - Profit

How Stuff Works

User generates gamma ast. This is a tree.
Compiler gets tree.
First: flatten-ast . This turns tree into a graph; we call this the 'db'.
Right now all transforms operate on this graphlike 'db'. This causes complexity. ToFix.

Transformation 1: bubble-terms.
This generates information needed for detecting common subexpressions.
For each subexpression in the db, we traverse its parents up the tree. For each parent, we associate the start node to its meta info (:shared )
Specifically, we add it to a hashmap of { start-node times-seen }
Every time a node equivalent to start-node passes though a parent on the way up, we increment this count.
This principle is extended to deal with conditionals. If a node is scoped within an 'if' it will not necessarily evaluate. However we wish to propagate this information. Therefor node counts

[todo more description]

gamma.api/* doesn't work with mat4

In the spirit of every gl-lib, I'm porting http://learningwebgl.com/blog/?p=28 over to gamma. Ran into an issue with the first example vertex shader:

 attribute vec3 aVertexPosition;

  uniform mat4 uMVMatrix;
  uniform mat4 uPMatrix;

  void main(void) {
    gl_Position = uPMatrix * uMVMatrix * vec4(aVertexPosition, 1.0);
  }

Which I believe the equivalent gamma is:

(def vertex-position
  (g/attribute "aVertexPosition" :vec3))

(def p-matrix
  (g/uniform "uPMatrix" :mat4))

(def mv-matrix
  (g/uniform "uMVMatrix" :mat4))

(def vertex-shader
  {(g/gl-position) (-> (g/* u-p-matrix u-mv-matrix)
                       (g/* (g/vec4 vertex-position 1)))})

But this results in:
Uncaught Error: Arguments to * must have type :int or :float, given :mat4 and :vec4

Allow multiple arguments to +/-/*/div/max/min

Maybe there are some others, but these should definitely be the starting point. Would remove some of the threading I need to do right now, and make the code look more clojure-ish.

Uncaught Error: Invalid literal: :fail

(def vertex-position (g/attribute "aVertexPosition" :vec3))
(def u-p-matrix (g/uniform "uPMatrix" :mat4))
(def u-mv-matrix (g/uniform "uMVMatrix" :mat4))
(def vtx-time        (g/uniform "u_Time" :vec2))

(def vertex-shader
  {(g/gl-position) (-> (g/* u-p-matrix u-mv-matrix)
                       (g/* (g/vec4 vertex-position 1))
                       (g/+ (g/vec4 (g/sin (g/swizzle vtx-time :x)) 0 0 0)))})

Errors somewhere in gamma.ast:
Uncaught Error: Invalid literal: :fail

Trying to produce the output:

uniform mat4 uPMatrix;
uniform mat4 uMVMatrix;
attribute vec3 aVertexPosition;
uniform vec2 u_Time;
void main(void){

(gl_Position = (((uPMatrix * uMVMatrix) * vec4(aVertexPosition, 1)) + vec4(
  (sin u_Time.x),
  0,
  0,
  0)));
}

basic operations on return of aget rejected

(require '[gamma.api :as g])
(g/+ 1 (g/aget (g/vec3 2) 0))

throws:

gamma-tools.core=> System.Exception: Arguments to + must have type :int or :float
  at gamma/api$_PLUS___172.invoke (System.Object , System.Object ) [0x000a1] in api.clj:210 
  at gamma_tools/core$eval__3333__3338.invoke () [0x00000] in <filename unknown>:0 
  at clojure.lang.Compiler.eval (System.Object form) [0x00000] in <filename unknown>:0 
  at clojure.lang.Compiler.eval (System.Object form) [0x00000] in <filename unknown>:0 
  at clojure/core$eval__57691.invoke (System.Object ) [0x00000] in core.clj:3076 
  at arcadia/repl$repl_eval_print$fn__28359__28363.invoke () [0x001e3] in repl.clj:59 
  at arcadia/repl$repl_eval_print__28366.invoke (System.Object , System.Object ) [0x0041e] in repl.clj:57 
  at arcadia/repl$repl_eval_string__28382.invoke (System.Object , System.Object ) [0x0002f] in repl.clj:75 
  at arcadia/repl$repl_eval_string__28382.invoke (System.Object ) [0x00000] in repl.clj:72 
  at arcadia/repl$eval_queue$fn__28390__28394.invoke () [0x00000] in repl.clj:84 

and

(g/float (g/aget (g/vec3 1) 0))

throws:

gamma-tools.core=> System.Exception: Wrong argument types for term float: 
  at gamma/api$build_standard_function_term__284.invoke (System.Object , System.Object , System.Object ) [0x00110] in api.clj:329 
  at gamma/api$float__338.doInvoke (System.Object ) [0x00000] in api.clj:331 
  at clojure.lang.RestFn.invoke (System.Object arg1) [0x00000] in <filename unknown>:0 
  at gamma_tools/core$eval__3347__3352.invoke () [0x00000] in <filename unknown>:0 
  at clojure.lang.Compiler.eval (System.Object form) [0x00000] in <filename unknown>:0 
  at clojure.lang.Compiler.eval (System.Object form) [0x00000] in <filename unknown>:0 
  at clojure/core$eval__57691.invoke (System.Object ) [0x00000] in core.clj:3076 
  at arcadia/repl$repl_eval_print$fn__28359__28363.invoke () [0x001e3] in repl.clj:59 
  at arcadia/repl$repl_eval_print__28366.invoke (System.Object , System.Object ) [0x0041e] in repl.clj:57 
  at arcadia/repl$repl_eval_string__28382.invoke (System.Object , System.Object ) [0x0002f] in repl.clj:75 
  at arcadia/repl$repl_eval_string__28382.invoke (System.Object ) [0x00000] in repl.clj:72 
  at arcadia/repl$eval_queue$fn__28390__28394.invoke () [0x00000] in repl.clj:84 

selector doesn't work

(require '[gamma.api :as g])

Implementation in gamma.emit.operator implies this should work:

(g/selector (g/vec2 1) "x")

but instead throws:

gamma-tools.core=> System.Exception: Invalid literal: "x"
  at gamma/ast$literal__186.invoke (System.Object ) [0x0014e] in ast.clj:33 
  at gamma/ast$term$fn__193__197.invoke (System.Object ) [0x0003e] in ast.clj:44 
  at clojure/core$map$fn__57190__57194.invoke () [0x00199] in core.clj:2614 
  at clojure.lang.LazySeq.sval () [0x00000] in <filename unknown>:0 
  at (wrapper synchronized) clojure.lang.LazySeq:sval ()
  at clojure.lang.LazySeq.seq () [0x00000] in <filename unknown>:0 
  at (wrapper synchronized) clojure.lang.LazySeq:seq ()
  at clojure.lang.Cons.next () [0x00000] in <filename unknown>:0 
  at clojure.lang.RT.next (System.Object x) [0x00000] in <filename unknown>:0 
  at clojure/core$next__55177.invoke (System.Object ) [0x00000] in core.clj:64 
  at clojure/core$nthnext__57657.invoke (System.Object , System.Object ) [0x00085] in core.clj:3033 
  at clojure/core$print_sequential__61068.invoke (System.Object , System.Object , System.Object , System.Object , System.Object , System.Object ) [0x00257] in core_print.clj:48 
  at clojure/core$fn__61368__61372.invoke (System.Object , System.Object ) [0x00017] in core_print.clj:191 
  at clojure.lang.MultiFn.invoke (System.Object arg1, System.Object arg2) [0x00000] in <filename unknown>:0 
  at clojure/core$pr_on__58136.invoke (System.Object , System.Object ) [0x00049] in core.clj:3543 
  at clojure/core$print_map$fn__61436__61440.invoke (System.Object , System.Object ) [0x0004e] in core_print.clj:248 
  at clojure/core$print_sequential__61068.invoke (System.Object , System.Object , System.Object , System.Object , System.Object , System.Object ) [0x002fd] in core_print.clj:53 
  at clojure/core$print_map__61443.invoke (System.Object , System.Object , System.Object ) [0x00024] in core_print.clj:251 
  at clojure/core$fn__61446__61450.invoke (System.Object , System.Object ) [0x00017] in core_print.clj:255 
  at clojure.lang.MultiFn.invoke (System.Object arg1, System.Object arg2) [0x00000] in <filename unknown>:0 
  at clojure/core$pr_on__58136.invoke (System.Object , System.Object ) [0x00049] in core.clj:3543 
  at clojure/core$pr__58144.invoke (System.Object ) [0x00000] in core.clj:3555 
  at clojure.lang.AFn.ApplyToHelper (IFn fn, ISeq argList) [0x00000] in <filename unknown>:0 
  at clojure.lang.RestFn.applyTo (ISeq arglist) [0x00000] in <filename unknown>:0 
  at clojure/core$apply__55543.invoke (System.Object , System.Object ) [0x00006] in core.clj:626 
  at clojure/core$prn__58162.doInvoke (System.Object ) [0x00000] in core.clj:3588 
  at clojure.lang.RestFn.invoke (System.Object arg1) [0x00000] in <filename unknown>:0 
  at arcadia/repl$repl_eval_print$fn__28359__28363.invoke () [0x0020d] in repl.clj:63 
  at arcadia/repl$repl_eval_print__28366.invoke (System.Object , System.Object ) [0x0041e] in repl.clj:57 
  at arcadia/repl$repl_eval_string__28382.invoke (System.Object , System.Object ) [0x0002f] in repl.clj:75 
  at arcadia/repl$repl_eval_string__28382.invoke (System.Object ) [0x00000] in repl.clj:72 
  at arcadia/repl$eval_queue$fn__28390__28394.invoke () [0x00000] in repl.clj:84 

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.