├── packages ├── shared │ ├── .gitignore │ ├── .prettierrc.json │ ├── .eslintrc.cjs │ ├── package.json │ └── stores.ts ├── cadmium │ ├── .gitignore │ ├── README.md │ ├── package.json │ ├── examples │ │ └── project_simple_extrusion.rs │ ├── src │ │ ├── error.rs │ │ ├── realization.rs │ │ ├── test.rs │ │ ├── main.rs │ │ ├── test_inputs │ │ │ ├── three_touching_circles.cadmium │ │ │ └── points_on_lines.cadmium │ │ ├── lib.rs │ │ ├── step.rs │ │ └── archetypes.rs │ └── Cargo.toml ├── config-eslint │ ├── .gitignore │ ├── .prettierrc.json │ ├── index.js │ └── package.json ├── config-prettier │ ├── .gitignore │ ├── .prettierrc.json │ ├── package.json │ └── index.js └── config-typescript │ ├── .gitignore │ ├── package.json │ ├── vite.json │ └── base.json ├── .prettierignore ├── applications ├── web │ ├── src │ │ ├── base.ts │ │ ├── global.d.ts │ │ ├── vite-env.d.ts │ │ ├── main.ts │ │ ├── components │ │ │ ├── controls │ │ │ │ ├── CubeGizmo │ │ │ │ │ ├── CubeGizmo.svelte.ts │ │ │ │ │ └── CubeGizmo.ts │ │ │ │ └── CadControls │ │ │ │ │ ├── useControlsContext.ts │ │ │ │ │ ├── CadControls.svelte │ │ │ │ │ └── TrackballControls.svelte.d.ts │ │ │ ├── BottomBar.svelte │ │ │ ├── tools │ │ │ │ ├── Select.svelte │ │ │ │ ├── NewCircle.svelte │ │ │ │ ├── NewLine.svelte │ │ │ │ └── NewRectangle.svelte │ │ │ ├── features │ │ │ │ ├── Point.svelte │ │ │ │ └── Plane.svelte │ │ │ ├── Sketch.svelte │ │ │ ├── Circle.svelte │ │ │ ├── Solid.svelte │ │ │ ├── MainDisplay.svelte │ │ │ ├── Arc.svelte │ │ │ ├── Line.svelte │ │ │ ├── ToolBar.svelte │ │ │ ├── Point2D.svelte │ │ │ ├── Point3D.svelte │ │ │ ├── FeatureHistory.svelte │ │ │ ├── SolidItem.svelte │ │ │ └── AppBar.svelte │ │ ├── app.postcss │ │ ├── utils.ts │ │ ├── App.svelte │ │ └── tests │ │ │ └── unit │ │ │ └── index.test.js │ ├── .prettierrc.json │ ├── .npmrc │ ├── public │ │ ├── favicon.png │ │ ├── favicons │ │ │ ├── favicon.ico │ │ │ ├── favicon-16x16.png │ │ │ ├── favicon-32x32.png │ │ │ ├── mstile-150x150.png │ │ │ ├── apple-touch-icon.png │ │ │ ├── android-chrome-192x192.png │ │ │ ├── browserconfig.xml │ │ │ ├── site.webmanifest │ │ │ └── safari-pinned-tab.svg │ │ ├── envmap │ │ │ └── hdr │ │ │ │ └── kloofendal_28d_misty_puresky_1k.hdr │ │ ├── actions │ │ │ ├── just_a_point.svg │ │ │ ├── sketch_min.svg │ │ │ ├── draft_min.svg │ │ │ ├── chamfer_min.svg │ │ │ ├── fillet_min.svg │ │ │ ├── plane_min.svg │ │ │ ├── extrude_min.svg │ │ │ ├── solve_min.svg │ │ │ ├── point_outline.svg │ │ │ ├── simple_point_min.svg │ │ │ ├── point_min.svg │ │ │ ├── point_min_icon.svg │ │ │ ├── revolve_min.svg │ │ │ ├── step_min.svg │ │ │ ├── line.svg │ │ │ ├── circle.svg │ │ │ ├── rectangle.svg │ │ │ ├── horizontal.svg │ │ │ ├── hole_min.svg │ │ │ └── part.svg │ │ ├── github-mark.svg │ │ ├── cadmium_logo_min.svg │ │ └── constraints │ │ │ ├── vertical.svg │ │ │ ├── horizontal.svg │ │ │ ├── parallel.svg │ │ │ ├── perpendicular.svg │ │ │ └── equal.svg │ ├── tsconfig.json │ ├── svelte.config.js │ ├── .gitignore │ ├── .eslintignore │ ├── .prettierignore │ ├── playwright.config.js │ ├── .eslintrc.cjs │ ├── postcss.config.cjs │ ├── tailwind.config.cjs │ ├── tests │ │ └── e2e │ │ │ └── test.ts │ ├── vite.config.ts │ ├── index.html │ └── package.json └── tauri │ ├── build.rs │ ├── icons │ ├── 32x32.png │ ├── icon.icns │ ├── icon.ico │ ├── icon.png │ ├── 128x128.png │ ├── 128x128@2x.png │ ├── StoreLogo.png │ ├── Square30x30Logo.png │ ├── Square44x44Logo.png │ ├── Square71x71Logo.png │ ├── Square89x89Logo.png │ ├── Square107x107Logo.png │ ├── Square142x142Logo.png │ ├── Square150x150Logo.png │ ├── Square284x284Logo.png │ ├── Square310x310Logo.png │ ├── ios │ │ ├── AppIcon-512@2x.png │ │ ├── AppIcon-20x20@1x.png │ │ ├── AppIcon-20x20@2x.png │ │ ├── AppIcon-20x20@3x.png │ │ ├── AppIcon-29x29@1x.png │ │ ├── AppIcon-29x29@2x.png │ │ ├── AppIcon-29x29@3x.png │ │ ├── AppIcon-40x40@1x.png │ │ ├── AppIcon-40x40@2x.png │ │ ├── AppIcon-40x40@3x.png │ │ ├── AppIcon-60x60@2x.png │ │ ├── AppIcon-60x60@3x.png │ │ ├── AppIcon-76x76@1x.png │ │ ├── AppIcon-76x76@2x.png │ │ ├── AppIcon-20x20@2x-1.png │ │ ├── AppIcon-29x29@2x-1.png │ │ ├── AppIcon-40x40@2x-1.png │ │ └── AppIcon-83.5x83.5@2x.png │ └── android │ │ ├── mipmap-hdpi │ │ ├── ic_launcher.png │ │ ├── ic_launcher_round.png │ │ └── ic_launcher_foreground.png │ │ ├── mipmap-mdpi │ │ ├── ic_launcher.png │ │ ├── ic_launcher_round.png │ │ └── ic_launcher_foreground.png │ │ ├── mipmap-xhdpi │ │ ├── ic_launcher.png │ │ ├── ic_launcher_round.png │ │ └── ic_launcher_foreground.png │ │ ├── mipmap-xxhdpi │ │ ├── ic_launcher.png │ │ ├── ic_launcher_round.png │ │ └── ic_launcher_foreground.png │ │ └── mipmap-xxxhdpi │ │ ├── ic_launcher.png │ │ ├── ic_launcher_round.png │ │ └── ic_launcher_foreground.png │ ├── .gitignore │ ├── src │ ├── main.rs │ └── lib.rs │ ├── capabilities │ └── default.json │ ├── Cargo.toml │ └── tauri.conf.json ├── pnpm-workspace.yaml ├── .npmrc ├── .git-blame-ignore-revs ├── .devcontainer ├── scripts │ └── on-create.sh └── devcontainer.json ├── Cargo.toml ├── .vscode ├── settings.json ├── extensions.json └── launch.json ├── .gitignore ├── .github ├── dependabot.yml └── workflows │ └── ci.yml ├── turbo.json ├── package.json ├── Notes.md └── LICENSE.md /packages/shared/.gitignore: -------------------------------------------------------------------------------- 1 | /node_modules/ 2 | -------------------------------------------------------------------------------- /.prettierignore: -------------------------------------------------------------------------------- 1 | LICENSE.md 2 | .vscode 3 | pkg/ 4 | -------------------------------------------------------------------------------- /applications/web/src/base.ts: -------------------------------------------------------------------------------- 1 | export const base = "./" 2 | -------------------------------------------------------------------------------- /packages/cadmium/.gitignore: -------------------------------------------------------------------------------- 1 | test_svgs 2 | node_modules 3 | -------------------------------------------------------------------------------- /applications/web/.prettierrc.json: -------------------------------------------------------------------------------- 1 | "@repo/config-prettier" 2 | -------------------------------------------------------------------------------- /applications/web/src/global.d.ts: -------------------------------------------------------------------------------- 1 | declare module "nurbs" 2 | -------------------------------------------------------------------------------- /packages/config-eslint/.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | .turbo 3 | -------------------------------------------------------------------------------- /packages/config-prettier/.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | .turbo 3 | -------------------------------------------------------------------------------- /packages/shared/.prettierrc.json: -------------------------------------------------------------------------------- 1 | "@repo/config-prettier" 2 | -------------------------------------------------------------------------------- /packages/config-eslint/.prettierrc.json: -------------------------------------------------------------------------------- 1 | "@repo/config-prettier" 2 | -------------------------------------------------------------------------------- /packages/config-typescript/.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | .turbo 3 | -------------------------------------------------------------------------------- /packages/config-prettier/.prettierrc.json: -------------------------------------------------------------------------------- 1 | "@repo/config-prettier" 2 | -------------------------------------------------------------------------------- /applications/tauri/build.rs: -------------------------------------------------------------------------------- 1 | fn main() { 2 | tauri_build::build() 3 | } 4 | -------------------------------------------------------------------------------- /applications/web/.npmrc: -------------------------------------------------------------------------------- 1 | engine-strict=true 2 | resolution-mode=highest 3 | -------------------------------------------------------------------------------- /packages/cadmium/README.md: -------------------------------------------------------------------------------- 1 | # To Build 2 | 3 | ```bash 4 | npm run wasm 5 | ``` 6 | -------------------------------------------------------------------------------- /pnpm-workspace.yaml: -------------------------------------------------------------------------------- 1 | packages: 2 | - "packages/*" 3 | - "applications/*" 4 | -------------------------------------------------------------------------------- /.npmrc: -------------------------------------------------------------------------------- 1 | engine-strict=true 2 | resolution-mode=highest 3 | use-node-version=20.13.1 4 | -------------------------------------------------------------------------------- /applications/web/src/vite-env.d.ts: -------------------------------------------------------------------------------- 1 | /// 2 | /// 3 | -------------------------------------------------------------------------------- /.git-blame-ignore-revs: -------------------------------------------------------------------------------- 1 | # ignore prettier config formatting change 2 | 2a782702640f4a84c32bbbc898ad6e5b54d0fe2b 3 | -------------------------------------------------------------------------------- /applications/tauri/icons/32x32.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CADmium-Co/CADmium/HEAD/applications/tauri/icons/32x32.png -------------------------------------------------------------------------------- /applications/tauri/icons/icon.icns: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CADmium-Co/CADmium/HEAD/applications/tauri/icons/icon.icns -------------------------------------------------------------------------------- /applications/tauri/icons/icon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CADmium-Co/CADmium/HEAD/applications/tauri/icons/icon.ico -------------------------------------------------------------------------------- /applications/tauri/icons/icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CADmium-Co/CADmium/HEAD/applications/tauri/icons/icon.png -------------------------------------------------------------------------------- /applications/tauri/icons/128x128.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CADmium-Co/CADmium/HEAD/applications/tauri/icons/128x128.png -------------------------------------------------------------------------------- /applications/web/public/favicon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CADmium-Co/CADmium/HEAD/applications/web/public/favicon.png -------------------------------------------------------------------------------- /applications/tauri/.gitignore: -------------------------------------------------------------------------------- 1 | # Generated by Cargo 2 | # will have compiled files and executables 3 | /target/ 4 | /gen/schemas 5 | -------------------------------------------------------------------------------- /applications/tauri/icons/128x128@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CADmium-Co/CADmium/HEAD/applications/tauri/icons/128x128@2x.png -------------------------------------------------------------------------------- /applications/tauri/icons/StoreLogo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CADmium-Co/CADmium/HEAD/applications/tauri/icons/StoreLogo.png -------------------------------------------------------------------------------- /applications/tauri/icons/Square30x30Logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CADmium-Co/CADmium/HEAD/applications/tauri/icons/Square30x30Logo.png -------------------------------------------------------------------------------- /applications/tauri/icons/Square44x44Logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CADmium-Co/CADmium/HEAD/applications/tauri/icons/Square44x44Logo.png -------------------------------------------------------------------------------- /applications/tauri/icons/Square71x71Logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CADmium-Co/CADmium/HEAD/applications/tauri/icons/Square71x71Logo.png -------------------------------------------------------------------------------- /applications/tauri/icons/Square89x89Logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CADmium-Co/CADmium/HEAD/applications/tauri/icons/Square89x89Logo.png -------------------------------------------------------------------------------- /applications/web/public/favicons/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CADmium-Co/CADmium/HEAD/applications/web/public/favicons/favicon.ico -------------------------------------------------------------------------------- /applications/tauri/icons/Square107x107Logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CADmium-Co/CADmium/HEAD/applications/tauri/icons/Square107x107Logo.png -------------------------------------------------------------------------------- /applications/tauri/icons/Square142x142Logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CADmium-Co/CADmium/HEAD/applications/tauri/icons/Square142x142Logo.png -------------------------------------------------------------------------------- /applications/tauri/icons/Square150x150Logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CADmium-Co/CADmium/HEAD/applications/tauri/icons/Square150x150Logo.png -------------------------------------------------------------------------------- /applications/tauri/icons/Square284x284Logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CADmium-Co/CADmium/HEAD/applications/tauri/icons/Square284x284Logo.png -------------------------------------------------------------------------------- /applications/tauri/icons/Square310x310Logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CADmium-Co/CADmium/HEAD/applications/tauri/icons/Square310x310Logo.png -------------------------------------------------------------------------------- /applications/tauri/icons/ios/AppIcon-512@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CADmium-Co/CADmium/HEAD/applications/tauri/icons/ios/AppIcon-512@2x.png -------------------------------------------------------------------------------- /.devcontainer/scripts/on-create.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | cargo install wasm-pack cargo-watch 4 | 5 | pnpm dlx playwright install --with-deps chromium 6 | -------------------------------------------------------------------------------- /applications/tauri/icons/ios/AppIcon-20x20@1x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CADmium-Co/CADmium/HEAD/applications/tauri/icons/ios/AppIcon-20x20@1x.png -------------------------------------------------------------------------------- /applications/tauri/icons/ios/AppIcon-20x20@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CADmium-Co/CADmium/HEAD/applications/tauri/icons/ios/AppIcon-20x20@2x.png -------------------------------------------------------------------------------- /applications/tauri/icons/ios/AppIcon-20x20@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CADmium-Co/CADmium/HEAD/applications/tauri/icons/ios/AppIcon-20x20@3x.png -------------------------------------------------------------------------------- /applications/tauri/icons/ios/AppIcon-29x29@1x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CADmium-Co/CADmium/HEAD/applications/tauri/icons/ios/AppIcon-29x29@1x.png -------------------------------------------------------------------------------- /applications/tauri/icons/ios/AppIcon-29x29@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CADmium-Co/CADmium/HEAD/applications/tauri/icons/ios/AppIcon-29x29@2x.png -------------------------------------------------------------------------------- /applications/tauri/icons/ios/AppIcon-29x29@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CADmium-Co/CADmium/HEAD/applications/tauri/icons/ios/AppIcon-29x29@3x.png -------------------------------------------------------------------------------- /applications/tauri/icons/ios/AppIcon-40x40@1x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CADmium-Co/CADmium/HEAD/applications/tauri/icons/ios/AppIcon-40x40@1x.png -------------------------------------------------------------------------------- /applications/tauri/icons/ios/AppIcon-40x40@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CADmium-Co/CADmium/HEAD/applications/tauri/icons/ios/AppIcon-40x40@2x.png -------------------------------------------------------------------------------- /applications/tauri/icons/ios/AppIcon-40x40@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CADmium-Co/CADmium/HEAD/applications/tauri/icons/ios/AppIcon-40x40@3x.png -------------------------------------------------------------------------------- /applications/tauri/icons/ios/AppIcon-60x60@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CADmium-Co/CADmium/HEAD/applications/tauri/icons/ios/AppIcon-60x60@2x.png -------------------------------------------------------------------------------- /applications/tauri/icons/ios/AppIcon-60x60@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CADmium-Co/CADmium/HEAD/applications/tauri/icons/ios/AppIcon-60x60@3x.png -------------------------------------------------------------------------------- /applications/tauri/icons/ios/AppIcon-76x76@1x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CADmium-Co/CADmium/HEAD/applications/tauri/icons/ios/AppIcon-76x76@1x.png -------------------------------------------------------------------------------- /applications/tauri/icons/ios/AppIcon-76x76@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CADmium-Co/CADmium/HEAD/applications/tauri/icons/ios/AppIcon-76x76@2x.png -------------------------------------------------------------------------------- /applications/tauri/icons/ios/AppIcon-20x20@2x-1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CADmium-Co/CADmium/HEAD/applications/tauri/icons/ios/AppIcon-20x20@2x-1.png -------------------------------------------------------------------------------- /applications/tauri/icons/ios/AppIcon-29x29@2x-1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CADmium-Co/CADmium/HEAD/applications/tauri/icons/ios/AppIcon-29x29@2x-1.png -------------------------------------------------------------------------------- /applications/tauri/icons/ios/AppIcon-40x40@2x-1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CADmium-Co/CADmium/HEAD/applications/tauri/icons/ios/AppIcon-40x40@2x-1.png -------------------------------------------------------------------------------- /applications/web/public/favicons/favicon-16x16.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CADmium-Co/CADmium/HEAD/applications/web/public/favicons/favicon-16x16.png -------------------------------------------------------------------------------- /applications/web/public/favicons/favicon-32x32.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CADmium-Co/CADmium/HEAD/applications/web/public/favicons/favicon-32x32.png -------------------------------------------------------------------------------- /applications/web/public/favicons/mstile-150x150.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CADmium-Co/CADmium/HEAD/applications/web/public/favicons/mstile-150x150.png -------------------------------------------------------------------------------- /applications/tauri/icons/ios/AppIcon-83.5x83.5@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CADmium-Co/CADmium/HEAD/applications/tauri/icons/ios/AppIcon-83.5x83.5@2x.png -------------------------------------------------------------------------------- /applications/web/public/favicons/apple-touch-icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CADmium-Co/CADmium/HEAD/applications/web/public/favicons/apple-touch-icon.png -------------------------------------------------------------------------------- /applications/web/public/favicons/android-chrome-192x192.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CADmium-Co/CADmium/HEAD/applications/web/public/favicons/android-chrome-192x192.png -------------------------------------------------------------------------------- /applications/tauri/icons/android/mipmap-hdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CADmium-Co/CADmium/HEAD/applications/tauri/icons/android/mipmap-hdpi/ic_launcher.png -------------------------------------------------------------------------------- /applications/tauri/icons/android/mipmap-mdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CADmium-Co/CADmium/HEAD/applications/tauri/icons/android/mipmap-mdpi/ic_launcher.png -------------------------------------------------------------------------------- /applications/tauri/icons/android/mipmap-xhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CADmium-Co/CADmium/HEAD/applications/tauri/icons/android/mipmap-xhdpi/ic_launcher.png -------------------------------------------------------------------------------- /applications/tauri/icons/android/mipmap-xxhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CADmium-Co/CADmium/HEAD/applications/tauri/icons/android/mipmap-xxhdpi/ic_launcher.png -------------------------------------------------------------------------------- /applications/web/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": ["@repo/config-typescript/vite.json", "@tsconfig/svelte/tsconfig.json"], 3 | "include": ["src", "vite.config.ts"] 4 | } 5 | -------------------------------------------------------------------------------- /packages/shared/.eslintrc.cjs: -------------------------------------------------------------------------------- 1 | /** @type {import("eslint").Linter.Config} */ 2 | module.exports = { 3 | root: true, 4 | extends: ["@repo/eslint-config/index.js"], 5 | } 6 | -------------------------------------------------------------------------------- /applications/tauri/icons/android/mipmap-xxxhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CADmium-Co/CADmium/HEAD/applications/tauri/icons/android/mipmap-xxxhdpi/ic_launcher.png -------------------------------------------------------------------------------- /applications/web/svelte.config.js: -------------------------------------------------------------------------------- 1 | export default { 2 | // Consult https://svelte.dev/docs#compile-time-svelte-preprocess 3 | // for more information about preprocessors 4 | } 5 | -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [workspace] 2 | members = ["packages/cadmium", "applications/tauri"] 3 | resolver = "2" 4 | 5 | [patch.crates-io] 6 | tsify = { git = "https://github.com/siefkenj/tsify" } 7 | -------------------------------------------------------------------------------- /applications/tauri/icons/android/mipmap-hdpi/ic_launcher_round.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CADmium-Co/CADmium/HEAD/applications/tauri/icons/android/mipmap-hdpi/ic_launcher_round.png -------------------------------------------------------------------------------- /applications/tauri/icons/android/mipmap-mdpi/ic_launcher_round.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CADmium-Co/CADmium/HEAD/applications/tauri/icons/android/mipmap-mdpi/ic_launcher_round.png -------------------------------------------------------------------------------- /applications/tauri/icons/android/mipmap-xhdpi/ic_launcher_round.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CADmium-Co/CADmium/HEAD/applications/tauri/icons/android/mipmap-xhdpi/ic_launcher_round.png -------------------------------------------------------------------------------- /applications/tauri/icons/android/mipmap-xxhdpi/ic_launcher_round.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CADmium-Co/CADmium/HEAD/applications/tauri/icons/android/mipmap-xxhdpi/ic_launcher_round.png -------------------------------------------------------------------------------- /applications/tauri/icons/android/mipmap-xxxhdpi/ic_launcher_round.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CADmium-Co/CADmium/HEAD/applications/tauri/icons/android/mipmap-xxxhdpi/ic_launcher_round.png -------------------------------------------------------------------------------- /applications/tauri/icons/android/mipmap-hdpi/ic_launcher_foreground.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CADmium-Co/CADmium/HEAD/applications/tauri/icons/android/mipmap-hdpi/ic_launcher_foreground.png -------------------------------------------------------------------------------- /applications/tauri/icons/android/mipmap-mdpi/ic_launcher_foreground.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CADmium-Co/CADmium/HEAD/applications/tauri/icons/android/mipmap-mdpi/ic_launcher_foreground.png -------------------------------------------------------------------------------- /applications/tauri/icons/android/mipmap-xhdpi/ic_launcher_foreground.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CADmium-Co/CADmium/HEAD/applications/tauri/icons/android/mipmap-xhdpi/ic_launcher_foreground.png -------------------------------------------------------------------------------- /applications/web/public/envmap/hdr/kloofendal_28d_misty_puresky_1k.hdr: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CADmium-Co/CADmium/HEAD/applications/web/public/envmap/hdr/kloofendal_28d_misty_puresky_1k.hdr -------------------------------------------------------------------------------- /applications/tauri/icons/android/mipmap-xxhdpi/ic_launcher_foreground.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CADmium-Co/CADmium/HEAD/applications/tauri/icons/android/mipmap-xxhdpi/ic_launcher_foreground.png -------------------------------------------------------------------------------- /applications/tauri/icons/android/mipmap-xxxhdpi/ic_launcher_foreground.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CADmium-Co/CADmium/HEAD/applications/tauri/icons/android/mipmap-xxxhdpi/ic_launcher_foreground.png -------------------------------------------------------------------------------- /packages/config-typescript/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@repo/config-typescript", 3 | "version": "0.0.0", 4 | "private": true, 5 | "publishConfig": { 6 | "access": "public" 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /.vscode/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "npm.packageManager": "pnpm", 3 | "vite.autoStart": false, 4 | "vite.buildCommand": "pnpm run build", 5 | "vite.devCommand": "pnpm run dev", 6 | "vite.showTerminal": true 7 | } 8 | -------------------------------------------------------------------------------- /applications/web/.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | node_modules 3 | /build 4 | /dist 5 | /test-results 6 | .env 7 | *.env.* 8 | vite.config.js.timestamp-* 9 | vite.config.ts.timestamp-* 10 | types.guard.ts 11 | -------------------------------------------------------------------------------- /applications/tauri/src/main.rs: -------------------------------------------------------------------------------- 1 | // Prevents additional console window on Windows in release, DO NOT REMOVE!! 2 | #![cfg_attr(not(debug_assertions), windows_subsystem = "windows")] 3 | 4 | fn main() { 5 | app_lib::run(); 6 | } 7 | -------------------------------------------------------------------------------- /applications/tauri/src/lib.rs: -------------------------------------------------------------------------------- 1 | #[cfg_attr(mobile, tauri::mobile_entry_point)] 2 | pub fn run() { 3 | tauri::Builder::default() 4 | .run(tauri::generate_context!()) 5 | .expect("error while running tauri application"); 6 | } 7 | -------------------------------------------------------------------------------- /applications/web/public/actions/just_a_point.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /applications/web/.eslintignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | node_modules 3 | /build 4 | /.svelte-kit 5 | /package 6 | .env 7 | .env.* 8 | !.env.example 9 | 10 | # Ignore files for PNPM, NPM and YARN 11 | pnpm-lock.yaml 12 | package-lock.json 13 | yarn.lock 14 | -------------------------------------------------------------------------------- /applications/web/.prettierignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | node_modules 3 | /build 4 | /.svelte-kit 5 | /package 6 | .env 7 | .env.* 8 | !.env.example 9 | 10 | # Ignore files for PNPM, NPM and YARN 11 | pnpm-lock.yaml 12 | package-lock.json 13 | yarn.lock 14 | -------------------------------------------------------------------------------- /packages/config-prettier/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@repo/config-prettier", 3 | "version": "0.0.0", 4 | "main": "index.js", 5 | "private": true, 6 | "dependencies": {}, 7 | "publishConfig": { 8 | "access": "public" 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /applications/web/src/main.ts: -------------------------------------------------------------------------------- 1 | import "@fontsource-variable/manrope" 2 | import "./app.postcss" 3 | import App from "./App.svelte" 4 | import {mount} from "svelte" 5 | 6 | const app = mount(App, {target: document.getElementById("app")}) 7 | 8 | export default app 9 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | /node_modules/ 3 | /build 4 | /dist 5 | /target 6 | /packages/cadmium/target 7 | **/.svelte-kit 8 | /package 9 | *.env* 10 | vite.config.js.timestamp-* 11 | vite.config.ts.timestamp-* 12 | .turbo 13 | /.pnpm-store/ 14 | **/*.obj 15 | **/*.step 16 | -------------------------------------------------------------------------------- /.vscode/extensions.json: -------------------------------------------------------------------------------- 1 | { 2 | "recommendations": [ 3 | "rust-lang.rust-analyzer", 4 | "svelte.svelte-vscode", 5 | "bradlc.vscode-tailwindcss", 6 | "esbenp.prettier-vscode", 7 | "ms-playwright.playwright", 8 | "vadimcn.vscode-lldb", 9 | "dbaeumer.vscode-eslint" 10 | ] 11 | } 12 | -------------------------------------------------------------------------------- /packages/shared/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "shared", 3 | "version": "0.0.1", 4 | "private": true, 5 | "type": "module", 6 | "scripts": { 7 | "clean": "rimraf node_modules" 8 | }, 9 | "dependencies": { 10 | "cadmium": "workspace:*", 11 | "svelte": "^4.2.12", 12 | "three": "^0.164.1" 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /applications/web/playwright.config.js: -------------------------------------------------------------------------------- 1 | /** @type {import('@playwright/test').PlaywrightTestConfig} */ 2 | const config = { 3 | webServer: { 4 | command: "npm run build && npm run preview", 5 | port: 4173, 6 | }, 7 | testDir: "tests", 8 | testMatch: /(.+\.)?(test|spec)\.[jt]s/, 9 | } 10 | 11 | export default config 12 | -------------------------------------------------------------------------------- /applications/web/public/favicons/browserconfig.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | #da532c 7 | 8 | 9 | 10 | -------------------------------------------------------------------------------- /applications/web/.eslintrc.cjs: -------------------------------------------------------------------------------- 1 | /** @type {import("eslint").Linter.Config} */ 2 | module.exports = { 3 | root: true, 4 | extends: ["@repo/config-eslint/index.js", "plugin:svelte/recommended"], 5 | parserOptions: { 6 | extraFileExtensions: [".svelte"], 7 | }, 8 | env: { 9 | browser: true, 10 | es2017: true, 11 | node: true, 12 | }, 13 | } 14 | -------------------------------------------------------------------------------- /applications/web/public/favicons/site.webmanifest: -------------------------------------------------------------------------------- 1 | { 2 | "name": "", 3 | "short_name": "", 4 | "icons": [ 5 | { 6 | "src": "android-chrome-192x192.png", 7 | "sizes": "192x192", 8 | "type": "image/png" 9 | } 10 | ], 11 | "theme_color": "#ffffff", 12 | "background_color": "#ffffff", 13 | "display": "standalone" 14 | } 15 | -------------------------------------------------------------------------------- /applications/web/src/components/controls/CubeGizmo/CubeGizmo.svelte.ts: -------------------------------------------------------------------------------- 1 | import {SvelteComponent} from "svelte" 2 | import type {CubeGizmoProps} from "./CubeGizmo" 3 | declare const __propDef: { 4 | props: CubeGizmoProps 5 | } 6 | type CubeGizmoProps_ = typeof __propDef.props 7 | export type {CubeGizmoProps_ as CubeGizmoProps} 8 | export default class CubeGizmo extends SvelteComponent {} 9 | -------------------------------------------------------------------------------- /applications/web/postcss.config.cjs: -------------------------------------------------------------------------------- 1 | const tailwindcss = require("tailwindcss") 2 | const autoprefixer = require("autoprefixer") 3 | 4 | const config = { 5 | plugins: [ 6 | //Some plugins, like tailwindcss/nesting, need to run before Tailwind, 7 | tailwindcss(), 8 | //But others, like autoprefixer, need to run after, 9 | autoprefixer, 10 | ], 11 | } 12 | 13 | module.exports = config 14 | -------------------------------------------------------------------------------- /packages/config-prettier/index.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | semi: false, 3 | singleQuote: false, 4 | useTabs: false, 5 | tabWidth: 2, 6 | trailingComma: "all", 7 | bracketSpacing: false, 8 | arrowParens: "avoid", 9 | printWidth: 160, 10 | plugins: ["prettier-plugin-svelte"], 11 | overrides: [ 12 | { 13 | files: "*.svelte", 14 | options: { 15 | parser: "svelte", 16 | }, 17 | }, 18 | ], 19 | } 20 | -------------------------------------------------------------------------------- /packages/config-eslint/index.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | env: { 3 | node: true, 4 | }, 5 | parser: "@typescript-eslint/parser", 6 | extends: ["eslint:recommended", "plugin:@typescript-eslint/recommended", "prettier"], 7 | plugins: ["@typescript-eslint"], 8 | parserOptions: { 9 | sourceType: "module", 10 | ecmaVersion: 2020, 11 | }, 12 | rules: { 13 | "@typescript-eslint/no-non-null-assertion": "off", 14 | }, 15 | } 16 | -------------------------------------------------------------------------------- /applications/web/tailwind.config.cjs: -------------------------------------------------------------------------------- 1 | /** @type {import('tailwindcss').Config}*/ 2 | const config = { 3 | content: ["./src/**/*.{html,js,svelte,ts}"], 4 | darkMode: "selector", 5 | theme: { 6 | extend: { 7 | gridTemplateColumns: { 8 | editor: "250px 1fr", 9 | }, 10 | gridTemplateRows: { 11 | editor: "45px 45px 1fr 45px", 12 | }, 13 | }, 14 | }, 15 | 16 | plugins: [], 17 | } 18 | 19 | module.exports = config 20 | -------------------------------------------------------------------------------- /packages/config-typescript/vite.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "./base.json", 3 | "compilerOptions": { 4 | "target": "ESNext", 5 | "useDefineForClassFields": true, 6 | "module": "ESNext", 7 | "lib": ["ESNext", "DOM"], 8 | "sourceMap": true, 9 | "resolveJsonModule": true, 10 | "noEmit": true, 11 | "noUnusedLocals": true, 12 | "noUnusedParameters": true, 13 | "noImplicitReturns": true 14 | }, 15 | "exclude": ["node_modules"] 16 | } 17 | -------------------------------------------------------------------------------- /applications/web/src/app.postcss: -------------------------------------------------------------------------------- 1 | /* Write your global styles here, in PostCSS syntax */ 2 | @tailwind base; 3 | @tailwind components; 4 | @tailwind utilities; 5 | 6 | @layer components { 7 | .btn-primary { 8 | @apply h-[45px] px-2 bg-blue-500 text-white font-semibold rounded-lg shadow-md hover:bg-blue-700 focus:outline-none focus:ring-2 focus:ring-blue-400 focus:ring-opacity-75; 9 | } 10 | } 11 | 12 | body { 13 | font-family: "Manrope Variable", sans-serif; 14 | } 15 | -------------------------------------------------------------------------------- /applications/tauri/capabilities/default.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "../gen/schemas/desktop-schema.json", 3 | "identifier": "default", 4 | "description": "enables the default permissions", 5 | "windows": ["main"], 6 | "permissions": [ 7 | "path:default", 8 | "event:default", 9 | "window:default", 10 | "webview:default", 11 | "app:default", 12 | "resources:default", 13 | "image:default", 14 | "menu:default", 15 | "tray:default" 16 | ] 17 | } 18 | -------------------------------------------------------------------------------- /packages/config-eslint/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@repo/config-eslint", 3 | "version": "0.0.0", 4 | "main": "index.js", 5 | "private": true, 6 | "dependencies": { 7 | "eslint": "8.x", 8 | "@typescript-eslint/eslint-plugin": "7.x", 9 | "@typescript-eslint/parser": "7.x", 10 | "eslint-config-prettier": "9.x" 11 | }, 12 | "devDependencies": { 13 | "@repo/config-prettier": "workspace:*" 14 | }, 15 | "publishConfig": { 16 | "access": "public" 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /.github/dependabot.yml: -------------------------------------------------------------------------------- 1 | # To get started with Dependabot version updates, you'll need to specify which 2 | # package ecosystems to update and where the package manifests are located. 3 | # Please see the documentation for more information: 4 | # https://docs.github.com/github/administering-a-repository/configuration-options-for-dependency-updates 5 | # https://containers.dev/guide/dependabot 6 | 7 | version: 2 8 | updates: 9 | - package-ecosystem: "devcontainers" 10 | directory: "/" 11 | schedule: 12 | interval: weekly 13 | -------------------------------------------------------------------------------- /applications/web/public/actions/sketch_min.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /.vscode/launch.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": "0.2.0", 3 | "configurations": [ 4 | { 5 | "name": "[Svelte] Run & Debug", 6 | "request": "launch", 7 | "runtimeExecutable": "pnpm", 8 | "runtimeArgs": ["dev"], 9 | "type": "node", 10 | "killBehavior": "polite", 11 | "serverReadyAction": { 12 | "action": "debugWithChrome", 13 | "pattern": "Local: +http://127.0.0.1:([0-9]+)/", 14 | "uriFormat": "http://127.0.0.1:%s", 15 | "webRoot": "${workspaceFolder}/applications/web/src", 16 | "killOnServerStop": true 17 | } 18 | }, 19 | ], 20 | } 21 | -------------------------------------------------------------------------------- /applications/web/public/actions/draft_min.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | -------------------------------------------------------------------------------- /turbo.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "https://turbo.build/schema.v1.json", 3 | "pipeline": { 4 | "build": { 5 | "dependsOn": ["^build"], 6 | "outputs": [".svelte-kit/**", "dist/**", "pkg/**"] 7 | }, 8 | "test": { 9 | "dependsOn": ["^build"] 10 | }, 11 | "lint": {}, 12 | "format": { 13 | "cache": false, 14 | "persistent": false 15 | }, 16 | "preview": {}, 17 | "clean": { 18 | "cache": false, 19 | "persistent": false 20 | }, 21 | "dev": { 22 | "cache": false, 23 | "persistent": true 24 | } 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /applications/web/tests/e2e/test.ts: -------------------------------------------------------------------------------- 1 | import {expect, test} from "@playwright/test" 2 | 3 | test("index page has expected title", async ({page}) => { 4 | await page.goto("") 5 | // await expect(page.getByRole('heading', { name: 'Welcome to SvelteKit' })).toBeVisible() 6 | await expect(page).toHaveTitle("CADmium") 7 | }) 8 | 9 | test("has history pane", async ({page}) => { 10 | await page.goto("") 11 | await expect(page.getByText("History")).toBeVisible() 12 | }) 13 | 14 | test("has origin in history panel", async ({page}) => { 15 | await page.goto("") 16 | await expect(page.getByText("origin")).toBeVisible() 17 | }) 18 | -------------------------------------------------------------------------------- /packages/config-typescript/base.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "https://json.schemastore.org/tsconfig", 3 | "display": "Default", 4 | "compilerOptions": { 5 | "composite": false, 6 | "declaration": true, 7 | "declarationMap": true, 8 | "esModuleInterop": true, 9 | "forceConsistentCasingInFileNames": true, 10 | "inlineSources": false, 11 | "isolatedModules": true, 12 | "module": "ESNext", 13 | "moduleResolution": "Bundler", 14 | "noUnusedLocals": false, 15 | "noUnusedParameters": false, 16 | "preserveWatchOutput": true, 17 | "skipLibCheck": true, 18 | "strict": true 19 | }, 20 | "exclude": ["node_modules"] 21 | } 22 | -------------------------------------------------------------------------------- /applications/tauri/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "cadmium-native" 3 | version = "0.1.0" 4 | description = "Native Cadmium" 5 | authors = ["you"] 6 | license = "" 7 | repository = "" 8 | edition = "2021" 9 | rust-version = "1.70" 10 | 11 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 12 | 13 | [lib] 14 | name = "app_lib" 15 | crate-type = ["staticlib", "cdylib", "rlib"] 16 | 17 | [build-dependencies] 18 | tauri-build = { version = "2.0.0-beta.17", features = [] } 19 | 20 | [dependencies] 21 | serde_json = "1.0" 22 | serde = { version = "1.0", features = ["derive"] } 23 | tauri = { version = "2.0.0-beta.22", features = [] } 24 | -------------------------------------------------------------------------------- /applications/web/public/actions/chamfer_min.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "private": true, 3 | "scripts": { 4 | "build": "turbo run build --cache-dir=.turbo --no-daemon", 5 | "dev": "turbo run dev --no-daemon", 6 | "preview": "turbo run preview --no-daemon", 7 | "test": "turbo run test --no-daemon", 8 | "clean": "turbo run clean --no-daemon", 9 | "lint": "turbo run lint --no-daemon", 10 | "format": "prettier --write \"**/*.{js,cjs,mjs,ts,tsx,json,svelte,md}\"" 11 | }, 12 | "devDependencies": { 13 | "@playwright/test": "~1.44.1", 14 | "@tauri-apps/cli": "2.0.0-beta.20", 15 | "cross-env": "^7.0.3", 16 | "rimraf": "^5.0.7", 17 | "turbo": "^1.13.4" 18 | }, 19 | "engines": { 20 | "node": ">=20.13.1", 21 | "pnpm": ">=9.1.0" 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /.devcontainer/devcontainer.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "Debian", 3 | "image": "mcr.microsoft.com/devcontainers/base:bookworm", 4 | "features": { 5 | "ghcr.io/devcontainers/features/node:1": { 6 | "version": "lts", 7 | "nvmVersion": "latest" 8 | }, 9 | "ghcr.io/devcontainers/features/rust:1": { 10 | "version": "latest", 11 | "profile": "minimal" 12 | } 13 | }, 14 | "onCreateCommand": "bash ./.devcontainer/scripts/on-create.sh", 15 | "customizations": { 16 | "vscode": { 17 | "extensions": [ 18 | "rust-lang.rust-analyzer", 19 | "svelte.svelte-vscode", 20 | "bradlc.vscode-tailwindcss", 21 | "esbenp.prettier-vscode", 22 | "ms-playwright.playwright", 23 | "vadimcn.vscode-lldb", 24 | "dbaeumer.vscode-eslint" 25 | ] 26 | } 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /applications/web/public/actions/fillet_min.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /applications/tauri/tauri.conf.json: -------------------------------------------------------------------------------- 1 | { 2 | "productName": "CADmium", 3 | "version": "0.0.1", 4 | "identifier": "com.cadmium.dev", 5 | "build": { 6 | "frontendDist": "../web/dist", 7 | "devUrl": "http://localhost:5173", 8 | "beforeDevCommand": "pnpm run dev", 9 | "beforeBuildCommand": "pnpm run build" 10 | }, 11 | "app": { 12 | "windows": [ 13 | { 14 | "title": "CADmium", 15 | "width": 800, 16 | "height": 600, 17 | "resizable": true, 18 | "fullscreen": false 19 | } 20 | ], 21 | "security": { 22 | "csp": null 23 | } 24 | }, 25 | "bundle": { 26 | "active": true, 27 | "targets": "all", 28 | "icon": [ 29 | "icons/32x32.png", 30 | "icons/128x128.png", 31 | "icons/128x128@2x.png", 32 | "icons/icon.icns", 33 | "icons/icon.ico" 34 | ] 35 | }, 36 | "$schema": "../node_modules/@tauri-apps/cli/schema.json" 37 | } 38 | -------------------------------------------------------------------------------- /applications/web/public/actions/plane_min.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 12 | 14 | 22 | 26 | 27 | -------------------------------------------------------------------------------- /applications/web/src/components/controls/CubeGizmo/CubeGizmo.ts: -------------------------------------------------------------------------------- 1 | import type {Key, ThrelteUseTaskOptions} from "@threlte/core" 2 | import {SvelteComponent} from "svelte" 3 | import type {ColorRepresentation} from "three" 4 | import type {SetCameraFocus} from "shared/types" 5 | 6 | type TaskOptions = Pick & {key?: Key} 7 | 8 | export type CubeGizmoProps = { 9 | renderTask?: TaskOptions 10 | animationTask?: TaskOptions 11 | turnRate?: number 12 | center?: [number, number, number] 13 | verticalPlacement?: "top" | "bottom" 14 | horizontalPlacement?: "left" | "right" 15 | size?: number 16 | xColor?: ColorRepresentation 17 | yColor?: ColorRepresentation 18 | zColor?: ColorRepresentation 19 | toneMapped?: boolean 20 | paddingX?: number 21 | paddingY?: number 22 | setCameraFocus: SetCameraFocus 23 | } 24 | 25 | export default class CubeGizmo extends SvelteComponent {} 26 | -------------------------------------------------------------------------------- /applications/web/public/github-mark.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /applications/web/src/utils.ts: -------------------------------------------------------------------------------- 1 | // currently not used since projectUtils & stores were pulled out and placed in packages/shared 2 | // todo 3 | 4 | export function isDevelopment() { 5 | return (globalThis as any).process.env.NODE_ENV === "development" 6 | } 7 | 8 | export function isProduction() { 9 | return (globalThis as any).process.env.NODE_ENV !== "development" 10 | } 11 | 12 | export function setDevelopment(shouldSet: boolean): void { 13 | ;(globalThis as any).process = (globalThis as any).process ?? {} 14 | const env = (globalThis as any).process.env ?? {} 15 | if (shouldSet) (globalThis as any).process.env = {...env, NODE_ENV: "development"} 16 | else (globalThis as any).process.env = {...env, NODE_ENV: "production"} 17 | } 18 | 19 | // disable logging 20 | if (isProduction()) { 21 | const methods = ["log", "debug", "warn", "info"] 22 | for (let i = 0; i < methods.length; i++) { 23 | // @ts-ignore 24 | console[methods[i]] = function () {} 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /packages/cadmium/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "cadmium", 3 | "collaborators": [ 4 | "Matt Ferraro" 5 | ], 6 | "description": "A CAD program written in Rust with a JS front end", 7 | "scripts": { 8 | "dev": "pnpm build:dev && onchange 'src/**/*.rs' -- pnpm build:dev", 9 | "build:dev": "cross-env cargo check && set RUST_BACKTRACE=1 wasm-pack build --no-pack --target web --dev", 10 | "build": "wasm-pack build --target web --no-pack", 11 | "clean": "rimraf target pkg node_modules", 12 | "test": "cargo test", 13 | "postinstall": "pnpm build" 14 | }, 15 | "version": "0.1.0", 16 | "license": "Elastic License 2.0", 17 | "repository": { 18 | "type": "git", 19 | "url": "https://github.com/mattferraro/cadmium" 20 | }, 21 | "files": [ 22 | "pkg/cadmium_bg.wasm", 23 | "pkg/cadmium.js", 24 | "pkg/cadmium.d.ts" 25 | ], 26 | "module": "pkg/cadmium.js", 27 | "types": "pkg/cadmium.d.ts", 28 | "type": "module", 29 | "devDependencies": { 30 | "onchange": "^7.1.0" 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /applications/web/src/components/controls/CadControls/useControlsContext.ts: -------------------------------------------------------------------------------- 1 | import {useThrelteUserContext} from "@threlte/core" 2 | import {writable, type Writable} from "svelte/store" 3 | import type {OrbitControls} from "three/examples/jsm/controls/OrbitControls.js" 4 | import type {TrackballControls} from "three/examples/jsm/controls/TrackballControls.js" 5 | 6 | type ControlsContext = { 7 | orbitControls: Writable 8 | trackballControls: Writable 9 | } 10 | 11 | /** 12 | * ### `useControlsContext` 13 | * 14 | * This hook is used to register the `OrbitControls` or `TrackballControls instance 15 | * with the `ControlsContext`. We're using this context to enable and disable the 16 | * controls when the user is interacting with the TransformControls. 17 | */ 18 | export const useControlsContext = (): ControlsContext => { 19 | return useThrelteUserContext("threlte-controls", { 20 | orbitControls: writable(undefined), 21 | trackballControls: writable(undefined), 22 | }) 23 | } 24 | -------------------------------------------------------------------------------- /applications/web/vite.config.ts: -------------------------------------------------------------------------------- 1 | import {defineConfig} from "vitest/config" 2 | import {svelte} from "@sveltejs/vite-plugin-svelte" 3 | import wasm from "vite-plugin-wasm" 4 | import topLevelAwait from "vite-plugin-top-level-await" 5 | import {base} from "./src/base" 6 | import child_process from "node:child_process" 7 | 8 | export default defineConfig({ 9 | base, 10 | plugins: [svelte(), wasm(), topLevelAwait()], 11 | define: { 12 | GIT_HASH: JSON.stringify(child_process.execSync('git rev-parse --short=10 HEAD').toString().trim()), 13 | GIT_BRANCH: JSON.stringify(child_process.execSync('git rev-parse --abbrev-ref HEAD').toString().trim()), 14 | }, 15 | build: { 16 | outDir: "dist", 17 | target: "esnext", 18 | }, 19 | test: { 20 | include: ["src/**/*.{test,spec}.{js,ts}", "../../packages/shared/**/*.{test,spec}.{js,ts}"], 21 | watch: false, 22 | }, 23 | server: { 24 | strictPort: true, 25 | host: "127.0.0.1", 26 | port: 5173, 27 | fs: { 28 | // Allow serving files from one level up to the project root 29 | // Alows vite dev server to access packages 30 | allow: ["../.."], 31 | }, 32 | }, 33 | }) 34 | -------------------------------------------------------------------------------- /packages/cadmium/examples/project_simple_extrusion.rs: -------------------------------------------------------------------------------- 1 | use cadmium::{ 2 | extrusion::{Direction, Extrusion, ExtrusionMode}, 3 | project::Project, 4 | }; 5 | 6 | fn main() { 7 | let mut p = Project::new("Example Project"); 8 | let wb = p.workbenches.get_mut(0).unwrap(); 9 | wb.add_sketch_to_plane("Sketch 1", "Plane-0"); 10 | let s = wb.get_sketch_mut("Sketch 1").unwrap(); 11 | let ll = s.add_point(0.0, 0.0); 12 | let lr = s.add_point(40.0, 0.0); 13 | let ul = s.add_point(0.0, 40.0); 14 | let ur = s.add_point(40.0, 40.0); 15 | s.add_segment(ll, lr); 16 | s.add_segment(lr, ur); 17 | s.add_segment(ur, ul); 18 | s.add_segment(ul, ll); 19 | 20 | let extrusion = Extrusion::new( 21 | "Sketch-0".to_owned(), 22 | vec![0], 23 | 25.0, 24 | 0.0, 25 | Direction::Normal, 26 | ExtrusionMode::New, 27 | ); 28 | wb.add_extrusion("Ext1", extrusion); 29 | 30 | let realization = p.get_realization(0, 1000); 31 | let solids = realization.solids; 32 | let solid = &solids["Ext1:0"]; 33 | 34 | println!("{:?}", solid); 35 | 36 | println!("Dump example files"); 37 | solid.save_as_step("example.step"); 38 | solid.save_as_obj("example.obj", 0.001); 39 | } 40 | -------------------------------------------------------------------------------- /applications/web/public/actions/extrude_min.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | -------------------------------------------------------------------------------- /packages/cadmium/src/error.rs: -------------------------------------------------------------------------------- 1 | use thiserror::Error; 2 | 3 | use crate::sketch::SketchFeatureType; 4 | 5 | #[derive(Error, Debug)] 6 | pub enum CADmiumError { 7 | // Message errors 8 | #[error("The workbench ID {0} was not found")] 9 | WorkbenchIDNotFound(u64), 10 | #[error("The workbench name {0} was not found")] 11 | WorkbenchNameNotFound(String), 12 | #[error("The step ID {0} was not found")] 13 | StepIDNotFound(String), 14 | #[error("The step name {0} was not found")] 15 | StepNameNotFound(String), 16 | #[error("The sketch ID {0} was not found")] 17 | SketchIDNotFound(u64), 18 | 19 | // StepData errors 20 | #[error("The step {0} data type is not as expected")] 21 | IncorrectStepDataType(String), 22 | 23 | // Sketch errors 24 | #[error("The {0} with ID {1} already exists in the sketch")] 25 | SketchFeatureAlreadyExists(SketchFeatureType, u64), 26 | #[error("The {0} ID is too low for {1}")] 27 | SketchFeatureIDTooLow(SketchFeatureType, u64), 28 | #[error("The {0} with ID {1} has a start point that doesn't exist in the current sketch")] 29 | SketchFeatureMissingStart(SketchFeatureType, u64), 30 | #[error("The {0} with ID {1} has an end point that doesn't exist in the current sketch")] 31 | SketchFeatureMissingEnd(SketchFeatureType, u64), 32 | 33 | 34 | #[error("This function is not implemented yet")] 35 | NotImplemented, 36 | } 37 | -------------------------------------------------------------------------------- /applications/web/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | CADmium 17 | 25 | 26 | 27 | 28 |
29 | 30 | 31 | 32 | -------------------------------------------------------------------------------- /packages/cadmium/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "cadmium" 3 | version = "0.1.0" 4 | edition = "2021" 5 | repository = "https://github.com/mattferraro/cadmium" 6 | authors = ["Matt Ferraro"] 7 | description = "A CAD program written in Rust with a JS front end" 8 | license = "Elastic License 2.0" 9 | 10 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 11 | 12 | [dependencies] 13 | console_error_panic_hook = "0.1.7" 14 | wasm-bindgen = "0.2.87" 15 | tsify = "0.4.5" 16 | truck-meshalgo = { git = "https://github.com/ricosjp/truck.git", rev = "c84318b8dec" } 17 | truck-modeling = { git = "https://github.com/ricosjp/truck.git", rev = "c84318b8dec" } 18 | truck-shapeops = { git = "https://github.com/ricosjp/truck.git", rev = "c84318b8dec" } 19 | truck-polymesh = { git = "https://github.com/ricosjp/truck.git", rev = "c84318b8dec" } 20 | truck-topology = { git = "https://github.com/ricosjp/truck.git", rev = "c84318b8dec" } 21 | truck-stepio = { git = "https://github.com/ricosjp/truck.git", rev = "c84318b8dec" } 22 | serde_json = "1.0.117" 23 | serde = "1.0.202" 24 | itertools = "0.12.0" 25 | svg = "0.13.1" 26 | geo = "0.26.0" 27 | serde_with = "3.4.0" 28 | crc32fast = "1.3.2" 29 | indexmap = "2.1.0" 30 | anyhow = { version = "1.0.86", features = ["backtrace"] } 31 | thiserror = "1.0.61" 32 | strum = { version = "0.26.2", features = ["derive"] } 33 | 34 | [lib] 35 | crate-type = ["cdylib", "rlib"] 36 | -------------------------------------------------------------------------------- /applications/web/public/favicons/safari-pinned-tab.svg: -------------------------------------------------------------------------------- 1 | 2 | 4 | 7 | 8 | Created by potrace 1.14, written by Peter Selinger 2001-2017 9 | 10 | 12 | 18 | 21 | 24 | 25 | 26 | -------------------------------------------------------------------------------- /applications/web/public/cadmium_logo_min.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 12 | 14 | 16 | 25 | 34 | 43 | 44 | 45 | -------------------------------------------------------------------------------- /applications/web/src/components/controls/CadControls/CadControls.svelte: -------------------------------------------------------------------------------- 1 | 40 | 41 | 42 | 43 | 44 | -------------------------------------------------------------------------------- /applications/web/public/actions/solve_min.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 16 | 34 | 36 | 40 | 41 | -------------------------------------------------------------------------------- /applications/web/public/actions/point_outline.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 16 | 36 | 38 | 45 | 46 | -------------------------------------------------------------------------------- /applications/web/public/actions/simple_point_min.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 16 | 34 | 36 | 39 | 46 | 47 | 48 | -------------------------------------------------------------------------------- /applications/web/public/constraints/vertical.svg: -------------------------------------------------------------------------------- 1 | 2 | 17 | 19 | 39 | 43 | 44 | -------------------------------------------------------------------------------- /applications/web/public/constraints/horizontal.svg: -------------------------------------------------------------------------------- 1 | 2 | 17 | 19 | 39 | 43 | 44 | -------------------------------------------------------------------------------- /applications/web/public/actions/point_min.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 16 | 34 | 36 | 43 | 49 | 50 | -------------------------------------------------------------------------------- /applications/web/public/actions/point_min_icon.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 16 | 34 | 36 | 43 | 49 | 50 | -------------------------------------------------------------------------------- /applications/web/public/actions/revolve_min.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /applications/web/public/actions/step_min.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 16 | 34 | 36 | 40 | 47 | 48 | -------------------------------------------------------------------------------- /packages/cadmium/src/realization.rs: -------------------------------------------------------------------------------- 1 | use serde::{Deserialize, Serialize}; 2 | use tsify::Tsify; 3 | use wasm_bindgen::prelude::*; 4 | 5 | use crate::archetypes::Point3; 6 | use crate::project::{RealPlane, RealSketch}; 7 | use crate::solid::Solid; 8 | use std::collections::HashMap; 9 | 10 | #[derive(Tsify, Debug, Serialize, Deserialize)] 11 | #[tsify(into_wasm_abi, from_wasm_abi)] 12 | pub struct Realization { 13 | // a Realization is what you get if you apply the steps in a Workbench's 14 | // history and build a bunch of geometry 15 | pub planes: HashMap, 16 | pub points: HashMap, 17 | pub sketches: HashMap, 18 | pub solids: HashMap, 19 | } 20 | 21 | impl Realization { 22 | pub fn new() -> Self { 23 | Realization { 24 | planes: HashMap::new(), 25 | points: HashMap::new(), 26 | sketches: HashMap::new(), 27 | solids: HashMap::new(), 28 | } 29 | } 30 | 31 | pub fn solid_to_obj(&self, solid_name: &str, tolerance: f64) -> String { 32 | let solid = &self.solids[solid_name]; 33 | let obj_text = solid.to_obj_string(tolerance); 34 | obj_text 35 | } 36 | 37 | pub fn save_solid_as_obj_file(&self, solid_name: &str, filename: &str, tolerance: f64) { 38 | let solid = &self.solids[solid_name]; 39 | solid.save_as_obj(filename, tolerance); 40 | } 41 | 42 | pub fn solid_to_step(&self, solid_name: &str) -> String { 43 | let solid = &self.solids[solid_name]; 44 | let step_text = solid.to_step_string(); 45 | step_text 46 | } 47 | 48 | pub fn save_solid_as_step_file(&self, solid_name: &str, filename: &str) { 49 | let solid = &self.solids[solid_name]; 50 | solid.save_as_step(filename) 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /applications/web/src/components/controls/CadControls/TrackballControls.svelte.d.ts: -------------------------------------------------------------------------------- 1 | import type {Events, Props, Slots} from "@threlte/core" 2 | import {SvelteComponent} from "svelte" 3 | import type {TrackballControls as ThreeTrackballControls} from "three/examples/jsm/controls/TrackballControls.js" 4 | 5 | export type TrackballControlsProps = Props 6 | export type TrackballControlsEvents = Events 7 | export type TrackballControlsSlots = Slots 8 | 9 | /** 10 | * `` allow the camera to orbit freely around a target 11 | * without causing gimbal lock. This type of camera controller is commonly used 12 | * when the concepts of up and down are less important than the ability to 13 | * carefully inspect a model from every angle. 14 | * 15 | * For an alternative camera controller, see 16 | * [``](https://threlte.xyz/docs/reference/extras/orbit-controls). 17 | * 18 | * The component `` must be a direct child of a camera 19 | * component and will mount itself to that camera. By default, damping is 20 | * enabled. You can disable this by setting `staticMoving` to true. 21 | * 22 | * ## Usage 23 | * 24 | * ```svelte 25 | * 29 | * 30 | * 34 | * 35 | * 36 | * ``` 37 | * 38 | * `` is a light wrapper that will use its parent as the target camera and the DOM element 39 | * the renderer is rendering to as the DOM element to listen to. It will also by demand invalidate the frame loop. 40 | */ 41 | export default class TrackballControls extends SvelteComponent {} 42 | -------------------------------------------------------------------------------- /applications/web/public/constraints/parallel.svg: -------------------------------------------------------------------------------- 1 | 2 | 17 | 19 | 39 | 43 | 47 | 48 | -------------------------------------------------------------------------------- /applications/web/public/constraints/perpendicular.svg: -------------------------------------------------------------------------------- 1 | 2 | 17 | 19 | 39 | 43 | 47 | 48 | -------------------------------------------------------------------------------- /applications/web/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "cadmium-web", 3 | "version": "0.0.1", 4 | "private": true, 5 | "scripts": { 6 | "dev": "vite dev", 7 | "build": "vite build", 8 | "preview": "vite preview", 9 | "test": "pnpm test:integration && pnpm test:unit", 10 | "lint": "prettier --check . && eslint \"src/**/*.ts\"", 11 | "format": "prettier . --write .", 12 | "check": "svelte-check --tsconfig ./tsconfig.json", 13 | "test:integration": "playwright test", 14 | "test:unit": "vitest", 15 | "types:guard": "ts-auto-guard --export-all src/types.d.ts" 16 | }, 17 | "devDependencies": { 18 | "@playwright/test": "^1.44.1", 19 | "@repo/config-typescript": "workspace:*", 20 | "@sveltejs/vite-plugin-svelte": "4.0.0-next.3", 21 | "@tsconfig/svelte": "5.0.x", 22 | "autoprefixer": "^10.4.19", 23 | "eslint": "^9.3.0", 24 | "eslint-config-prettier": "^9.1.0", 25 | "eslint-plugin-svelte": "^2.39.0", 26 | "phosphor-svelte": "^2.0.1", 27 | "postcss": "^8.4.38", 28 | "postcss-load-config": "^5.1.0", 29 | "prettier": "^3.2.5", 30 | "prettier-plugin-svelte": "^3.2.3", 31 | "svelte": "5.0.0-next.141", 32 | "svelte-check": "^3.7.1", 33 | "tailwindcss": "^3.4.3", 34 | "ts-auto-guard": "^4.2.0", 35 | "vite": "^5.2.11", 36 | "vite-plugin-top-level-await": "^1.4.1", 37 | "vite-plugin-wasm": "^3.3.0", 38 | "vitest": "^1.6.0" 39 | }, 40 | "type": "module", 41 | "dependencies": { 42 | "@fontsource-variable/manrope": "^5.0.20", 43 | "@threlte/core": "8.0.0-next.4", 44 | "@threlte/extras": "9.0.0-next.5", 45 | "@types/three": "^0.164.1", 46 | "cadmium": "workspace:*", 47 | "camera-controls": "^2.8.3", 48 | "gsap": "^3.12.5", 49 | "js-file-download": "^0.4.12", 50 | "just-debounce-it": "^3.2.0", 51 | "nurbs": "^1.1.1", 52 | "shared": "workspace:*", 53 | "three": "^0.164.1", 54 | "troika-three-text": "^0.49.1" 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /applications/web/public/constraints/equal.svg: -------------------------------------------------------------------------------- 1 | 2 | 17 | 19 | 39 | 46 | 53 | 54 | -------------------------------------------------------------------------------- /applications/web/src/App.svelte: -------------------------------------------------------------------------------- 1 | 42 | 43 |
44 | 45 | 46 |
47 | 48 |
49 | 50 |
51 | -------------------------------------------------------------------------------- /applications/web/src/components/BottomBar.svelte: -------------------------------------------------------------------------------- 1 | 11 | 12 |
13 | {#each workbenches as wb, i (wb.name)} 14 | {#if wb.renaming} 15 | { 20 | log("Renaming workbench index aborted") 21 | wb.renaming = false 22 | }} 23 | on:keydown={e => { 24 | if (e.key === "Enter") { 25 | log("Renaming workbench index:", i) 26 | renameWorkbench(new_workbench_name) 27 | wb.name = new_workbench_name 28 | workbenchIsStale.set(true) 29 | wb.renaming = false 30 | } 31 | }} 32 | /> 33 | {:else} 34 | 54 | {/if} 55 | {/each} 56 |
57 | -------------------------------------------------------------------------------- /applications/web/public/actions/line.svg: -------------------------------------------------------------------------------- 1 | 2 | 17 | 19 | 39 | 43 | 49 | 55 | 56 | -------------------------------------------------------------------------------- /applications/web/public/actions/circle.svg: -------------------------------------------------------------------------------- 1 | 2 | 17 | 19 | 39 | 45 | 51 | 58 | 59 | -------------------------------------------------------------------------------- /applications/web/src/components/tools/Select.svelte: -------------------------------------------------------------------------------- 1 | 50 | 51 | 52 | -------------------------------------------------------------------------------- /applications/web/public/actions/rectangle.svg: -------------------------------------------------------------------------------- 1 | 2 | 17 | 19 | 39 | 46 | 52 | 59 | 60 | -------------------------------------------------------------------------------- /applications/web/public/actions/horizontal.svg: -------------------------------------------------------------------------------- 1 | 2 | 17 | 19 | 37 | 39 | 45 | 48 | 54 | 55 | 56 | 57 | 61 | 62 | -------------------------------------------------------------------------------- /applications/web/src/components/features/Point.svelte: -------------------------------------------------------------------------------- 1 | 19 | 20 |
{ 25 | if ($featureIndex === index) { 26 | closeAndRefresh() 27 | } else { 28 | $featureIndex = index 29 | } 30 | }} 31 | > 32 | {name} 33 | {name} 34 |
35 | 36 | {#if $featureIndex === index} 37 |
38 |
{ 40 | closeAndRefresh() 41 | }} 42 | class="px-3 py-2 bg-gray-100 dark:bg-gray-600 flex flex-col space-y-2" 43 | autocomplete="off" 44 | > 45 | 54 | 55 |
56 | 62 | 63 | 66 |
67 |
68 |
69 | {/if} 70 | -------------------------------------------------------------------------------- /applications/web/src/components/Sketch.svelte: -------------------------------------------------------------------------------- 1 | 46 | 47 | {#if editing} 48 | 61 | {:else} 62 | 74 | {/if} 75 | 76 | 77 | -------------------------------------------------------------------------------- /Notes.md: -------------------------------------------------------------------------------- 1 | # Notes 2 | 3 | - Do I need a formal state machine for the UI, with a diagram of all the states and transitions? 4 | - After messages modify the state, if a get_representation() fails, I'm gonna need great error handling so that the user knows exactly what in the history is broken and how they need to fix it 5 | - When specifying a face to extrude, simple indices aren't sufficient because the number of faces will change, and they are sorted by size. This will cause weird jumps. What heuristics should be used to prevent jumping? Maybe use the face that has the most points in common with the original face? 6 | - Extrusions of two adjacent faces should probably merge together into a single solid. Should this happen before the extrusion by merging faces or after by merging solids? Also setting merge scope will be important: need to distinguish between "new", "add", and "remove". 7 | 8 | # UI Ideas 9 | 10 | Constant: can _always_ zoom, pan, rotate 11 | 12 | Default (no special interaction) 13 | 14 | Create new Step/Edit existing Step: dropdown in left window, looks like a form. Form inputs might be: 15 | 16 | - Set name 17 | - Select a plane (for sketch to live on) 18 | - Select a vector (to extrude in) 19 | - Select a point (to extrude to) 20 | - Select face(s) (to extrude, or extrude to) 21 | - Select a solid (merge scope) 22 | - Choose an option: add/new/remove 23 | - Choose a direction: forward/reverse 24 | 25 | When editing an extrusion: have an arrow that you can drag which controls the length 26 | 27 | When editing a sketch, there's mini state machines for each kind of feature. Hotkeys to trigger: 28 | 29 | - New Circle: 30 | - place center point (smart snapping to sensible points) 31 | - drag to set initial radius, which is not a constraint (smart snapping) 32 | - New Line Segment 33 | - place first point (smart snapping) 34 | - place second point (smart snapping) 35 | - New Rectangle: 36 | - Center point or lower left, upper right? 37 | - place first point (smart snapping) 38 | - place second point (smart snapping) 39 | - Round Corner: 40 | - Select corner(s) 41 | - Input to set radius 42 | - Constraint placing: 43 | - enter "contraint mode" 44 | - click on a line, assume length constraint 45 | - mouse switches to showing where to place the constraint 46 | - after placing it, brings up text box that allows you to enter precise dimensions 47 | 48 | Qs: 49 | 50 | - Should I keep a history of sketch steps? 51 | - How do I show how unsatisfied the constraints are in a sketch? 52 | -------------------------------------------------------------------------------- /applications/web/src/components/Circle.svelte: -------------------------------------------------------------------------------- 1 | 33 | 34 | 35 | ref.computeLineDistances()} /> 36 | { 40 | ref.computeLineDistances() 41 | }} 42 | /> 43 | { 47 | ref.computeLineDistances() 48 | }} 49 | on:pointerover={() => { 50 | if ($sketchTool === "select") { 51 | hovered = true 52 | $currentlyMousedOver = [...$currentlyMousedOver, {type, id}] 53 | // log("$currentlyMousedOver", $currentlyMousedOver) 54 | } 55 | }} 56 | on:pointerout={() => { 57 | if ($sketchTool === "select") { 58 | hovered = false 59 | $currentlyMousedOver = $currentlyMousedOver.filter(item => !(item.id === id && item.type === type)) 60 | } 61 | }} 62 | /> 63 | 64 | -------------------------------------------------------------------------------- /packages/cadmium/src/test.rs: -------------------------------------------------------------------------------- 1 | use super::*; 2 | 3 | #[test] 4 | fn my_test() { 5 | Project::new("Test Project"); 6 | } 7 | 8 | #[test] 9 | fn secondary_extrusion_simple() { 10 | let mut p = Project::new("Test Project"); 11 | let mut wb = p.workbenches.get_mut(0).unwrap(); 12 | wb.add_sketch_to_plane("Sketch 1", "Plane-0"); 13 | let mut s = wb.get_sketch_mut("Sketch 1").unwrap(); 14 | let ll = s.add_point(2.0, 2.0); 15 | let lr = s.add_point(42.0, 2.0); 16 | let ul = s.add_point(2.0, 42.0); 17 | let ur = s.add_point(42.0, 42.0); 18 | s.add_segment(ll, lr); 19 | s.add_segment(lr, ur); 20 | s.add_segment(ur, ul); 21 | s.add_segment(ul, ll); 22 | 23 | let extrusion = Extrusion::new( 24 | "Sketch-0".to_owned(), 25 | vec![0], 26 | 25.0, 27 | 0.0, 28 | Direction::Normal, 29 | ExtrusionMode::New, 30 | ); 31 | wb.add_extrusion("Ext1", extrusion); 32 | 33 | let s2_id = wb.add_sketch_to_solid_face("Sketch-2", "Ext1:0", Vector3::new(0.0, 0.0, 1.0)); 34 | let mut s2 = wb.get_sketch_mut("Sketch-2").unwrap(); 35 | 36 | // smaller 37 | let ll = s2.add_point(12.0, 12.0); 38 | let lr = s2.add_point(32.0, 12.0); 39 | let ul = s2.add_point(12.0, 32.0); 40 | let ur = s2.add_point(32.0, 32.0); 41 | // bigger! 42 | // let ll = s2.add_point(-10.0, -10.0); 43 | // let lr = s2.add_point(50.0, -10.0); 44 | // let ul = s2.add_point(-10.0, 50.0); 45 | // let ur = s2.add_point(50.0, 50.0); 46 | s2.add_segment(ll, lr); 47 | s2.add_segment(lr, ur); 48 | s2.add_segment(ur, ul); 49 | s2.add_segment(ul, ll); 50 | 51 | // println!("S2: {:?}", s2); 52 | 53 | let extrusion2 = Extrusion::new( 54 | s2_id.to_owned(), 55 | vec![0], 56 | 25.0, 57 | 0.0, 58 | Direction::Normal, 59 | ExtrusionMode::Add(vec!["Ext1:0".to_string()]), 60 | ); 61 | wb.add_extrusion("Ext2", extrusion2); 62 | 63 | let realization = p.get_realization(0, 1000); 64 | let solids = realization.solids; 65 | 66 | let num_solids = solids.len(); 67 | println!("Num Solids: {:?}", num_solids); 68 | assert!(num_solids == 1); 69 | 70 | let final_solid = &solids["Ext1:0"]; 71 | let mut mesh = final_solid.truck_solid.triangulation(0.02).to_polygon(); 72 | mesh.put_together_same_attrs(); 73 | let file = std::fs::File::create("secondary_extrusion.obj").unwrap(); 74 | obj::write(&mesh, file).unwrap(); 75 | 76 | let as_json = serde_json::to_string(&p).unwrap(); 77 | let file = std::fs::File::create("secondary_extrusion.json").unwrap(); 78 | // println!("As json: {}", as_json); 79 | serde_json::to_writer(file, &p).unwrap(); 80 | } 81 | -------------------------------------------------------------------------------- /applications/web/src/components/Solid.svelte: -------------------------------------------------------------------------------- 1 | 52 | 53 | 54 | 55 | 56 | 57 | {#each truck_faces as truck_face, i (i)} 58 | 70 | {/each} 71 | 72 | -------------------------------------------------------------------------------- /applications/web/src/components/MainDisplay.svelte: -------------------------------------------------------------------------------- 1 | 46 | 47 |
48 | 49 |
50 | 51 |
52 | 53 | 54 | 55 |
{ 59 | if ($selectingFor.length > 0) { 60 | // If the user is selecting shapes, then click events on the 3D screen 61 | // should not steal focus away from form inputs 62 | e.preventDefault() 63 | } 64 | }} 65 | > 66 | 67 | 68 | 69 |
{GIT_BRANCH} {GIT_HASH}
70 |
71 | 72 | 73 | -------------------------------------------------------------------------------- /.github/workflows/ci.yml: -------------------------------------------------------------------------------- 1 | name: CI 2 | 3 | on: 4 | push: 5 | branches: ["main"] 6 | pull_request: 7 | types: [opened, synchronize] 8 | 9 | # Allows you to run this workflow manually from the Actions tab 10 | workflow_dispatch: 11 | 12 | # Allow only one concurrent deployment, skipping runs queued between the run in-progress and latest queued. 13 | # However, do NOT cancel in-progress runs as we want to allow these production deployments to complete. 14 | concurrency: 15 | group: "pages" 16 | cancel-in-progress: false 17 | 18 | jobs: 19 | build: 20 | name: Build and Test 21 | timeout-minutes: 15 22 | runs-on: ubuntu-latest 23 | # env: 24 | # TURBO_TOKEN: ${{ secrets.TURBO_TOKEN }} 25 | # TURBO_TEAM: ${{ vars.TURBO_TEAM }} 26 | 27 | steps: 28 | - name: Check out code 29 | uses: actions/checkout@v4 30 | with: 31 | fetch-depth: 2 32 | - name: Cache turbo build setup 33 | uses: actions/cache@v4 34 | with: 35 | path: .turbo 36 | key: ${{ runner.os }}-turbo-${{ github.sha }} 37 | restore-keys: | 38 | ${{ runner.os }}-turbo- 39 | - uses: pnpm/action-setup@v3 40 | with: 41 | version: 9 42 | - name: Setup Node.js environment 43 | uses: actions/setup-node@v4 44 | with: 45 | node-version: 20 46 | cache: "pnpm" 47 | - uses: jetli/wasm-pack-action@v0.4.0 48 | with: 49 | # Optional version of wasm-pack to install(eg. 'v0.9.1', 'latest') 50 | version: "latest" 51 | - name: Install dependencies 52 | run: pnpm install 53 | - name: Build cadmium 54 | run: pnpm build 55 | - name: Install playwright dependencies 56 | run: pnpm exec playwright install 57 | - name: Run rust tests, vitest unit tests, and playwright e2e tests 58 | run: pnpm turbo run test 59 | - uses: actions/upload-artifact@v4 60 | with: 61 | name: cadmium 62 | path: applications/web/dist 63 | - name: Setup Github Pages 64 | uses: actions/configure-pages@v5 65 | - name: Upload artifact 66 | uses: actions/upload-pages-artifact@v3 67 | with: 68 | path: applications/web/dist 69 | 70 | # https://kit.svelte.dev/docs/adapter-static#github-pages 71 | deploy: 72 | if: github.event_name == 'push' && github.ref == 'refs/heads/main' 73 | needs: build 74 | runs-on: ubuntu-latest 75 | 76 | permissions: 77 | pages: write 78 | id-token: write 79 | 80 | environment: 81 | name: github-pages 82 | url: ${{ steps.deployment.outputs.page_url }} 83 | 84 | steps: 85 | - name: Deploy 86 | id: deployment 87 | uses: actions/deploy-pages@v4 88 | -------------------------------------------------------------------------------- /applications/web/src/components/Arc.svelte: -------------------------------------------------------------------------------- 1 | 37 | 38 | 39 | { 43 | ref.computeLineDistances() 44 | }} 45 | /> 46 | { 50 | ref.computeLineDistances() 51 | }} 52 | /> 53 | { 57 | ref.computeLineDistances() 58 | }} 59 | on:pointerover={() => { 60 | if ($sketchTool === "select") { 61 | hovered = true 62 | $currentlyMousedOver = [...$currentlyMousedOver, {type, id}] 63 | } 64 | }} 65 | on:pointerout={() => { 66 | if ($sketchTool === "select") { 67 | hovered = false 68 | $currentlyMousedOver = $currentlyMousedOver.filter(item => !(item.id === id && item.type === type)) 69 | } 70 | }} 71 | /> 72 | 73 | -------------------------------------------------------------------------------- /packages/cadmium/src/main.rs: -------------------------------------------------------------------------------- 1 | #![allow(dead_code, unused)] 2 | 3 | use std::ops::{Sub, SubAssign}; 4 | 5 | use cadmium::extrusion::fuse; 6 | use truck_meshalgo::filters::OptimizingFilter; 7 | use truck_meshalgo::tessellation::{MeshableShape, MeshedShape}; 8 | use truck_modeling::builder::{translated, tsweep, vertex}; 9 | use truck_modeling::{Plane, Point3, Surface, Vector3}; 10 | use truck_polymesh::{obj, InnerSpace, Invertible, ParametricSurface, Tolerance}; 11 | use truck_shapeops::{and, or, ShapeOpsCurve, ShapeOpsSurface}; 12 | use truck_topology::{Shell, Solid}; 13 | 14 | fn main() { 15 | let point_a = vertex(Point3::new(0.0, 0.0, 0.0)); 16 | let line_a = tsweep(&point_a, Vector3::new(1.0, 0.0, 0.0)); 17 | let square_a = tsweep(&line_a, Vector3::new(0.0, 1.0, 0.0)); 18 | let cube_a = tsweep(&square_a, Vector3::new(0.0, 0.0, 1.0)); 19 | 20 | // simplest case! 21 | // let point_b = vertex(Point3::new(0.4, 0.4, 1.0)); 22 | // let line_b = tsweep(&point_b, Vector3::new(0.2, 0.0, 0.0)); 23 | // let square_b = tsweep(&line_b, Vector3::new(0.0, 0.2, 0.0)); 24 | // let cube_b: Solid< 25 | // truck_meshalgo::prelude::cgmath::Point3, 26 | // truck_modeling::Curve, 27 | // truck_modeling::Surface, 28 | // > = tsweep(&square_b, Vector3::new(0.0, 0.0, 0.2)); 29 | 30 | // one flush side! 31 | let point_b = vertex(Point3::new(0.4, 0.4, 1.0)); 32 | let line_b = tsweep(&point_b, Vector3::new(0.6, 0.0, 0.0)); 33 | let square_b = tsweep(&line_b, Vector3::new(0.0, 0.2, 0.0)); 34 | let cube_b: Solid< 35 | truck_meshalgo::prelude::cgmath::Point3, 36 | truck_modeling::Curve, 37 | truck_modeling::Surface, 38 | > = tsweep(&square_b, Vector3::new(0.0, 0.0, 0.2)); 39 | 40 | // two flush sides! 41 | // let point_b = vertex(Point3::new(0.4, 0.4, 1.0)); 42 | // let line_b = tsweep(&point_b, Vector3::new(0.6, 0.0, 0.0)); 43 | // let square_b = tsweep(&line_b, Vector3::new(0.0, 0.6, 0.0)); 44 | // let cube_b: Solid< 45 | // truck_meshalgo::prelude::cgmath::Point3, 46 | // truck_modeling::Curve, 47 | // truck_modeling::Surface, 48 | // > = tsweep(&square_b, Vector3::new(0.0, 0.0, 0.2)); 49 | 50 | // extend the cube to be just 0.01 longer than it needs to be 51 | // let cube_b = tsweep(&square_b, Vector3::new(0.0, 0.0, 1.01)); 52 | // let bad_volume = tsweep(&square_b, Vector3::new(0.0, 0.0, -0.01)); 53 | // then translate it down 54 | // let cube_b = translated(&cube_b, Vector3::new(0.0, 0.0, -0.01)); 55 | // let combined_big = or(&cube_a, &cube_b, 0.01).unwrap(); 56 | 57 | // let combined = or(&cube_a, &cube_b, 0.01).unwrap(); 58 | let combined = fuse(&cube_a, &cube_b).unwrap(); 59 | 60 | println!( 61 | "combined_cube_or has {:?} shell boundaries", 62 | combined.boundaries().len() 63 | ); 64 | 65 | let mut mesh = combined.triangulation(0.01).to_polygon(); 66 | mesh.put_together_same_attrs(0.1); 67 | let file = std::fs::File::create("combined_cube.obj").unwrap(); 68 | obj::write(&mesh, file).unwrap(); 69 | } 70 | -------------------------------------------------------------------------------- /applications/web/src/components/Line.svelte: -------------------------------------------------------------------------------- 1 | 44 | 45 | 46 | { 50 | ref.computeLineDistances() 51 | }} 52 | /> 53 | { 57 | ref.computeLineDistances() 58 | }} 59 | /> 60 | { 64 | ref.computeLineDistances() 65 | }} 66 | on:pointerover={() => { 67 | if ($sketchTool === "select") { 68 | hovered = true 69 | $currentlyMousedOver = [...$currentlyMousedOver, {type, id: id}] 70 | } 71 | }} 72 | on:pointerout={() => { 73 | if ($sketchTool === "select") { 74 | hovered = false 75 | $currentlyMousedOver = $currentlyMousedOver.filter(item => !(item.id === id && item.type === type)) 76 | } 77 | }} 78 | /> 79 | 80 | -------------------------------------------------------------------------------- /packages/cadmium/src/test_inputs/three_touching_circles.cadmium: -------------------------------------------------------------------------------- 1 | {"name":"First Project","assemblies":[],"workbenches":[{"name":"Workbench 1","history":[{"name":"Origin","unique_id":"Point-0","suppressed":false,"data":{"type":"Point","point":{"x":0.0,"y":0.0,"z":0.0,"hidden":false}}},{"name":"Top","unique_id":"Plane-0","suppressed":false,"data":{"type":"Plane","plane":{"origin":{"x":0.0,"y":0.0,"z":0.0,"hidden":false},"primary":{"x":1.0,"y":0.0,"z":0.0},"secondary":{"x":0.0,"y":1.0,"z":0.0},"tertiary":{"x":0.0,"y":0.0,"z":1.0}},"width":100.0,"height":100.0}},{"name":"Front","unique_id":"Plane-1","suppressed":false,"data":{"type":"Plane","plane":{"origin":{"x":0.0,"y":0.0,"z":0.0,"hidden":false},"primary":{"x":1.0,"y":0.0,"z":0.0},"secondary":{"x":0.0,"y":0.0,"z":1.0},"tertiary":{"x":0.0,"y":-1.0,"z":0.0}},"width":100.0,"height":100.0}},{"name":"Right","unique_id":"Plane-2","suppressed":false,"data":{"type":"Plane","plane":{"origin":{"x":0.0,"y":0.0,"z":0.0,"hidden":false},"primary":{"x":0.0,"y":1.0,"z":0.0},"secondary":{"x":0.0,"y":0.0,"z":1.0},"tertiary":{"x":1.0,"y":0.0,"z":0.0}},"width":100.0,"height":100.0}},{"name":"Sketch 1","unique_id":"Sketch-0","suppressed":false,"data":{"type":"Sketch","plane_id":"Plane-0","width":1.25,"height":0.75,"sketch":{"points":{"6":{"x":7.9,"y":-2.2999999999999945,"m":1.0,"dx":0.0,"dy":0.0,"fx":0.0,"fy":0.0,"fixed":false,"hidden":true},"3":{"x":-15.923437500000007,"y":-5.299999999999989,"m":1.0,"dx":0.0,"dy":0.0,"fx":0.0,"fy":0.0,"fixed":false,"hidden":false},"4":{"x":-50.32343749999999,"y":-6.1,"m":1.0,"dx":0.0,"dy":0.0,"fx":0.0,"fy":0.0,"fixed":false,"hidden":true},"5":{"x":40.099999999999994,"y":17.5,"m":1.0,"dx":0.0,"dy":0.0,"fx":0.0,"fy":0.0,"fixed":false,"hidden":false},"2":{"x":-39.52343749999999,"y":10.299999999999995,"m":1.0,"dx":0.0,"dy":0.0,"fx":0.0,"fy":0.0,"fixed":false,"hidden":true},"1":{"x":-73.1234375,"y":-3.89999999999999,"m":1.0,"dx":0.0,"dy":0.0,"fx":0.0,"fy":0.0,"fixed":false,"hidden":false}},"highest_point_id":6,"line_segments":{},"highest_line_segment_id":0,"circles":{"2":{"center":3,"radius":34.40930106817049,"top":4},"3":{"center":5,"radius":37.80052909682614,"top":6},"1":{"center":1,"radius":36.477390257528015,"top":2}},"highest_circle_id":3,"arcs":{},"highest_arc_id":0,"constraints":{},"highest_constraint_id":0}}}],"step_counters":{"Point":1,"Plane":3,"Extrusion":0,"Sketch":1}},{"name":"Workbench 2","history":[{"name":"Origin","unique_id":"Point-0","suppressed":false,"data":{"type":"Point","point":{"x":0.0,"y":0.0,"z":0.0,"hidden":false}}},{"name":"Top","unique_id":"Plane-0","suppressed":false,"data":{"type":"Plane","plane":{"origin":{"x":0.0,"y":0.0,"z":0.0,"hidden":false},"primary":{"x":1.0,"y":0.0,"z":0.0},"secondary":{"x":0.0,"y":1.0,"z":0.0},"tertiary":{"x":0.0,"y":0.0,"z":1.0}},"width":100.0,"height":100.0}},{"name":"Front","unique_id":"Plane-1","suppressed":false,"data":{"type":"Plane","plane":{"origin":{"x":0.0,"y":0.0,"z":0.0,"hidden":false},"primary":{"x":1.0,"y":0.0,"z":0.0},"secondary":{"x":0.0,"y":0.0,"z":1.0},"tertiary":{"x":0.0,"y":-1.0,"z":0.0}},"width":100.0,"height":100.0}},{"name":"Right","unique_id":"Plane-2","suppressed":false,"data":{"type":"Plane","plane":{"origin":{"x":0.0,"y":0.0,"z":0.0,"hidden":false},"primary":{"x":0.0,"y":1.0,"z":0.0},"secondary":{"x":0.0,"y":0.0,"z":1.0},"tertiary":{"x":1.0,"y":0.0,"z":0.0}},"width":100.0,"height":100.0}}],"step_counters":{"Plane":3,"Extrusion":0,"Sketch":0,"Point":1}}]} -------------------------------------------------------------------------------- /applications/web/public/actions/hole_min.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | -------------------------------------------------------------------------------- /packages/cadmium/src/lib.rs: -------------------------------------------------------------------------------- 1 | use message::{Message, MessageResult}; 2 | use wasm_bindgen::prelude::*; 3 | extern crate console_error_panic_hook; 4 | 5 | pub mod archetypes; 6 | pub mod error; 7 | pub mod extrusion; 8 | pub mod message; 9 | pub mod project; 10 | pub mod realization; 11 | pub mod solid; 12 | pub mod sketch; 13 | pub mod step; 14 | pub mod workbench; 15 | 16 | #[wasm_bindgen] 17 | pub struct Project { 18 | native: project::Project, 19 | } 20 | 21 | #[wasm_bindgen] 22 | impl Project { 23 | #[wasm_bindgen(constructor)] 24 | pub fn new(name: &str) -> Project { 25 | console_error_panic_hook::set_once(); 26 | 27 | Project { 28 | native: project::Project::new(name), 29 | } 30 | } 31 | 32 | #[wasm_bindgen(getter)] 33 | pub fn name(&self) -> String { 34 | self.native.name.clone() 35 | } 36 | 37 | #[wasm_bindgen(setter)] 38 | pub fn set_name(&mut self, name: String) { 39 | self.native.name = name; 40 | } 41 | 42 | #[wasm_bindgen(getter)] 43 | pub fn json(&self) -> String { 44 | self.native.json() 45 | } 46 | 47 | #[wasm_bindgen] 48 | pub fn to_json(&self) -> String { 49 | self.native.json() 50 | } 51 | 52 | #[wasm_bindgen] 53 | pub fn from_json(json: String) -> Project { 54 | let p = project::Project::from_json(&json); 55 | Project { native: p } 56 | } 57 | 58 | #[wasm_bindgen] 59 | pub fn compute_constraint_errors(&mut self) { 60 | self.native.compute_constraint_errors(); 61 | } 62 | 63 | #[wasm_bindgen] 64 | pub fn get_realization(&self, workbench_id: u32, max_steps: u32) -> Realization { 65 | let realized = self 66 | .native 67 | .get_realization(workbench_id as u64, max_steps as u64); 68 | 69 | Realization { native: realized } 70 | } 71 | 72 | #[wasm_bindgen] 73 | pub fn get_workbench(&self, workbench_index: u32) -> String { 74 | let wb = &self.native.workbenches[workbench_index as usize]; 75 | wb.json() 76 | } 77 | 78 | #[wasm_bindgen] 79 | pub fn send_message(&mut self, message: Message) -> MessageResult { 80 | message.handle(&mut self.native).into() 81 | } 82 | 83 | // #[wasm_bindgen(getter)] 84 | // pub fn sketch(&self) -> sketch::Sketch { 85 | // sketch::Sketch::from(self.native.sketch.clone()) 86 | // } 87 | 88 | // #[wasm_bindgen(setter)] 89 | // pub fn set_sketch(&mut self, sketch: sketch::Sketch) { 90 | // self.native.sketch = sketch.native; 91 | // } 92 | } 93 | 94 | #[wasm_bindgen] 95 | pub struct Realization { 96 | native: realization::Realization, 97 | } 98 | 99 | #[wasm_bindgen] 100 | impl Realization { 101 | #[wasm_bindgen] 102 | pub fn to_json(&self) -> String { 103 | let result = serde_json::to_string(&self.native); 104 | match result { 105 | Ok(json) => json, 106 | Err(e) => format!("Error: {}", e), 107 | } 108 | } 109 | 110 | #[wasm_bindgen] 111 | pub fn solid_to_obj(&self, solid_name: String, tolerance: f64) -> String { 112 | self.native.solid_to_obj(&solid_name, tolerance) 113 | } 114 | 115 | #[wasm_bindgen] 116 | pub fn solid_to_step(&self, solid_name: String) -> String { 117 | self.native.solid_to_step(&solid_name) 118 | } 119 | } 120 | -------------------------------------------------------------------------------- /applications/web/src/components/ToolBar.svelte: -------------------------------------------------------------------------------- 1 | 43 | 44 |
45 | {#if $sketchBeingEdited !== ""} 46 | {#each sketchActions as action} 47 | 50 | {/each} 51 | {:else} 52 | {#each actions as action} 53 | 56 | {/each} 57 | {/if} 58 | 59 | {#if debugging} 60 | Selecting For [ 61 | {#each $selectingFor as sf} 62 |
63 | {sf}, 64 |
65 | {/each} 66 | ] Currently Selected [ 67 | {#each $currentlySelected as cs} 68 |
69 | {cs.type} 70 | {cs.id}, 71 |
72 | {/each} 73 | ] Moused Over [ 74 | {#each $currentlyMousedOver as cm} 75 |
76 | {cm.type} 77 | {cm.id}, 78 |
79 | {/each} 80 | ] Hidden Sketches [ 81 | {#each $hiddenSketches as hs} 82 |
83 | {hs}, 84 |
85 | {/each} 86 | ] 87 | {/if} 88 |
89 | -------------------------------------------------------------------------------- /applications/web/src/components/features/Plane.svelte: -------------------------------------------------------------------------------- 1 | 23 | 24 |
{ 29 | if ($featureIndex === index) { 30 | closeAndRefresh() 31 | } else { 32 | $featureIndex = index 33 | } 34 | }} 35 | > 36 | {#if $featureIndex < index} 37 | {name} 38 | {name} 39 | {:else} 40 | {name} 41 | {name} 42 | {/if} 43 | 44 | 45 | 46 |
{ 49 | setCameraFocus(plane.tertiary, plane.origin, plane.secondary) 50 | // move camera to focus on plane 51 | }} 52 | > 53 | 54 |
55 |
56 | 57 | {#if $featureIndex === index} 58 |
59 |
{ 61 | // editing = false 62 | closeAndRefresh() 63 | }} 64 | class="px-3 py-2 bg-gray-100 dark:bg-gray-600 flex flex-col space-y-2" 65 | autocomplete="off" 66 | > 67 | 76 | 77 |
78 | 84 | 85 | 88 |
89 |
90 |
91 | {/if} 92 | -------------------------------------------------------------------------------- /applications/web/src/components/Point2D.svelte: -------------------------------------------------------------------------------- 1 | 43 | 44 | {#if !hidden} 45 | {#await pointTexture then pointImg} 46 | {#await outlineTexture then outlineImg} 47 | { 49 | if (isPreview) return 50 | if (validTools.includes($sketchTool)) { 51 | hovered = true 52 | $currentlyMousedOver = [...$currentlyMousedOver, {type, id}] 53 | } 54 | }} 55 | on:pointerout={() => { 56 | if (isPreview) return 57 | if (validTools.includes($sketchTool)) { 58 | hovered = false 59 | $currentlyMousedOver = $currentlyMousedOver.filter(item => !(item.id === id && item.type === type)) 60 | } 61 | }} 62 | > 63 | { 67 | ref.computeLineDistances() 68 | }} 69 | /> 70 | 71 | { 75 | ref.computeLineDistances() 76 | }} 77 | /> 78 | 79 | 80 | 81 | 82 | {#if hovered || selected} 83 | 84 | 85 | 86 | {/if} 87 | {/await} 88 | {/await} 89 | {/if} 90 | -------------------------------------------------------------------------------- /packages/cadmium/src/step.rs: -------------------------------------------------------------------------------- 1 | 2 | use serde::{Deserialize, Serialize}; 3 | use tsify::Tsify; 4 | use wasm_bindgen::prelude::*; 5 | 6 | use crate::archetypes::{Plane, PlaneDescription, Point3, Vector3}; 7 | use crate::sketch::Sketch; 8 | use crate::extrusion::Extrusion; 9 | 10 | #[derive(Tsify, Debug, Serialize, Deserialize)] 11 | #[serde(tag = "type")] 12 | #[tsify(into_wasm_abi, from_wasm_abi)] 13 | pub enum StepData { 14 | Point { 15 | point: Point3, 16 | }, 17 | Plane { 18 | plane: Plane, 19 | width: f64, 20 | height: f64, 21 | }, 22 | Sketch { 23 | plane_description: PlaneDescription, 24 | width: f64, 25 | height: f64, 26 | sketch: Sketch, 27 | }, 28 | Extrusion { 29 | extrusion: Extrusion, 30 | }, 31 | } 32 | 33 | #[derive(Tsify, Debug, Serialize, Deserialize)] 34 | #[tsify(into_wasm_abi, from_wasm_abi)] 35 | pub struct Step { 36 | pub(crate) name: String, 37 | pub(crate) unique_id: String, 38 | pub(crate) suppressed: bool, 39 | pub(crate) data: StepData, 40 | } 41 | 42 | impl Step { 43 | pub fn new_point(name: &str, point: Point3, point_id: u64) -> Self { 44 | Step { 45 | name: name.to_owned(), 46 | unique_id: format!("Point-{}", point_id), 47 | suppressed: false, 48 | data: StepData::Point { 49 | point: point.clone(), 50 | }, 51 | } 52 | } 53 | 54 | pub fn new_plane(name: &str, plane: Plane, plane_id: u64) -> Self { 55 | Step { 56 | name: name.to_owned(), 57 | unique_id: format!("Plane-{}", plane_id), 58 | suppressed: false, 59 | data: StepData::Plane { 60 | plane, 61 | height: 100.0, 62 | width: 100.0, 63 | }, 64 | } 65 | } 66 | 67 | pub fn new_sketch(name: &str, plane_id: &str, sketch_id: u64) -> Self { 68 | Step { 69 | name: name.to_owned(), 70 | unique_id: format!("Sketch-{}", sketch_id), 71 | suppressed: false, 72 | data: StepData::Sketch { 73 | plane_description: PlaneDescription::PlaneId(plane_id.to_owned()), 74 | width: 1.25, 75 | height: 0.75, 76 | sketch: Sketch::new(), 77 | }, 78 | } 79 | } 80 | 81 | pub fn new_sketch_on_solid_face( 82 | name: &str, 83 | solid_id: &str, 84 | normal: Vector3, 85 | sketch_id: u64, 86 | ) -> Self { 87 | Step { 88 | name: name.to_owned(), 89 | unique_id: format!("Sketch-{}", sketch_id), 90 | suppressed: false, 91 | data: StepData::Sketch { 92 | plane_description: PlaneDescription::SolidFace { 93 | solid_id: solid_id.to_owned(), 94 | normal, 95 | }, 96 | width: 12.5, 97 | height: 7.5, 98 | sketch: Sketch::new(), 99 | }, 100 | } 101 | } 102 | 103 | pub fn new_extrusion(name: &str, extrusion: Extrusion, extrusion_id: u64) -> Self { 104 | Step { 105 | name: name.to_owned(), 106 | unique_id: format!("Extrusion-{}", extrusion_id), 107 | suppressed: false, 108 | data: StepData::Extrusion { extrusion }, 109 | } 110 | } 111 | } 112 | -------------------------------------------------------------------------------- /applications/web/src/components/Point3D.svelte: -------------------------------------------------------------------------------- 1 | 43 | 44 | {#if !hidden} 45 | {#await pointTexture then pointImg} 46 | {#await outlineTexture then outlineImg} 47 | { 49 | if (isPreview) return 50 | if (validTools.includes($sketchTool)) { 51 | hovered = true 52 | $currentlyMousedOver = [...$currentlyMousedOver, {type, id, x, y, z}] 53 | } 54 | }} 55 | on:pointerout={() => { 56 | if (isPreview) return 57 | if (validTools.includes($sketchTool)) { 58 | hovered = false 59 | $currentlyMousedOver = $currentlyMousedOver.filter(item => !(item.id === id && item.type === type)) 60 | } 61 | }} 62 | > 63 | { 67 | ref.computeLineDistances() 68 | }} 69 | /> 70 | 71 | { 75 | ref.computeLineDistances() 76 | }} 77 | /> 78 | 79 | 80 | 81 | 82 | {#if hovered || selected} 83 | 84 | 85 | 86 | {/if} 87 | {/await} 88 | {/await} 89 | {/if} 90 | -------------------------------------------------------------------------------- /applications/web/src/components/FeatureHistory.svelte: -------------------------------------------------------------------------------- 1 | 55 | 56 |
57 |
58 |
History ({history.length})
59 | {#each history as feature, featureIdx (feature.data.type + ":" + feature.unique_id)} 60 |
61 | {#if isPoint(feature)} 62 | 63 | {:else if isPlane(feature)} 64 | 65 | {:else if isSketch(feature)} 66 | 67 | {:else if isExtrusion(feature)} 68 | 69 | {:else} 70 | TODO: {feature.name} {feature.data.type} 71 | {/if} 72 |
73 | {/each} 74 |
75 | 76 |
77 |
78 |
79 | Solids ({solids ? Object.keys(solids).length : 0}) 80 |
81 | {#each Object.keys(solids) as name (name)} 82 | 83 | {/each} 84 |
85 |
86 | 87 | 88 | -------------------------------------------------------------------------------- /packages/cadmium/src/test_inputs/points_on_lines.cadmium: -------------------------------------------------------------------------------- 1 | {"name":"First Project","assemblies":[],"workbenches":[{"name":"Workbench 1","history":[{"name":"Origin","unique_id":"Point-0","suppressed":false,"data":{"type":"Point","point":{"x":0.0,"y":0.0,"z":0.0,"hidden":false}}},{"name":"Top","unique_id":"Plane-0","suppressed":false,"data":{"type":"Plane","plane":{"origin":{"x":0.0,"y":0.0,"z":0.0,"hidden":false},"primary":{"x":1.0,"y":0.0,"z":0.0},"secondary":{"x":0.0,"y":1.0,"z":0.0},"tertiary":{"x":0.0,"y":0.0,"z":1.0}},"width":100.0,"height":100.0}},{"name":"Front","unique_id":"Plane-1","suppressed":false,"data":{"type":"Plane","plane":{"origin":{"x":0.0,"y":0.0,"z":0.0,"hidden":false},"primary":{"x":1.0,"y":0.0,"z":0.0},"secondary":{"x":0.0,"y":0.0,"z":1.0},"tertiary":{"x":0.0,"y":-1.0,"z":0.0}},"width":100.0,"height":100.0}},{"name":"Right","unique_id":"Plane-2","suppressed":false,"data":{"type":"Plane","plane":{"origin":{"x":0.0,"y":0.0,"z":0.0,"hidden":false},"primary":{"x":0.0,"y":1.0,"z":0.0},"secondary":{"x":0.0,"y":0.0,"z":1.0},"tertiary":{"x":1.0,"y":0.0,"z":0.0}},"width":100.0,"height":100.0}},{"name":"Sketch 1","unique_id":"Sketch-0","suppressed":false,"data":{"type":"Sketch","plane_id":"Plane-0","width":1.25,"height":0.75,"sketch":{"points":{"4":{"x":-73.5234375,"y":38.3,"m":1.0,"dx":0.0,"dy":0.0,"fx":0.0,"fy":0.0,"fixed":false,"hidden":false},"8":{"x":73.47656250000001,"y":8.100000000000001,"m":1.0,"dx":0.0,"dy":0.0,"fx":0.0,"fy":0.0,"fixed":false,"hidden":false},"2":{"x":-25.7234375,"y":38.3,"m":1.0,"dx":0.0,"dy":0.0,"fx":0.0,"fy":0.0,"fixed":false,"hidden":false},"1":{"x":-73.5234375,"y":-24.50000000000001,"m":1.0,"dx":0.0,"dy":0.0,"fx":0.0,"fy":0.0,"fixed":false,"hidden":false},"6":{"x":33.27656250000001,"y":38.3,"m":1.0,"dx":0.0,"dy":0.0,"fx":0.0,"fy":0.0,"fixed":false,"hidden":false},"3":{"x":-25.723437500000003,"y":-24.50000000000001,"m":1.0,"dx":0.0,"dy":0.0,"fx":0.0,"fy":0.0,"fixed":false,"hidden":false},"9":{"x":73.47656250000001,"y":38.3,"m":1.0,"dx":0.0,"dy":0.0,"fx":0.0,"fy":0.0,"fixed":false,"hidden":false},"5":{"x":33.27656250000002,"y":-7.699999999999995,"m":1.0,"dx":0.0,"dy":0.0,"fx":0.0,"fy":0.0,"fixed":false,"hidden":false},"7":{"x":-25.7234375,"y":-7.699999999999996,"m":1.0,"dx":0.0,"dy":0.0,"fx":0.0,"fy":0.0,"fixed":false,"hidden":false},"10":{"x":33.27656250000001,"y":8.100000000000001,"m":1.0,"dx":0.0,"dy":0.0,"fx":0.0,"fy":0.0,"fixed":false,"hidden":false}},"highest_point_id":10,"line_segments":{"7":{"start":5,"end":6},"11":{"start":8,"end":9},"9":{"start":6,"end":10},"2":{"start":4,"end":2},"4":{"start":3,"end":1},"10":{"start":10,"end":8},"1":{"start":1,"end":4},"3":{"start":2,"end":3},"5":{"start":2,"end":7},"12":{"start":9,"end":6},"6":{"start":7,"end":5},"8":{"start":6,"end":2}},"highest_line_segment_id":12,"circles":{},"highest_circle_id":0,"arcs":{},"highest_arc_id":0,"constraints":{},"highest_constraint_id":0}}}],"step_counters":{"Point":1,"Plane":3,"Extrusion":0,"Sketch":1}},{"name":"Workbench 2","history":[{"name":"Origin","unique_id":"Point-0","suppressed":false,"data":{"type":"Point","point":{"x":0.0,"y":0.0,"z":0.0,"hidden":false}}},{"name":"Top","unique_id":"Plane-0","suppressed":false,"data":{"type":"Plane","plane":{"origin":{"x":0.0,"y":0.0,"z":0.0,"hidden":false},"primary":{"x":1.0,"y":0.0,"z":0.0},"secondary":{"x":0.0,"y":1.0,"z":0.0},"tertiary":{"x":0.0,"y":0.0,"z":1.0}},"width":100.0,"height":100.0}},{"name":"Front","unique_id":"Plane-1","suppressed":false,"data":{"type":"Plane","plane":{"origin":{"x":0.0,"y":0.0,"z":0.0,"hidden":false},"primary":{"x":1.0,"y":0.0,"z":0.0},"secondary":{"x":0.0,"y":0.0,"z":1.0},"tertiary":{"x":0.0,"y":-1.0,"z":0.0}},"width":100.0,"height":100.0}},{"name":"Right","unique_id":"Plane-2","suppressed":false,"data":{"type":"Plane","plane":{"origin":{"x":0.0,"y":0.0,"z":0.0,"hidden":false},"primary":{"x":0.0,"y":1.0,"z":0.0},"secondary":{"x":0.0,"y":0.0,"z":1.0},"tertiary":{"x":1.0,"y":0.0,"z":0.0}},"width":100.0,"height":100.0}}],"step_counters":{"Plane":3,"Extrusion":0,"Sketch":0,"Point":1}}]} -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | Elastic License 2.0 2 | 3 | URL: https://www.elastic.co/licensing/elastic-license 4 | 5 | ## Acceptance 6 | 7 | By using the software, you agree to all of the terms and conditions below. 8 | 9 | ## Copyright License 10 | 11 | The licensor grants you a non-exclusive, royalty-free, worldwide, 12 | non-sublicensable, non-transferable license to use, copy, distribute, make 13 | available, and prepare derivative works of the software, in each case subject to 14 | the limitations and conditions below. 15 | 16 | ## Limitations 17 | 18 | You may not provide the software to third parties as a hosted or managed 19 | service, where the service provides users with access to any substantial set of 20 | the features or functionality of the software. 21 | 22 | You may not move, change, disable, or circumvent the license key functionality 23 | in the software, and you may not remove or obscure any functionality in the 24 | software that is protected by the license key. 25 | 26 | You may not alter, remove, or obscure any licensing, copyright, or other notices 27 | of the licensor in the software. Any use of the licensor’s trademarks is subject 28 | to applicable law. 29 | 30 | ## Patents 31 | 32 | The licensor grants you a license, under any patent claims the licensor can 33 | license, or becomes able to license, to make, have made, use, sell, offer for 34 | sale, import and have imported the software, in each case subject to the 35 | limitations and conditions in this license. This license does not cover any 36 | patent claims that you cause to be infringed by modifications or additions to 37 | the software. If you or your company make any written claim that the software 38 | infringes or contributes to infringement of any patent, your patent license for 39 | the software granted under these terms ends immediately. If your company makes 40 | such a claim, your patent license ends immediately for work on behalf of your 41 | company. 42 | 43 | ## Notices 44 | 45 | You must ensure that anyone who gets a copy of any part of the software from you 46 | also gets a copy of these terms. 47 | 48 | If you modify the software, you must include in any modified copies of the 49 | software prominent notices stating that you have modified the software. 50 | 51 | ## No Other Rights 52 | 53 | These terms do not imply any licenses other than those expressly granted in 54 | these terms. 55 | 56 | ## Termination 57 | 58 | If you use the software in violation of these terms, such use is not licensed, 59 | and your licenses will automatically terminate. If the licensor provides you 60 | with a notice of your violation, and you cease all violation of this license no 61 | later than 30 days after you receive that notice, your licenses will be 62 | reinstated retroactively. However, if you violate these terms after such 63 | reinstatement, any additional violation of these terms will cause your licenses 64 | to terminate automatically and permanently. 65 | 66 | ## No Liability 67 | 68 | *As far as the law allows, the software comes as is, without any warranty or 69 | condition, and the licensor will not be liable to you for any damages arising 70 | out of these terms or the use or nature of the software, under any kind of 71 | legal claim.* 72 | 73 | ## Definitions 74 | 75 | The **licensor** is the entity offering these terms, and the **software** is the 76 | software the licensor makes available under these terms, including any portion 77 | of it. 78 | 79 | **you** refers to the individual or entity agreeing to these terms. 80 | 81 | **your company** is any legal entity, sole proprietorship, or other kind of 82 | organization that you work for, plus all organizations that have control over, 83 | are under the control of, or are under common control with that 84 | organization. **control** means ownership of substantially all the assets of an 85 | entity, or the power to direct its management and policies by vote, contract, or 86 | otherwise. Control can be direct or indirect. 87 | 88 | **your licenses** are all the licenses granted to you for the software under 89 | these terms. 90 | 91 | **use** means anything you do with the software requiring one of your licenses. 92 | 93 | **trademark** means trademarks, service marks, and similar rights. -------------------------------------------------------------------------------- /applications/web/public/actions/part.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 16 | 18 | 36 | 41 | 46 | 51 | 55 | 60 | 64 | 69 | 73 | 77 | 81 | 85 | 86 | -------------------------------------------------------------------------------- /applications/web/src/components/SolidItem.svelte: -------------------------------------------------------------------------------- 1 | 71 | 72 | 73 |
{ 78 | log("solid", e) 79 | rightClickContextMenu(e) 80 | }} 81 | > 82 | {name} 83 | {name} 84 |
85 | 86 | {#if contextMenuVisible} 87 | 110 | {/if} 111 | 112 | 113 | -------------------------------------------------------------------------------- /applications/web/src/components/tools/NewCircle.svelte: -------------------------------------------------------------------------------- 1 | 125 | 126 | 127 | -------------------------------------------------------------------------------- /packages/shared/stores.ts: -------------------------------------------------------------------------------- 1 | import {Project as WasmProject, Realization as WasmRealization} from "cadmium" 2 | import {writable} from "svelte/store" 3 | import type {WorkBench, MessageHistory, Project, Realization, Entity, EntityType, SnapEntity, PointLikeById, PreviewGeometry} from "./types" 4 | import {isArcEntity, isCircleEntity, isEntity, isFaceEntity, isLineEntity, isMeshFaceEntity, isPlaneEntity, isPoint3DEntity, isPointEntity} from "./typeGuards" 5 | // import { isDevelopment } from "../+layout" 6 | 7 | // prettier-ignore 8 | const log = (function () { const context = "[stores.ts]"; const color = "hotpink"; return Function.prototype.bind.call(console.log, console, `%c${context}`, `font-weight:bold;color:${color};`) })() 9 | 10 | // @ts-ignore 11 | export const wasmProject = writable({}) 12 | export const project = writable(emptyProject()) 13 | export const projectIsStale = writable(false) 14 | 15 | export const workbenchIndex = writable(0) 16 | export const workbench = writable(emptyWorkBench()) 17 | export const workbenchIsStale = writable(false) 18 | 19 | export const featureIndex = writable(1000) 20 | export const extrusionFeatures = writable([]) 21 | export const wasmRealization = writable() 22 | export const realization = writable(emptyRealization()) 23 | export const realizationIsStale = writable(false) 24 | 25 | export const hiddenSketches = writable([]) 26 | export const sketchBeingEdited = writable("") 27 | export const sketchTool = writable("") 28 | 29 | // could be looking for 'face' or 'plane' or other things 30 | export const selectingFor = writable([]) 31 | export const selectionMax = writable(1000) 32 | export const selectionMin = writable(0) 33 | 34 | export const currentlyMousedOver = writable([]) 35 | export const currentlySelected = writable([]) 36 | export const snapPoints = writable([]) 37 | export const previewGeometry = writable([]) 38 | 39 | export const messageHistory = writable([]) 40 | 41 | // if (isDevelopment()) { 42 | project.subscribe(store => log("[project]", store)) 43 | workbenchIndex.subscribe(store => log("[workbenchIndex]", store)) 44 | workbench.subscribe(store => log("[workbench]", store)) 45 | workbenchIsStale.subscribe(store => log("[workbenchIsStale]", store)) 46 | featureIndex.subscribe(store => log("[featureIndex]", store)) 47 | extrusionFeatures.subscribe(store => log("[extrusionFeatures]", store)) 48 | realization.subscribe(store => log("[realization]", store)) 49 | realizationIsStale.subscribe(store => log("[realizationIsStale]", store)) 50 | sketchBeingEdited.subscribe(store => log("[sketchBeingEdited]", store)) 51 | messageHistory.subscribe(store => log("[messageHistory]", store)) 52 | 53 | currentlySelected.subscribe(store => { 54 | log("[currentlySelected]", store) 55 | const allValid = store.every(entity => isEntity(entity)) 56 | const error = "[stores.ts] [currentlySelected] has invalid entities" 57 | if (!allValid) { 58 | console.error(error, store) 59 | // throw new Error(error) 60 | } 61 | 62 | const types = ["circle", "arc", "face", "line", "plane", "point", "point3D", "meshFace"] as EntityType[] 63 | types.forEach(type => { 64 | const [isType, entity] = latestIsEntity(store, type) 65 | if (isType) log(`[currentlySelected] entity is ${type === "arc" ? "an" : "a"} ${type}:`, entity) 66 | }) 67 | }) 68 | // } 69 | 70 | function latestIsEntity(store: Entity[], type: EntityType) { 71 | if (store.length === 0) return [false, null] 72 | const entity = store[store.length - 1] 73 | switch (type) { 74 | case "circle": 75 | return [isCircleEntity(entity), entity] 76 | case "arc": 77 | return [isArcEntity(entity), entity] 78 | case "face": 79 | return [isFaceEntity(entity), entity] 80 | case "line": 81 | return [isLineEntity(entity), entity] 82 | case "plane": 83 | return [isPlaneEntity(entity), entity] 84 | case "point": 85 | return [isPointEntity(entity), entity] 86 | case "point3D": 87 | return [isPoint3DEntity(entity), entity] 88 | case "meshFace": 89 | return [isMeshFaceEntity(entity), entity] 90 | default: 91 | break 92 | } 93 | log("[latestIsEntity] has incorrect switch statement implemented") 94 | return [false, null] 95 | } 96 | 97 | function emptyWorkBench(): WorkBench { 98 | return { 99 | name: "", 100 | history: [], 101 | step_counters: { 102 | Extrusion: 0, 103 | Plane: 0, 104 | Point: 0, 105 | Sketch: 0, 106 | }, 107 | } 108 | } 109 | function emptyProject(): Project { 110 | return { 111 | name: "", 112 | assemblies: [], 113 | workbenches: [], 114 | } 115 | } 116 | function emptyRealization(): Realization { 117 | return { 118 | planes: {}, 119 | points: {}, 120 | sketches: {}, 121 | solids: {}, 122 | } 123 | } 124 | -------------------------------------------------------------------------------- /applications/web/src/tests/unit/index.test.js: -------------------------------------------------------------------------------- 1 | import {describe, it, expect} from "vitest" 2 | import {isEntity, isEntityType, isProject} from "shared/typeGuards" 3 | import {currentlySelected, project} from "./fixtures" 4 | 5 | describe("isEntityType typechecks", () => { 6 | const types = ["circle", "arc", "face", "line", "plane", "point", "point3D", "meshFace"] 7 | const circleObject = {} 8 | const circleFunction = () => "circle" 9 | 10 | it("typechecks all EntityType", () => { 11 | expect(types.every(entityType => isEntityType(entityType))).toBe(true) 12 | }) 13 | it("'circles' type fails", () => { 14 | expect([...types, "circles"].every(entityType => isEntityType(entityType))).toBe(false) 15 | }) 16 | it("'{}' fails", () => { 17 | expect(["circle", circleObject].every(entityType => isEntityType(entityType))).toBe(false) 18 | }) 19 | it("function returning string of correct type succeeds", () => { 20 | expect(["circle", circleFunction()].every(entityType => isEntityType(entityType))).toBe(true) 21 | }) 22 | }) 23 | 24 | describe("currentlySelected is Entity[]", () => { 25 | it("every currentlySelected item isEntity", () => { 26 | expect(currentlySelected.every(entity => isEntity(entity))).toBe(true) 27 | }) 28 | 29 | it("fails on type:'circles'", () => { 30 | expect([...currentlySelected, {type: "circles", id: "1"}].every(entity => isEntity(entity))).toBe(false) 31 | }) 32 | it("fails with id:number", () => { 33 | expect( 34 | [ 35 | {type: "point", id: 3}, 36 | {type: "circle", id: "1"}, 37 | ].every(entity => isEntity(entity)), 38 | ).toBe(false) 39 | }) 40 | it("fails with id:object", () => { 41 | expect( 42 | [ 43 | {type: "point", id: {type: "circle", id: "1"}}, 44 | {type: "circle", id: "1"}, 45 | ].every(entity => isEntity(entity)), 46 | ).toBe(false) 47 | }) 48 | it("fails when missing type", () => { 49 | expect( 50 | [ 51 | {type: "point", id: "3"}, 52 | {id: "1", typez: "point"}, 53 | ].every(entity => isEntity(entity)), 54 | ).toBe(false) 55 | }) 56 | it("fails when missing id", () => { 57 | expect( 58 | [ 59 | {type: "point", notId: "99"}, 60 | {type: "circle", id: "1"}, 61 | ].every(entity => isEntity(entity)), 62 | ).toBe(false) 63 | }) 64 | it("fails on extra properties", () => { 65 | expect( 66 | [ 67 | {type: "point", id: "a string"}, 68 | {type: "circle", id: "1", errantProperty: "string"}, 69 | ].every(entity => isEntity(entity)), 70 | ).toBe(false) 71 | }) 72 | }) 73 | 74 | describe("currentlySelected has malformed entities", () => { 75 | it("currentlySelected has no duplicates", () => { 76 | const set = new Set() 77 | currentlySelected.forEach(entity => set.add(entity.id)) 78 | expect(currentlySelected.length === set.size).toBe(true) 79 | }) 80 | it("fails when currentlySelected has duplicates", () => { 81 | const set = new Set() 82 | const duplicates = [...currentlySelected, {type: "circle", id: "1"}] 83 | duplicates.forEach(entity => set.add(entity.id)) 84 | expect(duplicates.length === set.size).toBe(false) 85 | }) 86 | }) 87 | 88 | describe("currentlySelected has malformed entities", () => { 89 | it("currentlySelected has no duplicates", () => { 90 | const set = new Set() 91 | currentlySelected.forEach(entity => set.add(entity.id)) 92 | expect(currentlySelected.length === set.size).toBe(true) 93 | }) 94 | it("fails when currentlySelected has duplicates", () => { 95 | const set = new Set() 96 | const duplicates = [...currentlySelected, {type: "circle", id: "1"}] 97 | duplicates.forEach(entity => set.add(entity.id)) 98 | expect(duplicates.length === set.size).toBe(false) 99 | }) 100 | }) 101 | 102 | describe("isProject typechecks", () => { 103 | it("project is correct shape", () => { 104 | expect(isProject(project)).toBe(true) 105 | }) 106 | it("project is valid when workbenches: []", () => { 107 | expect(isProject({...project, workbenches: []})).toBe(true) 108 | }) 109 | 110 | it("fails when assemblies: null", () => { 111 | expect(isProject({...project, assemblies: null})).toBe(false) 112 | }) 113 | it("fails when name: null", () => { 114 | expect(isProject({...project, name: null})).toBe(false) 115 | }) 116 | it("fails when name: number", () => { 117 | expect(isProject({...project, name: 123})).toBe(false) 118 | }) 119 | it("fails when name: object", () => { 120 | expect(isProject({...project, name: {}})).toBe(false) 121 | }) 122 | it("fails when name: array", () => { 123 | expect(isProject({...project, name: []})).toBe(false) 124 | }) 125 | it("fails when workbenches: null", () => { 126 | expect(isProject({...project, workbenches: null})).toBe(false) 127 | }) 128 | it("fails when workbenches: {}", () => { 129 | expect(isProject({...project, workbenches: {}})).toBe(false) 130 | }) 131 | }) 132 | -------------------------------------------------------------------------------- /applications/web/src/components/tools/NewLine.svelte: -------------------------------------------------------------------------------- 1 | 132 | 133 | 134 | -------------------------------------------------------------------------------- /applications/web/src/components/AppBar.svelte: -------------------------------------------------------------------------------- 1 | 46 | 47 |
48 | 49 |
50 |
51 | logo 52 |
53 |
CADmium
54 | {#if renaming} 55 | { 60 | log("Renaming project aborted") 61 | renaming = false 62 | newProjectName = project.name ?? "" 63 | }} 64 | on:keydown={e => { 65 | if (e.key === "Enter") { 66 | log("Renaming project") 67 | renameProject(newProjectName) 68 | project.name = newProjectName 69 | renaming = false 70 | } 71 | }} 72 | /> 73 | {:else} 74 | 75 |
{ 78 | log("Renaming project") 79 | renaming = true 80 | newProjectName = project.name ?? "" 81 | }} 82 | > 83 | {project.name ?? ""} 84 |
85 | {/if} 86 | 87 |
{ 90 | let asString = $wasmProject.to_json() 91 | fileDownload(asString, `${project.name}.cadmium`) 92 | }} 93 | > 94 | 95 |
96 | 97 | 98 |
99 | 100 | 101 | 105 |
106 | 107 | 108 |
{ 111 | let asString = JSON.stringify($messageHistory) 112 | fileDownload(asString, `${project.name}.history.json`) 113 | }} 114 | > 115 | 116 |
117 | 118 | 119 |
{ 122 | if (localStorage.getItem("theme") === "light") { 123 | document.documentElement.classList.add("dark") 124 | localStorage.setItem("theme", "dark") 125 | isDarkMode = true 126 | } else { 127 | document.documentElement.classList.remove("dark") 128 | localStorage.setItem("theme", "light") 129 | isDarkMode = false 130 | } 131 | }} 132 | > 133 | {#if isDarkMode} 134 | 135 | {:else} 136 | 137 | {/if} 138 |
139 | 140 |
141 |
142 | 143 |
144 |
145 | 146 |
147 |
148 |
149 |
150 | -------------------------------------------------------------------------------- /applications/web/src/components/tools/NewRectangle.svelte: -------------------------------------------------------------------------------- 1 | 159 | 160 | 161 | -------------------------------------------------------------------------------- /packages/cadmium/src/archetypes.rs: -------------------------------------------------------------------------------- 1 | use tsify::Tsify; 2 | use serde::{Deserialize, Serialize}; 3 | use truck_modeling::Plane as TruckPlane; 4 | use truck_modeling::InnerSpace; 5 | 6 | use crate::sketch::Point2; 7 | 8 | #[derive(Tsify, Debug, Serialize, Deserialize)] 9 | #[tsify(into_wasm_abi, from_wasm_abi)] 10 | pub enum PlaneDescription { 11 | PlaneId(String), 12 | SolidFace { solid_id: String, normal: Vector3 }, 13 | } 14 | 15 | #[derive(Tsify, Debug, Clone, Serialize, Deserialize)] 16 | #[tsify(into_wasm_abi, from_wasm_abi)] 17 | pub struct Plane { 18 | pub origin: Point3, 19 | pub primary: Vector3, 20 | pub secondary: Vector3, 21 | pub tertiary: Vector3, // aka Normal 22 | } 23 | 24 | impl Plane { 25 | /* 26 | 27 | z y 28 | 29 | ^ ^ 30 | | / 31 | | / 32 | |/ 33 | |--------> x 34 | 35 | So "front" is xz plane with -y normal 36 | and "top" is xy plane with z normal 37 | and "right" is yz plane with x normal 38 | 39 | */ 40 | 41 | pub fn new(origin: Point3, primary: Vector3, secondary: Vector3, tertiary: Vector3) -> Self { 42 | Plane { 43 | origin, 44 | primary, 45 | secondary, 46 | tertiary, 47 | } 48 | } 49 | 50 | pub fn front() -> Self { 51 | Plane { 52 | origin: Point3::new(0.0, 0.0, 0.0), 53 | primary: Vector3::new(1.0, 0.0, 0.0), 54 | secondary: Vector3::new(0.0, 0.0, 1.0), 55 | tertiary: Vector3::new(0.0, -1.0, 0.0), 56 | } 57 | } 58 | 59 | pub fn top() -> Self { 60 | Plane { 61 | origin: Point3::new(0.0, 0.0, 0.0), 62 | primary: Vector3::new(1.0, 0.0, 0.0), 63 | secondary: Vector3::new(0.0, 1.0, 0.0), 64 | tertiary: Vector3::new(0.0, 0.0, 1.0), 65 | } 66 | } 67 | 68 | pub fn right() -> Self { 69 | Plane { 70 | origin: Point3::new(0.0, 0.0, 0.0), 71 | primary: Vector3::new(0.0, 1.0, 0.0), 72 | secondary: Vector3::new(0.0, 0.0, 1.0), 73 | tertiary: Vector3::new(1.0, 0.0, 0.0), 74 | } 75 | } 76 | 77 | pub fn from_truck(tp: TruckPlane) -> Self { 78 | let o = tp.origin(); 79 | let u = tp.u_axis().normalize(); 80 | let v = tp.v_axis().normalize(); 81 | let n = tp.normal().normalize(); 82 | Plane { 83 | origin: Point3::new(o.x, o.y, o.z), 84 | primary: Vector3::new(u.x, u.y, u.z), 85 | secondary: Vector3::new(v.x, v.y, v.z), 86 | tertiary: Vector3::new(n.x, n.y, n.z), 87 | } 88 | } 89 | 90 | pub fn project(&self, point: &Point3) -> Point2 { 91 | let minus_origin = point.minus(&self.origin); 92 | let x = minus_origin.dot(&self.primary); 93 | let y = minus_origin.dot(&self.secondary); 94 | Point2::new(x, y) 95 | } 96 | 97 | pub fn unproject(&self, point: &Point2) -> Point3 { 98 | let x = self.origin.plus(self.primary.times(point.x)); 99 | let y = self.origin.plus(self.secondary.times(point.y)); 100 | x.plus(y).to_point3() 101 | } 102 | } 103 | 104 | #[derive(Tsify, Debug, Clone, Serialize, Deserialize)] 105 | #[tsify(into_wasm_abi, from_wasm_abi)] 106 | pub struct Vector3 { 107 | pub x: f64, 108 | pub y: f64, 109 | pub z: f64, 110 | } 111 | 112 | impl Vector3 { 113 | pub fn new(x: f64, y: f64, z: f64) -> Self { 114 | Vector3 { x, y, z } 115 | } 116 | 117 | pub fn to_point3(&self) -> Point3 { 118 | Point3::new(self.x, self.y, self.z) 119 | } 120 | 121 | pub fn times(&self, s: f64) -> Self { 122 | Self { 123 | x: self.x * s, 124 | y: self.y * s, 125 | z: self.z * s, 126 | } 127 | } 128 | 129 | pub fn plus(&self, v: Self) -> Self { 130 | Self { 131 | x: self.x + v.x, 132 | y: self.y + v.y, 133 | z: self.z + v.z, 134 | } 135 | } 136 | 137 | pub fn dot(&self, other: &Vector3) -> f64 { 138 | self.x * other.x + self.y * other.y + self.z * other.z 139 | } 140 | } 141 | 142 | #[derive(Tsify, Debug, Clone, Serialize, Deserialize)] 143 | #[tsify(into_wasm_abi, from_wasm_abi)] 144 | pub struct Point3 { 145 | pub x: f64, 146 | pub y: f64, 147 | pub z: f64, 148 | pub hidden: bool, 149 | } 150 | 151 | impl Point3 { 152 | pub fn new(x: f64, y: f64, z: f64) -> Self { 153 | Point3 { 154 | x, 155 | y, 156 | z, 157 | hidden: false, 158 | } 159 | } 160 | 161 | pub fn plus(&self, v: Vector3) -> Vector3 { 162 | Vector3 { 163 | x: self.x + v.x, 164 | y: self.y + v.y, 165 | z: self.z + v.z, 166 | } 167 | } 168 | 169 | pub fn minus(&self, other: &Point3) -> Vector3 { 170 | Vector3 { 171 | x: self.x - other.x, 172 | y: self.y - other.y, 173 | z: self.z - other.z, 174 | } 175 | } 176 | 177 | pub fn distance_to(&self, other: &Point3) -> f64 { 178 | let dx = self.x - other.x; 179 | let dy = self.y - other.y; 180 | let dz = self.z - other.z; 181 | (dx * dx + dy * dy + dz * dz).sqrt() 182 | } 183 | } 184 | 185 | #[derive(Tsify, Debug, Clone, Serialize, Deserialize)] 186 | #[tsify(into_wasm_abi, from_wasm_abi)] 187 | pub struct Line3 { 188 | pub start: u64, 189 | pub end: u64, 190 | } 191 | 192 | #[derive(Tsify, Debug, Clone, Serialize, Deserialize)] 193 | #[tsify(into_wasm_abi, from_wasm_abi)] 194 | pub struct Arc3 { 195 | pub center: u64, 196 | pub start: u64, 197 | pub end: u64, 198 | pub clockwise: bool, 199 | } 200 | 201 | #[derive(Tsify, Debug, Clone, Serialize, Deserialize)] 202 | #[tsify(into_wasm_abi, from_wasm_abi)] 203 | pub struct Circle3 { 204 | pub center: u64, 205 | pub radius: f64, 206 | pub top: u64, 207 | } 208 | --------------------------------------------------------------------------------