Giter VIP home page Giter VIP logo

plotly.kt's Introduction

JetBrains Research DOI Gradle build Kotlin JS IR supported

Plotlykt logo

Artifact details

Maven Central

Dev builds and intermediate artifacts are available via https://repo.kotlin.link maven repository.

Compatibility note

The current $version version of the library is compatible with kotlin 1.4 with JS-IR and kotlinx-serialization 1.1.0. The JVM part requires JVM 11 to run.

TL;DR

See examples. See original library samples to understand capabilities.

Description

This project is developed to allow simple access to plotly functionality from kotlin-multiplatform. The API allows to create plotly configuration and render it as a plotly chart.

The library supports three drawable plot objects:

  • Plot itself stands for a stand-alone plot frame. It requires external infrastructure to load appropriate JavaScript libraries.
  • PlotFragment is an HTML fragment possibly including several plots. The API for html is provided by kotlinx-html library.
  • PlotlyPage is a complete page, including body fragment and page headers (needed to load JavaScript part of Plotly).

The work with plotly graphs could be rendered in following modes:

HTML page export

(JVM and native) Export plot or page in a standalone html file, using CDN distribution or local JS file (JVM only). This mode does not support updates.

See staticPlot and
customPage for examples.

Ktor-based server with dynamic updates

(JVM only) A Ktor CIO server with full multi-page and update capabilities.

See simpleServer and
dynamicServer for examples.

Kotlin-JS

Plotly is a JavaScript library, yet it is convenient to have a type-safe API when using in with Kotlin-JS. The sample application is available in js-demo module. One should node that Plotly.kt for JS is not a zero-cost wrapper like TypeScript definitions, it maintains its own object structure, could generate stand-alone models and some internal optimizations.

Plotly-kt does not support LEGACY JS target. Be sure to use IR compiler

JavaFX browser

Plotly.kt could be run in a JavaFX browser. An example project is presented in fx-demo.

Kotlin jupyter kernel

Plotly.kt comes with (beta-version) support for integration with Kotlin Jupyter kernel. See details here.

The examples of the notebooks are shown in notebooks directory. Plotly.kt uses Kotlin jupyter notebook API for integration (available in kernel version 0.8.3.236 and later). In order to load the library together with automatic imports one need to simply load a library in a following way:

@file:Repository("https://repo.kotlin.link")
@file:DependsOn("space.kscience:plotlykt-jupyter:$version")
//@file:DependsOn("space.kscience:plotlykt-server:$version") // Use this one for sever integration.

The module plotly allows rendering static plots in Jupyter. Jupyter lab is currently supported. Jupyter notebook (classic) is able to render only PlotlyPage objects, so one must convert plots to pages to be able to use notebook (see demo notebook).

The module plotly-server adds server capabilities and allows to render dynamic plots in notebooks (see demo notebook). One must note that for dynamic pages, one must pass renderer parameter explicitly to plot like it is done in examples.

IMPORTANT: By default, Plotly-kt jupyter integration is configured to work with Jupyter Lab frontend, which renders all cells in the same page. Jupyter classic notebook and DataLore use cell isolation with iframes, so you will see blanks instead of plots. It could be fixed by switching into a notebook mode by running Plotly.jupyter.notebook() in a cell after plotly library is loaded.

Direct image render via Orca (experimental)

Plotly Orca application allows direct rendering of plots (not fragments or pages) to raster of vector images. Plot.export extension could be used to call it. It requires for orca to be installed in the system and available on the system path.

Kotlin-scripting (experimental)

It is possible to separate script logic into stand-alone plotly.kts script file and generate an html from the command line. See plotlykt-script module for details.

Kotlin/Native (experimental)

Plotly model now fully supports Kotlin/Native. It means that you can use it to create a proper Plotly-based HTML file. You will still need browser to view it. You can use native-demo example.

The feature I need is not implemented!

There are three ways to solve it:

  1. Contribute! It is easy! Just add a model you need.
  2. Create a model you need in your project or add an extension. Since the inner model is dynamic, you can add features on flight.
  3. You can dynamically add missing features directly into configuration like it done in unsupportedFeature example.

Build and usage

In order to use the library, one needs to use following gradle.kts configuration:

plugins {
    kotlin("jvm")
}

repositories {
    maven("https://repo.kotlin.link")
}

dependencies {
    implementation("space.kscience:plotlykt-server:$version")
}

If you do not need the server, then use plotlykt-core instead.

Naming

The library keeps original Plotly API naming wherever it is possible. There are some usability shortcuts, usually provided via kotlin extensions, included in order to simplify user interaction. For example, text and shape extensions in the top level API.

Keeping the original naming sometimes causes clashes with Kotlin code style. For example enum names are unorthodox.

Planned features

  • Table widgets
  • Serverside plot events
  • Online plot editor
  • Dynamic data
  • Mathjax and latex support

Contributions and thanks

The project was partially founded by JetBrains Research grant.

plotly.kt's People

Contributors

altavir avatar artificialpb avatar dependabot[bot] avatar esamorodova avatar joffrey-bion avatar joseph-hui avatar omnieboer avatar spc-code avatar zakhenry 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

plotly.kt's Issues

How can I stop a live server?

I've been testing this package out and so far so amazing!

I'm consuming a Flow and when it completes I would like to stop the live server as there are no further updates to send. How can I get a reference to the ApplicationServer in order to stop it?

I'm using https://github.com/mipt-npm/plotly.kt/blob/master/examples/src/main/kotlin/complexDynamicServer.kt as the starting point, but as an example instead of doing while(true) on the flow producer I'm doing repeat(100)

The server dependency does not work with java 8

The problem is that current build generates an unnecessary gradle metadata file for plotlykt-server and it enforces use of java 11 in gradle 5.4+.

It will be fixed once we move to a common build plugin for all scientifik projects.

A workaround is to use nightly build from artifactory:

import org.jetbrains.kotlin.gradle.tasks.KotlinCompile

plugins {
    kotlin("jvm") version "1.3.40"
}

repositories {
    jcenter()
    maven("https://dl.bintray.com/mipt-npm/dataforge")
    maven("https://dl.bintray.com/mipt-npm/scientifik")
    maven("https://dl.bintray.com/kotlin/ktor/")
    maven("http://npm.mipt.ru:8081/artifactory/gradle-dev")
}

dependencies {
    implementation("scientifik:plotlykt-server:0.1.2-dev-1")
}

tasks.withType<KotlinCompile> {
    kotlinOptions {
        jvmTarget = "1.8"
    }
}

Updating multiple traces `textsList` in the live server does not update for each value

Hi me again with another bug I'm afraid

0.3.0 fixes the issue raised in #51 however there remains a very similar issue, this time it is the textsList property that is not updated correctly, however I suspect the source of the issue is different as the data going through the websocket appears correct.

Repro based on the example one

import hep.dataforge.meta.invoke
import kotlinx.coroutines.GlobalScope
import kotlinx.coroutines.delay
import kotlinx.coroutines.isActive
import kotlinx.coroutines.launch
import kscience.plotly.Plotly
import kscience.plotly.models.Bar
import kscience.plotly.plot
import kscience.plotly.server.close
import kscience.plotly.server.pushUpdates
import kscience.plotly.server.serve
import kscience.plotly.server.show
import kotlin.random.Random


fun main() {
    val initialValue = (1..10).toList()

    val traces = (0..2).associate { i ->
        val name = "Series $i"

        name to Bar {
            x.strings = initialValue.map { "Column: $it" }
            y.numbers = initialValue
            textsList = initialValue.map { "Initial value of this datapoint is: ${it}"} // <-- LINE ADDED
            this.name = name
        }
    }

    val server = Plotly.serve(port = 3872) {

        //root level plots go to default page
        page { plotly ->
            plot(renderer = plotly) {
                traces(traces.values)
                layout {
                    title = "Other dynamic plot"
                    xaxis.title = "x axis name"
                    yaxis.title = "y axis name"
                }
            }
        }

        pushUpdates(100)       // start sending updates via websocket to the front-end
    }

    server.show()

    //Start pushing updates
    GlobalScope.launch {
        delay(1000)
        while (isActive) {
            repeat(10) { columnIndex ->
                repeat(3) { seriesIndex ->
                    delay(200)
                    traces["Series $seriesIndex"]?.let {bar->
                        println("Updating ${bar.name}, Column $columnIndex")
                        //TODO replace with dynamic data API
                        val yValues = bar.y.doubles
                        yValues[columnIndex] = Random.nextInt(0,100).toDouble()
                        bar.y.doubles = yValues
                        bar.textsList = yValues.map { "Updated value of this datapoint is: ${it}"} // <-- LINE ADDED
                    }
                }
            }
        }
    }

    println("Press Enter to close server")
    readLine()

    server.close()
}

See my two // <-- LINE ADDED comments for the diff.

When running this example you would expect the hover label to contain the text "Updated value of this datapoint is: ${it}" where it is the y value of that bar, however the value appears to be being set to the value of the first bar in that series always.

Add API for background images

As in this plotly behaviour.

This would be greatly helpful, as I can see no other way at the moment to display a map behind a set of coordinates. If there is such a method in PlotlyKt, please tell me and feel free to close the issue.

Thank you.

Unmaintained dependencies

I get the warning:
warning workspace-aggregator-6ad3d204-022c-4712-bd57-40e22af0df44 > plotly.kt-plotlykt-core > [email protected]: no longer maintained

not sure if it is important or not.

Self-contained HTML files

Right now some HTML file assets are loaded from the Internet.

I can implement solution for building self-contained HTML files for offline use.

(Crossposting Issue) Installing Plotlykt-Core as dependency causes project build to fail (without any new code added)

I have a Kotlin/JS for browser targeted program that builds fine how it is, until I add Plotlykt as a Gradle dependency. This causes one of my files, which contains Kotlinx Serialization, to fail to compile. I created an issue report for the Kotlinx Serialization repo first, but nothing suggests the issue is necessarily on that end.
Please see this issue for a detailed error:
Kotlin/kotlinx.serialization#1030
and this project for a reproducible error (contains everything from the Intellij project to the Build Script):
https://github.com/CaptainZidgel/Erroring_Project

I hope this finds you well, please let me know if there are any other details I can give you.

Bad deploy

  1. Version 0.1.2 is absent in bintray
  2. Version 0.1.1 in bintray don't work with Java 1.8

Make API accept named parameters instead of receiver lambdas when appropriate

Currently in many places the API looks like:

foo {
   param1 = 2
   param2 = "hello"
   ...
}

For example:

xaxis {
   name = "x axis name"
   mode = Mode.lines
}

The api would be easier to use if it was in this form:

xaxis = Axis(
   title = "sin"
   type= Type.`-`
)

There are a couple of upsides to this:

  1. By looking at the function signature of xaxis{} you don't know what kind of configuration is available. Sometimes the available variables are not even in the same file as the builder.
    When having Axis() accept the parameters directly, they are available immediately in the call site with their default values, and will be shown by IntelliJ when you type the constructor.
  2. With named parameters IntelliJ will nicely autocomplete all of the available parameters, unlike when using lambdas.
  3. Having a function body implies you can do something other than assign variables, which is not true in many cases.
  4. Calling the constructor of Foo is the natural way to create an object of type Foo, instead of some Foo.build{} function.

Places where passing parameters in receiver lambdas is still appropriate:

  • When additional function calls are nested, like plot in Plotly.Page{} (title should still be a named parameter though), or trace in plot.

@altavir If you agree I would like to submit a PR to start improving the API in this direction.

Common dependency is empty / doesn't work?

Hey, I made a new Kotlin Multi Platform project with the following build.gradle:

plugins {
    id 'org.jetbrains.kotlin.multiplatform' version '1.3.40'
}
repositories {
    mavenCentral()
    maven { url "https://dl.bintray.com/mipt-npm/scientifik/"}
}
group 'com.example'
version '0.0.1'

apply plugin: 'maven-publish'

kotlin {
    jvm()
    js {
        browser {
        }
        nodejs {
        }
    }
    // For ARM, should be changed to iosArm32 or iosArm64
    // For Linux, should be changed to e.g. linuxX64
    // For MacOS, should be changed to e.g. macosX64
    // For Windows, should be changed to e.g. mingwX64
    mingwX64("mingw")
    sourceSets {
        commonMain {
            dependencies {
                implementation kotlin('stdlib-common')
                implementation "scientifik:plotlykt-core:0.1.1"
            }
        }
        commonTest {
            dependencies {
                implementation kotlin('test-common')
                implementation kotlin('test-annotations-common')
            }
        }
        jvmMain {
            dependencies {
                implementation kotlin('stdlib-jdk8')
            }
        }
        jvmTest {
            dependencies {
                implementation kotlin('test')
                implementation kotlin('test-junit')
            }
        }
        jsMain {
            dependencies {
                implementation kotlin('stdlib-js')
            }
        }
        jsTest {
            dependencies {
                implementation kotlin('test-js')
            }
        }
        mingwMain {
        }
        mingwTest {
        }
    }
}

Although trying to import scientifik.* does not compile.
I tried again in a gradle-jvm project:

plugins {
    id 'org.jetbrains.kotlin.jvm' version '1.3.40'
}

group 'plot'
version '1.0-SNAPSHOT'

repositories {
    mavenCentral()
    maven { url "https://dl.bintray.com/mipt-npm/scientifik/"}
}

dependencies {
    implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk8"
    compile 'scientifik:plotlykt-core:0.1.1'
}

compileKotlin {
    kotlinOptions.jvmTarget = "1.8"
}
compileTestKotlin {
    kotlinOptions.jvmTarget = "1.8"
}

And still no luck.
If I change scientifik:plotlykt-core:0.1.1 to scientifik:plotlykt-core-jvm:0.1.1 it does work seems to work, but build failes.
Please explain how to use the library as a dependency.

Setup Issue

I running into some issue with the setup:

  1. Try to use in Jupyter, based on the documentation I red, just copy the plotly.json and plotyly-server.json into my ..../run_kotlin_kernel/libraries folder then kotlin jupyter kernel should load it in. However I checked that it does not show up. (by type :help in my kotlin notebook, can not see those two libraries listed). I can not see any errors

  2. Then I try to set up in my project and hope to get some clue what is going on, here are portion of my build.gradle.kts

plugins {
    java
    kotlin("jvm") version "1.4.0"
//    kotlin("plugin.serialization") version "1.4.0"
}

repositories {
    mavenCentral()
    jcenter()

    // for plotly.kt
    maven("https://dl.bintray.com/mipt-npm/dataforge")
    maven("https://dl.bintray.com/mipt-npm/kscience")
    maven("https://dl.bintray.com/mipt-npm/dev")
    maven("https://kotlin.bintray.com/kotlinx")
    maven("https://dl.bintray.com/kotlin/ktor")
}


dependencies {
    implementation(kotlin("stdlib-jdk8"))
    implementation("org.jetbrains.kotlinx:kotlinx-coroutines-core:1.3.9")
    implementation("org.nield:kotlin-statistics:1.2.1")
    implementation("org.apache.commons:commons-collections4:4.4")
    implementation( "org.jetbrains.kotlin:kotlin-reflect:1.4.0" )

    implementation( "org.jetbrains.lets-plot-kotlin:lets-plot-kotlin-api:1.0.0")

    // for json support
    implementation("com.fasterxml.jackson.module:jackson-module-kotlin:2.9.8")
//    implementation("io.jhdf:jhdf:0.5.8")
    implementation("de.mpicbg.scicomp:krangl:0.13")
//    implementation("org.jetbrains.kotlinx:kotlinx-serialization-core:1.0.0-RC") // JVM dependency

    // for plotly.kt
    implementation("scientifik:plotlykt-core:0.2.0")
    implementation("scientifik:plotlykt-server:0.2.0")


    testImplementation("org.junit.jupiter:junit-jupiter:5.6.0")

}

when I try to build, got following complain

Could not determine the dependencies of task ':jar'.
> Could not resolve all files for configuration ':runtimeClasspath'.
   > Could not find scientifik:plotlykt-core:0.2.0.
     Searched in the following locations:
       - https://repo.maven.apache.org/maven2/scientifik/plotlykt-core/0.2.0/plotlykt-core-0.2.0.pom
       - https://jcenter.bintray.com/scientifik/plotlykt-core/0.2.0/plotlykt-core-0.2.0.pom
       - https://dl.bintray.com/mipt-npm/dataforge/scientifik/plotlykt-core/0.2.0/plotlykt-core-0.2.0.pom
       - https://dl.bintray.com/mipt-npm/kscience/scientifik/plotlykt-core/0.2.0/plotlykt-core-0.2.0.pom
       - https://dl.bintray.com/mipt-npm/dev/scientifik/plotlykt-core/0.2.0/plotlykt-core-0.2.0.pom
       - https://kotlin.bintray.com/kotlinx/scientifik/plotlykt-core/0.2.0/plotlykt-core-0.2.0.pom
       - https://dl.bintray.com/kotlin/ktor/scientifik/plotlykt-core/0.2.0/plotlykt-core-0.2.0.pom
     Required by:
         project :
   > Could not find scientifik:plotlykt-server:0.2.0.
     Searched in the following locations:
       - https://repo.maven.apache.org/maven2/scientifik/plotlykt-server/0.2.0/plotlykt-server-0.2.0.pom
       - https://jcenter.bintray.com/scientifik/plotlykt-server/0.2.0/plotlykt-server-0.2.0.pom
       - https://dl.bintray.com/mipt-npm/dataforge/scientifik/plotlykt-server/0.2.0/plotlykt-server-0.2.0.pom
       - https://dl.bintray.com/mipt-npm/kscience/scientifik/plotlykt-server/0.2.0/plotlykt-server-0.2.0.pom
       - https://dl.bintray.com/mipt-npm/dev/scientifik/plotlykt-server/0.2.0/plotlykt-server-0.2.0.pom
       - https://kotlin.bintray.com/kotlinx/scientifik/plotlykt-server/0.2.0/plotlykt-server-0.2.0.pom
       - https://dl.bintray.com/kotlin/ktor/scientifik/plotlykt-server/0.2.0/plotlykt-server-0.2.0.pom
     Required by:
         project :

Possible solution:
 - Declare repository providing the artifact, see the documentation at https://docs.gradle.org/current/userguide/declaring_repositories.html

Date-time API

Include kotlinx-datetime and provide API to use it with TraceValues.

Updating multiple traces in live server is applying only to the first trace

Steps to reproduce:

Create multiple traces, and in dynamic server mode try to update the values of each trace. All values appear to be assigned to the first trace.

I've narrowed this down to Plot.collectUpdates

fun Plot.collectUpdates(plotId: String, scope: CoroutineScope, updateInterval: Long): Flow<Update> {
    return config.flowChanges(scope, updateInterval).flatMapMerge { change ->
        flow<Update> {
            change["layout"].node?.let { emit(Update.Layout(plotId, it)) }
            change.getIndexed("data").values.mapNotNull { it.node }.forEachIndexed { index, metaItem ->
                emit(Update.Trace(plotId, index, metaItem))
            }
        }
    }

The index param that is passed to Update.Trace is always 0. This appears to be because there needs to be another inner loop like metaItem.getIndexed to actually iterate through the traces.

The result of this is evident when inspecting the websocket messages, as I can see the json message field "trace" is always 0, despite having the data for each one of the traces that I am updating.

I must apologise, I'm very new to Kotlin so I'm not really sure how to raise a PR that I can test locally to verify my fix.

JDK version compatibility

When I try to use a newer version of Plotly.kt (v0.3.0) in a Kotlin Jupyter notebook, it doesn't work due to the following error when trying to plot something:

Line_9.jupyter.kts (2:5 - 12) Cannot inline bytecode built with JVM target 11 into bytecode that is being built with JVM target 1.8. Please specify proper '-jvm-target' option
Line_9.jupyter.kts (3:5 - 11) Cannot inline bytecode built with JVM target 11 into bytecode that is being built with JVM target 1.8. Please specify proper '-jvm-target' option

I guess this error is caused by the fact that the Kotlin notebook kernel is compiled against version 1.8 while newer versions of Plotly.kt use JVM version 11 as build target.

Is there any known solution to make this combination working?

No plot in runnning dynamicServer.kt

I've checked out the repo, imported it into Intellij as gradle project. When running the dynamicServer.kt, it starts up correctly, but the plot is missing:

image

Judging from the sources I'd expect some sinus/cosinus action to happen.

Plotly specific meta delegate

Create or adjust meta delegate for plotly types.

List of plotly types:

  • data_array --- An {array} of data. The value MUST be an {array}, or we ignore it. Note that typed arrays (e.g. Float32Array) are supported.
  • enumerated --- Enumerated value type. The available values are listed in values.
  • boolean --- A boolean (true/false) value.
  • number --- A number or a numeric value (e.g. a number inside a string). When applicable, values greater (less) than max (min) are coerced to the dflt.
  • integer --- An integer or an integer inside a string. When applicable, values greater (less) than max (min) are coerced to the dflt.
  • string --- A string value. Numbers are converted to strings except for attributes with strict set to true.
  • color --- A string describing color. Supported formats: - hex (e.g. '#d3d3d3') - rgb (e.g. 'rgb(255, 0, 0)') - rgba (e.g. 'rgb(255, 0, 0, 0.5)') - hsl (e.g. 'hsl(0, 100%, 50%)') - hsv (e.g. 'hsv(0, 100%, 100%)') - named colors (full list: http://www.w3.org/TR/css3-color/#svg-color)
  • colorlist --- A list of colors. Must be an {array} containing valid colors.
  • colorscale --- A Plotly colorscale either picked by a name: (any of Greys, YlGnBu, Greens, YlOrRd, Bluered, RdBu, Reds, Blues, Picnic, Rainbow, Portland, Jet, Hot, Blackbody, Earth, Electric, Viridis, Cividis ) customized as an {array} of 2-element {arrays} where the first element is the normalized color level value (starting at 0 and ending at 1), and the second item is a valid color string.
  • angle --- A number (in degree) between -180 and 180.
  • subplotid --- An id string of a subplot type (given by dflt), optionally followed by an integer >1. e.g. if dflt='geo', we can have 'geo', 'geo2', 'geo3', ...
  • flaglist --- A string representing a combination of flags (order does not matter here). Combine any of the available flags with +. (e.g. ('lines+markers')). Values in extras cannot be combined.
  • any --- Any type.
  • info_array --- An {array} of plot information.

Text annotations

Is it possible to add some text annotations to the plots? Probably similar to the following snippet:

  plt.plot(1, 8) {
        trace(trace)
        text { 
            x=0.4
            y=0.9
            text = "asdfsafdasfdasdf"
        }
        layout {
            title = plot_title
            xaxis { title = xlabel }
            yaxis { title = ylabel }
        }
    }

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.