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
Hazte developer
Necesitas una cuenta Dentra. En /app, en «Developer», pide el acceso: lo aprobamos nosotros.
- 2
Empieza por el ejemplo
Descarga el plugin de ejemplo: cuatro flechas que giran el modelo. Dentro tienes todo lo que necesitas.
- 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
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
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
- 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.
- Lo probamos en el Viewer antes de publicarlo. Si lee los modelos y usa internet, pasa también una revisión de privacidad.
- 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.
- 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.