Giter VIP home page Giter VIP logo

jsartoolkitnft's Introduction

jsartoolkitnft's People

Contributors

dependabot-preview[bot] avatar dependabot[bot] avatar github-actions[bot] avatar j-era avatar jcansdale avatar kalwalt avatar omahs avatar squareys 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

jsartoolkitnft's Issues

Issue with minification

I embedded the WebWorker inside the lib with #8 but something went wrong with the minification process, actually it can not load the webworker. Probably the issue is caused by how is parsed the Blob by terser, Further investigation is required.

Make artoolkit.api.js node.js compliant and remove references to DOM object in typescript version and javascript

There few key points to solve to make artoolkitNFT.api.js (to be precise the libs compiled with artoolkitNFT.api.js -> artoolkitNFT_wasm.js and artoolkitNFT.min.js) to use on node.js. At the moment it can not be used because there are DOM objects and self is not recognized as globalThis:

var scope;
if (typeof window !== 'undefined') {
scope = window;
} else {
scope = self;
}

self is accepted in a worker env but not under NODE.
I think that code should add a conditional with scope = global for the NODE env.
Plus we need to remove all references to canvas or document objects. We don't need them because we pass imageData. This is also applicable to the Typescript version and need a rewrite in this sense.

Q's re hosting and NFT without AR

Great work! I love the idea of o lightweight option for NFT.

I have two questions:

  1. Would it be possible to host the example, e.g., on github pages, or provide instructions to do so on node? I'm not clever enough with http-server.
  2. If not displaying AR, is threejs required at all (e.g. for camera matrix)? Is is possible to extract the quat /corners directly as in AR.js?

An important use case will be to know the location and orientation of the NFT marker (or corners). without displaying AR. I know that isn't the goal, but the jsartoolkitNFT wasm would be perfect. I'm trying to put this into a vue single file component with the source video as a prop.

delegateMethods interface should be outisde ARControllerNFT

We could put the delegateMethods interface

interface delegateMethods {
setup: {
(width: number, height: number, cameraId: number): number;
};
setupAR2: {
(id: number): void;
};
setDebugMode: (id: number, mode: boolean) => number;
getDebugMode: (id: number) => boolean;
getProcessingImage: (id: number) => number;
setLogLevel: (mode: boolean) => number;
getLogLevel: () => number;
frameMalloc: {
framepointer: number;
framesize: number;
videoLumaPointer: number;
camera: number;
transform: number;
};
instance: {
frameMalloc: {
framepointer: number;
framesize: number;
videoLumaPointer: number;
camera: number;
transform: number;
};
NFTMarkerInfo: {
error: number;
found: number;
id: number;
pose: Float64Array;
};
HEAPU8: {
buffer: Uint8Array;
};
};
NFTMarkerInfo: {
error: number;
found: number;
id: number;
pose: Float64Array;
};
loadCamera: (cameraParam: string) => Promise<number>;
setProjectionNearPlane: {
(id: number, value: number): void;
};
getProjectionNearPlane: (id: number) => number;
setProjectionFarPlane: (id: number, value: number) => void;
getProjectionFarPlane: (id: number) => number;
setThresholdMode: (id: number, mode: number) => number;
getThresholdMode: (id: number) => number;
setThreshold: (id: number, threshold: number) => number;
getThreshold: (id: number) => number;
addNFTMarkers: (
arId: number,
urls: Array<string>,
callback: (filename: any) => void,
onError2: (errorNumber: any) => void
) => [{ id: number }];
detectMarker: (id: number) => number;
detectNFTMarker: (arId: number) => void;
getNFTMarker: (id: number, markerIndex: number) => number;
getNFTData: (id: number, index: number) => object;
setImageProcMode: (id: number, mode: number) => number;
getImageProcMode: (id: number) => number;
}
outside the class so it could be also accessed by ARToolkitNFT, in detail the instance property
public instance: any;
could be declared with that interface intead the odd any declaration.

loadNFTMarkers has incorrect onSuccess signature

loadNFTMarkers has incorrect return signature

(method) ARControllerNFT.loadNFTMarkers(urlOrData: string[], onSuccess: (ids: number) => void, onError: () => void): Promise<[{
    id: number;
}]>
onSuccess: (ids: number) should be onSuccess: (ids: number[])

currently have to convert it to unknow first:

await controller?.loadNFTMarkers(markers, (ids:unknown) => {...}, ... )

How to build the lib with more than 9 NFT Markers

I'm trying to load multiple NFT markers, namely more than 9, but I'm getting the error exceed maximum pages.
I've changed the max pages count here -> https://github.com/webarkit/jsartoolkitNFT/blob/master/emscripten/ARToolKitJS.cpp#L26
based on this thread -> artoolkitx/jsartoolkit5#95 (comment).

I've rebuilt the project (based on the wiki instructions) but it doesn't seem to change anything.
Surely I'm doing something wrong or am I missing something?

Developing the ES6 version of the lib

Collecting here all the resources for converting the project to the ES6 standard. Initially i was thinking that this should be done in another step but maybe better do this as soon as possible. There are some difficulties for me to convert the code to the ES6 standard. Not much for the arNFT lib but most for JsartoolkitNFT. In particular convert the addNFTMarker https://github.com/kalwalt/jsartoolkitNFT/blob/bc2e61c413dda6b3b72e10776eff3ec1a0e574ea/js/artoolkitNFT.api.js#L963-L977 method to ES6. I need to solve this problem. This is the most important.

remove artoolkitNFT gloabl variable

Remove artoolkitNFT gloabl variable

The global artoolkitNFT variable is not necessary and we can remove it, there are other way to pass data and objects.
I will try to summarize how we can do this and which steps are required to accomplish this.

Remove artoolkitNFT from getNFTMarkerInfo()

First step is to remove the artoolkitNFT global variable defined here

EM_ASM_({
var $a = arguments;
var i = 0;
if (!artoolkitNFT["NFTMarkerInfo"]) {
artoolkitNFT["NFTMarkerInfo"] = ({
id: 0,
error: -1,
found: 0,
pose: [0,0,0,0, 0,0,0,0, 0,0,0,0]
});
}
var markerInfo = artoolkitNFT["NFTMarkerInfo"];
markerInfo["id"] = $a[i++];
markerInfo["error"] = $a[i++];
markerInfo["found"] = 1;
markerInfo["pose"][0] = $a[i++];
markerInfo["pose"][1] = $a[i++];
markerInfo["pose"][2] = $a[i++];
markerInfo["pose"][3] = $a[i++];
markerInfo["pose"][4] = $a[i++];
markerInfo["pose"][5] = $a[i++];
markerInfo["pose"][6] = $a[i++];
markerInfo["pose"][7] = $a[i++];
markerInfo["pose"][8] = $a[i++];
markerInfo["pose"][9] = $a[i++];
markerInfo["pose"][10] = $a[i++];
markerInfo["pose"][11] = $a[i++];
},
markerIndex,
err,
#if WITH_FILTERING
transFLerp[0][0],
transFLerp[0][1],
transFLerp[0][2],
transFLerp[0][3],
transFLerp[1][0],
transFLerp[1][1],
transFLerp[1][2],
transFLerp[1][3],
transFLerp[2][0],
transFLerp[2][1],
transFLerp[2][2],
transFLerp[2][3]
#else
trans[0][0],
trans[0][1],
trans[0][2],
trans[0][3],
trans[1][0],
trans[1][1],
trans[1][2],
trans[1][3],
trans[2][0],
trans[2][1],
trans[2][2],
trans[2][3]
#endif
);
we can output data thanks to emscripten::val and avoid the global variable. I'm working on this in PR #275

Remove artoolkitNFT from setup

Second step is remove it from setup() method

int setup(int width, int height, int cameraID) {
int id = gARControllerID++;
arController *arc = &(arControllers[id]);
arc->id = id;
arc->width = width;
arc->height = height;
arc->videoFrameSize = width * height * 4 * sizeof(ARUint8);
arc->videoFrame = (ARUint8*) malloc(arc->videoFrameSize);
arc->videoLuma = (ARUint8*) malloc(arc->videoFrameSize / 4);
setCamera(id, cameraID);
webarkitLOGi("Allocated videoFrameSize %d", arc->videoFrameSize);
EM_ASM_({
if (!artoolkitNFT["frameMalloc"]) {
artoolkitNFT["frameMalloc"] = ({});
}
var frameMalloc = artoolkitNFT["frameMalloc"];
frameMalloc["framepointer"] = $1;
frameMalloc["framesize"] = $2;
frameMalloc["camera"] = $3;
frameMalloc["transform"] = $4;
frameMalloc["videoLumaPointer"] = $5;
},
arc->id,
arc->videoFrame,
arc->videoFrameSize,
arc->cameraLens,
gTransform,
arc->videoLuma //$5
);
return arc->id;
}
frameMalloc is not required anymore if we pass videoFrame and videoLuma data directly where is required that is in detectMarker()
int detectMarker(int id) {
if (arControllers.find(id) == arControllers.end()) { return ARCONTROLLER_NOT_FOUND; }
arController *arc = &(arControllers[id]);
// Convert video frame to AR2VideoBufferT
AR2VideoBufferT buff = {0};
buff.buff = arc->videoFrame;
buff.fillFlag = 1;
buff.buffLuma = arc->videoLuma;
return arDetectMarker( arc->arhandle, &buff);
}

in detectNFTMarker()
kpmMatching( arc->kpmHandle, arc->videoLuma );

and lastly in getNFTMarkerInfo()
int trackResult = ar2TrackingMod(arc->ar2Handle, arc->surfaceSet[arc->detectedPage], arc->videoFrame, trans, &err);

arc->videoLuma and arc->videoFrame are pointers used to pass the grayscale data and the video data stream, but we need to pass this data in another way (not with frameMalloc).

Summarizing

  • Remove artoolkitNFT from getNFTMarkerInfo() see PR #275
  • Remove artoolkitNFT from setup() see PR #283
  • Testing

fix for typescript version of the lib

testing the new 0.9.0 version with typescript support, i see that we need to make some changes, i will write a list here:

  • initWithDimensions and initWithImage methods options should be an optional argument
  • remove from npm package not necessary files (docs, zip pkgs...)

Overlay 2D text

Can jsartoolkitNFT overlay 2D text?I want to mark his name, color and other text on the target, I wonder if it can be achieved?How?I have studied for a long time and have no solution. I hope to get your help. Thank you!

Developing the higher level of abstractions

In this issue i will start to collect some ideas. I begin to think about a simpler approach while i was coding with jsartoolkit5. The recently nftLoader make me think that I have to make this idea grow. If you want display an image onto a NFT marker this should be simple as possible. I want to summarize the concept in a few words:

  • simplicity
  • easy setup
  • lightness

For this reason the idea, from the nftLaoder, to store all config data in a single external json file is a must. The compactness of the code (only one file if possible) , and smart functions is the direction.
I will mimick the nftLoader in this sense. I will update this issue as i will focus all the ideas.

To do list:

  • ARnft base class (function)
  • set of function to init and load the class
  • WebWorker embedded as in Ar.js with a Blob object
  • embeedd artoolkitNFT_wasm.js inside the worker It will be do inside ARnft lib
  • Wasm version of the lib as single lib to simplify things
  • better video handling
  • json config file
  • event listeners
  • examples
  • src folder where to strore the non minified version of ARnft with every single function separated.
  • dst folder for the minified version
  • Moularize jsartoolkitNFT ? with ES6 support ? It will be do in another PR see branch https://github.com/kalwalt/jsartoolkitNFT/tree/feature-ES6
  • grunt or gulp as parser/bundler?

loading a gltf model failed

Hi
I want to run the example ARToolkitNFT_ES6_example.html with a gltf model ,but not lucky i was failed ,here is the code i am use

var loader = new THREE.GLTFLoader();
  loader.load( './Flamingo.glb', function ( gltf ) {
	gltf.scene.rotation.x = 0.5*Math.PI;
	gltf.scene.position.z = 40;
	gltf.scene.position.x = 80;
	gltf.scene.position.y = 80;
	gltf.scene.scale.set(80,80,80);});		
	var root3 = new THREE.Object3D();
		scene.add(root3);
		root3.matrixAutoUpdate = false;
		root3.add(gltf);

of course i have the GLTFLoader.js
So please help me

Unhandled Promise Rejection on iOS safari

I just testing the multi.html in examples and I get "Unhandled Promise Rejection: RuntimeError: Aborted(CompileError: WebAssembly.Module doesn't parse at byte 272: invalid opcode 253, in function at index 75). Build with -s ASSERTIONS=1 for more info." at artoolkitNFT_wasm.js

The error only occur in iOS devices but android device run smooth.

loadNFTMarker and loadNFTMarkers in ARControllerNFT.ts are not updated

I missed to change some part of the code done in #231.
loadNFTMarkers (ARControllerNFT.ts) should be:

(ids: number[]) => {
  this.nftMarkerCount += ids.length;
  onSuccess(ids);
},

but now is

(ids) => {
    this.nftMarkerCount += ids.length;
    onSuccess(ids);
},

loadNFTMarker should be:

 (ids: number) => {
    this.nftMarkerCount += ids.length;
    onSuccess(ids);
},
(ids: any) => {
     this.nftMarkerCount += ids.length;
     onSuccess(ids);
},

Fails to install 0.7.1 version of the npm package

@jcansdale trying to install the 0.7.1 version of the npm package fails with this error:

~/kalwalt-github/ARnft$ npm install
npm WARN deprecated [email protected]: fsevents 1 will break on node v14+ and could be using insecure binaries. Upgrade to fsevents 2.
npm ERR! code ETARGET
npm ERR! notarget No matching version found for @kalwalt/jsartoolkit-nft@^0.7.1.
npm ERR! notarget In most cases you or one of your dependencies are requesting
npm ERR! notarget a package version that doesn't exist.
npm ERR! notarget 
npm ERR! notarget It was specified as a dependency of 'ARnft'
npm ERR! notarget 

npm ERR! A complete log of this run can be found in:
npm ERR!     /home/walter/.npm/_logs/2020-09-22T18_35_58_328Z-debug.log

but if you look at https://github.com/kalwalt/jsartoolkitNFT/packages/197592 it should exist. That is strange... Can be solved this?

you can test with npm install @kalwalt/[email protected]

Three js object shaking too much.

The objects in the scene are scaled way too big (200 Units) in the example. If we didn't scale it the object is too small. How to fix this and the shaking too?

Code enhancement: add interfaces IARToolKitNFT and IARControllerNFT

I think it wolud be better tranform the delegateMethods interface

interface delegateMethods {
setup: {
(width: number, height: number, cameraId: number): number;
};
setupAR2: {
(id: number): void;
};
setDebugMode: (id: number, mode: boolean) => number;
getDebugMode: (id: number) => boolean;
getProcessingImage: (id: number) => number;
setLogLevel: (mode: boolean) => number;
getLogLevel: () => number;
frameMalloc: {
framepointer: number;
framesize: number;
videoLumaPointer: number;
camera: number;
transform: number;
};
instance: {
frameMalloc: {
framepointer: number;
framesize: number;
videoLumaPointer: number;
camera: number;
transform: number;
};
NFTMarkerInfo: {
error: number;
found: number;
id: number;
pose: Float64Array;
};
HEAPU8: {
buffer: Uint8Array;
};
};
NFTMarkerInfo: {
error: number;
found: number;
id: number;
pose: Float64Array;
};
loadCamera: (cameraParam: string) => Promise<number>;
setProjectionNearPlane: {
(id: number, value: number): void;
};
getProjectionNearPlane: (id: number) => number;
setProjectionFarPlane: (id: number, value: number) => void;
getProjectionFarPlane: (id: number) => number;
setThresholdMode: (id: number, mode: number) => number;
getThresholdMode: (id: number) => number;
setThreshold: (id: number, threshold: number) => number;
getThreshold: (id: number) => number;
addNFTMarkers: (
arId: number,
urls: Array<string>,
callback: (ids: number[]) => void,
onError2: (errorNumber: number) => void
) => Array<number>;
detectMarker: (id: number) => number;
detectNFTMarker: (arId: number) => void;
getNFTMarker: (id: number, markerIndex: number) => number;
getNFTData: (id: number, index: number) => object;
setImageProcMode: (id: number, mode: number) => number;
getImageProcMode: (id: number) => number;
}
into the IARToolKitNFT interface and provide also an IARControllerNFT interface to extends the ARControllerNFT class.

matrixCopy is not required

No effect when changing the near and far plane

Hey,
i believe that i have become aware of an error concerning changing the near and far plane after camera initialization. When i change those after initialization (e.g. arControllerNft.setProjectionFarPlane(..)) the camera matrix will not be updated:

Seems the values are only taken into account in the setup method?

setCamera(id, cameraID);

arglCameraFrustumRH(&((arc->paramLT)->param), arc->nearPlane, arc->farPlane, arc->cameraLens);

I try to increase the far plane value, because the augmentation model gets lost when the tracking image is to far away from the camera, but it's still tracked (what i can read from the logs).

Did I get something wrong?
Thanks a lot.

examples on gh-pages fails

I have enabled gh-pages for the project, but if you go to the example:

https://kalwalt.github.io/jsartoolkitNFT/examples/arNFT_example.html

the WebWorker can not import the wasm file because the wrong path:

https://kalwalt.github.io/build/artoolkitNFT_wasm.js

but it sould be instead:

https://kalwalt.github.io/jsartoolkitNFT/build/artoolkitNFT_wasm.js

i will fix this, and i will add also a index file for the examples.

A similar issue also happens for codepen, i think i need to implement the regex test as i did for AR.js:

https://github.com/AR-js-org/AR.js/blob/da5ea830211c323a41ebd55a97b99f85add1cd40/three.js/src/threex/threex-armarkercontrols-nft-end.js#L24-L34

import as module from npm howto

hi
i just found this great lib, now using the multi-marker dev branch.
I use npm to import the lib in the worker as 'importscripts()' is not working in my webpack setup.
import { ARToolkitNFT, ARControllerNFT } from '@webarkit/jsartoolkit-nft'
i got the following error: Cannot read properties of undefined (reading 'initWithDimensions')
when this line runs: ARToolkitNFT.ARControllerNFT.initWithDimensions(msg.pw, msg.ph, msg.camera_para).then(onLoad).catch(onError)
if i use new ARControllerNFT.initWithDimensions(..) it goes on, but later it is not recognising ar.loadNFTMarkers() as a function... though arobject arrives in the callback.

could be a worker-level thing i am not familiar with.

maybe you know what is the best workaround.
keep up the good work!
thanks.

Improving documentation

Docs are very important. At this point JsartoolkitNFT and ARnft needs more documentation.
wiki page is outdated, there are references to ARToolkit5 instead JsartoolkitNFT use our WebARKitLib. This need to be fixed.

Support individual descriptor filenames

Having the use case where I need to fetch the descriptor files from a file server. Unfortunately those filenames include an individual hash value. So I need to set the individual file paths for the fset, fset3 and iset descriptor files, but jsartoolkitNFT just supports the prefix url string assuming all files have the same file name excluding the extension.
What about defining those urls paths optionally by an array ? -> ["path/to/file.fset", "path/to/file.fset3", "path/to/file.iset"]
Tried this in my fork and it works with some minor changes. I will create a PR, so you cann have a look at the implementation.

Camera config .exe

Where would I find the camera_config.exe? I was wondering if you were able to save and or build this exe I read about in the ARTookKit documentation? My search has found nothing but a bunch of dead ARToolKit sites

I am running your NFT library in BabylonJS with some good results. But, the tracking is off when the camera is at an angle with the target, and I am hoping it will be better if I calibrated this project for the web camera I am using. This is for a Desktop AR project and I believe the existing camera-param.dat file is better fitted for phone camera lenses.

Missed libar debug lib

During compilation libar.bc is compiled, and that is intended for "production" but there is no a debug version. So if you run th debug version it will be missed all the demagled names and other infos. It could be a nice addition for debugging.

error with WebAssembly, failing to load example

With my Android Oppo A72 i got this error running the ES6 example:
failed to asynchronously prepare wasm: RangeError: WebAssembly.instantiate(): Out of memory: wasm memory (anonymous) @ artoolkitNFT_ES6_wasm.js:9 artoolkitNFT_ES6_wasm.js:9 RangeError: WebAssembly.instantiate(): Out of memory: wasm memory artoolkitNFT_ES6_wasm.js:9 Uncaught (in promise) RuntimeError: abort(RangeError: WebAssembly.instantiate(): Out of memory: wasm memory). Build with -s ASSERTIONS=1 for more info. at abort (https://kalwalt.github.io/kalwalt-interactivity-AR/resources/ARToolkitNFT.js:35:11612) at https://kalwalt.github.io/kalwalt-interactivity-AR/resources/ARToolkitNFT.js:35:650135 
and the example stop to load any resources.

new positioning not well

I have tried almost all versions of jsartoolkit5 and ar.js .
it seems that moving to your "new positioning" method for tracking , makes movement of overlay object a little weired .
Now , object or image or video , seems not sticking to the target and continuesly moving by camera's movement .
i think this is far from the concept of augmented reality which is must the overlay object stick on target and move exactly with target movement .
but I have tried your approach for "new filtering with worker" and it was really better than other methods .
thank you

removing ModuleLoader

ModuleLoader.ts it's not needed to import the wasm library. If we build the lib with these ES6 flags:
var ES6_FLAGS = ' -s EXPORT_ES6=1 -s USE_ES6_IMPORT_META=0 -s EXPORT_NAME="artoolkitNFT" -s MODULARIZE=1 ';
instead of:
var ES6_FLAGS = " -s EXPORT_ES6=1 -s USE_ES6_IMPORT_META=0 -s MODULARIZE=1 ";
that is if we add only -s EXPORT_NAME="artoolkitNFT" then inside ARToolkitNFT.ts we simply import the wasm lib:

import artoolkitNFT from '../build/artoolkitNFT_ES6_wasm.js'

and inside initialize:

async initialize() {
        const runtime = await artoolkitNFT();
        this.instance = runtime
        this._decorate()
        const scope = (typeof window !== 'undefined') ? window : global
        //...other code
        return this;
    }

Module '"@webarkit/jsartoolkit-nft"' has no exported member 'ARToolkitNFT'

Hey, trying first steps with this module but get this error in my Vite react-ts project:

What I did:

$ npm create vite@latest (etc.)
$ npm i @webarkit/jsartoolkit-nft
import { ARToolkitNFT, ARControllerNFT } from '@webarkit/jsartoolkit-nft'

VS Code says:

Module '"@webarkit/jsartoolkit-nft"' has no exported member 'ARToolkitNFT'
Module '"@webarkit/jsartoolkit-nft"' has no exported member 'ARControllerNFT'

But it works when I add // @ts-ignore above.

Any ideas what' the issue?

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.