chore (plugin): revamp frontend plugin

This commit is contained in:
MickaelK 2025-06-06 17:15:27 +10:00
parent fb29c7b6c2
commit f4a99c4ad6
45 changed files with 179 additions and 18787 deletions

View file

@ -1,5 +1,6 @@
import rxjs from "../lib/rx.js";
import ajax from "../lib/ajax.js";
import { join } from "../lib/path.js";
let LNG = {};
@ -42,7 +43,7 @@ export async function init() {
return Promise.resolve();
}
return ajax({
url: "assets/locales/" + selectedLanguage + ".json",
url: join(import.meta.url, selectedLanguage + ".json"),
}).pipe(rxjs.tap(({ responseHeaders, response }) => {
const contentType = responseHeaders["content-type"].trim();
if (contentType === "application/json") {

View file

@ -10,7 +10,7 @@ import ctrlError from "../ctrl_error.js";
import componentDownloader, { init as initDownloader } from "./application_downloader.js";
import { renderMenubar, buttonDownload } from "./component_menubar.js";
import * as THREE from "../../../lib/vendor/three/three.module.js";
import * as THREE from "../../lib/vendor/three/three.module.js";
import setup3D from "./application_3d/init.js";
import withLight from "./application_3d/scene_light.js";
import withCube from "./application_3d/scene_cube.js";

View file

@ -1,5 +1,5 @@
import { createElement, onDestroy } from "../../../lib/skeleton/index.js";
import { OrbitControls } from "../../../../lib/vendor/three/OrbitControls.js";
import { OrbitControls } from "../../../lib/vendor/three/OrbitControls.js";
export default function({ THREE, $page, $menubar, mesh, refresh, is2D }) {
// setup the dom

View file

@ -1,5 +1,5 @@
import { onDestroy } from "../../../lib/skeleton/index.js";
import { ViewCubeGizmo, SimpleCameraControls, ObjectPosition } from "../../../../lib/vendor/three/viewcube.js";
import { ViewCubeGizmo, SimpleCameraControls, ObjectPosition } from "../../../lib/vendor/three/viewcube.js";
export default function({ camera, renderer, refresh, controls }) {
const viewCubeGizmo = new ViewCubeGizmo(camera, renderer, {

View file

@ -1,5 +1,5 @@
import { settings_get } from "../../../lib/settings.js";
import * as THREE from "../../../../lib/vendor/three/three.module.js";
import * as THREE from "../../../lib/vendor/three/three.module.js";
const LIGHT_COLOR = 0xf5f5f5;

View file

@ -1,6 +1,6 @@
import { createElement } from "../../../lib/skeleton/index.js";
import { qs } from "../../../lib/dom.js";
import * as THREE from "../../../../lib/vendor/three/three.module.js";
import * as THREE from "../../../lib/vendor/three/three.module.js";
export default function(render, { camera, controls, mesh, $menubar, $toolbar, is2D }) {
if (mesh.children.length <= 1) return;

View file

@ -6,10 +6,10 @@ import t from "../../locales/index.js";
import ctrlError from "../ctrl_error.js";
import { transition } from "./common.js";
import { renderMenubar } from "./component_menubar.js";
import "../../components/icon.js";
import "./component_menubar.js";
export default async function(render, { acl$, getFilename, getDownloadUrl, hasMenubar = true }) {
export default async function(render, { acl$, $menubar, getFilename, getDownloadUrl, hasMenubar = true }) {
const $page = createElement(`
<div class="component_filedownloader">
<component-menubar filename="${getFilename()}" class="${!hasMenubar && "hidden"}"></component-menubar>
@ -20,6 +20,7 @@ export default async function(render, { acl$, getFilename, getDownloadUrl, hasMe
</div>
`);
render(transition($page));
renderMenubar(qs($page, "component-menubar"));
const $link = qs($page, "a");
const $loading = qs($page, "component-icon");

View file

@ -3,6 +3,7 @@ import rxjs, { effect } from "../../lib/rx.js";
import { animate, slideXIn, opacityOut } from "../../lib/animate.js";
import { qs } from "../../lib/dom.js";
import { get as getConfig } from "../../model/config.js";
import { load as loadPlugin } from "../../model/plugin.js";
import { createLoader } from "../../components/loader.js";
import { createModal, MODAL_RIGHT_BUTTON } from "../../components/modal.js";
import { loadCSS, loadJS } from "../../helpers/loader.js";
@ -21,7 +22,9 @@ import "../../components/icon.js";
const TIME_BEFORE_ABORT_EDIT = 5000;
export default async function(render, { acl$, getFilename, getDownloadUrl }) {
class IEditor {}
export default async function(render, { acl$, getFilename, getDownloadUrl, mime }) {
const $page = createElement(`
<div class="component_ide">
<component-menubar filename="${getFilename()}" class="hidden"></component-menubar>
@ -107,6 +110,11 @@ export default async function(render, { acl$, getFilename, getDownloadUrl }) {
rxjs.tap((editor) => requestAnimationFrame(() => editor.refresh())),
);
}),
rxjs.mergeMap(async (editor) => {
const loader = await loadPlugin(mime);
if (loader) new (await loader(IEditor, { mime, $menubar: $dom.menubar(), getFilename, getDownloadUrl }))(editor);
return editor;
}),
rxjs.catchError(ctrlError()),
rxjs.share(),
);

View file

@ -40,8 +40,8 @@ export default async function(render, { mime, getDownloadUrl = nop, getFilename
catch (err) { componentDownloader(render, { mime, acl$, getFilename, getDownloadUrl }); }
return rxjs.EMPTY;
}
const mapImpl = new (await loader(IMap))(response, {
map, $page, $menubar, L: window.L,
const mapImpl = new (await loader(IMap, { mime, getDownloadUrl, getFilename, $menubar }))(response, {
map, $page, L: window.L,
});
loadGeoJSON(map, await mapImpl.toGeoJSON());
}),

View file

@ -6,6 +6,7 @@ import { loadCSS } from "../../helpers/loader.js";
import { createLoader } from "../../components/loader.js";
import ctrlError from "../ctrl_error.js";
import componentDownloader, { init as initDownloader } from "./application_downloader.js";
import { renderMenubar } from "./component_menubar.js";
export default function(render, { mime, getFilename, getDownloadUrl, acl$, hasMenubar = true }) {
const $page = createElement(`
@ -15,7 +16,7 @@ export default function(render, { mime, getFilename, getDownloadUrl, acl$, hasMe
</div>
`);
render($page);
const $menubar = qs($page, "component-menubar");
const $menubar = renderMenubar(qs($page, "component-menubar"));
const $container = qs($page, ".component_skeleton_container");
const removeLoader = createLoader($container);
effect(rxjs.from(loadPlugin(mime)).pipe(

View file

@ -55,7 +55,7 @@ export default async function(render, { mime, getDownloadUrl = nop, getFilename
if (!loader) throw new TypeError(`unsupported mimetype "${mime}"`);
const [, url] = loader;
const module = await import(url);
let table = new (await module.default(ITable))(response, { $menubar });
let table = new (await module.default(ITable, { $menubar }))(response);
if (typeof table.then === "function") table = await table;
STATE.header = table.getHeader();
STATE.body = table.getBody();

View file

@ -37,11 +37,12 @@ self.addEventListener("activate", (event) => {
})());
});
self.addEventListener("fetch", (event) => {
self.addEventListener("fetch", async (event) => {
if (!event.request.url.startsWith(location.origin + "/assets/")) return;
event.respondWith((async() => {
const cachedResponse = await caches.match(event.request);
const cache = await caches.open(CACHENAME);
const cachedResponse = await cache.match(event.request);
if (cachedResponse) return cachedResponse;
return fetch(event.request);
})());
@ -55,37 +56,36 @@ self.addEventListener("message", (event) => {
);
});
const handlePreloadMessage = (() => {
return async(chunks, resolve, reject, id) => {
const cleanup = [];
try {
caches.delete(CACHENAME);
const cache = await caches.open(CACHENAME);
await Promise.all(chunks.map((urls) => {
return preload({ urls, cache, cleanup });
}));
resolve();
} catch (err) {
reject(err);
} finally {
cleanup.forEach((fn) => fn());
}
};
})();
async function handlePreloadMessage(chunks, resolve, reject, id) {
const cleanup = [];
try {
await caches.delete(CACHENAME);
const cache = await caches.open(CACHENAME);
await Promise.all(chunks.map((urls) => {
return preload({ urls, cache, cleanup });
}));
resolve();
} catch (err) {
console.log("ERR", err);
reject(err);
} finally {
cleanup.forEach((fn) => fn());
}
};
async function preload({ urls, cache, cleanup }) {
const evtsrc = new self.EventSource("/assets/bundle?" + urls.map((url) => `url=${url}`).join("&"));
cleanup.push(() => evtsrc.close());
let i = 0;
const messageHandler = (resolve, event, decoder) => {
const messageHandler = async (resolve, event, decoder) => {
const url = event.lastEventId;
let mime = "application/octet-stream";
if (url.endsWith(".css")) mime = "text/css";
else if (url.endsWith(".js")) mime = "application/javascript";
i += 1;
cache.put(
await cache.put(
location.origin + url,
new Response(
decoder(new Blob([Uint8Array.from(atob(event.data), (c) => c.charCodeAt(0))]).stream()),
@ -111,6 +111,9 @@ async function preload({ urls, cache, cleanup }) {
event,
(stream) => stream.pipeThrough(new DecompressionStream("gzip")),
));
evtsrc.onerror = (err) => errorHandler(reject, err);
evtsrc.onerror = (err) => {
if (i === urls.length) return;
errorHandler(reject, err);
};
});
}

View file

@ -38,129 +38,16 @@
<script type="module" src="./assets/{{ .version }}/components/notification.js" defer></script>
</template>
<script id="preload" type="application/json">{{ .preload }}</script>
<script type="module">
function boot() {
document.head.appendChild(document.querySelector("template#head").content);
document.body.appendChild(document.querySelector("template#body").content);
}
const URLS = [
[
"/assets/{{ .version }}/lib/vendor/rxjs/rxjs.min.js",
"/assets/{{ .version }}/lib/vendor/rxjs/rxjs-ajax.min.js",
"/assets/{{ .version }}/lib/vendor/rxjs/rxjs-shared.min.js",
],
[
"/assets/{{ .version }}/boot/ctrl_boot_frontoffice.js",
"/assets/{{ .version }}/locales/index.js",
"/assets/{{ .version }}/css/designsystem.css",
"/assets/{{ .version }}/css/designsystem_input.css",
"/assets/{{ .version }}/css/designsystem_textarea.css",
"/assets/{{ .version }}/css/designsystem_inputgroup.css",
"/assets/{{ .version }}/css/designsystem_checkbox.css",
"/assets/{{ .version }}/css/designsystem_formbuilder.css",
"/assets/{{ .version }}/css/designsystem_button.css",
"/assets/{{ .version }}/css/designsystem_icon.css",
"/assets/{{ .version }}/css/designsystem_dropdown.css",
"/assets/{{ .version }}/css/designsystem_container.css",
"/assets/{{ .version }}/css/designsystem_box.css",
"/assets/{{ .version }}/css/designsystem_darkmode.css",
"/assets/{{ .version }}/css/designsystem_skeleton.css",
"/assets/{{ .version }}/css/designsystem_utils.css",
"/assets/{{ .version }}/css/designsystem_alert.css",
"/assets/{{ .version }}/components/loader.js",
"/assets/{{ .version }}/components/modal.js",
"/assets/{{ .version }}/components/modal.css",
"/assets/{{ .version }}/components/notification.js",
"/assets/{{ .version }}/components/notification.css",
"/assets/{{ .version }}/boot/router_frontoffice.js",
"/assets/{{ .version }}/helpers/loader.js",
"/assets/{{ .version }}/lib/skeleton/index.js",
"/assets/{{ .version }}/lib/rx.js",
"/assets/{{ .version }}/lib/ajax.js",
"/assets/{{ .version }}/lib/animate.js",
"/assets/{{ .version }}/lib/assert.js",
"/assets/{{ .version }}/lib/dom.js",
"/assets/{{ .version }}/lib/skeleton/router.js",
"/assets/{{ .version }}/lib/skeleton/lifecycle.js",
"/assets/{{ .version }}/lib/error.js",
"/assets/{{ .version }}/model/config.js",
"/assets/{{ .version }}/model/plugin.js",
"/assets/{{ .version }}/model/chromecast.js",
"/assets/{{ .version }}/model/session.js",
"/assets/{{ .version }}/helpers/log.js",
"/assets/{{ .version }}/boot/common.js",
"/assets/{{ .version }}/helpers/sdk.js",
"/assets/{{ .version }}/components/breadcrumb.js",
"/assets/{{ .version }}/components/breadcrumb.css",
"/assets/{{ .version }}/components/form.js",
"/assets/{{ .version }}/components/sidebar.js",
"/assets/{{ .version }}/components/sidebar.css",
"/assets/{{ .version }}/components/dropdown.js",
"/assets/{{ .version }}/components/icon.js",
"/assets/{{ .version }}/lib/store.js",
"/assets/{{ .version }}/lib/random.js",
"/assets/{{ .version }}/lib/form.js",
"/assets/{{ .version }}/lib/path.js",
"/assets/{{ .version }}/components/decorator_shell_filemanager.js",
"/assets/{{ .version }}/components/decorator_shell_filemanager.css",
"/assets/{{ .version }}/pages/ctrl_error.js",
],
[
"/assets/{{ .version }}/pages/ctrl_connectpage.js",
"/assets/{{ .version }}/pages/connectpage/ctrl_form.js",
"/assets/{{ .version }}/pages/connectpage/ctrl_forkme.js",
"/assets/{{ .version }}/pages/connectpage/ctrl_poweredby.js",
"/assets/{{ .version }}/lib/path.js",
"/assets/{{ .version }}/lib/form.js",
"/assets/{{ .version }}/lib/settings.js",
"/assets/{{ .version }}/components/form.js",
"/assets/{{ .version }}/model/session.js",
"/assets/{{ .version }}/pages/ctrl_error.js",
"/assets/{{ .version }}/pages/connectpage/model_backend.js",
"/assets/{{ .version }}/pages/connectpage/model_config.js",
"/assets/{{ .version }}/pages/connectpage/ctrl_form_state.js",
"/assets/{{ .version }}/lib/random.js",
"/assets/{{ .version }}/components/icon.js",
"/assets/{{ .version }}/pages/ctrl_connectpage.css",
"/assets/{{ .version }}/pages/connectpage/ctrl_form.css",
],
[
"/assets/{{ .version }}/pages/ctrl_filespage.js",
"/assets/{{ .version }}/pages/ctrl_filespage.css",
"/assets/{{ .version }}/pages/filespage/ctrl_filesystem.js",
"/assets/{{ .version }}/pages/filespage/ctrl_submenu.js",
"/assets/{{ .version }}/pages/filespage/ctrl_newitem.js",
"/assets/{{ .version }}/pages/filespage/ctrl_upload.js",
"/assets/{{ .version }}/pages/filespage/cache.js",
"/assets/{{ .version }}/pages/filespage/state_config.js",
"/assets/{{ .version }}/pages/filespage/thing.js",
"/assets/{{ .version }}/pages/filespage/state_newthing.js",
"/assets/{{ .version }}/pages/filespage/helper.js",
"/assets/{{ .version }}/pages/filespage/model_files.js",
"/assets/{{ .version }}/pages/filespage/model_virtual_layer.js",
"/assets/{{ .version }}/pages/filespage/modal_share.js",
"/assets/{{ .version }}/pages/filespage/modal_tag.js",
"/assets/{{ .version }}/pages/filespage/modal_rename.js",
"/assets/{{ .version }}/pages/filespage/modal_delete.js",
"/assets/{{ .version }}/pages/filespage/state_selection.js",
"/assets/{{ .version }}/pages/filespage/model_acl.js",
"/assets/{{ .version }}/pages/filespage/ctrl_filesystem.css",
"/assets/{{ .version }}/pages/filespage/thing.css",
"/assets/{{ .version }}/pages/filespage/modal.css",
"/assets/{{ .version }}/pages/filespage/ctrl_submenu.css",
"/assets/{{ .version }}/pages/filespage/modal_share.css",
"/assets/{{ .version }}/pages/filespage/modal_tag.css",
"/assets/{{ .version }}/pages/filespage/ctrl_newitem.css",
"/assets/{{ .version }}/pages/filespage/ctrl_upload.css",
],
];
if ("serviceWorker" in navigator) {
const URLS = JSON.parse(document.getElementById("preload").textContent);
try {
const register = await navigator.serviceWorker.register("sw.js");
await new Promise((resolve) => {

View file

@ -33,7 +33,7 @@ func PluginExportHandler(ctx *App, res http.ResponseWriter, req *http.Request) {
}
plgExports[module["mime"]] = []string{
module["application"],
WithBase(JoinPath("/plugin/", filepath.Join(name, index))),
WithBase(JoinPath("/assets/"+BUILD_REF+"/plugin/", filepath.Join(name+".zip", index))),
}
}
}

View file

@ -4,6 +4,7 @@ import (
"bytes"
_ "embed"
"encoding/base64"
"encoding/json"
"fmt"
"io"
"io/fs"
@ -233,11 +234,6 @@ func ServeFile(chroot string) func(*App, http.ResponseWriter, *http.Request) {
)
head := res.Header()
if filePath == "/assets/bundle" {
ServeBundle(ctx, res, req)
return
}
// case: patch must be apply because of a "StaticPatch" plugin
if f := applyPatch(filePath); f != nil {
head.Set("Content-Type", GetMimeType(filepath.Ext(filePath)))
@ -311,13 +307,13 @@ func ServeIndex(indexPath string) func(*App, http.ResponseWriter, *http.Request)
}
head.Set("Content-Type", "text/html")
res.WriteHeader(http.StatusOK)
tmpl := template.Must(template.New(indexPath).Parse(string(b)))
tmpl = template.Must(tmpl.Parse(string(TmplLoader)))
tmpl.Execute(res, map[string]any{
"base": WithBase("/"),
"version": BUILD_REF,
"license": LICENSE,
"preload": preload(),
})
}
}
@ -429,3 +425,123 @@ func InitPluginList(code []byte) {
}
}
}
func preload() string {
out, _ := json.Marshal([][]string{
{
"/assets/" + BUILD_REF + "/lib/vendor/rxjs/rxjs.min.js",
"/assets/" + BUILD_REF + "/lib/vendor/rxjs/rxjs-ajax.min.js",
"/assets/" + BUILD_REF + "/lib/vendor/rxjs/rxjs-shared.min.js",
},
{
"/assets/" + BUILD_REF + "/boot/ctrl_boot_frontoffice.js",
"/assets/" + BUILD_REF + "/locales/index.js",
"/assets/" + BUILD_REF + "/css/designsystem.css",
"/assets/" + BUILD_REF + "/css/designsystem_input.css",
"/assets/" + BUILD_REF + "/css/designsystem_textarea.css",
"/assets/" + BUILD_REF + "/css/designsystem_inputgroup.css",
"/assets/" + BUILD_REF + "/css/designsystem_checkbox.css",
"/assets/" + BUILD_REF + "/css/designsystem_formbuilder.css",
"/assets/" + BUILD_REF + "/css/designsystem_button.css",
"/assets/" + BUILD_REF + "/css/designsystem_icon.css",
"/assets/" + BUILD_REF + "/css/designsystem_dropdown.css",
"/assets/" + BUILD_REF + "/css/designsystem_container.css",
"/assets/" + BUILD_REF + "/css/designsystem_box.css",
"/assets/" + BUILD_REF + "/css/designsystem_darkmode.css",
"/assets/" + BUILD_REF + "/css/designsystem_skeleton.css",
"/assets/" + BUILD_REF + "/css/designsystem_utils.css",
"/assets/" + BUILD_REF + "/css/designsystem_alert.css",
"/assets/" + BUILD_REF + "/components/loader.js",
"/assets/" + BUILD_REF + "/components/modal.js",
"/assets/" + BUILD_REF + "/components/modal.css",
"/assets/" + BUILD_REF + "/components/notification.js",
"/assets/" + BUILD_REF + "/components/notification.css",
"/assets/" + BUILD_REF + "/boot/router_frontoffice.js",
"/assets/" + BUILD_REF + "/helpers/loader.js",
"/assets/" + BUILD_REF + "/lib/skeleton/index.js",
"/assets/" + BUILD_REF + "/lib/rx.js",
"/assets/" + BUILD_REF + "/lib/ajax.js",
"/assets/" + BUILD_REF + "/lib/animate.js",
"/assets/" + BUILD_REF + "/lib/assert.js",
"/assets/" + BUILD_REF + "/lib/dom.js",
"/assets/" + BUILD_REF + "/lib/skeleton/router.js",
"/assets/" + BUILD_REF + "/lib/skeleton/lifecycle.js",
"/assets/" + BUILD_REF + "/lib/error.js",
"/assets/" + BUILD_REF + "/model/config.js",
"/assets/" + BUILD_REF + "/model/plugin.js",
"/assets/" + BUILD_REF + "/model/chromecast.js",
"/assets/" + BUILD_REF + "/model/session.js",
"/assets/" + BUILD_REF + "/helpers/log.js",
"/assets/" + BUILD_REF + "/boot/common.js",
"/assets/" + BUILD_REF + "/helpers/sdk.js",
"/assets/" + BUILD_REF + "/components/breadcrumb.js",
"/assets/" + BUILD_REF + "/components/breadcrumb.css",
"/assets/" + BUILD_REF + "/components/form.js",
"/assets/" + BUILD_REF + "/components/sidebar.js",
"/assets/" + BUILD_REF + "/components/sidebar.css",
"/assets/" + BUILD_REF + "/components/dropdown.js",
"/assets/" + BUILD_REF + "/components/icon.js",
"/assets/" + BUILD_REF + "/lib/store.js",
"/assets/" + BUILD_REF + "/lib/random.js",
"/assets/" + BUILD_REF + "/lib/form.js",
"/assets/" + BUILD_REF + "/lib/path.js",
"/assets/" + BUILD_REF + "/components/decorator_shell_filemanager.js",
"/assets/" + BUILD_REF + "/components/decorator_shell_filemanager.css",
"/assets/" + BUILD_REF + "/pages/ctrl_error.js",
},
{
"/assets/" + BUILD_REF + "/pages/ctrl_connectpage.js",
"/assets/" + BUILD_REF + "/pages/ctrl_connectpage.css",
"/assets/" + BUILD_REF + "/pages/connectpage/ctrl_form.js",
"/assets/" + BUILD_REF + "/pages/connectpage/ctrl_forkme.js",
"/assets/" + BUILD_REF + "/pages/connectpage/ctrl_poweredby.js",
"/assets/" + BUILD_REF + "/pages/connectpage/ctrl_form.css",
"/assets/" + BUILD_REF + "/lib/path.js",
"/assets/" + BUILD_REF + "/lib/form.js",
"/assets/" + BUILD_REF + "/lib/settings.js",
"/assets/" + BUILD_REF + "/components/form.js",
"/assets/" + BUILD_REF + "/model/session.js",
"/assets/" + BUILD_REF + "/pages/ctrl_error.js",
"/assets/" + BUILD_REF + "/pages/connectpage/model_backend.js",
"/assets/" + BUILD_REF + "/pages/connectpage/model_config.js",
"/assets/" + BUILD_REF + "/pages/connectpage/ctrl_form_state.js",
"/assets/" + BUILD_REF + "/lib/random.js",
"/assets/" + BUILD_REF + "/components/icon.js",
"/assets/" + BUILD_REF + "/pages/filespage/ctrl_filesystem.css",
"/assets/" + BUILD_REF + "/pages/filespage/thing.css",
"/assets/" + BUILD_REF + "/pages/filespage/ctrl_filesystem.js",
"/assets/" + BUILD_REF + "/pages/filespage/thing.js",
"/assets/" + BUILD_REF + "/pages/filespage/state_newthing.js",
},
{
"/assets/" + BUILD_REF + "/pages/ctrl_filespage.js",
"/assets/" + BUILD_REF + "/pages/ctrl_filespage.css",
"/assets/" + BUILD_REF + "/pages/filespage/ctrl_submenu.js",
"/assets/" + BUILD_REF + "/pages/filespage/ctrl_newitem.js",
"/assets/" + BUILD_REF + "/pages/filespage/ctrl_upload.js",
"/assets/" + BUILD_REF + "/pages/filespage/cache.js",
"/assets/" + BUILD_REF + "/pages/filespage/state_config.js",
"/assets/" + BUILD_REF + "/pages/filespage/helper.js",
"/assets/" + BUILD_REF + "/pages/filespage/model_files.js",
"/assets/" + BUILD_REF + "/pages/filespage/model_virtual_layer.js",
"/assets/" + BUILD_REF + "/pages/filespage/modal_share.js",
"/assets/" + BUILD_REF + "/pages/filespage/modal_tag.js",
"/assets/" + BUILD_REF + "/pages/filespage/modal_rename.js",
"/assets/" + BUILD_REF + "/pages/filespage/modal_delete.js",
"/assets/" + BUILD_REF + "/pages/filespage/state_selection.js",
"/assets/" + BUILD_REF + "/pages/filespage/model_acl.js",
"/assets/" + BUILD_REF + "/pages/filespage/modal.css",
"/assets/" + BUILD_REF + "/pages/filespage/ctrl_submenu.css",
"/assets/" + BUILD_REF + "/pages/filespage/modal_share.css",
"/assets/" + BUILD_REF + "/pages/filespage/modal_tag.css",
"/assets/" + BUILD_REF + "/pages/filespage/ctrl_newitem.css",
"/assets/" + BUILD_REF + "/pages/filespage/ctrl_upload.css",
},
})
return string(out)
}

View file

@ -1,10 +0,0 @@
all:
make install
install:
zip -r application_3d.zip .
mv application_3d.zip ../../../dist/data/state/plugins/application_3d.zip
deps_mesh:
[ -d vendor ] || mkdir vendor
curl https://gist.githubusercontent.com/mickael-kerjean/9a517f95112410fcacaad8563c1ba314/raw/ab483b38aebb7048832aad7b342f9161dd928e49/gistfile1.txt > vendor/MeshLoader.js

View file

@ -1,18 +0,0 @@
export default async function(I3D, { THREE }) {
const module = await import("./vendor/FBXLoader.js");
return class Impl extends I3D {
constructor() {
super();
}
load(url, onLoad, onProgress, onError) {
return (new module.FBXLoader()).load(url, onLoad, onProgress, onError);
}
transform(obj) {
obj.name = "All";
return obj;
}
}
}

View file

@ -1,19 +0,0 @@
export default async function(I3D, { THREE }) {
const module = await import("./vendor/GLTFLoader.js");
return class Impl extends I3D {
constructor() {
super();
}
load(url, onLoad, onProgress, onError) {
return new module.GLTFLoader().load(url, onLoad, onProgress, onError);
}
transform(gltf) {
const mesh = gltf.scene;
mesh.animations = gltf.animations;
return mesh;
}
}
}

View file

@ -1,17 +0,0 @@
export default async function(I3D, { THREE }) {
const module = await import("./vendor/MeshLoader.js");
return class Impl extends I3D {
constructor() {
super();
}
load(url, onLoad, onProgress, onError) {
return (new module.MeshLoader()).load(url, onLoad, onProgress, onError);
}
transform(obj) {
return obj;
}
}
}

View file

@ -1,33 +0,0 @@
import { toCreasedNormals } from "./vendor/utils/BufferGeometryUtils.js";
export default async function(I3D, { THREE }) {
const module = await import("./vendor/OBJLoader.js");
return class Impl extends I3D {
constructor() {
super();
}
load(url, onLoad, onProgress, onError) {
return (new module.OBJLoader()).load(url, onLoad, onProgress, onError);
}
transform(obj) {
obj.name = "All";
obj.traverse((child) => {
if (child.isMesh) {
child.material = new THREE.MeshPhongMaterial({
color: 0x40464b,
emissive: 0x40464b,
specular: 0xf9f9fa,
shininess: 10,
transparent: true,
});
// smooth the edges: https://discourse.threejs.org/t/how-to-smooth-an-obj-with-threejs/3950/16
child.geometry = toCreasedNormals(child.geometry, (30 / 180) * Math.PI);
}
});
return obj;
}
}
}

View file

@ -1,102 +0,0 @@
import { loadJS } from "../../assets/helpers/loader.js";
await loadJS("https://cdn.jsdelivr.net", "/npm/occt-import-js@0.0.22/dist/occt-import-js.min.js");
async function LoadGeometry(url) {
const occt = await occtimportjs()
let fileUrl = "https://raw.githubusercontent.com/kovacsv/occt-import-js/main/test/testfiles/cax-if/as1_pe_203.stp"
// let response = await fetch(fileUrl)
let response = await fetch(url)
let buffer = await response.arrayBuffer()
let fileBuffer = new Uint8Array(buffer)
return occt.ReadStepFile(fileBuffer, null);
}
export default async function(I3D, { THREE }) {
return class Impl extends I3D {
constructor() {
super();
}
load(url, onLoad, onProgress, onError) {
LoadGeometry(url)
.then(({ success, ...obj }) => {
if (success === false) throw new Error("NOT SUPPORTED");
onLoad(obj);
})
.catch((err) => onError(err));
}
transform({ root, meshes }) {
const group = new THREE.Group();
const recurse = ({ meshes: m, children, name}) => {
if (m.length > 0) {
const obj = new THREE.Object3D();
obj.name = name;
for (let i=0; i<m.length; i++) {
const resultMesh = meshes[m[i]];
console.log(name, resultMesh);
let geometry = new THREE.BufferGeometry()
geometry.setAttribute(
"position",
new THREE.Float32BufferAttribute(resultMesh.attributes.position.array, 3),
)
if (resultMesh.attributes.normal) geometry.setAttribute(
"normal",
new THREE.Float32BufferAttribute(resultMesh.attributes.normal.array, 3),
);
geometry.setIndex(new THREE.BufferAttribute(
Uint32Array.from(resultMesh.index.array),
1,
));
let material = null
if (resultMesh.color) material = new THREE.MeshPhongMaterial({ color: new THREE.Color(
resultMesh.color[0],
resultMesh.color[1],
resultMesh.color[2],
)});
else material = new THREE.MeshPhongMaterial({ color: 0xcccccc });
obj.add(new THREE.Mesh(geometry, material));
}
group.add(obj);
}
if (children.length > 0) children.forEach((obj) => recurse(obj));
}
root.children.forEach(recurse);
return group;
}
transformOld({ meshes, root }) {
console.log(meshes, root)
const targetObject = new THREE.Object3D();
for (let resultMesh of meshes) {
let geometry = new THREE.BufferGeometry()
geometry.setAttribute(
"position",
new THREE.Float32BufferAttribute(resultMesh.attributes.position.array, 3),
)
if (resultMesh.attributes.normal) geometry.setAttribute(
"normal",
new THREE.Float32BufferAttribute(resultMesh.attributes.normal.array, 3),
);
geometry.setIndex(new THREE.BufferAttribute(
Uint32Array.from(resultMesh.index.array),
1,
));
let material = null
if (resultMesh.color) material = new THREE.MeshPhongMaterial({ color: new THREE.Color(
resultMesh.color[0],
resultMesh.color[1],
resultMesh.color[2],
)});
else material = new THREE.MeshPhongMaterial({ color: 0xcccccc });
targetObject.add(new THREE.Mesh(geometry, material));
}
return targetObject;
}
}
}

View file

@ -1,25 +0,0 @@
export default async function(I3D, { THREE }) {
const module = await import("./vendor/STLLoader.js");
return class Impl extends I3D {
constructor() {
super();
}
load(url, onLoad, onProgress, onError) {
return (new module.STLLoader()).load(url, onLoad, onProgress, onError);
}
transform(geometry) {
const material = new THREE.MeshPhongMaterial({
emissive: 0x40464b,
specular: 0xf9f9fa,
shininess: 15,
transparent: true,
});
if (geometry.hasColors) material.vertexColors = true;
else material.color = material.emissive;
return new THREE.Mesh(geometry, material);
}
}
}

View file

@ -1,76 +0,0 @@
export default async function(I3D, { THREE }) {
const module = await import("./vendor/SVGLoader.js");
const threecolor = (color) => {
if (color && color.substr && color.substr(0, 4) === "RGB(") {
function componentToHex(c) {
const hex = c.toString(16);
return hex.length === 1 ? "0" + hex : hex;
}
const [r, g, b] = color.replace(/^RGB\(/, "").replace(/\)/, "").split(",").map((i) => parseInt(i));
return "#" + componentToHex(r) + componentToHex(g) + componentToHex(b);
}
return color;
};
const createMaterial = (color, opacity = 1) => new THREE.MeshBasicMaterial({
color: new THREE.Color().setStyle(color),
opacity,
transparent: true,
side: THREE.DoubleSide,
depthWrite: false,
wireframe: false,
});
return class Impl extends I3D {
constructor() {
super();
}
load(url, onLoad, onProgress, onError) {
return (new module.SVGLoader()).load(url, onLoad, onProgress, onError);
}
transform(data) {
const group = new THREE.Group();
group.name = "All";
group.scale.y *= -1;
let renderOrder = 0;
for (const path of data.paths) {
const fillColor = threecolor(path.userData.style.fill);
if (fillColor !== undefined && fillColor !== "none") {
const material = createMaterial(
fillColor,
path.userData.style.fillOpacity,
);
const shapes = module.SVGLoader.createShapes(path);
for (const shape of shapes) {
const mesh = new THREE.Mesh(
new THREE.ShapeGeometry(shape),
material,
);
mesh.renderOrder = renderOrder++;
group.add(mesh);
}
}
const strokeColor = threecolor(path.userData.style.stroke);
if (strokeColor !== undefined && strokeColor !== "none") {
const material = createMaterial(strokeColor);
for (const subPath of path.subPaths) {
const geometry = module.SVGLoader.pointsToStroke(subPath.getPoints(), path.userData.style);
if (geometry) {
const mesh = new THREE.Mesh(geometry, material);
mesh.renderOrder = renderOrder++;
group.add(mesh);
}
}
}
}
return group;
}
is2D() {
return true;
}
}
}

View file

@ -1,48 +0,0 @@
{
"author": "Filestash Pty Ltd",
"version": "v0.0",
"modules": [
{
"type": "xdg-open",
"mime": "application/fbx",
"entrypoint": "/index_fbx.js",
"application": "3d"
},
{
"type": "xdg-open",
"mime": "model/gltf-binary",
"entrypoint": "/index_gltf.js",
"application": "3d"
},
{
"type": "xdg-open",
"mime": "application/object",
"entrypoint": "/index_obj.js",
"application": "3d"
},
{
"type": "xdg-open",
"mime": "model/stl",
"entrypoint": "/index_stl.js",
"application": "3d"
},
{
"type": "xdg-open",
"mime": "image/svg+xml",
"entrypoint": "/index_svg.js",
"application": "3d"
},
{
"type": "xdg-open",
"mime": "model/step",
"entrypoint": "/index_step.js",
"application": "3d"
},
{
"type": "xdg-open",
"mime": "model/mesh",
"entrypoint": "/index_mesh.js",
"application": "3d"
}
]
}

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

View file

@ -1,906 +0,0 @@
// @ts-nocheck
import {
BufferGeometry,
FileLoader,
Float32BufferAttribute,
Group,
LineBasicMaterial,
LineSegments,
Loader,
Material,
Mesh,
MeshPhongMaterial,
Points,
PointsMaterial,
Vector3,
Color
} from '../../../assets/lib/vendor/three/three.module.js';
// o object_name | g group_name
const _object_pattern = /^[og]\s*(.+)?/;
// mtllib file_reference
const _material_library_pattern = /^mtllib /;
// usemtl material_name
const _material_use_pattern = /^usemtl /;
// usemap map_name
const _map_use_pattern = /^usemap /;
const _face_vertex_data_separator_pattern = /\s+/;
const _vA = new Vector3();
const _vB = new Vector3();
const _vC = new Vector3();
const _ab = new Vector3();
const _cb = new Vector3();
const _color = new Color();
function ParserState() {
const state = {
objects: [],
object: {},
vertices: [],
normals: [],
colors: [],
uvs: [],
materials: {},
materialLibraries: [],
startObject: function ( name, fromDeclaration ) {
// If the current object (initial from reset) is not from a g/o declaration in the parsed
// file. We need to use it for the first parsed g/o to keep things in sync.
if ( this.object && this.object.fromDeclaration === false ) {
this.object.name = name;
this.object.fromDeclaration = ( fromDeclaration !== false );
return;
}
const previousMaterial = ( this.object && typeof this.object.currentMaterial === 'function' ? this.object.currentMaterial() : undefined );
if ( this.object && typeof this.object._finalize === 'function' ) {
this.object._finalize( true );
}
this.object = {
name: name || '',
fromDeclaration: ( fromDeclaration !== false ),
geometry: {
vertices: [],
normals: [],
colors: [],
uvs: [],
hasUVIndices: false
},
materials: [],
smooth: true,
startMaterial: function ( name, libraries ) {
const previous = this._finalize( false );
// New usemtl declaration overwrites an inherited material, except if faces were declared
// after the material, then it must be preserved for proper MultiMaterial continuation.
if ( previous && ( previous.inherited || previous.groupCount <= 0 ) ) {
this.materials.splice( previous.index, 1 );
}
const material = {
index: this.materials.length,
name: name || '',
mtllib: ( Array.isArray( libraries ) && libraries.length > 0 ? libraries[ libraries.length - 1 ] : '' ),
smooth: ( previous !== undefined ? previous.smooth : this.smooth ),
groupStart: ( previous !== undefined ? previous.groupEnd : 0 ),
groupEnd: - 1,
groupCount: - 1,
inherited: false,
clone: function ( index ) {
const cloned = {
index: ( typeof index === 'number' ? index : this.index ),
name: this.name,
mtllib: this.mtllib,
smooth: this.smooth,
groupStart: 0,
groupEnd: - 1,
groupCount: - 1,
inherited: false
};
cloned.clone = this.clone.bind( cloned );
return cloned;
}
};
this.materials.push( material );
return material;
},
currentMaterial: function () {
if ( this.materials.length > 0 ) {
return this.materials[ this.materials.length - 1 ];
}
return undefined;
},
_finalize: function ( end ) {
const lastMultiMaterial = this.currentMaterial();
if ( lastMultiMaterial && lastMultiMaterial.groupEnd === - 1 ) {
lastMultiMaterial.groupEnd = this.geometry.vertices.length / 3;
lastMultiMaterial.groupCount = lastMultiMaterial.groupEnd - lastMultiMaterial.groupStart;
lastMultiMaterial.inherited = false;
}
// Ignore objects tail materials if no face declarations followed them before a new o/g started.
if ( end && this.materials.length > 1 ) {
for ( let mi = this.materials.length - 1; mi >= 0; mi -- ) {
if ( this.materials[ mi ].groupCount <= 0 ) {
this.materials.splice( mi, 1 );
}
}
}
// Guarantee at least one empty material, this makes the creation later more straight forward.
if ( end && this.materials.length === 0 ) {
this.materials.push( {
name: '',
smooth: this.smooth
} );
}
return lastMultiMaterial;
}
};
// Inherit previous objects material.
// Spec tells us that a declared material must be set to all objects until a new material is declared.
// If a usemtl declaration is encountered while this new object is being parsed, it will
// overwrite the inherited material. Exception being that there was already face declarations
// to the inherited material, then it will be preserved for proper MultiMaterial continuation.
if ( previousMaterial && previousMaterial.name && typeof previousMaterial.clone === 'function' ) {
const declared = previousMaterial.clone( 0 );
declared.inherited = true;
this.object.materials.push( declared );
}
this.objects.push( this.object );
},
finalize: function () {
if ( this.object && typeof this.object._finalize === 'function' ) {
this.object._finalize( true );
}
},
parseVertexIndex: function ( value, len ) {
const index = parseInt( value, 10 );
return ( index >= 0 ? index - 1 : index + len / 3 ) * 3;
},
parseNormalIndex: function ( value, len ) {
const index = parseInt( value, 10 );
return ( index >= 0 ? index - 1 : index + len / 3 ) * 3;
},
parseUVIndex: function ( value, len ) {
const index = parseInt( value, 10 );
return ( index >= 0 ? index - 1 : index + len / 2 ) * 2;
},
addVertex: function ( a, b, c ) {
const src = this.vertices;
const dst = this.object.geometry.vertices;
dst.push( src[ a + 0 ], src[ a + 1 ], src[ a + 2 ] );
dst.push( src[ b + 0 ], src[ b + 1 ], src[ b + 2 ] );
dst.push( src[ c + 0 ], src[ c + 1 ], src[ c + 2 ] );
},
addVertexPoint: function ( a ) {
const src = this.vertices;
const dst = this.object.geometry.vertices;
dst.push( src[ a + 0 ], src[ a + 1 ], src[ a + 2 ] );
},
addVertexLine: function ( a ) {
const src = this.vertices;
const dst = this.object.geometry.vertices;
dst.push( src[ a + 0 ], src[ a + 1 ], src[ a + 2 ] );
},
addNormal: function ( a, b, c ) {
const src = this.normals;
const dst = this.object.geometry.normals;
dst.push( src[ a + 0 ], src[ a + 1 ], src[ a + 2 ] );
dst.push( src[ b + 0 ], src[ b + 1 ], src[ b + 2 ] );
dst.push( src[ c + 0 ], src[ c + 1 ], src[ c + 2 ] );
},
addFaceNormal: function ( a, b, c ) {
const src = this.vertices;
const dst = this.object.geometry.normals;
_vA.fromArray( src, a );
_vB.fromArray( src, b );
_vC.fromArray( src, c );
_cb.subVectors( _vC, _vB );
_ab.subVectors( _vA, _vB );
_cb.cross( _ab );
_cb.normalize();
dst.push( _cb.x, _cb.y, _cb.z );
dst.push( _cb.x, _cb.y, _cb.z );
dst.push( _cb.x, _cb.y, _cb.z );
},
addColor: function ( a, b, c ) {
const src = this.colors;
const dst = this.object.geometry.colors;
if ( src[ a ] !== undefined ) dst.push( src[ a + 0 ], src[ a + 1 ], src[ a + 2 ] );
if ( src[ b ] !== undefined ) dst.push( src[ b + 0 ], src[ b + 1 ], src[ b + 2 ] );
if ( src[ c ] !== undefined ) dst.push( src[ c + 0 ], src[ c + 1 ], src[ c + 2 ] );
},
addUV: function ( a, b, c ) {
const src = this.uvs;
const dst = this.object.geometry.uvs;
dst.push( src[ a + 0 ], src[ a + 1 ] );
dst.push( src[ b + 0 ], src[ b + 1 ] );
dst.push( src[ c + 0 ], src[ c + 1 ] );
},
addDefaultUV: function () {
const dst = this.object.geometry.uvs;
dst.push( 0, 0 );
dst.push( 0, 0 );
dst.push( 0, 0 );
},
addUVLine: function ( a ) {
const src = this.uvs;
const dst = this.object.geometry.uvs;
dst.push( src[ a + 0 ], src[ a + 1 ] );
},
addFace: function ( a, b, c, ua, ub, uc, na, nb, nc ) {
const vLen = this.vertices.length;
let ia = this.parseVertexIndex( a, vLen );
let ib = this.parseVertexIndex( b, vLen );
let ic = this.parseVertexIndex( c, vLen );
this.addVertex( ia, ib, ic );
this.addColor( ia, ib, ic );
// normals
if ( na !== undefined && na !== '' ) {
const nLen = this.normals.length;
ia = this.parseNormalIndex( na, nLen );
ib = this.parseNormalIndex( nb, nLen );
ic = this.parseNormalIndex( nc, nLen );
this.addNormal( ia, ib, ic );
} else {
this.addFaceNormal( ia, ib, ic );
}
// uvs
if ( ua !== undefined && ua !== '' ) {
const uvLen = this.uvs.length;
ia = this.parseUVIndex( ua, uvLen );
ib = this.parseUVIndex( ub, uvLen );
ic = this.parseUVIndex( uc, uvLen );
this.addUV( ia, ib, ic );
this.object.geometry.hasUVIndices = true;
} else {
// add placeholder values (for inconsistent face definitions)
this.addDefaultUV();
}
},
addPointGeometry: function ( vertices ) {
this.object.geometry.type = 'Points';
const vLen = this.vertices.length;
for ( let vi = 0, l = vertices.length; vi < l; vi ++ ) {
const index = this.parseVertexIndex( vertices[ vi ], vLen );
this.addVertexPoint( index );
this.addColor( index );
}
},
addLineGeometry: function ( vertices, uvs ) {
this.object.geometry.type = 'Line';
const vLen = this.vertices.length;
const uvLen = this.uvs.length;
for ( let vi = 0, l = vertices.length; vi < l; vi ++ ) {
this.addVertexLine( this.parseVertexIndex( vertices[ vi ], vLen ) );
}
for ( let uvi = 0, l = uvs.length; uvi < l; uvi ++ ) {
this.addUVLine( this.parseUVIndex( uvs[ uvi ], uvLen ) );
}
}
};
state.startObject( '', false );
return state;
}
//
class OBJLoader extends Loader {
constructor( manager ) {
super( manager );
this.materials = null;
}
load( url, onLoad, onProgress, onError ) {
const scope = this;
const loader = new FileLoader( this.manager );
loader.setPath( this.path );
loader.setRequestHeader( this.requestHeader );
loader.setWithCredentials( this.withCredentials );
loader.load( url, function ( text ) {
try {
onLoad( scope.parse( text ) );
} catch ( e ) {
if ( onError ) {
onError( e );
} else {
console.error( e );
}
scope.manager.itemError( url );
}
}, onProgress, onError );
}
setMaterials( materials ) {
this.materials = materials;
return this;
}
parse( text ) {
const state = new ParserState();
if ( text.indexOf( '\r\n' ) !== - 1 ) {
// This is faster than String.split with regex that splits on both
text = text.replace( /\r\n/g, '\n' );
}
if ( text.indexOf( '\\\n' ) !== - 1 ) {
// join lines separated by a line continuation character (\)
text = text.replace( /\\\n/g, '' );
}
const lines = text.split( '\n' );
let result = [];
for ( let i = 0, l = lines.length; i < l; i ++ ) {
const line = lines[ i ].trimStart();
if ( line.length === 0 ) continue;
const lineFirstChar = line.charAt( 0 );
// @todo invoke passed in handler if any
if ( lineFirstChar === '#' ) continue; // skip comments
if ( lineFirstChar === 'v' ) {
const data = line.split( _face_vertex_data_separator_pattern );
switch ( data[ 0 ] ) {
case 'v':
state.vertices.push(
parseFloat( data[ 1 ] ),
parseFloat( data[ 2 ] ),
parseFloat( data[ 3 ] )
);
if ( data.length >= 7 ) {
_color.setRGB(
parseFloat( data[ 4 ] ),
parseFloat( data[ 5 ] ),
parseFloat( data[ 6 ] )
).convertSRGBToLinear();
state.colors.push( _color.r, _color.g, _color.b );
} else {
// if no colors are defined, add placeholders so color and vertex indices match
state.colors.push( undefined, undefined, undefined );
}
break;
case 'vn':
state.normals.push(
parseFloat( data[ 1 ] ),
parseFloat( data[ 2 ] ),
parseFloat( data[ 3 ] )
);
break;
case 'vt':
state.uvs.push(
parseFloat( data[ 1 ] ),
parseFloat( data[ 2 ] )
);
break;
}
} else if ( lineFirstChar === 'f' ) {
const lineData = line.slice( 1 ).trim();
const vertexData = lineData.split( _face_vertex_data_separator_pattern );
const faceVertices = [];
// Parse the face vertex data into an easy to work with format
for ( let j = 0, jl = vertexData.length; j < jl; j ++ ) {
const vertex = vertexData[ j ];
if ( vertex.length > 0 ) {
const vertexParts = vertex.split( '/' );
faceVertices.push( vertexParts );
}
}
// Draw an edge between the first vertex and all subsequent vertices to form an n-gon
const v1 = faceVertices[ 0 ];
for ( let j = 1, jl = faceVertices.length - 1; j < jl; j ++ ) {
const v2 = faceVertices[ j ];
const v3 = faceVertices[ j + 1 ];
state.addFace(
v1[ 0 ], v2[ 0 ], v3[ 0 ],
v1[ 1 ], v2[ 1 ], v3[ 1 ],
v1[ 2 ], v2[ 2 ], v3[ 2 ]
);
}
} else if ( lineFirstChar === 'l' ) {
const lineParts = line.substring( 1 ).trim().split( ' ' );
let lineVertices = [];
const lineUVs = [];
if ( line.indexOf( '/' ) === - 1 ) {
lineVertices = lineParts;
} else {
for ( let li = 0, llen = lineParts.length; li < llen; li ++ ) {
const parts = lineParts[ li ].split( '/' );
if ( parts[ 0 ] !== '' ) lineVertices.push( parts[ 0 ] );
if ( parts[ 1 ] !== '' ) lineUVs.push( parts[ 1 ] );
}
}
state.addLineGeometry( lineVertices, lineUVs );
} else if ( lineFirstChar === 'p' ) {
const lineData = line.slice( 1 ).trim();
const pointData = lineData.split( ' ' );
state.addPointGeometry( pointData );
} else if ( ( result = _object_pattern.exec( line ) ) !== null ) {
// o object_name
// or
// g group_name
// WORKAROUND: https://bugs.chromium.org/p/v8/issues/detail?id=2869
// let name = result[ 0 ].slice( 1 ).trim();
const name = ( ' ' + result[ 0 ].slice( 1 ).trim() ).slice( 1 );
state.startObject( name );
} else if ( _material_use_pattern.test( line ) ) {
// material
state.object.startMaterial( line.substring( 7 ).trim(), state.materialLibraries );
} else if ( _material_library_pattern.test( line ) ) {
// mtl file
state.materialLibraries.push( line.substring( 7 ).trim() );
} else if ( _map_use_pattern.test( line ) ) {
// the line is parsed but ignored since the loader assumes textures are defined MTL files
// (according to https://www.okino.com/conv/imp_wave.htm, 'usemap' is the old-style Wavefront texture reference method)
console.warn( 'THREE.OBJLoader: Rendering identifier "usemap" not supported. Textures must be defined in MTL files.' );
} else if ( lineFirstChar === 's' ) {
result = line.split( ' ' );
// smooth shading
// @todo Handle files that have varying smooth values for a set of faces inside one geometry,
// but does not define a usemtl for each face set.
// This should be detected and a dummy material created (later MultiMaterial and geometry groups).
// This requires some care to not create extra material on each smooth value for "normal" obj files.
// where explicit usemtl defines geometry groups.
// Example asset: examples/models/obj/cerberus/Cerberus.obj
/*
* http://paulbourke.net/dataformats/obj/
*
* From chapter "Grouping" Syntax explanation "s group_number":
* "group_number is the smoothing group number. To turn off smoothing groups, use a value of 0 or off.
* Polygonal elements use group numbers to put elements in different smoothing groups. For free-form
* surfaces, smoothing groups are either turned on or off; there is no difference between values greater
* than 0."
*/
if ( result.length > 1 ) {
const value = result[ 1 ].trim().toLowerCase();
state.object.smooth = ( value !== '0' && value !== 'off' );
} else {
// ZBrush can produce "s" lines #11707
state.object.smooth = true;
}
const material = state.object.currentMaterial();
if ( material ) material.smooth = state.object.smooth;
} else {
// Handle null terminated files without exception
if ( line === '\0' ) continue;
console.warn( 'THREE.OBJLoader: Unexpected line: "' + line + '"' );
}
}
state.finalize();
const container = new Group();
container.materialLibraries = [].concat( state.materialLibraries );
const hasPrimitives = ! ( state.objects.length === 1 && state.objects[ 0 ].geometry.vertices.length === 0 );
if ( hasPrimitives === true ) {
for ( let i = 0, l = state.objects.length; i < l; i ++ ) {
const object = state.objects[ i ];
const geometry = object.geometry;
const materials = object.materials;
const isLine = ( geometry.type === 'Line' );
const isPoints = ( geometry.type === 'Points' );
let hasVertexColors = false;
// Skip o/g line declarations that did not follow with any faces
if ( geometry.vertices.length === 0 ) continue;
const buffergeometry = new BufferGeometry();
buffergeometry.setAttribute( 'position', new Float32BufferAttribute( geometry.vertices, 3 ) );
if ( geometry.normals.length > 0 ) {
buffergeometry.setAttribute( 'normal', new Float32BufferAttribute( geometry.normals, 3 ) );
}
if ( geometry.colors.length > 0 ) {
hasVertexColors = true;
buffergeometry.setAttribute( 'color', new Float32BufferAttribute( geometry.colors, 3 ) );
}
if ( geometry.hasUVIndices === true ) {
buffergeometry.setAttribute( 'uv', new Float32BufferAttribute( geometry.uvs, 2 ) );
}
// Create materials
const createdMaterials = [];
for ( let mi = 0, miLen = materials.length; mi < miLen; mi ++ ) {
const sourceMaterial = materials[ mi ];
const materialHash = sourceMaterial.name + '_' + sourceMaterial.smooth + '_' + hasVertexColors;
let material = state.materials[ materialHash ];
if ( this.materials !== null ) {
material = this.materials.create( sourceMaterial.name );
// mtl etc. loaders probably can't create line materials correctly, copy properties to a line material.
if ( isLine && material && ! ( material instanceof LineBasicMaterial ) ) {
const materialLine = new LineBasicMaterial();
Material.prototype.copy.call( materialLine, material );
materialLine.color.copy( material.color );
material = materialLine;
} else if ( isPoints && material && ! ( material instanceof PointsMaterial ) ) {
const materialPoints = new PointsMaterial( { size: 10, sizeAttenuation: false } );
Material.prototype.copy.call( materialPoints, material );
materialPoints.color.copy( material.color );
materialPoints.map = material.map;
material = materialPoints;
}
}
if ( material === undefined ) {
if ( isLine ) {
material = new LineBasicMaterial();
} else if ( isPoints ) {
material = new PointsMaterial( { size: 1, sizeAttenuation: false } );
} else {
material = new MeshPhongMaterial();
}
material.name = sourceMaterial.name;
material.flatShading = sourceMaterial.smooth ? false : true;
material.vertexColors = hasVertexColors;
state.materials[ materialHash ] = material;
}
createdMaterials.push( material );
}
// Create mesh
let mesh;
if ( createdMaterials.length > 1 ) {
for ( let mi = 0, miLen = materials.length; mi < miLen; mi ++ ) {
const sourceMaterial = materials[ mi ];
buffergeometry.addGroup( sourceMaterial.groupStart, sourceMaterial.groupCount, mi );
}
if ( isLine ) {
mesh = new LineSegments( buffergeometry, createdMaterials );
} else if ( isPoints ) {
mesh = new Points( buffergeometry, createdMaterials );
} else {
mesh = new Mesh( buffergeometry, createdMaterials );
}
} else {
if ( isLine ) {
mesh = new LineSegments( buffergeometry, createdMaterials[ 0 ] );
} else if ( isPoints ) {
mesh = new Points( buffergeometry, createdMaterials[ 0 ] );
} else {
mesh = new Mesh( buffergeometry, createdMaterials[ 0 ] );
}
}
mesh.name = object.name;
container.add( mesh );
}
} else {
// if there is only the default parser state object with no geometry data, interpret data as point cloud
if ( state.vertices.length > 0 ) {
const material = new PointsMaterial( { size: 1, sizeAttenuation: false } );
const buffergeometry = new BufferGeometry();
buffergeometry.setAttribute( 'position', new Float32BufferAttribute( state.vertices, 3 ) );
if ( state.colors.length > 0 && state.colors[ 0 ] !== undefined ) {
buffergeometry.setAttribute( 'color', new Float32BufferAttribute( state.colors, 3 ) );
material.vertexColors = true;
}
const points = new Points( buffergeometry, material );
container.add( points );
}
}
return container;
}
}
export { OBJLoader };

View file

@ -1,411 +0,0 @@
// @ts-nocheck
import {
BufferAttribute,
BufferGeometry,
Color,
FileLoader,
Float32BufferAttribute,
Loader,
Vector3
} from '../../../assets/lib/vendor/three/three.module.js';
/**
* Description: A THREE loader for STL ASCII files, as created by Solidworks and other CAD programs.
*
* Supports both binary and ASCII encoded files, with automatic detection of type.
*
* The loader returns a non-indexed buffer geometry.
*
* Limitations:
* Binary decoding supports "Magics" color format (http://en.wikipedia.org/wiki/STL_(file_format)#Color_in_binary_STL).
* There is perhaps some question as to how valid it is to always assume little-endian-ness.
* ASCII decoding assumes file is UTF-8.
*
* Usage:
* const loader = new STLLoader();
* loader.load( './models/stl/slotted_disk.stl', function ( geometry ) {
* scene.add( new THREE.Mesh( geometry ) );
* });
*
* For binary STLs geometry might contain colors for vertices. To use it:
* // use the same code to load STL as above
* if (geometry.hasColors) {
* material = new THREE.MeshPhongMaterial({ opacity: geometry.alpha, vertexColors: true });
* } else { .... }
* const mesh = new THREE.Mesh( geometry, material );
*
* For ASCII STLs containing multiple solids, each solid is assigned to a different group.
* Groups can be used to assign a different color by defining an array of materials with the same length of
* geometry.groups and passing it to the Mesh constructor:
*
* const mesh = new THREE.Mesh( geometry, material );
*
* For example:
*
* const materials = [];
* const nGeometryGroups = geometry.groups.length;
*
* const colorMap = ...; // Some logic to index colors.
*
* for (let i = 0; i < nGeometryGroups; i++) {
*
* const material = new THREE.MeshPhongMaterial({
* color: colorMap[i],
* wireframe: false
* });
*
* }
*
* materials.push(material);
* const mesh = new THREE.Mesh(geometry, materials);
*/
class STLLoader extends Loader {
constructor( manager ) {
super( manager );
}
load( url, onLoad, onProgress, onError ) {
const scope = this;
const loader = new FileLoader( this.manager );
loader.setPath( this.path );
loader.setResponseType( 'arraybuffer' );
loader.setRequestHeader( this.requestHeader );
loader.setWithCredentials( this.withCredentials );
loader.load( url, function ( text ) {
try {
onLoad( scope.parse( text ) );
} catch ( e ) {
if ( onError ) {
onError( e );
} else {
console.error( e );
}
scope.manager.itemError( url );
}
}, onProgress, onError );
}
parse( data ) {
function isBinary( data ) {
const reader = new DataView( data );
const face_size = ( 32 / 8 * 3 ) + ( ( 32 / 8 * 3 ) * 3 ) + ( 16 / 8 );
const n_faces = reader.getUint32( 80, true );
const expect = 80 + ( 32 / 8 ) + ( n_faces * face_size );
if ( expect === reader.byteLength ) {
return true;
}
// An ASCII STL data must begin with 'solid ' as the first six bytes.
// However, ASCII STLs lacking the SPACE after the 'd' are known to be
// plentiful. So, check the first 5 bytes for 'solid'.
// Several encodings, such as UTF-8, precede the text with up to 5 bytes:
// https://en.wikipedia.org/wiki/Byte_order_mark#Byte_order_marks_by_encoding
// Search for "solid" to start anywhere after those prefixes.
// US-ASCII ordinal values for 's', 'o', 'l', 'i', 'd'
const solid = [ 115, 111, 108, 105, 100 ];
for ( let off = 0; off < 5; off ++ ) {
// If "solid" text is matched to the current offset, declare it to be an ASCII STL.
if ( matchDataViewAt( solid, reader, off ) ) return false;
}
// Couldn't find "solid" text at the beginning; it is binary STL.
return true;
}
function matchDataViewAt( query, reader, offset ) {
// Check if each byte in query matches the corresponding byte from the current offset
for ( let i = 0, il = query.length; i < il; i ++ ) {
if ( query[ i ] !== reader.getUint8( offset + i ) ) return false;
}
return true;
}
function parseBinary( data ) {
const reader = new DataView( data );
const faces = reader.getUint32( 80, true );
let r, g, b, hasColors = false, colors;
let defaultR, defaultG, defaultB, alpha;
// process STL header
// check for default color in header ("COLOR=rgba" sequence).
for ( let index = 0; index < 80 - 10; index ++ ) {
if ( ( reader.getUint32( index, false ) == 0x434F4C4F /*COLO*/ ) &&
( reader.getUint8( index + 4 ) == 0x52 /*'R'*/ ) &&
( reader.getUint8( index + 5 ) == 0x3D /*'='*/ ) ) {
hasColors = true;
colors = new Float32Array( faces * 3 * 3 );
defaultR = reader.getUint8( index + 6 ) / 255;
defaultG = reader.getUint8( index + 7 ) / 255;
defaultB = reader.getUint8( index + 8 ) / 255;
alpha = reader.getUint8( index + 9 ) / 255;
}
}
const dataOffset = 84;
const faceLength = 12 * 4 + 2;
const geometry = new BufferGeometry();
const vertices = new Float32Array( faces * 3 * 3 );
const normals = new Float32Array( faces * 3 * 3 );
const color = new Color();
for ( let face = 0; face < faces; face ++ ) {
const start = dataOffset + face * faceLength;
const normalX = reader.getFloat32( start, true );
const normalY = reader.getFloat32( start + 4, true );
const normalZ = reader.getFloat32( start + 8, true );
if ( hasColors ) {
const packedColor = reader.getUint16( start + 48, true );
if ( ( packedColor & 0x8000 ) === 0 ) {
// facet has its own unique color
r = ( packedColor & 0x1F ) / 31;
g = ( ( packedColor >> 5 ) & 0x1F ) / 31;
b = ( ( packedColor >> 10 ) & 0x1F ) / 31;
} else {
r = defaultR;
g = defaultG;
b = defaultB;
}
}
for ( let i = 1; i <= 3; i ++ ) {
const vertexstart = start + i * 12;
const componentIdx = ( face * 3 * 3 ) + ( ( i - 1 ) * 3 );
vertices[ componentIdx ] = reader.getFloat32( vertexstart, true );
vertices[ componentIdx + 1 ] = reader.getFloat32( vertexstart + 4, true );
vertices[ componentIdx + 2 ] = reader.getFloat32( vertexstart + 8, true );
normals[ componentIdx ] = normalX;
normals[ componentIdx + 1 ] = normalY;
normals[ componentIdx + 2 ] = normalZ;
if ( hasColors ) {
color.set( r, g, b ).convertSRGBToLinear();
colors[ componentIdx ] = color.r;
colors[ componentIdx + 1 ] = color.g;
colors[ componentIdx + 2 ] = color.b;
}
}
}
geometry.setAttribute( 'position', new BufferAttribute( vertices, 3 ) );
geometry.setAttribute( 'normal', new BufferAttribute( normals, 3 ) );
if ( hasColors ) {
geometry.setAttribute( 'color', new BufferAttribute( colors, 3 ) );
geometry.hasColors = true;
geometry.alpha = alpha;
}
return geometry;
}
function parseASCII( data ) {
const geometry = new BufferGeometry();
const patternSolid = /solid([\s\S]*?)endsolid/g;
const patternFace = /facet([\s\S]*?)endfacet/g;
const patternName = /solid\s(.+)/;
let faceCounter = 0;
const patternFloat = /[\s]+([+-]?(?:\d*)(?:\.\d*)?(?:[eE][+-]?\d+)?)/.source;
const patternVertex = new RegExp( 'vertex' + patternFloat + patternFloat + patternFloat, 'g' );
const patternNormal = new RegExp( 'normal' + patternFloat + patternFloat + patternFloat, 'g' );
const vertices = [];
const normals = [];
const groupNames = [];
const normal = new Vector3();
let result;
let groupCount = 0;
let startVertex = 0;
let endVertex = 0;
while ( ( result = patternSolid.exec( data ) ) !== null ) {
startVertex = endVertex;
const solid = result[ 0 ];
const name = ( result = patternName.exec( solid ) ) !== null ? result[ 1 ] : '';
groupNames.push( name );
while ( ( result = patternFace.exec( solid ) ) !== null ) {
let vertexCountPerFace = 0;
let normalCountPerFace = 0;
const text = result[ 0 ];
while ( ( result = patternNormal.exec( text ) ) !== null ) {
normal.x = parseFloat( result[ 1 ] );
normal.y = parseFloat( result[ 2 ] );
normal.z = parseFloat( result[ 3 ] );
normalCountPerFace ++;
}
while ( ( result = patternVertex.exec( text ) ) !== null ) {
vertices.push( parseFloat( result[ 1 ] ), parseFloat( result[ 2 ] ), parseFloat( result[ 3 ] ) );
normals.push( normal.x, normal.y, normal.z );
vertexCountPerFace ++;
endVertex ++;
}
// every face have to own ONE valid normal
if ( normalCountPerFace !== 1 ) {
console.error( 'THREE.STLLoader: Something isn\'t right with the normal of face number ' + faceCounter );
}
// each face have to own THREE valid vertices
if ( vertexCountPerFace !== 3 ) {
console.error( 'THREE.STLLoader: Something isn\'t right with the vertices of face number ' + faceCounter );
}
faceCounter ++;
}
const start = startVertex;
const count = endVertex - startVertex;
geometry.userData.groupNames = groupNames;
geometry.addGroup( start, count, groupCount );
groupCount ++;
}
geometry.setAttribute( 'position', new Float32BufferAttribute( vertices, 3 ) );
geometry.setAttribute( 'normal', new Float32BufferAttribute( normals, 3 ) );
return geometry;
}
function ensureString( buffer ) {
if ( typeof buffer !== 'string' ) {
return new TextDecoder().decode( buffer );
}
return buffer;
}
function ensureBinary( buffer ) {
if ( typeof buffer === 'string' ) {
const array_buffer = new Uint8Array( buffer.length );
for ( let i = 0; i < buffer.length; i ++ ) {
array_buffer[ i ] = buffer.charCodeAt( i ) & 0xff; // implicitly assumes little-endian
}
return array_buffer.buffer || array_buffer;
} else {
return buffer;
}
}
// start
const binData = ensureBinary( data );
return isBinary( binData ) ? parseBinary( binData ) : parseASCII( ensureString( data ) );
}
}
export { STLLoader };

File diff suppressed because it is too large Load diff

View file

@ -1,81 +0,0 @@
// @ts-nocheck
import {
Curve,
Vector3,
Vector4
} from '../../../../assets/lib/vendor/three/three.module.js';
import * as NURBSUtils from './NURBSUtils.js';
/**
* NURBS curve object
*
* Derives from Curve, overriding getPoint and getTangent.
*
* Implementation is based on (x, y [, z=0 [, w=1]]) control points with w=weight.
*
**/
class NURBSCurve extends Curve {
constructor(
degree,
knots /* array of reals */,
controlPoints /* array of Vector(2|3|4) */,
startKnot /* index in knots */,
endKnot /* index in knots */
) {
super();
this.degree = degree;
this.knots = knots;
this.controlPoints = [];
// Used by periodic NURBS to remove hidden spans
this.startKnot = startKnot || 0;
this.endKnot = endKnot || ( this.knots.length - 1 );
for ( let i = 0; i < controlPoints.length; ++ i ) {
// ensure Vector4 for control points
const point = controlPoints[ i ];
this.controlPoints[ i ] = new Vector4( point.x, point.y, point.z, point.w );
}
}
getPoint( t, optionalTarget = new Vector3() ) {
const point = optionalTarget;
const u = this.knots[ this.startKnot ] + t * ( this.knots[ this.endKnot ] - this.knots[ this.startKnot ] ); // linear mapping t->u
// following results in (wx, wy, wz, w) homogeneous point
const hpoint = NURBSUtils.calcBSplinePoint( this.degree, this.knots, this.controlPoints, u );
if ( hpoint.w !== 1.0 ) {
// project to 3D space: (wx, wy, wz, w) -> (x, y, z, 1)
hpoint.divideScalar( hpoint.w );
}
return point.set( hpoint.x, hpoint.y, hpoint.z );
}
getTangent( t, optionalTarget = new Vector3() ) {
const tangent = optionalTarget;
const u = this.knots[ 0 ] + t * ( this.knots[ this.knots.length - 1 ] - this.knots[ 0 ] );
const ders = NURBSUtils.calcNURBSDerivatives( this.degree, this.knots, this.controlPoints, u, 1 );
tangent.copy( ders[ 1 ] ).normalize();
return tangent;
}
}
export { NURBSCurve };

View file

@ -1,488 +0,0 @@
// @ts-nocheck
import {
Vector3,
Vector4
} from '../../../../assets/lib/vendor/three/three.module.js';
/**
* NURBS utils
*
* See NURBSCurve and NURBSSurface.
**/
/**************************************************************
* NURBS Utils
**************************************************************/
/*
Finds knot vector span.
p : degree
u : parametric value
U : knot vector
returns the span
*/
function findSpan( p, u, U ) {
const n = U.length - p - 1;
if ( u >= U[ n ] ) {
return n - 1;
}
if ( u <= U[ p ] ) {
return p;
}
let low = p;
let high = n;
let mid = Math.floor( ( low + high ) / 2 );
while ( u < U[ mid ] || u >= U[ mid + 1 ] ) {
if ( u < U[ mid ] ) {
high = mid;
} else {
low = mid;
}
mid = Math.floor( ( low + high ) / 2 );
}
return mid;
}
/*
Calculate basis functions. See The NURBS Book, page 70, algorithm A2.2
span : span in which u lies
u : parametric point
p : degree
U : knot vector
returns array[p+1] with basis functions values.
*/
function calcBasisFunctions( span, u, p, U ) {
const N = [];
const left = [];
const right = [];
N[ 0 ] = 1.0;
for ( let j = 1; j <= p; ++ j ) {
left[ j ] = u - U[ span + 1 - j ];
right[ j ] = U[ span + j ] - u;
let saved = 0.0;
for ( let r = 0; r < j; ++ r ) {
const rv = right[ r + 1 ];
const lv = left[ j - r ];
const temp = N[ r ] / ( rv + lv );
N[ r ] = saved + rv * temp;
saved = lv * temp;
}
N[ j ] = saved;
}
return N;
}
/*
Calculate B-Spline curve points. See The NURBS Book, page 82, algorithm A3.1.
p : degree of B-Spline
U : knot vector
P : control points (x, y, z, w)
u : parametric point
returns point for given u
*/
function calcBSplinePoint( p, U, P, u ) {
const span = findSpan( p, u, U );
const N = calcBasisFunctions( span, u, p, U );
const C = new Vector4( 0, 0, 0, 0 );
for ( let j = 0; j <= p; ++ j ) {
const point = P[ span - p + j ];
const Nj = N[ j ];
const wNj = point.w * Nj;
C.x += point.x * wNj;
C.y += point.y * wNj;
C.z += point.z * wNj;
C.w += point.w * Nj;
}
return C;
}
/*
Calculate basis functions derivatives. See The NURBS Book, page 72, algorithm A2.3.
span : span in which u lies
u : parametric point
p : degree
n : number of derivatives to calculate
U : knot vector
returns array[n+1][p+1] with basis functions derivatives
*/
function calcBasisFunctionDerivatives( span, u, p, n, U ) {
const zeroArr = [];
for ( let i = 0; i <= p; ++ i )
zeroArr[ i ] = 0.0;
const ders = [];
for ( let i = 0; i <= n; ++ i )
ders[ i ] = zeroArr.slice( 0 );
const ndu = [];
for ( let i = 0; i <= p; ++ i )
ndu[ i ] = zeroArr.slice( 0 );
ndu[ 0 ][ 0 ] = 1.0;
const left = zeroArr.slice( 0 );
const right = zeroArr.slice( 0 );
for ( let j = 1; j <= p; ++ j ) {
left[ j ] = u - U[ span + 1 - j ];
right[ j ] = U[ span + j ] - u;
let saved = 0.0;
for ( let r = 0; r < j; ++ r ) {
const rv = right[ r + 1 ];
const lv = left[ j - r ];
ndu[ j ][ r ] = rv + lv;
const temp = ndu[ r ][ j - 1 ] / ndu[ j ][ r ];
ndu[ r ][ j ] = saved + rv * temp;
saved = lv * temp;
}
ndu[ j ][ j ] = saved;
}
for ( let j = 0; j <= p; ++ j ) {
ders[ 0 ][ j ] = ndu[ j ][ p ];
}
for ( let r = 0; r <= p; ++ r ) {
let s1 = 0;
let s2 = 1;
const a = [];
for ( let i = 0; i <= p; ++ i ) {
a[ i ] = zeroArr.slice( 0 );
}
a[ 0 ][ 0 ] = 1.0;
for ( let k = 1; k <= n; ++ k ) {
let d = 0.0;
const rk = r - k;
const pk = p - k;
if ( r >= k ) {
a[ s2 ][ 0 ] = a[ s1 ][ 0 ] / ndu[ pk + 1 ][ rk ];
d = a[ s2 ][ 0 ] * ndu[ rk ][ pk ];
}
const j1 = ( rk >= - 1 ) ? 1 : - rk;
const j2 = ( r - 1 <= pk ) ? k - 1 : p - r;
for ( let j = j1; j <= j2; ++ j ) {
a[ s2 ][ j ] = ( a[ s1 ][ j ] - a[ s1 ][ j - 1 ] ) / ndu[ pk + 1 ][ rk + j ];
d += a[ s2 ][ j ] * ndu[ rk + j ][ pk ];
}
if ( r <= pk ) {
a[ s2 ][ k ] = - a[ s1 ][ k - 1 ] / ndu[ pk + 1 ][ r ];
d += a[ s2 ][ k ] * ndu[ r ][ pk ];
}
ders[ k ][ r ] = d;
const j = s1;
s1 = s2;
s2 = j;
}
}
let r = p;
for ( let k = 1; k <= n; ++ k ) {
for ( let j = 0; j <= p; ++ j ) {
ders[ k ][ j ] *= r;
}
r *= p - k;
}
return ders;
}
/*
Calculate derivatives of a B-Spline. See The NURBS Book, page 93, algorithm A3.2.
p : degree
U : knot vector
P : control points
u : Parametric points
nd : number of derivatives
returns array[d+1] with derivatives
*/
function calcBSplineDerivatives( p, U, P, u, nd ) {
const du = nd < p ? nd : p;
const CK = [];
const span = findSpan( p, u, U );
const nders = calcBasisFunctionDerivatives( span, u, p, du, U );
const Pw = [];
for ( let i = 0; i < P.length; ++ i ) {
const point = P[ i ].clone();
const w = point.w;
point.x *= w;
point.y *= w;
point.z *= w;
Pw[ i ] = point;
}
for ( let k = 0; k <= du; ++ k ) {
const point = Pw[ span - p ].clone().multiplyScalar( nders[ k ][ 0 ] );
for ( let j = 1; j <= p; ++ j ) {
point.add( Pw[ span - p + j ].clone().multiplyScalar( nders[ k ][ j ] ) );
}
CK[ k ] = point;
}
for ( let k = du + 1; k <= nd + 1; ++ k ) {
CK[ k ] = new Vector4( 0, 0, 0 );
}
return CK;
}
/*
Calculate "K over I"
returns k!/(i!(k-i)!)
*/
function calcKoverI( k, i ) {
let nom = 1;
for ( let j = 2; j <= k; ++ j ) {
nom *= j;
}
let denom = 1;
for ( let j = 2; j <= i; ++ j ) {
denom *= j;
}
for ( let j = 2; j <= k - i; ++ j ) {
denom *= j;
}
return nom / denom;
}
/*
Calculate derivatives (0-nd) of rational curve. See The NURBS Book, page 127, algorithm A4.2.
Pders : result of function calcBSplineDerivatives
returns array with derivatives for rational curve.
*/
function calcRationalCurveDerivatives( Pders ) {
const nd = Pders.length;
const Aders = [];
const wders = [];
for ( let i = 0; i < nd; ++ i ) {
const point = Pders[ i ];
Aders[ i ] = new Vector3( point.x, point.y, point.z );
wders[ i ] = point.w;
}
const CK = [];
for ( let k = 0; k < nd; ++ k ) {
const v = Aders[ k ].clone();
for ( let i = 1; i <= k; ++ i ) {
v.sub( CK[ k - i ].clone().multiplyScalar( calcKoverI( k, i ) * wders[ i ] ) );
}
CK[ k ] = v.divideScalar( wders[ 0 ] );
}
return CK;
}
/*
Calculate NURBS curve derivatives. See The NURBS Book, page 127, algorithm A4.2.
p : degree
U : knot vector
P : control points in homogeneous space
u : parametric points
nd : number of derivatives
returns array with derivatives.
*/
function calcNURBSDerivatives( p, U, P, u, nd ) {
const Pders = calcBSplineDerivatives( p, U, P, u, nd );
return calcRationalCurveDerivatives( Pders );
}
/*
Calculate rational B-Spline surface point. See The NURBS Book, page 134, algorithm A4.3.
p1, p2 : degrees of B-Spline surface
U1, U2 : knot vectors
P : control points (x, y, z, w)
u, v : parametric values
returns point for given (u, v)
*/
function calcSurfacePoint( p, q, U, V, P, u, v, target ) {
const uspan = findSpan( p, u, U );
const vspan = findSpan( q, v, V );
const Nu = calcBasisFunctions( uspan, u, p, U );
const Nv = calcBasisFunctions( vspan, v, q, V );
const temp = [];
for ( let l = 0; l <= q; ++ l ) {
temp[ l ] = new Vector4( 0, 0, 0, 0 );
for ( let k = 0; k <= p; ++ k ) {
const point = P[ uspan - p + k ][ vspan - q + l ].clone();
const w = point.w;
point.x *= w;
point.y *= w;
point.z *= w;
temp[ l ].add( point.multiplyScalar( Nu[ k ] ) );
}
}
const Sw = new Vector4( 0, 0, 0, 0 );
for ( let l = 0; l <= q; ++ l ) {
Sw.add( temp[ l ].multiplyScalar( Nv[ l ] ) );
}
Sw.divideScalar( Sw.w );
target.set( Sw.x, Sw.y, Sw.z );
}
export {
findSpan,
calcBasisFunctions,
calcBSplinePoint,
calcBasisFunctionDerivatives,
calcBSplineDerivatives,
calcKoverI,
calcRationalCurveDerivatives,
calcNURBSDerivatives,
calcSurfacePoint,
};

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

View file

@ -1,15 +0,0 @@
all:
make build
make install
make clean
build:
emcc -O2 -c loader_symbol.c
emcc --no-entry loader_symbol.o -o loader_symbol.wasm
install:
zip -r application_dev.zip .
mv application_dev.zip ../../../dist/data/state/plugins/
clean:
rm *.o *.wasm

View file

@ -1,45 +0,0 @@
import assert from "../../assets/lib/assert.js";
import loadWASM, { writeFS, readFS } from "../../assets/helpers/loader_wasm.js";
export default async function(ITable) {
const { instance } = await loadWASM(import.meta.url, "./loader_symbol.wasm");
return class TableImpl extends ITable {
constructor(response) {
super();
const fdIn = writeFS(new Uint8Array(response));
const fdOut = writeFS(new Uint8Array([]));
const res = assert.truthy(instance.exports["execute"])(fdIn, fdOut);
if (res !== 0) throw new Error(`WASM exited with code=${res}`);
const buffer = readFS(fdOut);
this.header = [
{ name: "Mode", size: 3 },
{ name: "Timestamp", size: 6 },
{ name: "Size", size: 4 },
{ name: "Owner", size: 6 },
{ name: "Group", size: 6 },
{ name: "Object", size: 40 },
];
this.rows = new TextDecoder().decode(buffer).trimRight().split("\n").map((line) => {
const row = line.split(",");
return {
"Object": row[0],
"Timestamp": row[1],
"Size": row[5],
"Mode": row[4],
"Owner": row[2],
"Group": row[3],
};
});
}
getHeader() {
return this.header;
}
getBody() {
return this.rows;
}
};
}

View file

@ -1,89 +0,0 @@
#include <stdio.h>
#include <stdbool.h>
#include <string.h>
#include <emscripten/emscripten.h>
#define ARMAG "!<arch>\n"
#define SARMAG sizeof(ARMAG) - 1
#define AR_HDR_SIZE 60
struct ar_hdr {
char name[16];
char timestamp[12];
char owner[6];
char group[6];
char mode[8];
char size[10];
char fmag[2];
};
EMSCRIPTEN_KEEPALIVE int execute(int fdinput, int fdoutput) {
if (fdinput == 0) {
fprintf(stderr, "ERROR - missing input %d\n", fdinput);
return 1;
}
if (fdoutput == 0) {
fprintf(stderr, "ERROR - missing input %d\n", fdoutput);
return 1;
}
FILE* finput = fdopen(fdinput, "rb");
if (!finput) {
fprintf(stderr, "ERROR - cannot open input file\n");
return 1;
}
FILE* foutput = fdopen(fdoutput, "wb");
if (!foutput) {
fprintf(stderr, "ERROR - cannot open output file\n");
fclose(finput);
return 1;
}
char magic[SARMAG];
size_t c = fread(magic, 1, SARMAG, finput);
if (c == 0) {
fprintf(stderr, "ERROR count=%zu error=%d\n", c, ferror(finput));
fclose(finput);
fclose(foutput);
return 1;
}
if (strncmp(magic, ARMAG, SARMAG) != 0) {
fprintf(stderr, "ERROR bad magic value");
fclose(finput);
fclose(foutput);
return 1;
}
struct ar_hdr header;
while (fread(&header, 1, AR_HDR_SIZE, finput) == AR_HDR_SIZE) {
if (strncmp(header.fmag, "`\n", 2) != 0) {
fprintf(stderr, "Invalid header format.\n");
break;
}
long size = strtol(header.size, NULL, 10);
char filename[17] = {0};
strncpy(filename, header.name, 16);
for (int i = strlen(filename) - 1; i >= 0; i--) {
if (filename[i] == ' ' || filename[i] == '/') {
filename[i] = '\0';
}
}
if (strlen(filename) > 0) fprintf(
foutput,
"%.16s,%ld,%ld,%ld,%ld,%ld\n",
filename,
strtol(header.timestamp, NULL, 10),
strtol(header.owner, NULL, 10),
strtol(header.group, NULL, 10),
strtol(header.mode, NULL, 10),
size,
);
fseek(finput, (size + 1) & ~1, SEEK_CUR);
}
fprintf(stdout, "hello world!\n");
fflush(foutput);
fclose(foutput);
fclose(finput);
return 0;
}

View file

@ -1,11 +0,0 @@
{
"author": "Filestash Pty Ltd",
"version": "v0.0",
"modules": [
{
"type": "xdg-open",
"mime": "application/x-archive",
"application": "table"
}
]
}

View file

@ -1,6 +0,0 @@
all:
make install
install:
zip -r application_map.zip .
mv application_map.zip ../../../dist/data/state/plugins/

View file

@ -1,4 +0,0 @@
export const PLUGINS = [
"plugin_grayscale",
];
export const DEFAULT_TILE_SERVER = "https://tile.openstreetmap.org/{z}/{x}/{y}.png";

View file

@ -1,19 +0,0 @@
import { PLUGINS, DEFAULT_TILE_SERVER } from "./constant.js";
export default async function(IMap) {
const plugins = await Promise.all(PLUGINS.map((name) => import(`./plugins/${name}.js`)));
return class MapImpl extends IMap {
constructor(response, { map, $page }) {
super();
this.response = JSON.parse(new TextDecoder().decode(response));
window.L.tileLayer(DEFAULT_TILE_SERVER, { maxZoom: 21 }).addTo(map);
plugins.forEach((plugin) => plugin.default({ map, $page }));
}
async toGeoJSON() {
return this.response;
}
}
}

View file

@ -1,128 +0,0 @@
import { PLUGINS, DEFAULT_TILE_SERVER } from "./constant.js";
export default async function(IMap) {
const plugins = await Promise.all(PLUGINS.map((name) => import(`./plugins/${name}.js`)));
return class MapImpl extends IMap {
constructor(response, { L, map, $page }) {
super();
const xmlDoc = new DOMParser().parseFromString(
new TextDecoder().decode(response),
"application/xml",
);
this.xmlDoc = xmlDoc;
L.tileLayer(DEFAULT_TILE_SERVER, { maxZoom: 21 }).addTo(map);
plugins.forEach((plugin) => plugin.default({ map, $page }));
}
async toGeoJSON() {
let geoJSON = {
type: "FeatureCollection",
features: [],
};
this.xmlDoc.querySelectorAll("wpt").forEach((wayPoint) => {
geoJSON.features.push({
type: "Feature",
geometry: {
type: "Point",
coordinates: [
Number(wayPoint.getAttribute("lon")),
Number(wayPoint.getAttribute("lat")),
],
},
properties: {
group: "waypoint",
"popup::type": "html",
"popup::content": createLegendFromXML(wayPoint),
},
});
});
this.xmlDoc.querySelectorAll("rte").forEach((route) => {
const coordinates = [];
route.querySelectorAll("rtept").forEach((point) => {
const lon = Number(point.getAttribute("lon"));
const lat = Number(point.getAttribute("lat"));
if (!isNaN(lon) && !isNaN(lat)) {
coordinates.push([lon, lat]);
}
});
if (coordinates.length > 0) geoJSON.features.push({
type: "Feature",
geometry: {
type: "LineString",
coordinates: coordinates,
},
properties: {
group: "path",
weight: 5,
name: route.querySelector("name").textContent,
},
});
});
this.xmlDoc.querySelectorAll("trk").forEach((track) => {
track.querySelectorAll("trkseg").forEach((segment) => {
const group = "Track: " + track.querySelector("name").textContent;
const coordinates = [];
segment.querySelectorAll("trkpt").forEach((point) => {
const lon = Number(point.getAttribute("lon"));
const lat = Number(point.getAttribute("lat"));
if (!isNaN(lon) && !isNaN(lat)) {
coordinates.push([lon, lat]);
}
});
if (coordinates.length > 0) {
geoJSON.features.push({
type: "Feature",
geometry: {
type: "LineString",
coordinates: coordinates,
},
properties: {
group,
color: "brown",
weight: 3,
name: track.querySelector("name").textContent,
},
});
geoJSON.features.push({
type: "Feature",
geometry: {
type: "Point",
coordinates: coordinates[0],
},
properties: {
group,
color: "green",
name: "Start: " + group,
},
});
geoJSON.features.push({
type: "Feature",
geometry: {
type: "Point",
coordinates: coordinates.slice(-1)[0],
},
properties: {
group,
color: "red",
name: "Arrival: " + group,
},
});
}
});
});
return geoJSON;
}
}
}
function createLegendFromXML(point) {
return [...point.children]
.map((tag) => `
<div class="ellipsis" style="line-height: 1rem;">
<strong style="text-transform: capitalize;">${tag.tagName}: </strong>
${tag.textContent || "N/A"}
</div>
`)
.join("");
}

View file

@ -1,38 +0,0 @@
import { PLUGINS, DEFAULT_TILE_SERVER } from "./constant.js";
export default async function(IMap) {
let modules = await Promise.all(PLUGINS.map((name) => import(`./plugins/${name}.js`)));
return class MapImpl extends IMap {
constructor(response, { map, $page }) {
super();
for (let i=0; i<PLUGINS.length; i++) {
modules.forEach((module) => module.default({ map, $page }));
}
const xmlDoc = new DOMParser().parseFromString(
new TextDecoder().decode(response),
"application/xml",
);
const svcURL = (function() {
const svc = xmlDoc.querySelector("Capability OnlineResource");
if (!svc) return "";
return svc.getAttribute("xlink:href");
}());
let _layer = "";
const baseLayers = {};
xmlDoc.querySelectorAll("Layer").forEach((layer) => {
_layer = layer.querySelector("Name").textContent;
baseLayers[layer.querySelector("Title").textContent] = window.L.tileLayer.wms(svcURL, {
layers: _layer,
});
});
map.setView([0, 0], 1);
window.L.tileLayer.wms(svcURL, { layers: _layer }).addTo(map);
window.L.control.layers(baseLayers, {}).addTo(map);
}
async toGeoJSON() {
return null;
}
}
}

View file

@ -1,24 +0,0 @@
{
"author": "Filestash Pty Ltd",
"version": "v0.0",
"modules": [
{
"type": "xdg-open",
"mime": "application/geo+json",
"entrypoint": "/index_geojson.js",
"application": "map"
},
{
"type": "xdg-open",
"mime": "application/vnd.ogc.wms_xml",
"entrypoint": "/index_wms.js",
"application": "map"
},
{
"type": "xdg-open",
"mime": "application/gpx+xml",
"entrypoint": "/index_gpx.js",
"application": "map"
}
]
}

View file

@ -1,9 +0,0 @@
import { createElement } from "../../../assets/lib/skeleton/index.js";
const PERCENT = "85%";
export default async function({ $page }) {
$page.appendChild(createElement(`
<style>#map img.leaflet-tile { filter: grayscale(${PERCENT}); }</style>
`));
}

View file

@ -87,11 +87,12 @@ func Build(r *mux.Router, a App) {
r.HandleFunc(WithBase("/api/plugin"), NewMiddlewareChain(PluginExportHandler, append(middlewares, PublicCORS), a)).Methods("GET", "OPTIONS")
r.HandleFunc(WithBase("/api/config"), NewMiddlewareChain(PublicConfigHandler, append(middlewares, PublicCORS), a)).Methods("GET", "OPTIONS")
middlewares = []Middleware{StaticHeaders, SecureHeaders, PublicCORS, PluginInjector}
r.PathPrefix(WithBase("/assets")).Handler(http.HandlerFunc(NewMiddlewareChain(ServeFile("/"), middlewares, a))).Methods("GET", "OPTIONS")
r.HandleFunc(WithBase("/assets/"+BUILD_REF+"/plugin/{name}.zip/{path:.+}"), NewMiddlewareChain(PluginStaticHandler, middlewares, a)).Methods("GET", "OPTIONS")
r.HandleFunc(WithBase("/assets/"+BUILD_REF+"/plugin/{name}.zip"), NewMiddlewareChain(PluginDownloadHandler, middlewares, a)).Methods("GET")
r.PathPrefix(WithBase("/assets/"+BUILD_REF)).Handler(http.HandlerFunc(NewMiddlewareChain(ServeFile("/"), middlewares, a))).Methods("GET", "OPTIONS")
r.PathPrefix(WithBase("/assets/bundle")).Handler(http.HandlerFunc(NewMiddlewareChain(ServeBundle, middlewares, a))).Methods("GET", "OPTIONS")
r.HandleFunc(WithBase("/sw.js"), http.HandlerFunc(NewMiddlewareChain(ServeFile("/assets/"), middlewares, a))).Methods("GET")
r.HandleFunc(WithBase("/favicon.ico"), NewMiddlewareChain(ServeFavicon, middlewares, a)).Methods("GET")
r.HandleFunc(WithBase("/plugin/{name}/{path:.+}"), NewMiddlewareChain(PluginStaticHandler, middlewares, a)).Methods("GET")
r.HandleFunc(WithBase("/plugin/{name}.zip"), NewMiddlewareChain(PluginDownloadHandler, middlewares, a)).Methods("GET")
// Other endpoints
middlewares = []Middleware{ApiHeaders, PluginInjector, PublicCORS}