forma-embedded-view-sdk
Version:
The Forma Embedded View SDK is a JavaScript library for creating custom extensions in Autodesk Forma (previously Spacemaker).
234 lines (233 loc) • 8.76 kB
JavaScript
import { RepresentationsApi } from "./representations.js";
/**
* The Elements API allows you to read all elements and create in a selection of Forma elements systems.
*
* @remarks
* Available via {@link auto.Forma | Forma}.{@link index.EmbeddedViewSdk.elements | elements}.
*/
export class ElementsApi {
#iframeMessenger;
floorStack;
representations;
blobs;
/** @hidden */
constructor(iframeMessenger) {
this.#iframeMessenger = iframeMessenger;
this.floorStack = new FloorStackApi(iframeMessenger);
this.representations = new RepresentationsApi(iframeMessenger);
this.blobs = new BlobsApi(iframeMessenger);
}
/**
* Get an element by urn.
*
* The method can return multiple elements. This can happen
* if the element has children that are bundled with it.
*
* This method can also be used to include all the transitive children of the element
* by setting the recursive parameter to true.
*
* @example
* // Get the root element of the current proposal
* const urn = await Forma.proposal.getRootUrn()
* const { element, elements } = await Forma.elements.get({ urn })
*
* @returns
* A map from identifiers to elements.
* A single element for the requested element
*/
async get(request) {
return await this.#iframeMessenger.sendRequest("elements/get-v2", {
rootUrn: request.urn,
recursive: request.recursive,
});
}
/**
* Get an element hierarchy located at the path relative to the current
* root. You default to current proposal by omitting the urn parameter.
*
* @example
* // Get all the building elements of the current proposal
* Forma.selection.subscribe(({ paths }) => {
* const { element, elements } = await Forma.elements.getByPath({ path: paths[0] })
* });
*
* @returns
* A map from identifiers to elements.
*/
async getByPath(request) {
return await this.#iframeMessenger.sendRequest("elements/get-v2", request);
}
/**
* Get the world transform of an element relative to the root element.
*
* The world transform is the affine transformation matrix that transforms
* the element from its local coordinate system to the root coordinate system.
*
* @example
* const { transform } = await Forma.elements.getWorldTransform({ path })
* const { element } = await Forma.elements.getByPath({ path })
*
* if (element.representations?.footprint) {
* const footprint = await Forma.elements.representations.footprint(element)
*
* const { data: geojson } = footprint
*
* void Forma.render.geojson.add({ geojson, transform })
* }
*
* @param request the path to element from root
* @returns The affine transformation matrix of the element
*/
async getWorldTransform(request) {
const response = await this.#iframeMessenger.sendRequest("elements/get-world-transform", request);
return response;
}
/**
* Allows adding, editing and removing custom properties attached directly to elements.
* All properties must live under a namespace which follows `\w{1,20}:\w{1,50}` pattern (e.g.`my:namespace`).
* The properties are managed by [JSON Merge Patch](https://datatracker.ietf.org/doc/html/rfc7386) specification.
*
* Requires edit access to the project. See {@link EmbeddedViewSdk.getCanEdit | getCanEdit} for more info.
*
* @example
* // Add two properties within a namespace to an element and replace its previous urn with the updated urn in a proposal
* const addTwoProperties = {
* "my:namespace": {
* "myFirstProperty": "myFirstValue"
* "mySecondProperty": "mySecondValue"
* }
* }
* const result = await Forma.elements.editProperties({urn, propertiesJsonMergePatch: myProperties});
* await Forma.proposal.replaceElement({path, urn: result.urn})
*
* @example
* // Change value of the first property and remove the second property at the same time
* const changeFirstRemoveSecondProperty = {
* "my:namespace": {
* "myFirstProperty": "veryDifferentValue"
* "mySecondProperty": null
* }
* }
* const result = await Forma.elements.editProperties({urn, propertiesJsonMergePatch: changeFirstRemoveSecondProperty});
* await Forma.proposal.replaceElement({path, urn: result.urn})
*
* @example
* // Remove the whole namespace from the element
* const removeWholeNamespace = {
* "my:namespace": null
* }
* const result = await Forma.elements.editProperties({urn, propertiesJsonMergePatch: removeWholeNamespace});
* await Forma.proposal.replaceElement({path, urn: result.urn})
*
*/
async editProperties(request) {
return await this.#iframeMessenger.sendRequest("elements/edit-properties", request);
}
}
/**
* API for creating Floor Stack buildings.
*
* You can use this API together with the Forma.proposal.addElement
* API to create a building and add it to a the current proposal.
*
* @example
* // Create a building with 3 floors with a 2 meter setback
* // Place it where the user clicks in the scene
* const { urn } = await Forma.elements.floorStack.createFromFloors({
* floors: [
* {
* polygon: [ [0, 0], [10, 0], [10, 10], [0, 10], [0, 0] ],
* height: 3,
* },
* {
* polygon: [ [0, 0], [10, 0], [10, 10], [0, 10], [0, 0] ],
* height: 2.6,
* },
* {
* polygon: [ [0, 0], [10, 0], [10, 8], [0, 8], [0, 0] ],
* height: 2.6,
* },
* ],
* })
* const point = await Forma.designTool.getPoint()
* if (!point) return
* const transform = [1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, point.x, point.y, point.z, 1]
* await Forma.proposal.addElement({ urn, transform });
*
* @remarks
* Available via {@link auto.Forma | Forma}.{@link index.EmbeddedViewSdk.elements | elements}.{@link elements.ElementsApi.floorStack | floorStack}.
*/
export class FloorStackApi {
#iframeMessenger;
/** @hidden */
constructor(iframeMessenger) {
this.#iframeMessenger = iframeMessenger;
}
/**
* Create a 2.5D building from a stack of floors.
*
* By 2.5D, we mean that the building only has vertical walls and flat roofs.
* The floors are given from bottom to top.
*
* Requires edit access to the project. See {@link EmbeddedViewSdk.getCanEdit | getCanEdit} for more info.
*
* @returns URN of the created building element.
*/
async createFromFloors(request) {
const response = await this.#iframeMessenger.sendRequest("elements/floor-stack-v2/create-from-floors", request);
return response;
}
/**
* Create multiple 2.5D buildings from stacks of floors.
*
* By 2.5D, we mean that the building only has vertical walls and flat roofs.
* The floors are given from bottom to top.
*
* Requires edit access to the project. See {@link EmbeddedViewSdk.getCanEdit | getCanEdit} for more info.
*
* @returns URNs of the created building elements.
*/
async createFromFloorsBatch(request) {
const response = await this.#iframeMessenger.sendRequest("elements/floor-stack-v2/create-from-floors-batch", request);
return response;
}
}
/**
* The Blobs API allows you to read blobs related to an element
*
* @remarks
* Available via {@link auto.Forma | Forma}.{@link index.EmbeddedViewSdk.elements | elements}.{@link elements.ElementsApi.blobs | blobs}.
*/
export class BlobsApi {
#iframeMessenger;
/** @hidden */
constructor(iframeMessenger) {
this.#iframeMessenger = iframeMessenger;
}
/**
* Retrieve a blob by its id.
*
* Blobs are binary data that can be used for various purposes.
* A common use case is together with linked representations
*
* @example
* ```typescript
* const { element } = await Forma.elements.get({
* urn: "urn:adsk-forma-elements:terrain:pro_cnusxrl4s1:c418dafe-1963-4160-9df5-239a49eef10b:1716818829774",
* })
*
* if (element.representations?.volumeMesh?.type === "linked") {
* const blobResponse = await Forma.elements.blobs.get({
* blobId: element.representations.volumeMesh.blobId,
* })
* const arrayBuffer: ArrayBuffer = blobResponse.data
* }
* ```
*
* @returns an object containing the ArrayBuffer of the blob as data
*/
async get(request) {
const response = await this.#iframeMessenger.sendRequest("elements/blobs/get", request);
return response;
}
}