mirror of
https://github.com/yeicor-3d/yet-another-cad-viewer.git
synced 2025-12-20 06:27:04 +01:00
Compare commits
9 Commits
v0.1.0-alp
...
v0.1.0
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
d3fbe254cb | ||
|
|
2475a00622 | ||
|
|
4bd025e7d5 | ||
|
|
94e316472a | ||
|
|
258256912b | ||
|
|
075682ff18 | ||
|
|
cb1088fe71 | ||
|
|
682f41d3f8 | ||
|
|
dad2b4471a |
35
.github/workflows/autoupdate.yml
vendored
Normal file
35
.github/workflows/autoupdate.yml
vendored
Normal file
@@ -0,0 +1,35 @@
|
|||||||
|
on: "pull_request_target"
|
||||||
|
|
||||||
|
permissions:
|
||||||
|
pull-requests: "write"
|
||||||
|
contents: "write"
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
dependabot:
|
||||||
|
runs-on: "ubuntu-latest"
|
||||||
|
# Checking the actor will prevent your Action run failing on non-Dependabot
|
||||||
|
# PRs but also ensures that it only does work for Dependabot PRs.
|
||||||
|
if: "${{ github.actor == 'dependabot[bot]' }}"
|
||||||
|
steps:
|
||||||
|
# This first step will fail if there's no metadata and so the approval
|
||||||
|
# will not occur.
|
||||||
|
- name: "Dependabot metadata"
|
||||||
|
id: "dependabot-metadata"
|
||||||
|
uses: "dependabot/fetch-metadata@v1"
|
||||||
|
with:
|
||||||
|
github-token: "${{ secrets.GITHUB_TOKEN }}"
|
||||||
|
# Here the PR gets approved.
|
||||||
|
- uses: "actions/checkout@v4"
|
||||||
|
- name: "Approve a PR"
|
||||||
|
run: "gh pr review --approve $PR_URL"
|
||||||
|
env:
|
||||||
|
PR_URL: "${{ github.event.pull_request.html_url }}"
|
||||||
|
GITHUB_TOKEN: "${{ secrets.GITHUB_TOKEN }}"
|
||||||
|
# Finally, this sets the PR to allow auto-merging for patch and minor
|
||||||
|
# updates if all checks pass
|
||||||
|
- name: "Enable auto-merge for Dependabot PRs"
|
||||||
|
#if: "${{ steps.dependabot-metadata.outputs.update-type != 'version-update:semver-major' }}"
|
||||||
|
run: "gh pr merge --auto --squash $PR_URL"
|
||||||
|
env:
|
||||||
|
PR_URL: "${{ github.event.pull_request.html_url }}"
|
||||||
|
GITHUB_TOKEN: "${{ secrets.GITHUB_TOKEN }}"
|
||||||
6
.github/workflows/build.yml
vendored
6
.github/workflows/build.yml
vendored
@@ -37,8 +37,8 @@ jobs:
|
|||||||
with:
|
with:
|
||||||
python-version: "3.11"
|
python-version: "3.11"
|
||||||
cache: "poetry"
|
cache: "poetry"
|
||||||
- run: "poetry install"
|
- run: "SKIP_BUILD_FRONTEND=true poetry install"
|
||||||
- run: "poetry build"
|
- run: "SKIP_BUILD_FRONTEND=true poetry build"
|
||||||
|
|
||||||
build-logo:
|
build-logo:
|
||||||
name: "Build logo"
|
name: "Build logo"
|
||||||
@@ -53,7 +53,7 @@ jobs:
|
|||||||
with:
|
with:
|
||||||
python-version: "3.11"
|
python-version: "3.11"
|
||||||
cache: "poetry"
|
cache: "poetry"
|
||||||
- run: "poetry install"
|
- run: "SKIP_BUILD_FRONTEND=true poetry install"
|
||||||
- run: "poetry run python yacv_server/logo.py"
|
- run: "poetry run python yacv_server/logo.py"
|
||||||
- run: "cp assets/fox.glb assets/logo_build/fox.glb"
|
- run: "cp assets/fox.glb assets/logo_build/fox.glb"
|
||||||
- uses: "actions/upload-artifact@v4"
|
- uses: "actions/upload-artifact@v4"
|
||||||
|
|||||||
18
.github/workflows/deploy.yml
vendored
18
.github/workflows/deploy.yml
vendored
@@ -17,7 +17,7 @@ concurrency:
|
|||||||
cancel-in-progress: false
|
cancel-in-progress: false
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
deploy:
|
deploy-frontend:
|
||||||
runs-on: "ubuntu-latest"
|
runs-on: "ubuntu-latest"
|
||||||
environment:
|
environment:
|
||||||
name: "github-pages"
|
name: "github-pages"
|
||||||
@@ -35,18 +35,18 @@ jobs:
|
|||||||
name: "logo"
|
name: "logo"
|
||||||
path: "./public"
|
path: "./public"
|
||||||
allow_forks: false
|
allow_forks: false
|
||||||
- uses: "svenstaro/upload-release-action@v2"
|
|
||||||
with:
|
|
||||||
repo_token: "${{ secrets.GITHUB_TOKEN }}"
|
|
||||||
file: "./public/*"
|
|
||||||
asset_name: "frontend"
|
|
||||||
tag: "${{ github.ref }}"
|
|
||||||
overwrite: true
|
|
||||||
file_glob: true
|
|
||||||
- uses: "actions/configure-pages@v4"
|
- uses: "actions/configure-pages@v4"
|
||||||
- uses: "actions/upload-pages-artifact@v3"
|
- uses: "actions/upload-pages-artifact@v3"
|
||||||
with:
|
with:
|
||||||
path: 'public'
|
path: 'public'
|
||||||
- id: "deployment"
|
- id: "deployment"
|
||||||
uses: "actions/deploy-pages@v4"
|
uses: "actions/deploy-pages@v4"
|
||||||
|
- run: 'zip -r frontend.zip public'
|
||||||
|
- uses: "svenstaro/upload-release-action@v2"
|
||||||
|
with:
|
||||||
|
repo_token: "${{ secrets.GITHUB_TOKEN }}"
|
||||||
|
file: "frontend.zip"
|
||||||
|
tag: "${{ github.ref }}"
|
||||||
|
overwrite: true
|
||||||
|
|
||||||
|
# TODO: deploy-backend
|
||||||
|
|||||||
@@ -24,7 +24,7 @@ The [logo](yacv_server/logo.py) also works as an example of how to use the viewe
|
|||||||
|
|
||||||
To see the live updates you will need to run the [yacv_server](yacv_server) and
|
To see the live updates you will need to run the [yacv_server](yacv_server) and
|
||||||
open [the viewer](https://yeicor-3d.github.io/yet-another-cad-viewer/) with
|
open [the viewer](https://yeicor-3d.github.io/yet-another-cad-viewer/) with
|
||||||
the `preloadModels=ws://<host>:32323/` query parameter (by default it already tries localhost).
|
the `preload=ws://<host>:32323/` query parameter (by default it already tries localhost).
|
||||||
|
|
||||||
Note that [yacv_server](yacv_server) also hosts the frontend at `http://localhost:32323/` if you have no access to the
|
Note that [yacv_server](yacv_server) also hosts the frontend at `http://localhost:32323/` if you have no access to the
|
||||||
internet.
|
internet.
|
||||||
@@ -32,13 +32,13 @@ internet.
|
|||||||
### Static deployment
|
### Static deployment
|
||||||
|
|
||||||
To deploy the viewer and models as a static website you can simply copy the latest build directory to your server.
|
To deploy the viewer and models as a static website you can simply copy the latest build directory to your server.
|
||||||
To load models use the `preloadModels=...` query parameter in the URL.
|
To load models use the `preload=...` query parameter in the URL.
|
||||||
It can be set multiple times to load multiple models.
|
It can be set multiple times to load multiple models.
|
||||||
|
|
||||||
Note that you can simply reuse the [main deployment](https://yeicor-3d.github.io/yet-another-cad-viewer/) and host only
|
Note that you can simply reuse the [main deployment](https://yeicor-3d.github.io/yet-another-cad-viewer/) and host only
|
||||||
your own models (linking them from the viewer with the `preloadModels` query parameter).
|
your own models (linking them from the viewer with the `preload` query parameter).
|
||||||
|
|
||||||
To see a working example of a static deployment you can check out
|
To see a working example of a static deployment you can check out
|
||||||
the [demo](https://yeicor-3d.github.io/yet-another-cad-viewer/?preloadModels=base.glb&preloadModels=fox.glb&preloadModels=img.jpg.glb&preloadModels=location.glb).
|
the [demo](https://yeicor-3d.github.io/yet-another-cad-viewer/?preload=base.glb&preload=fox.glb&preload=img.jpg.glb&preload=location.glb).
|
||||||
|
|
||||||

|

|
||||||
|
|||||||
7
build.py
7
build.py
@@ -2,6 +2,7 @@ import os
|
|||||||
import subprocess
|
import subprocess
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
# When building the backend, make sure the frontend is built first
|
if os.getenv('SKIP_BUILD_FRONTEND') is None:
|
||||||
subprocess.run(['yarn', 'install'], check=True)
|
# When building the backend, make sure the frontend is built first
|
||||||
subprocess.run(['yarn', 'build', '--outDir', 'yacv_server/frontend'], check=True)
|
subprocess.run(['yarn', 'install'], check=True)
|
||||||
|
subprocess.run(['yarn', 'build', '--outDir', 'yacv_server/frontend'], check=True)
|
||||||
|
|||||||
@@ -46,7 +46,7 @@ async function onModelRemoveRequest(name: string) {
|
|||||||
let networkMgr = new NetworkManager();
|
let networkMgr = new NetworkManager();
|
||||||
networkMgr.addEventListener('update', (e) => onModelLoadRequest(e as NetworkUpdateEvent));
|
networkMgr.addEventListener('update', (e) => onModelLoadRequest(e as NetworkUpdateEvent));
|
||||||
// Start loading all configured models ASAP
|
// Start loading all configured models ASAP
|
||||||
for (let model of settings.preloadModels) {
|
for (let model of settings.preload) {
|
||||||
networkMgr.load(model);
|
networkMgr.load(model);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
// These are the default values for the settings, which are overridden below
|
// These are the default values for the settings, which are overridden below
|
||||||
export const settings = {
|
export const settings = {
|
||||||
preloadModels: [
|
preload: [
|
||||||
// @ts-ignore
|
// @ts-ignore
|
||||||
// new URL('../../assets/fox.glb', import.meta.url).href,
|
// new URL('../../assets/fox.glb', import.meta.url).href,
|
||||||
// @ts-ignore
|
// @ts-ignore
|
||||||
@@ -29,15 +29,19 @@ function parseSetting(name: string, value: string): any {
|
|||||||
if (arrayElem) name = name.slice(0, -2);
|
if (arrayElem) name = name.slice(0, -2);
|
||||||
let prevValue = (settings as any)[name];
|
let prevValue = (settings as any)[name];
|
||||||
if (prevValue === undefined) throw new Error(`Unknown setting: ${name}`);
|
if (prevValue === undefined) throw new Error(`Unknown setting: ${name}`);
|
||||||
if (Array.isArray(prevValue) && !arrayElem) {
|
if (Array.isArray(prevValue)) {
|
||||||
let toExtend = []
|
if (!arrayElem) {
|
||||||
if (!firstTimeNames.includes(name)) {
|
let toExtend = []
|
||||||
firstTimeNames.push(name);
|
if (!firstTimeNames.includes(name)) {
|
||||||
|
firstTimeNames.push(name);
|
||||||
|
} else {
|
||||||
|
toExtend = prevValue;
|
||||||
|
}
|
||||||
|
toExtend.push(parseSetting(name + ".0", value));
|
||||||
|
return toExtend;
|
||||||
} else {
|
} else {
|
||||||
toExtend = prevValue;
|
prevValue = prevValue[0];
|
||||||
}
|
}
|
||||||
toExtend.push(parseSetting(name + ".0", value));
|
|
||||||
return toExtend;
|
|
||||||
}
|
}
|
||||||
switch (typeof prevValue) {
|
switch (typeof prevValue) {
|
||||||
case 'boolean':
|
case 'boolean':
|
||||||
@@ -47,7 +51,7 @@ function parseSetting(name: string, value: string): any {
|
|||||||
case 'string':
|
case 'string':
|
||||||
return value;
|
return value;
|
||||||
default:
|
default:
|
||||||
throw new Error(`Unknown setting type: ${typeof prevValue}`);
|
throw new Error(`Unknown setting type: ${typeof prevValue} -- ${prevValue}`);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -88,7 +88,7 @@ let selectionListener = (event: MouseEvent) => {
|
|||||||
// Find all hit objects and select the wanted one based on the filter
|
// Find all hit objects and select the wanted one based on the filter
|
||||||
const hits = raycaster.intersectObject(scene, true);
|
const hits = raycaster.intersectObject(scene, true);
|
||||||
let hit = hits.find((hit: Intersection<Object3D>) => {
|
let hit = hits.find((hit: Intersection<Object3D>) => {
|
||||||
if (!hit.object || !(hit.object as any).isMesh) return false;
|
if (!hit.object) return false;
|
||||||
const kind = hit.object.type
|
const kind = hit.object.type
|
||||||
let isFace = kind === 'Mesh' || kind === 'SkinnedMesh';
|
let isFace = kind === 'Mesh' || kind === 'SkinnedMesh';
|
||||||
let isEdge = kind === 'Line' || kind === 'LineSegments';
|
let isEdge = kind === 'Line' || kind === 'LineSegments';
|
||||||
|
|||||||
@@ -6,6 +6,7 @@ import vueJsx from '@vitejs/plugin-vue-jsx'
|
|||||||
|
|
||||||
// https://vitejs.dev/config/
|
// https://vitejs.dev/config/
|
||||||
export default defineConfig({
|
export default defineConfig({
|
||||||
|
base: "",
|
||||||
plugins: [
|
plugins: [
|
||||||
vue({
|
vue({
|
||||||
template: {
|
template: {
|
||||||
|
|||||||
@@ -28,6 +28,7 @@ if not os.path.exists(FRONTEND_BASE_PATH):
|
|||||||
FRONTEND_BASE_PATH = os.path.join(FILE_DIR, '..', 'dist')
|
FRONTEND_BASE_PATH = os.path.join(FILE_DIR, '..', 'dist')
|
||||||
else:
|
else:
|
||||||
logger.warning('Frontend not found at %s', FRONTEND_BASE_PATH)
|
logger.warning('Frontend not found at %s', FRONTEND_BASE_PATH)
|
||||||
|
FRONTEND_BASE_PATH = None
|
||||||
|
|
||||||
# Define the API paths (also available at the root path for simplicity)
|
# Define the API paths (also available at the root path for simplicity)
|
||||||
UPDATES_API_PATH = '/api/updates'
|
UPDATES_API_PATH = '/api/updates'
|
||||||
@@ -84,7 +85,8 @@ class Server:
|
|||||||
self.app.router.add_get('/', self._entrypoint)
|
self.app.router.add_get('/', self._entrypoint)
|
||||||
# - Static files from the frontend
|
# - Static files from the frontend
|
||||||
self.app.router.add_get('/{path:(.*/|)}', _index_handler) # Any folder -> index.html
|
self.app.router.add_get('/{path:(.*/|)}', _index_handler) # Any folder -> index.html
|
||||||
self.app.router.add_static('/', path=FRONTEND_BASE_PATH, name='static_frontend')
|
if FRONTEND_BASE_PATH is not None:
|
||||||
|
self.app.router.add_static('/', path=FRONTEND_BASE_PATH, name='static_frontend')
|
||||||
# --- CORS ---
|
# --- CORS ---
|
||||||
cors = aiohttp_cors.setup(self.app, defaults={
|
cors = aiohttp_cors.setup(self.app, defaults={
|
||||||
"*": aiohttp_cors.ResourceOptions(
|
"*": aiohttp_cors.ResourceOptions(
|
||||||
|
|||||||
Reference in New Issue
Block a user