4.3.3 • Published 15 days ago

avvir v4.3.3

Weekly downloads
-
License
Apache-2.0
Repository
github
Last release
15 days ago

Avvir

Getting Started

Install the library by running:

yarn install avvir

Definitions

  • Project - Groups together all of the information related to a construction project such as the name and location. Also contains the building model indirectly through Areas.
  • Area - Projects are divided into areas to keep the complexity of the model manageable. They have been called "floors" in the past because they would usually map to a building floor but they have been renamed because that is not always true.
  • Capture Dataset - In order to perform analysis on the BIM, data must be captured from reality and this is where it is stored. Previously called "Scan Datasets" because the data was in the form of a laser scan but it has been renamed to match the fact that other types of data such as photos can be captured.

Getting A Project ID

Get your project id:

  1. Navigate to https://portal.avvir.io/
  2. Log in with your credentials
  3. Choose project you'd like to work with
  4. Take project id from the URL

Sample client code (node)

As a basic example this code will retrieve all the areas associated with a project along with their capture datasets and log the results to the console. This example as well as others can be found in the samples directory.

const AvvirApi = require("avvir");
// Make sure to replace the credentials with your actual username and password
const username = "you@example.com";
const password = "yourPassw0rd123";

// Replace this with the project ID you retrieved from the "Getting a project ID" step
const projectId = "your-project-id";

async function printAreasAndCaptureDatasets() {
  const user = await AvvirApi.api.auth.login(username, password);
  const areas = await AvvirApi.api.floors.listFloorsForProject(projectId, user);

  for (const area of areas) {
    console.log(`Area: ${area.floorNumber}`);
    console.log(area);

    console.log(`\nCapture Datasets for area ${area.floorNumber}: [`)
    const captureDatasets = await AvvirApi.api.scanDatasets.listScanDatasetsForFloor({
      projectId,
      floorId: area.firebaseId
    }, user);
    for (const dataset of captureDatasets) {
      console.log(dataset);
    }
    console.log("]");
  }
}

printAreasAndCaptureDatasets();

Using in the Browser

If you are using avvir in the browser, either use the cdn source in a script tag

<script src="https://unpkg.com/avvir/browser.js"></script>

or use ES imports with the source module.js

<script type="module">
    import AvvirApi from "https://unpkg.com/avvir/module";
</script>

Uploading a Project File

When you have a scan file that you wish to upload into your project, you can use the api to pull a file from an external data source, and apply it to your project. All uploaded project files can be found at:

https://acceptance-portal.avvir.io/projects/{projectId}/uploads

Where the projectId should be replaced with the projectID acquired through the steps above.

const uploadProjectFile = async () => {
  const user = await AvvirApi.api.auth.login(username, password);
  //create the payload for creating a project file
  const apiCloudFile = new ApiCloudFile({
    url: 'https://some-external-host.com/scan-file.las',
    purposeType: OTHER
  });

  //This will make the external file reference available to you in the avvir portal in on the files page
  let fileRef = await AvvirApi.files.createProjectFile({ projectId }, apiCloudFile, user);
  console.log(fileRef)
  let pipeline = new ApiPipeline({
    name: Pipelines.INGEST_PROJECT_FILE,
    firebaseProjectId: projectId,
    options: {
      url: apiCloudFile.url,
      fileType: 'las'
    }
  });

  //This will trigger the avvir portal to dowload the created file reference into your project
  let pipelineResponse = await AvvirApi.pipelines.triggerPipeline(pipeline, user);
  //you must poll the pipeline in to check on its status while downloading your file.
  let pipelineStatus = await AvvirApi.other.checkPipelineStatus({ projectId }, pipelineResponse.id, user);
  while (pipeline.status == 'RUNNING') {
    pipelineStatus = await AvvirApi.other.checkPipelineStatus({ projectId }, pipelineResponse.id, user);
  }
  if (pipeline.status == 'COMPLETE') {
    //once completed, you can get the file by listing the project files, and grabbing the latest.
    let files = await AvvirApi.files.listProjectFiles({ projectId }, user);
    const newFile = files.pop();
    //you can also navigate to /uploads in the portal to view the file in the UI
    return newFile
  }
}
uploadProjectFile();

Uploading a BIM file to an area

Using the files api, you have the ability to upload a bim to an area to be viewable in the 3d Viewer. The bim can be either an .nwd file or an .ifc file.

To begin uploading a bim to an area, first get your project id and area id from the area you would like to upload the bim to. You will need to ingest the file first before saving it to the floor.

const AvvirApi = require("avvir");
const {Pipelines, ApiFloorPurposeType, ApiCloudFile, ApiPipeline} = AvvirApi;

//The instructions above details how to get the following variables
let username = "<You-User-Login>";
let password = "<Your-Password>";
let projectId = "<Your-Project-ID>";

//use the instructions above to get area id
const areaId = '<Your-Area-Id>';
//replace this with an actual bim file. 
const fileUrl = 'https://some-external-host.com/bim-file.nwd';

const pollIterations = 100;
const pollTimeout = 1000; //milliseconds
//helper function to poll the pipeline
const checkPipeline = (pipelineResponse, user, index = 0) => {
  console.log("Checking Pipeline:", index + " of " + pollIterations + " iterations");
  return new Promise((resolve, reject) => {
    AvvirApi.api.other.checkPipelineStatus({projectId}, pipelineResponse.id, user)
      .then((response) => {
        // console.log(index, response);
        if (index > pollIterations) {
          reject("Too Many Calls: Check endpoint to make sure the implementation isn't flawed.")
        } else if (response.status !== 'COMPLETED') {
          setTimeout( () => resolve(checkPipeline(response, user,  ++index)), pollTimeout);
        } else {
          resolve(response);
        }
      })
  })
}

const uploadBim = async () => {
  const user = await AvvirApi.api.auth.login(username, password);
  let floorId = areaId;

  const apiCloudFile = new ApiCloudFile({
    url: fileUrl,
    purposeType: ApiFloorPurposeType.BIM_NWD //Can also be BIM_IFC if your uploading an ifc
  });

  //Creates the project file and associates it the project
  //can view by navigating to the files page in the portal
  const projectFile = await AvvirApi.api.files.createProjectFile({projectId}, apiCloudFile, user);
  let pipeline = new ApiPipeline({
    name: Pipelines.INGEST_PROJECT_FILE,
    firebaseProjectId: projectId,
    options: {
      url: apiCloudFile.url,
      fileType: 'nwd'
    }
  });
  let pipelineResponse = await AvvirApi.api.pipelines.triggerPipeline(pipeline, user);
  console.log("Check Ingest Project File Pipeline: ", pipelineResponse.externalUrl);
  await checkPipeline(pipelineResponse, user);

  //get the bim file from the project files list
  const allProjectfiles = await AvvirApi.api.files.listProjectFiles({projectId}, user);
  const bimFile = allProjectfiles[allProjectfiles.length - 1];
  const newFile = new ApiCloudFile({
    url: bimFile.url,
    purposeType: ApiFloorPurposeType.BIM_NWD
  });

  //save the project file as a floor file
  let file = await AvvirApi.api.files.saveFloorFile({ projectId, floorId }, newFile, user);
  //check the pipeline to check on your the file upload status of the bim.
  console.log(file, "\n");

  pipeline = new ApiPipeline({
    name: Pipelines.CREATE_AND_PROCESS_SVF,
    firebaseProjectId: projectId,
    firebaseFloorId: floorId,
    options: {
      matchAndUpdateElements: false
    }
  });

  pipelineResponse = await AvvirApi.api.pipelines.triggerPipeline(pipeline, user);
  console.log("Check Bim Processing Pipeline: ", pipelineResponse.externalUrl);
  await checkPipeline(pipelineResponse, user);

  //check the floor for updated bim and check the viewer to make sure it is "viewable"
}

//call the method
uploadBim();

Creating a Capture Dataset

If you are an authenticated user, you have the ability to create a scan dataset via the API.

const AvvirApi = require("avvir");
//The instructions above details how to get the following variables
let username = "<You-User-Login>";
let password = "<Your-Password>";
let projectId = "<Your-Project-ID>";
//use the instructions above to get area id
const areaId = '<Your-Area-Id>';

const createScanDataset = async () => {
  const user = await AvvirApi.api.auth.login(username, password);
  const scanDataset = await AvvirApi.api.scanDatasets.createScanDataset({ projectId, floorId: areaId }, user);
  return scanDataset
}

createScanDataset().then(console.log)

Associating a Scan File to a Project Area

Once a scan file has be ingested by the portal, you can now associate a scan to an area's scan dataset. By doing this, you gain the ability to run analysis on the scan to determine deviations and progress for the given area.

You must first acquire an area id from the portal by navigating to a floor in the portal and copying the id from the url:

https://portal.avvir.io/admin/organizations/{organizationId}/projects/{projectId}/floors/{areaId}

Or can create a floor programatically:

const createFloor = async () => {
  return await AvvirApi.api.floors.createFloor(projectId, "<Any Area Name>", user);
}
let area = await createFloor()
let areaId = area.firebaseId;

Then, assuming you've already uploaded a scan file to your project, apply the following steps to associate the scan to an area.

If you haven't uploaded a file using the api, you can subsequently upload a file through the portal by navigating to the "files" section of the portal, and dragging your file into the dropzone located at the top of that page. Once file has completed uploading, bring the file into view, and copy the file address by right clicking the cloud icon, and selecting "Copy link address".

const AvvirApi = require("avvir");
//The instructions above details how to get the following variables
let username = "<You-User-Login>";
let password = "<Your-Password>";
let projectId = "<Your-Project-ID>";

//use the instructions above to get area id
const areaId = '<Your-Area-Id>';
//you can get this url from the portal or by calling the uploadProjectFile() above, and getting the url property. 
const fileUrl = 'https://some-file-source.com/scan.las';

const associateScan = async (areaId, fileUrl) => {
  const user = await AvvirApi.api.auth.login(username, password);
  let floorId = areaId;
  //this creates a new scan on the area you've selected.
  const scanDataset = await AvvirApi.api.scanDatasets.createScanDataset({ projectId, floorId }, user);
  const cloudFile = new AvvirApi.ApiCloudFile({
    url: fileUrl,
    purposeType: 'PREPROCESSED_SCAN'
  });
  let scanDatasetId = scanDataset.firebaseId;
  await AvvirApi.api.files.saveScanDatasetFile({ projectId, floorId, scanDatasetId }, cloudFile, user);
  console.log(`Go to Scan #${scanDataset.scanNumber} in the scan datasets dropdown of the portal under your selected floor`);
  console.log(`Completed associating scan dataset...`)
}

associateScan(areaId, fileUrl)

Process Deviations and Progress analysis on Scan Dataset

Given you have a scan associated to a scan dataset on your area, to get analysis on the scandataset, you must trigger the process-steps pipeline, with the type of analysis you wish to compute.

let Avvir = require("avvir");
let AvvirApi = Avvir.api;
let username = "<You-User-Login>";
let password = "<Your-Password>";
let projectId = "<Your-Project-ID>";
let captureDatasetId = "<Your-Capture-Dataset-ID>";
let areaId = "<Your-Area-ID>";

const processSteps = async (type) => {
  let steps = [];
  if (type == "deviation") {
    steps.push("compute-deviations")
  } else if (type == "progress") {
    steps.push("compute-progress");
  } else {
    steps = ["compute-deviations", "compute-progress"]
  }

  let user = await AvvirApi.auth.login(username, password)
  let pipeline = new Avvir.ApiPipeline({
    name: "pipeline-steps",
    firebaseProjectId: projectId,
    firebaseFloorId: areaId,
    firebaseScanDatasetId: captureDatasetId,
    options: {
      steps
    }
  })
  let response = await AvvirApi.pipelines.triggerPipeline(pipeline, user);
  console.log(response.status);
}

//will process both deviations and progress
processSteps()

After the pipeline has finished processing your scans, you should be able to navigate to the 3d Viewer and see the deviations displayed in the model. You can also view progress data by exporting the Progress Pdf from the Exports & Reports dropdown on the upper right of the viewer.

Process scan for 3d Viewer

Given you have associated a scan to a scan dataset, using the same scan dataset id and credentials as above, if you want to view your scan in the viewer, you will need to run another pipeline.

const Avvir = require("avvir");
const { ApiPipeline } = require("avvir");

let username = "<You-User-Login>";
let password = "<Your-Password>";
let projectId = "<Your-Project-ID>";
let captureDatasetId = "<Your-Capture-Dataset>";
let areaId = "<Your-Area-ID>";

const processScan = async () => {
  const user = await Avvir.api.auth.login(username, password);
  let pipeline = new ApiPipeline({
    name: "downsample-scan",
    firebaseProjectId: projectId,
    firebaseFloorId: areaId,
    firebaseScanDatasetId: captureDatasetId
  });

  return Avvir.api.pipelines.triggerPipeline(pipeline, user).then((response) => {
    console.log(`Pipeline Status: ${response.status}`);
    // expect(response.status).to.be.eq(RunningProcessStatus.RUNNING);
  });
}

processScan();

After the pipeline has completed processing your scan, you should be able to navigate to the 3d viewer in the portal, and view your scan by changing viewer's mode to Inspection Mode in the left panel, and toggling View Point Cloud.

Getting Project Deviation Tsv

There exists a tab separated file that users can export from the portal which provides deviation metrics for the entire project. To download this file, use getProjectDeviationsReportTsv and it will return a plaintext response with the contents of the file.

const Avvir = require("avvir");
const email = "youremail@email.com"
const password = "yourpassword";
const projectId = "<your-project-id>";

const getTsv = async () => {
  let user = await Avvir.api.auth.login(email, password);
  let tsv = await Avvir.api.projects.getProjectDeviationsReportTsv({projectId}, user);
  return tsv;
}

getTsv().then(console.log);

Saving and Converting E57 Files to project

Avvir portal gateway utilizes scans to detect clashes and deviances in the Bim. The portal only ingests scans that are in las format utilizing saveScanDatasetFile().

If you have a scan that is in e57 format, there is a different method which will convert your e57 file before ingesting it into the portal.

const Avvir = require("avvir");
const email = "youremail@email.com"
const password = "yourpassword";
const projectId = "<your-project-id>";
const url = "some-url.com/external-e57-point-cloud.e57"

const saveAndConvertE57 = async () => {
  let user = await Avvir.api.auth.login(email, password);
  let apiFile = new Avvir.ApiCloudFile({
    url,
    purposeType: 'OTHER'
  });
  return await Avvir.api.files.saveAndConvertE57ProjectFile({projectId}, apiFile, user);
}

saveAndConvertE57().then(console.log)

Creating New Area

If you are an authenticated user on a project, the API allows you to create an area.

const Avvir = require("avvir");
const email = "youremail@email.com"
const password = "yourpassword";
const projectId = "<your-project-id>";
const floorNumber = "some-floor-number";

const createArea = async () => {
  let user = await Avvir.api.auth.login(email, password);
  return await Avvir.api.floors.createFloor(projectId, floorNumber, user);
}

createArea().then(console.log)

Updating Elements in BIM

Once you have a bim and or scan associated to a floor on your project, you may have the need to update elements which may have been mislabeled, or some other metadata exists which needs correcting. Avvir provides a method updatePlannedElements which allows you to update the elements through the api.

The following meta fields may be updated through this method:

  name: string;
  ifcType?: string;
  uniformat?: UniformatId;
  itemId?: string;
  discipline?: string;
  primaryUnitOfMeasurement?: string;
  primaryMeasurement?: number;

Below is a demonstration of how an authenticated user can update a planned building element utilizing this method.

const Avvir = require("avvir");
const { ApiPlannedElement } = Avvir;

const email = "youremail@email.com"
const password = "yourpassword";
const projectId = "<your-project-id>";
const floorId = "<some-floor-id-with-processed-bim>";
const globalId = "Obj9999"; //reference your bim or project export for the global id for the element you wish to update. 

//make an array of the planned elements you wish to change
const elements = [
  new ApiPlannedElement({
    globalId,
    name: 'Wall',
    ifcType: 'some-ifc-type',
    uniformat: "A1010.10"
  })
]

const updatePlannedElement = async (user) => {
  //supply the elements to the update method
  await Avvir.api.elements.updatePlannedBuildingElements(
    {projectId, floorNumber},
    elements,
    user);
}

const getPlannedElements = async (user) => {
  //get all planned elements from floor.
  return await Avvir.api.elements.getPlannedBuildingElements(
    {projectId, floorNumber},
    user);
}

const updateAndGetPlannedElements = async () => {
  //login
  let user = await Avvir.api.auth.login(email, password);
  //update and get
  await updatePlannedElement(user);
  return await getPlannedElements(user)
}
updateAndGetPlannedElements().then((elements) =>{
  let updatedElement = elements.filter(element => element.globalId == globalId);
  console.log(updatedElement) // the element you updated
}) //

Realigning of a Photo Location (Experimental)

This api is still labelled experimental, and should be used with caution if integrating into existing project.

PhotoLocations are points in a scan where a 360 photo was taken of the area. These photo locations are placed within the portal at designated positions within the bim, and oriented to align with the view of the bim.

Given a PhotoLocation is out of alignment with the bim either rotationally or positionally, we've provided a method on our photo api called updatePhotoLocationPositionAndOrientation which allows for a location to have its coordinate data updated.

  • The center of the image is aligned to a vector pointing horizontally to the north (positive Y), with positive Z being up.
  • The X axis controls pitch, the Y axis controls roll, and the Z axis controls yaw.
  • Positive pitch moves the center of the image up.
  • Positive roll rotates clockwise around the center of the image.
  • Positive yaw moves the center of the image to the left.
const Avvir = require("avvir");
const THREE = require("three"); //helper for working with angles

let username = "<You-User-Login>";
let password = "<Your-Password>";
let projectId = "<Your-Project-ID>";
let photoAreaId = 1234; //some photo area id (can be sourced from url in portal when location is selected)
let photoLocationId = 1234; //some photo location id (can be sourced from url in portal when location is selected)

Avvir.api.auth.login(username, password).then(async (user)=>{
  // This angle will rotate the center of the image to the left 90 degrees
  let euler = new THREE.Euler(0,0,Math.PI/2);

  // This angle would pitch the image up 45 degrees
  // let euler = new THREE.Euler(Math.PI/4,0,0);

  // This angle would rotate the image clockwise about its center 90 degrees
  // let euler = new THREE.Euler(0,Math.PI/2,0);

  let quaternion = new THREE.Quaternion().setFromEuler(euler);
  let orientation = { 
    a: quaternion.x, 
    b: quaternion.y, 
    c: quaternion.z, 
    d: quaternion.w
  };

  let locationData = new ApiPhotoLocation3d({
    position: {x: 2, y: 1, z: 1},
    orientation
  });
  
  let photolocation = await Avvir.api.photos
    .updatePhotoLocationPositionAndOrientation({projectId, photoAreaId, photoLocationId}, locationData, user);
  console.log("check the location in your project to see it realigned: \n", `${Avvir.Http.baseUrl()}/projects/${projectId}?photoLocationId=${photoLocationId}`);
  console.log(photoLocation)
})

Contributing

Read our contributing guide to learn about our development process, how to propose bugfixes and improvements, and how to build and test your changes to avvir.

Api Reference


Floor

listFloorsForProject(projectId, user)

Get a list of all the floors for a given project

Params

  1. projectId String - Id for your project
  2. user User - Authenticated User object

Returns

  1. Promise<ApiFloor[]> - list of floors from that specific project

Example

AvvirApi.api.floors.listFloorsForProject(projectId, user).then((response) => {
  console.log(response);
});

createFloor(projectId, areaNumber, user)

Creates a floor for a given project

Params

  1. projectId String - Id for your project
  2. areaNumber String - The label which references the area (usually a number)
  3. user User - Authenticated User object

Returns

  1. Promise<ApiFloor[]> - An api object which represents the meta data of a floor.

Example

AvvirApi.api.floors.createFloor(projectId, "<Any Area Name>", user).then((floor) => {
    console.log(floor)
})

getFloor(associationIds, user)

gets a floor by a given projectId and floorId

Params

  1. associationIds AssociationIds - Wrapper object for housing projectId and floorId
  2. user User - Authenticated User object

Returns

  1. Promise<ApiFloor[]> - list of floors that match param requirements *see ApiFloor for type info

Example

const associationIds = { projectId: '-MiR1yIeEPw0l8-gxr01', floorId: '-MiRiAGSt7-S1kt_xBRY' };
AvvirApi.api.floors.getFloor(associationIds, user).then((response) => {
    console.log(response);
});

Scan Dataset

Photo Area

Types

ApiFloor

Properties

  • readonly Number id
  • readonly String firebaseId
  • readonly String firebaseProjectId
  • Number ordinal
  • String floorNumber
  • String defaultFirebaseScanDatasetId
  • String[] firebaseScanDatasetIds
  • ApiConstructionGrid | Null constructionGrid
  • Number | Null plannedElementCount
  • Boolean | Null plannedElementsExist
  • Number | Null scanDate
  • readonly Vector2Like offset
  • Number | Null photoAreaId
  • ApiMatrix3 | Null photoAreaMinimapPixelToBimMinimapPixel
  • ApiMatrix3 | Null bimMinimapToWorld
  • Number | Null floorElevation
4.3.3

15 days ago

4.3.2

1 month ago

4.3.1

2 months ago

3.2.2

10 months ago

3.2.1

10 months ago

4.0.0

7 months ago

3.9.0

7 months ago

3.4.14

8 months ago

3.8.0

7 months ago

3.4.10

8 months ago

3.4.11

8 months ago

3.4.12

8 months ago

3.4.13

8 months ago

3.8.2

7 months ago

3.8.1

7 months ago

3.7.0

7 months ago

3.6.0

8 months ago

3.5.1

8 months ago

3.5.0

8 months ago

4.3.0

6 months ago

3.4.0

10 months ago

3.4.4

9 months ago

3.4.3

9 months ago

3.4.2

9 months ago

3.4.1

9 months ago

4.2.2

6 months ago

4.2.1

7 months ago

4.2.0

7 months ago

3.4.8

9 months ago

3.4.7

9 months ago

3.4.6

9 months ago

3.4.5

9 months ago

3.4.9

9 months ago

3.3.1

10 months ago

3.3.0

10 months ago

4.1.3

7 months ago

4.1.0

7 months ago

4.1.2

7 months ago

4.1.1

7 months ago

2.0.1

11 months ago

2.0.0

11 months ago

3.2.0

11 months ago

3.1.1

11 months ago

3.1.0

11 months ago

1.0.1

11 months ago

1.0.0

11 months ago

3.0.0

11 months ago

0.4.26

12 months ago

0.4.25

12 months ago

0.5.4

11 months ago

0.5.3

12 months ago

0.5.6

11 months ago

0.5.5

11 months ago

0.5.0

12 months ago

0.5.2

12 months ago

0.5.1

12 months ago

0.4.20

1 year ago

0.4.21

1 year ago

0.4.24

12 months ago

0.4.22

1 year ago

0.4.23

1 year ago

0.4.19

1 year ago

0.4.17

1 year ago

0.4.18

1 year ago

0.4.16

1 year ago

0.4.9

1 year ago

0.4.8

1 year ago

0.4.10

1 year ago

0.4.15

1 year ago

0.4.13

1 year ago

0.4.14

1 year ago

0.4.11

1 year ago

0.4.12

1 year ago

0.3.50

1 year ago

0.3.42

1 year ago

0.3.41

1 year ago

0.3.40

1 year ago

0.3.49

1 year ago

0.3.48

1 year ago

0.3.47

1 year ago

0.3.46

1 year ago

0.3.45

1 year ago

0.3.44

1 year ago

0.3.43

1 year ago

0.4.5

1 year ago

0.4.4

1 year ago

0.4.7

1 year ago

0.4.6

1 year ago

0.4.1

1 year ago

0.4.0

1 year ago

0.4.3

1 year ago

0.4.2

1 year ago

0.3.31

1 year ago

0.3.30

1 year ago

0.3.39

1 year ago

0.3.38

1 year ago

0.3.37

1 year ago

0.3.36

1 year ago

0.3.35

1 year ago

0.3.34

1 year ago

0.3.33

1 year ago

0.3.32

1 year ago

0.3.29

1 year ago

0.3.28

1 year ago

0.3.27

1 year ago

0.3.26

1 year ago

0.3.25

1 year ago

0.3.24

1 year ago

0.3.23

1 year ago

0.3.22

1 year ago

0.3.20

1 year ago

0.3.21

1 year ago

0.3.19

1 year ago

0.3.18

1 year ago

0.3.9

2 years ago

0.3.17

1 year ago

0.3.16

2 years ago

0.3.15

2 years ago

0.3.14

2 years ago

0.3.13

2 years ago

0.3.12

2 years ago

0.3.11

2 years ago

0.3.10

2 years ago

0.3.0

2 years ago

0.3.6

2 years ago

0.3.5

2 years ago

0.3.8

2 years ago

0.3.7

2 years ago

0.3.2

2 years ago

0.3.1

2 years ago

0.3.4

2 years ago

0.3.3

2 years ago

0.2.27

2 years ago

0.2.26

2 years ago

0.2.25

2 years ago

0.2.24

2 years ago

0.2.23

2 years ago

0.2.22

2 years ago

0.2.21

2 years ago

0.2.20

2 years ago

0.2.41

2 years ago

0.2.40

2 years ago

0.2.42

2 years ago

0.2.39

2 years ago

0.2.30

2 years ago

0.2.38

2 years ago

0.2.35

2 years ago

0.2.34

2 years ago

0.2.33

2 years ago

0.2.32

2 years ago

0.2.31

2 years ago

0.2.29

2 years ago

0.2.28

2 years ago

0.2.19

2 years ago

0.2.18

2 years ago

0.2.17

2 years ago

0.2.16

2 years ago

0.2.15

2 years ago

0.2.14

2 years ago

0.2.13

2 years ago

0.2.12

2 years ago

0.1.21

2 years ago

0.1.22

2 years ago

0.2.11

2 years ago

0.2.10

2 years ago

0.2.7

2 years ago

0.2.6

2 years ago

0.2.9

2 years ago

0.2.8

2 years ago

0.2.5

2 years ago

0.2.4

2 years ago

0.1.20

2 years ago

0.2.1

2 years ago

0.2.0

2 years ago

0.1.17

2 years ago

0.1.18

2 years ago

0.1.19

2 years ago

0.2.3

2 years ago

0.2.2

2 years ago

0.1.10

2 years ago

0.1.11

2 years ago

0.1.12

2 years ago

0.1.13

2 years ago

0.1.14

2 years ago

0.1.15

2 years ago

0.1.0

2 years ago

0.0.0

2 years ago

0.1.2

2 years ago

0.0.3

2 years ago

0.1.1

2 years ago

0.1.16

2 years ago

0.1.8

2 years ago

0.1.7

2 years ago

0.1.9

2 years ago

0.1.4

2 years ago

0.1.3

2 years ago

0.0.4

2 years ago

0.1.6

2 years ago

0.1.5

2 years ago

0.0.2

3 years ago

0.0.1

3 years ago