Saltar al contenido

Write a plugin for Dentra Viewer

Un plugin añade funciones al Viewer. Tú lo escribes, nosotros lo probamos y, si nos gusta, todos lo encuentran en el panel «Plugins».

Introducción

Los plugins los escribe la comunidad: sirven a quien quiere personalizar el Viewer o añadirle funciones. Nosotros ponemos el entorno y esta documentación.

Un plugin es una pequeña página web (HTML, JavaScript y CSS) que el Viewer abre en su propio marco. Desde el marco, el plugin habla con el Viewer a través del kit DentraViewer, que ya está cargado.

El plugin funciona en un marco cerrado: no ve la página, la cuenta ni los datos de Dentra. Solo puede hacer lo que pide en sus permisos, y el dentista los lee antes de activarlo.

Qué puedes construir

  • Nuevas formas de mover la vista: gestos con la mano delante de la webcam, mandos de videojuegos, comandos de voz.
  • Medidas y anotaciones: clics sobre el modelo, puntos, líneas y textos en la escena.
  • Análisis de modelos: comprobaciones de la malla, cálculos, resultados guardados en un archivo.
  • Conexiones con otros servicios: modelos traídos de fuera, el móvil como mando a distancia.

Primeros pasos

  1. 1

    Hazte developer

    Necesitas una cuenta Dentra. En /app, en «Developer», pide el acceso: lo aprobamos nosotros.

  2. 2

    Empieza por el ejemplo

    Descarga el plugin de ejemplo: cuatro flechas que giran el modelo. Dentro tienes todo lo que necesitas.

  3. 3

    Escribe el tuyo

    Cambia dentra-plugin.json (id, name, permissions) y la página. El kit DentraViewer ya está ahí: no hace falta importarlo.

  4. 4

    Pruébalo en el Viewer

    Comprime la carpeta en un zip y súbelo desde tu área Developer con «Subir como borrador»: solo lo ves tú y no va a revisión. «Probar en el Viewer» abre el Viewer con tu plugin activado, sobre dos arcadas de ejemplo o sobre tus propios modelos. ¿Has cambiado algo? Vuelve a subir el zip con el mismo número de versión y pruébalo otra vez.

  5. 5

    Preséntalo

    Cuando funcione, pulsa «Enviar a revisión» junto al borrador. Si algo en el zip falla, te lo decimos enseguida, ya al subirlo.

La estructura del zip

my-plugin.zip
├── dentra-plugin.json
├── index.html        ← "entry"
├── plugin.js
├── style.css
└── icon.png          ← "icon" (optional)

En el zip solo pueden ir estos tipos de archivo: html, js, mjs, css, json, wasm, png, jpg, jpeg, svg, webp, gif, ico, woff, woff2, txt, md, task, tflite, bin, data, onnx. Como máximo 400 archivos y 120 MB una vez descomprimido; el zip en sí puede pesar hasta 40 MB.

dentra-plugin.json

El archivo va en la raíz del zip y dice quién es el plugin y qué necesita.

{
  "id": "click-markers",
  "name": "Markers",
  "version": "1.0.0",
  "description": "Places a numbered marker wherever you click on the model.",
  "author": {
    "name": "Your name",
    "email": "[email protected]",
    "website": "https://example.com"
  },
  "entry": "index.html",
  "permissions": [
    "pick",
    "draw",
    "toolbar"
  ],
  "reasons": {
    "pick": "To place a marker where you click.",
    "draw": "To draw the marker on the model.",
    "toolbar": "A button to clear the markers."
  },
  "platforms": [
    "web",
    "desktop"
  ],
  "api": 1
}
id
El nombre técnico: 3-40 caracteres entre minúsculas, números y guiones, con una letra o un número al principio y al final. Es de la cuenta que lo sube primero, también como borrador: nadie más puede usarlo.
name
El nombre que ve el dentista, hasta 40 caracteres.
version
Tres números, por ejemplo 1.0.0. Para una versión nueva, súbelo: un número ya enviado a revisión se rechaza, mientras que un borrador se vuelve a subir con el mismo número.
description
Una línea sobre lo que hace, hasta 300 caracteres.
author
Tu nombre; el email y la web son opcionales.
entry
La página HTML que el Viewer abre en el marco. Si falta, index.html.
permissions
Lo que pide al Viewer: mira «Permisos».
reasons
Para cada permiso (menos view), una línea sobre el porqué, hasta 160 caracteres. El dentista la lee antes de activarlo.
platforms
Dónde funciona: web, desktop o los dos. Si falta, los dos. Con internet y models juntos, solo desktop.
send
Obligatorio solo si pides internet y models juntos: to (el dominio que recibe los escaneos, por ejemplo api.example.com, sin https://) y why (por qué).
icon
Opcional: un .png, .svg o .webp dentro del zip.
api
La versión del kit: hoy 1.

Permisos

Se declaran en el campo permissions. Cada permiso es una sola cosa. Antes de activar el plugin, el dentista lee lo que pide, con los motivos que escribiste en reasons.

view
girar, mover y acercar el modelo, devolver la vista a como estaba, saber hacia dónde mira.
pick
saber dónde hace clic el dentista sobre el modelo: punto, dirección de la superficie y qué modelo.
draw
dibujar en la escena puntos, líneas, textos y modelos traídos por el plugin (STL, PLY, OBJ).
edit
ocultar, colorear o hacer transparentes los modelos abiertos.
models
leer los archivos de los modelos abiertos.
files
guardar un archivo en el ordenador del dentista.
toolbar
poner hasta 4 botones en la barra del Viewer.
camera
recibir las imágenes de la cámara. La abre el Viewer, pidiendo permiso al dentista; al plugin solo le llegan las imágenes.
microphone
recibir el audio del micrófono. Lo abre el Viewer, pidiendo permiso al dentista.
internet
conectarse a internet. Sin él, el plugin no puede conectarse a ningún otro sitio.
clipboard
copiar un texto al portapapeles.

Web y app de escritorio

Un plugin puede funcionar en el Viewer en la web (web), en la app de escritorio (desktop) o en los dos. Lo declaras en el campo platforms, y lo mostramos en la lista y en la ficha del plugin. Hoy el panel de plugins está en el Viewer de la web; la app de escritorio, por ahora, no lo tiene.

Internet y models juntos quieren decir que el plugin puede enviar fuera los escaneos de los pacientes. Esto solo se puede en la app de escritorio, donde los archivos son del dentista, y solo declarando en el campo send adónde van y por qué. El dentista lo lee claramente antes de activarlo; en la web un plugin así no se puede activar.

"platforms": ["web", "desktop"]

// internet + models: desktop only, and you must say where the scans go
"permissions": ["internet", "models"],
"send": { "to": "api.example.com", "why": "Analyses the scan and returns a report." }

Referencia del kit

En la página de entrada de tu plugin (entry) ya tienes window.DentraViewer: el Viewer lo carga solo. Las funciones escritas con await devuelven una Promise; las demás no esperan respuesta. Cada función necesita el permiso indicado; sin él, el Viewer la ignora o la Promise falla con un error. DentraViewer solo existe dentro del Viewer: si abres la página del plugin por separado, desde tu ordenador, no está.

await DentraViewer.ready()

espera al Viewer; devuelve { language, permissions, api }: el idioma de quien lo usa, los permisos concedidos y la versión del kit.

DentraViewer.view.move({ rotate: { x, y, z }, pan: { x, y }, zoom })

rotate en radianes alrededor de los ejes de la pantalla (x arriba y abajo, y izquierda y derecha, z giro sobre sí mismo), pan en fracciones del tamaño del modelo, un zoom positivo acerca (0.1 = el 10%). Cada valor cuenta como máximo 0.5 por llamada.

Permiso: view

DentraViewer.view.reset()

devuelve la vista a como estaba.

Permiso: view

await DentraViewer.view.get()

adónde mira la cámara ahora: position y target, cada uno [x, y, z].

Permiso: view

DentraViewer.pick.onClick(({ point, normal, model }) => …)

en cada clic sobre el modelo (no al arrastrar): point y normal como [x, y, z], y model, el id del modelo pulsado o null.

Permiso: pick

DentraViewer.scene.draw(key, { shape, … })

dibuja una forma: shape "points" (points, color, size), "line" (points, color, closed), "text" (point, text, color, size) o "mesh" (data, format stl/ply/obj, color, opacity). Las coordenadas son las mismas de pick.onClick. La misma key la sustituye. Hasta 500 formas, una mesh hasta 100 MB.

Permiso: draw

DentraViewer.scene.remove(key) / DentraViewer.scene.clear()

quita una forma, o todas las del plugin.

Permiso: draw

await DentraViewer.models.list()

los modelos abiertos: id, name, format.

Permiso: models

await DentraViewer.models.read(id)

el archivo de un modelo: data es un ArrayBuffer.

Permiso: models

DentraViewer.models.change(id, { visible, opacity, color })

muestra u oculta (visible), cambia la transparencia (opacity, de 0 a 1) o el color (color, "#rrggbb"; null vuelve al color de antes) de un modelo abierto.

Permiso: edit

DentraViewer.files.save(name, data, type)

descarga un archivo en el ordenador (string o ArrayBuffer, hasta 200 MB); type es el tipo del archivo, por ejemplo "text/csv".

Permiso: files

DentraViewer.toolbar.button(key, label) / toolbar.onPress((key) => …) / toolbar.remove(key)

un botón en la barra del Viewer (hasta 4, etiqueta de hasta 24 caracteres), lo que pasa cuando lo pulsan, y toolbar.remove(key) para quitarlo.

Permiso: toolbar

DentraViewer.camera.start({ fps, width }) / camera.onFrame((image, t) => …)

enciende la cámara: fps de 1 a 30 (por defecto 15), width de 160 a 1280 píxeles (por defecto 640). Las imágenes llegan como ImageBitmap; camera.onStatus dice si está encendida o por qué no. camera.stop() la apaga.

Permiso: camera

DentraViewer.microphone.start() / microphone.onAudio((samples, sampleRate) => …)

enciende el micrófono; el audio llega en trozos Float32Array, con la frecuencia de muestreo. microphone.onStatus dice si está encendido o por qué no; microphone.stop() lo apaga.

Permiso: microphone

await DentraViewer.storage.get() / DentraViewer.storage.set(value)

un valor JSON guardado en este dispositivo, solo para tu plugin, hasta 100 KB: si es más grande no se guarda. get() devuelve null si no hay nada.

DentraViewer.clipboard.write(text)

copia un texto en el portapapeles, hasta 2000 caracteres.

Permiso: clipboard

DentraViewer.panel.height(px)

la altura del marco del plugin en píxeles, de 80 a 560 (por defecto 180).

Un ejemplo completo

// plugin.js: place a numbered marker wherever the dentist clicks
// Permissions in dentra-plugin.json: "pick", "draw", "toolbar" (the manifest above)
let n = 0;

DentraViewer.ready().then(({ language, permissions }) => {
  // language: the dentist's language ("it", "en", "de"…), to translate your labels
  DentraViewer.panel.height(120);
  DentraViewer.toolbar.button("clear", "Clear");
});

DentraViewer.pick.onClick(({ point }) => {
  n++;
  DentraViewer.scene.draw("dot-" + n, { shape: "points", points: [point], color: "#e5484d", size: 0.6 });
  DentraViewer.scene.draw("label-" + n, { shape: "text", point: [point[0], point[1] + 2, point[2]], text: "#" + n });
});

DentraViewer.toolbar.onPress((key) => {
  if (key === "clear") { DentraViewer.scene.clear(); n = 0; }
});

Revisión y publicación

  1. Presentas el zip desde tu área Developer, o pulsas «Enviar a revisión» en un borrador que ya has probado. Revisamos los archivos y dentra-plugin.json en cuanto subes el zip, también como borrador, y te decimos qué falta (los mensajes están en inglés). Cuando entra en revisión, te llega un email de confirmación.
  2. Lo probamos en el Viewer antes de publicarlo. Si lee los modelos y usa internet, pasa también una revisión de privacidad.
  3. Si lo aprobamos, lo publicamos: aparece en la lista pública de plugins y en el panel del Viewer. Si no, te escribimos qué cambiar, y en tu área Developer esa versión aparece como «Hay que cambiarlo», con nuestra nota. En los dos casos te llega un email.
  4. Para una versión nueva, también después de un rechazo, sube el número de version y preséntala otra vez: un número ya presentado no se puede repetir. Un borrador, en cambio, lo vuelves a subir con el mismo número hasta que lo presentas. Pasa la misma prueba, y hasta que la aprobemos en el Viewer sigue la anterior. Cuando la aprobamos, quien ya tenía el plugin activado usa la versión nueva automáticamente. Pero si pide permisos que la versión anterior no pedía, quien lo tenía activado lo encuentra en pausa: el Viewer le muestra los permisos nuevos con tus motivos, y el plugin vuelve a funcionar solo cuando los acepta.

Reglas

  • Los plugins son gratis para quien los usa.
  • Necesitas una cuenta Dentra con acceso Developer, que aprobamos nosotros. Después probamos cada plugin, uno por uno.
  • El plugin funciona en su propio marco cerrado: no ve la página, las cuentas ni los datos de Dentra.
  • Cada permiso necesita un motivo en una línea.
  • Los escaneos solo salen en la app de escritorio, declarando en send adónde van y por qué.
  • Al presentarlo aceptas que lo probemos y, si nos gusta, lo publiquemos gratis en el Viewer. El plugin sigue siendo tuyo.
  • Podemos retirar un plugin del catálogo: por un problema de seguridad lo hacemos enseguida, sin esperar. Te enviamos el motivo por email y lo encuentras en tu área Developer; quien lo tenía activado lo encuentra apagado.

Hazte developer

Necesitas una cuenta Dentra, con los datos de facturación como cualquier cuenta. Desde tu cuenta pides el acceso Developer; cuando lo aprobamos, presentas los plugins desde tu área y ves en qué punto están.

El acceso Developer es el principio: más adelante también pasarán por ahí las API de Dentra.