chore: rename H7CAD to Open CAD Studio
Joining the OpenAEC Foundation (open-aec.com). Crate/struct become OpenCADStudio, user-facing strings use the spaced "Open CAD Studio" form, repo URLs point to HakanSeven12/open-cad-studio. Logo (window icon + assets/logo.svg) redrawn with OCS initials. Drop examples/ binaries — unused. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
parent
e2bc8c29eb
commit
ef1f9a08d6
57 changed files with 159 additions and 1301 deletions
44
.github/workflows/release.yml
vendored
44
.github/workflows/release.yml
vendored
|
|
@ -34,10 +34,10 @@ jobs:
|
||||||
mkdir -p AppDir/usr/bin
|
mkdir -p AppDir/usr/bin
|
||||||
mkdir -p AppDir/usr/share/applications
|
mkdir -p AppDir/usr/share/applications
|
||||||
mkdir -p AppDir/usr/share/icons/hicolor/256x256/apps
|
mkdir -p AppDir/usr/share/icons/hicolor/256x256/apps
|
||||||
cp target/release/H7CAD AppDir/usr/bin/H7CAD
|
cp target/release/OpenCADStudio AppDir/usr/bin/OpenCADStudio
|
||||||
cp packaging/H7CAD.desktop AppDir/usr/share/applications/io.github.HakanSeven12.H7CAD.desktop
|
cp packaging/OpenCADStudio.desktop AppDir/usr/share/applications/io.github.HakanSeven12.OpenCadStudio.desktop
|
||||||
rsvg-convert -w 256 -h 256 assets/logo.svg \
|
rsvg-convert -w 256 -h 256 assets/logo.svg \
|
||||||
-o AppDir/usr/share/icons/hicolor/256x256/apps/io.github.HakanSeven12.H7CAD.png
|
-o AppDir/usr/share/icons/hicolor/256x256/apps/io.github.HakanSeven12.OpenCadStudio.png
|
||||||
|
|
||||||
- name: Download linuxdeploy
|
- name: Download linuxdeploy
|
||||||
run: |
|
run: |
|
||||||
|
|
@ -50,18 +50,18 @@ jobs:
|
||||||
run: |
|
run: |
|
||||||
./linuxdeploy-x86_64.AppImage \
|
./linuxdeploy-x86_64.AppImage \
|
||||||
--appdir AppDir \
|
--appdir AppDir \
|
||||||
--desktop-file AppDir/usr/share/applications/io.github.HakanSeven12.H7CAD.desktop \
|
--desktop-file AppDir/usr/share/applications/io.github.HakanSeven12.OpenCadStudio.desktop \
|
||||||
--icon-file AppDir/usr/share/icons/hicolor/256x256/apps/io.github.HakanSeven12.H7CAD.png \
|
--icon-file AppDir/usr/share/icons/hicolor/256x256/apps/io.github.HakanSeven12.OpenCadStudio.png \
|
||||||
--output appimage
|
--output appimage
|
||||||
mv H7CAD*.AppImage H7CAD.AppImage
|
mv Open*CAD*Studio*.AppImage OpenCADStudio.AppImage
|
||||||
|
|
||||||
- name: Upload AppImage to release
|
- name: Upload AppImage to release
|
||||||
if: github.event_name == 'release'
|
if: github.event_name == 'release'
|
||||||
env:
|
env:
|
||||||
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||||
run: |
|
run: |
|
||||||
mv H7CAD.AppImage H7CAD-${{ github.ref_name }}-linux-x86_64.AppImage
|
mv OpenCADStudio.AppImage OpenCADStudio-${{ github.ref_name }}-linux-x86_64.AppImage
|
||||||
gh release upload ${{ github.ref_name }} H7CAD-${{ github.ref_name }}-linux-x86_64.AppImage --clobber --repo ${{ github.repository }}
|
gh release upload ${{ github.ref_name }} OpenCADStudio-${{ github.ref_name }}-linux-x86_64.AppImage --clobber --repo ${{ github.repository }}
|
||||||
|
|
||||||
build-windows:
|
build-windows:
|
||||||
runs-on: windows-latest
|
runs-on: windows-latest
|
||||||
|
|
@ -83,8 +83,8 @@ jobs:
|
||||||
env:
|
env:
|
||||||
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||||
run: |
|
run: |
|
||||||
mv target/release/H7CAD.exe H7CAD-${{ github.ref_name }}-windows-x86_64.exe
|
mv target/release/OpenCADStudio.exe OpenCADStudio-${{ github.ref_name }}-windows-x86_64.exe
|
||||||
gh release upload ${{ github.ref_name }} H7CAD-${{ github.ref_name }}-windows-x86_64.exe --clobber --repo ${{ github.repository }}
|
gh release upload ${{ github.ref_name }} OpenCADStudio-${{ github.ref_name }}-windows-x86_64.exe --clobber --repo ${{ github.repository }}
|
||||||
|
|
||||||
build-macos:
|
build-macos:
|
||||||
runs-on: macos-14 # Apple Silicon (arm64) only
|
runs-on: macos-14 # Apple Silicon (arm64) only
|
||||||
|
|
@ -110,25 +110,25 @@ jobs:
|
||||||
|
|
||||||
- name: Build .icns from SVG
|
- name: Build .icns from SVG
|
||||||
run: |
|
run: |
|
||||||
mkdir -p H7CAD.iconset
|
mkdir -p OpenCADStudio.iconset
|
||||||
# Render all the sizes Apple expects in an .icns.
|
# Render all the sizes Apple expects in an .icns.
|
||||||
for SIZE in 16 32 64 128 256 512 1024; do
|
for SIZE in 16 32 64 128 256 512 1024; do
|
||||||
rsvg-convert -w $SIZE -h $SIZE assets/logo.svg -o H7CAD.iconset/icon_${SIZE}x${SIZE}.png
|
rsvg-convert -w $SIZE -h $SIZE assets/logo.svg -o OpenCADStudio.iconset/icon_${SIZE}x${SIZE}.png
|
||||||
done
|
done
|
||||||
# @2x retina variants (half-size base name)
|
# @2x retina variants (half-size base name)
|
||||||
for BASE in 16 32 128 256 512; do
|
for BASE in 16 32 128 256 512; do
|
||||||
DOUBLE=$((BASE * 2))
|
DOUBLE=$((BASE * 2))
|
||||||
cp H7CAD.iconset/icon_${DOUBLE}x${DOUBLE}.png H7CAD.iconset/icon_${BASE}x${BASE}@2x.png
|
cp OpenCADStudio.iconset/icon_${DOUBLE}x${DOUBLE}.png OpenCADStudio.iconset/icon_${BASE}x${BASE}@2x.png
|
||||||
done
|
done
|
||||||
iconutil -c icns H7CAD.iconset -o AppIcon.icns
|
iconutil -c icns OpenCADStudio.iconset -o AppIcon.icns
|
||||||
|
|
||||||
- name: Assemble .app bundle
|
- name: Assemble .app bundle
|
||||||
run: |
|
run: |
|
||||||
APP=H7CAD.app
|
APP=OpenCADStudio.app
|
||||||
rm -rf "$APP"
|
rm -rf "$APP"
|
||||||
mkdir -p "$APP/Contents/MacOS" "$APP/Contents/Resources"
|
mkdir -p "$APP/Contents/MacOS" "$APP/Contents/Resources"
|
||||||
cp target/aarch64-apple-darwin/release/H7CAD "$APP/Contents/MacOS/H7CAD"
|
cp target/aarch64-apple-darwin/release/OpenCADStudio "$APP/Contents/MacOS/OpenCADStudio"
|
||||||
chmod +x "$APP/Contents/MacOS/H7CAD"
|
chmod +x "$APP/Contents/MacOS/OpenCADStudio"
|
||||||
cp AppIcon.icns "$APP/Contents/Resources/AppIcon.icns"
|
cp AppIcon.icns "$APP/Contents/Resources/AppIcon.icns"
|
||||||
# Substitute version into Info.plist.
|
# Substitute version into Info.plist.
|
||||||
VERSION="${{ github.ref_name }}"
|
VERSION="${{ github.ref_name }}"
|
||||||
|
|
@ -139,15 +139,15 @@ jobs:
|
||||||
- name: Create .dmg
|
- name: Create .dmg
|
||||||
run: |
|
run: |
|
||||||
hdiutil create \
|
hdiutil create \
|
||||||
-volname "H7CAD" \
|
-volname "Open CAD Studio" \
|
||||||
-srcfolder H7CAD.app \
|
-srcfolder OpenCADStudio.app \
|
||||||
-ov -format UDZO \
|
-ov -format UDZO \
|
||||||
H7CAD.dmg
|
OpenCADStudio.dmg
|
||||||
|
|
||||||
- name: Upload .dmg to release
|
- name: Upload .dmg to release
|
||||||
if: github.event_name == 'release'
|
if: github.event_name == 'release'
|
||||||
env:
|
env:
|
||||||
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||||
run: |
|
run: |
|
||||||
mv H7CAD.dmg H7CAD-${{ github.ref_name }}-macos-arm64.dmg
|
mv OpenCADStudio.dmg OpenCADStudio-${{ github.ref_name }}-macos-arm64.dmg
|
||||||
gh release upload ${{ github.ref_name }} H7CAD-${{ github.ref_name }}-macos-arm64.dmg --clobber --repo ${{ github.repository }}
|
gh release upload ${{ github.ref_name }} OpenCADStudio-${{ github.ref_name }}-macos-arm64.dmg --clobber --repo ${{ github.repository }}
|
||||||
|
|
|
||||||
2
.gitignore
vendored
2
.gitignore
vendored
|
|
@ -15,7 +15,7 @@ target
|
||||||
|
|
||||||
# Flatpak
|
# Flatpak
|
||||||
flatpak/cargo-sources.json
|
flatpak/cargo-sources.json
|
||||||
flatpak/io.github.HakanSeven12.H7CAD.local.yml
|
flatpak/io.github.HakanSeven12.OpenCadStudio.local.yml
|
||||||
.flatpak-builder/
|
.flatpak-builder/
|
||||||
build-dir/
|
build-dir/
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
# H7CAD — Command Reference
|
# Open CAD Studio — Command Reference
|
||||||
|
|
||||||
Status of every standard CAD command in H7CAD:
|
Status of every standard CAD command in Open CAD Studio:
|
||||||
|
|
||||||
- ✅ **Implemented** — fully working
|
- ✅ **Implemented** — fully working
|
||||||
- 🔶 **Partial** — command is accepted but logic is a stub
|
- 🔶 **Partial** — command is accepted but logic is a stub
|
||||||
|
|
|
||||||
2
Cargo.lock
generated
2
Cargo.lock
generated
|
|
@ -3,7 +3,7 @@
|
||||||
version = 4
|
version = 4
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "H7CAD"
|
name = "OpenCADStudio"
|
||||||
version = "0.3.8"
|
version = "0.3.8"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"acadrust",
|
"acadrust",
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
[package]
|
[package]
|
||||||
name = "H7CAD"
|
name = "OpenCADStudio"
|
||||||
version = "0.3.8"
|
version = "0.3.8"
|
||||||
edition = "2021"
|
edition = "2021"
|
||||||
build = "build.rs"
|
build = "build.rs"
|
||||||
|
|
|
||||||
22
README.md
22
README.md
|
|
@ -1,4 +1,4 @@
|
||||||
# H7CAD
|
# Open CAD Studio
|
||||||
|
|
||||||
<img width="1920" height="1020" alt="Ekran görüntüsü 2026-05-23 105715" src="https://github.com/user-attachments/assets/14e4287a-79d3-4bfa-a509-15726ca4f830" />
|
<img width="1920" height="1020" alt="Ekran görüntüsü 2026-05-23 105715" src="https://github.com/user-attachments/assets/14e4287a-79d3-4bfa-a509-15726ca4f830" />
|
||||||
|
|
||||||
|
|
@ -102,34 +102,34 @@ A CAD application for 2D drafting and 3D modeling, built with Rust. Reads and wr
|
||||||
|
|
||||||
### Linux (AppImage)
|
### Linux (AppImage)
|
||||||
|
|
||||||
Download `H7CAD-*-linux-x86_64.AppImage` from the [latest release](https://github.com/HakanSeven12/H7CAD/releases/latest), then:
|
Download `OpenCADStudio-*-linux-x86_64.AppImage` from the [latest release](https://github.com/HakanSeven12/open-cad-studio/releases/latest), then:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
chmod +x H7CAD-*-linux-x86_64.AppImage
|
chmod +x OpenCADStudio-*-linux-x86_64.AppImage
|
||||||
./H7CAD-*-linux-x86_64.AppImage
|
./OpenCADStudio-*-linux-x86_64.AppImage
|
||||||
```
|
```
|
||||||
|
|
||||||
No installation required — runs directly on any modern Linux distribution.
|
No installation required — runs directly on any modern Linux distribution.
|
||||||
|
|
||||||
### Windows
|
### Windows
|
||||||
|
|
||||||
Download `H7CAD-*-windows-x86_64.exe` from the [latest release](https://github.com/HakanSeven12/H7CAD/releases/latest) and run it directly. Windows SmartScreen may show "Windows protected your PC" because the binary is not yet code-signed — click **More info → Run anyway**.
|
Download `OpenCADStudio-*-windows-x86_64.exe` from the [latest release](https://github.com/HakanSeven12/open-cad-studio/releases/latest) and run it directly. Windows SmartScreen may show "Windows protected your PC" because the binary is not yet code-signed — click **More info → Run anyway**.
|
||||||
|
|
||||||
### macOS (Apple Silicon)
|
### macOS (Apple Silicon)
|
||||||
|
|
||||||
Download `H7CAD-*-macos-arm64.dmg` from the [latest release](https://github.com/HakanSeven12/H7CAD/releases/latest) — Apple Silicon (M-series) only; Intel macOS isn't built.
|
Download `OpenCADStudio-*-macos-arm64.dmg` from the [latest release](https://github.com/HakanSeven12/open-cad-studio/releases/latest) — Apple Silicon (M-series) only; Intel macOS isn't built.
|
||||||
|
|
||||||
Open the .dmg and drag `H7CAD.app` to `/Applications`. The first launch may be blocked by Gatekeeper because the app is not notarised — right-click `H7CAD.app → Open` and confirm, or run once via `xattr -dr com.apple.quarantine /Applications/H7CAD.app`.
|
Open the .dmg and drag `OpenCADStudio.app` to `/Applications`. The first launch may be blocked by Gatekeeper because the app is not notarised — right-click `OpenCADStudio.app → Open` and confirm, or run once via `xattr -dr com.apple.quarantine /Applications/OpenCADStudio.app`.
|
||||||
|
|
||||||
### Build from Source
|
### Build from Source
|
||||||
|
|
||||||
Requirements: Rust 1.75+
|
Requirements: Rust 1.75+
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
git clone https://github.com/HakanSeven12/H7CAD.git
|
git clone https://github.com/HakanSeven12/open-cad-studio.git
|
||||||
cd H7CAD
|
cd open-cad-studio
|
||||||
cargo build --release --bin H7CAD
|
cargo build --release --bin OpenCADStudio
|
||||||
./target/release/H7CAD
|
./target/release/OpenCADStudio
|
||||||
```
|
```
|
||||||
|
|
||||||
## License
|
## License
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
# H7CAD — File Open & Render Speed Roadmap
|
# Open CAD Studio — File Open & Render Speed Roadmap
|
||||||
|
|
||||||
This document lists the planned improvements for cutting **file open time**
|
This document lists the planned improvements for cutting **file open time**
|
||||||
and **on-screen draw (render) time**. It builds on the already-landed
|
and **on-screen draw (render) time**. It builds on the already-landed
|
||||||
|
|
@ -91,7 +91,7 @@ decode until **first render** (per-handle lazy `OnceCell`).
|
||||||
### 1.7 File-hash cache (warm re-open)
|
### 1.7 File-hash cache (warm re-open)
|
||||||
|
|
||||||
When re-opening the same file (`(path, mtime, size)` key) keep a disk
|
When re-opening the same file (`(path, mtime, size)` key) keep a disk
|
||||||
snapshot of `CadDocument` + `DerivedCaches` (e.g. `~/.cache/h7cad/`). Skip
|
snapshot of `CadDocument` + `DerivedCaches` (e.g. `~/.cache/open-cad-studio/`). Skip
|
||||||
DWG parse entirely. **Win:** most-recently-opened file goes from 1-2 s to
|
DWG parse entirely. **Win:** most-recently-opened file goes from 1-2 s to
|
||||||
sub-100 ms.
|
sub-100 ms.
|
||||||
|
|
||||||
|
|
@ -278,6 +278,6 @@ Then, measurement-guided:
|
||||||
- **GPU compute culling:** for orthographic 2D CAD the CPU quadtree is
|
- **GPU compute culling:** for orthographic 2D CAD the CPU quadtree is
|
||||||
enough. Already covered by Phase 1-4.
|
enough. Already covered by Phase 1-4.
|
||||||
- **Out-of-core entity streaming:** meaningful for 100 MB+ single files;
|
- **Out-of-core entity streaming:** meaningful for 100 MB+ single files;
|
||||||
typical H7CAD files are not there yet.
|
typical Open CAD Studio files are not there yet.
|
||||||
- **Multi-frame async tessellation pipeline:** if 2.3 progressive render
|
- **Multi-frame async tessellation pipeline:** if 2.3 progressive render
|
||||||
works cleanly, this isn't needed.
|
works cleanly, this isn't needed.
|
||||||
|
|
|
||||||
|
|
@ -29,60 +29,26 @@
|
||||||
inkscape:window-y="0"
|
inkscape:window-y="0"
|
||||||
inkscape:window-maximized="1"
|
inkscape:window-maximized="1"
|
||||||
inkscape:current-layer="svg8" />
|
inkscape:current-layer="svg8" />
|
||||||
<!-- Arka plan -->
|
<!-- Background -->
|
||||||
<rect
|
<rect
|
||||||
width="48"
|
width="48"
|
||||||
height="48"
|
height="48"
|
||||||
rx="6"
|
rx="6"
|
||||||
fill="#B03020"
|
fill="#B03020"
|
||||||
id="rect1" />
|
id="rect1" />
|
||||||
<!-- İnce grid çizgileri (CAD atmosferi) -->
|
<!-- O -->
|
||||||
<!-- H harfi -->
|
<line x1="5" y1="10" x2="13" y2="10" stroke="white" stroke-width="3" stroke-linecap="round" id="o-top" />
|
||||||
<line
|
<line x1="5" y1="38" x2="13" y2="38" stroke="white" stroke-width="3" stroke-linecap="round" id="o-bot" />
|
||||||
x1="7"
|
<line x1="5" y1="10" x2="5" y2="38" stroke="white" stroke-width="3" stroke-linecap="round" id="o-left" />
|
||||||
y1="10"
|
<line x1="13" y1="10" x2="13" y2="38" stroke="white" stroke-width="3" stroke-linecap="round" id="o-right" />
|
||||||
x2="7"
|
<!-- C -->
|
||||||
y2="38"
|
<line x1="18" y1="10" x2="26" y2="10" stroke="white" stroke-width="3" stroke-linecap="round" id="c-top" />
|
||||||
stroke="white"
|
<line x1="18" y1="38" x2="26" y2="38" stroke="white" stroke-width="3" stroke-linecap="round" id="c-bot" />
|
||||||
stroke-width="3.5"
|
<line x1="18" y1="10" x2="18" y2="38" stroke="white" stroke-width="3" stroke-linecap="round" id="c-left" />
|
||||||
stroke-linecap="round"
|
<!-- S -->
|
||||||
id="line3" />
|
<line x1="31" y1="10" x2="39" y2="10" stroke="white" stroke-width="3" stroke-linecap="round" id="s-top" />
|
||||||
<line
|
<line x1="31" y1="10" x2="31" y2="24" stroke="white" stroke-width="3" stroke-linecap="round" id="s-tl" />
|
||||||
x1="19"
|
<line x1="31" y1="24" x2="39" y2="24" stroke="white" stroke-width="3" stroke-linecap="round" id="s-mid" />
|
||||||
y1="10"
|
<line x1="39" y1="24" x2="39" y2="38" stroke="white" stroke-width="3" stroke-linecap="round" id="s-br" />
|
||||||
x2="19"
|
<line x1="31" y1="38" x2="39" y2="38" stroke="white" stroke-width="3" stroke-linecap="round" id="s-bot" />
|
||||||
y2="38"
|
|
||||||
stroke="white"
|
|
||||||
stroke-width="3.5"
|
|
||||||
stroke-linecap="round"
|
|
||||||
id="line4" />
|
|
||||||
<line
|
|
||||||
x1="7"
|
|
||||||
y1="24"
|
|
||||||
x2="19"
|
|
||||||
y2="24"
|
|
||||||
stroke="white"
|
|
||||||
stroke-width="3.5"
|
|
||||||
stroke-linecap="round"
|
|
||||||
id="line5" />
|
|
||||||
<!-- 7 rakamı -->
|
|
||||||
<line
|
|
||||||
x1="26"
|
|
||||||
y1="10"
|
|
||||||
x2="41"
|
|
||||||
y2="10"
|
|
||||||
stroke="white"
|
|
||||||
stroke-width="3.5"
|
|
||||||
stroke-linecap="round"
|
|
||||||
id="line6" />
|
|
||||||
<line
|
|
||||||
x1="41"
|
|
||||||
y1="10"
|
|
||||||
x2="30"
|
|
||||||
y2="38"
|
|
||||||
stroke="white"
|
|
||||||
stroke-width="3.5"
|
|
||||||
stroke-linecap="round"
|
|
||||||
id="line7" />
|
|
||||||
<!-- 7 üzerindeki orta çizgi (stil için) -->
|
|
||||||
</svg>
|
</svg>
|
||||||
|
|
|
||||||
|
Before Width: | Height: | Size: 1.9 KiB After Width: | Height: | Size: 2.4 KiB |
|
|
@ -1,171 +0,0 @@
|
||||||
// Diagnostic: simulate Scene::entity_index() build on a DXF/DWG file
|
|
||||||
// and report per-type stats for indexed / unbounded / unindexable
|
|
||||||
// entities, plus how many would survive a hypothetical view query.
|
|
||||||
//
|
|
||||||
// cargo run --release --example dump_index_stats -- <file>
|
|
||||||
|
|
||||||
use acadrust::EntityType;
|
|
||||||
use std::collections::BTreeMap;
|
|
||||||
|
|
||||||
fn entity_kind(e: &EntityType) -> &'static str {
|
|
||||||
match e {
|
|
||||||
EntityType::Line(_) => "Line",
|
|
||||||
EntityType::LwPolyline(_) => "LwPolyline",
|
|
||||||
EntityType::Polyline2D(_) => "Polyline2D",
|
|
||||||
EntityType::Polyline3D(_) => "Polyline3D",
|
|
||||||
EntityType::Polyline(_) => "Polyline",
|
|
||||||
EntityType::Circle(_) => "Circle",
|
|
||||||
EntityType::Arc(_) => "Arc",
|
|
||||||
EntityType::Ellipse(_) => "Ellipse",
|
|
||||||
EntityType::Spline(_) => "Spline",
|
|
||||||
EntityType::Text(_) => "Text",
|
|
||||||
EntityType::MText(_) => "MText",
|
|
||||||
EntityType::Hatch(_) => "Hatch",
|
|
||||||
EntityType::Wipeout(_) => "Wipeout",
|
|
||||||
EntityType::Insert(_) => "Insert",
|
|
||||||
EntityType::Viewport(_) => "Viewport",
|
|
||||||
EntityType::Block(_) => "Block",
|
|
||||||
EntityType::BlockEnd(_) => "BlockEnd",
|
|
||||||
EntityType::Dimension(_) => "Dimension",
|
|
||||||
EntityType::Leader(_) => "Leader",
|
|
||||||
EntityType::MLine(_) => "MLine",
|
|
||||||
EntityType::Point(_) => "Point",
|
|
||||||
EntityType::Solid(_) => "Solid",
|
|
||||||
EntityType::Face3D(_) => "Face3D",
|
|
||||||
EntityType::RasterImage(_) => "RasterImage",
|
|
||||||
EntityType::Solid3D(_) => "Solid3D",
|
|
||||||
EntityType::Region(_) => "Region",
|
|
||||||
EntityType::Body(_) => "Body",
|
|
||||||
EntityType::Ole2Frame(_) => "Ole2Frame",
|
|
||||||
EntityType::Ray(_) => "Ray",
|
|
||||||
EntityType::XLine(_) => "XLine",
|
|
||||||
_ => "Other",
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn is_unindexable(e: &EntityType) -> bool {
|
|
||||||
matches!(
|
|
||||||
e,
|
|
||||||
EntityType::Insert(_)
|
|
||||||
| EntityType::Viewport(_)
|
|
||||||
| EntityType::Block(_)
|
|
||||||
| EntityType::BlockEnd(_)
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn world_aabb_f64(e: &EntityType) -> Option<[f64; 4]> {
|
|
||||||
let bb = e.as_entity().bounding_box();
|
|
||||||
let (xmin, ymin, xmax, ymax) = (bb.min.x, bb.min.y, bb.max.x, bb.max.y);
|
|
||||||
if xmin == xmax && ymin == ymax {
|
|
||||||
return None;
|
|
||||||
}
|
|
||||||
if !xmin.is_finite() || !ymin.is_finite() || !xmax.is_finite() || !ymax.is_finite() {
|
|
||||||
return None;
|
|
||||||
}
|
|
||||||
Some([xmin, ymin, xmax, ymax])
|
|
||||||
}
|
|
||||||
|
|
||||||
fn main() -> Result<(), Box<dyn std::error::Error>> {
|
|
||||||
let path = std::env::args().nth(1).expect("usage: dump_index_stats <file>");
|
|
||||||
let mut doc = if path.to_lowercase().ends_with(".dwg") {
|
|
||||||
acadrust::io::dwg::DwgReader::from_file(&path)?.read()?
|
|
||||||
} else {
|
|
||||||
acadrust::io::dxf::DxfReader::from_file(&path)?.read()?
|
|
||||||
};
|
|
||||||
let base_dir = std::path::Path::new(&path)
|
|
||||||
.parent()
|
|
||||||
.unwrap_or(std::path::Path::new("."));
|
|
||||||
let _ = H7CAD::io::xref::resolve_xrefs(&mut doc, base_dir);
|
|
||||||
|
|
||||||
let model_block = doc.header.model_space_block_handle;
|
|
||||||
println!("file: {}", path);
|
|
||||||
println!("model_space_block_handle: {:?}", model_block);
|
|
||||||
println!("total entities: {}", doc.entities().count());
|
|
||||||
println!();
|
|
||||||
|
|
||||||
let mut total: BTreeMap<&str, usize> = BTreeMap::new();
|
|
||||||
let mut indexed: BTreeMap<&str, usize> = BTreeMap::new();
|
|
||||||
let mut unbounded: BTreeMap<&str, usize> = BTreeMap::new();
|
|
||||||
let mut unindexable_count: BTreeMap<&str, usize> = BTreeMap::new();
|
|
||||||
let mut top_level: BTreeMap<&str, usize> = BTreeMap::new();
|
|
||||||
let mut block_internal: BTreeMap<&str, usize> = BTreeMap::new();
|
|
||||||
let mut union: Option<[f64; 4]> = None;
|
|
||||||
|
|
||||||
for e in doc.entities() {
|
|
||||||
let k = entity_kind(e);
|
|
||||||
*total.entry(k).or_default() += 1;
|
|
||||||
let owner = e.common().owner_handle;
|
|
||||||
if owner == model_block {
|
|
||||||
*top_level.entry(k).or_default() += 1;
|
|
||||||
} else {
|
|
||||||
*block_internal.entry(k).or_default() += 1;
|
|
||||||
}
|
|
||||||
if is_unindexable(e) {
|
|
||||||
*unindexable_count.entry(k).or_default() += 1;
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
match world_aabb_f64(e) {
|
|
||||||
Some(ab) => {
|
|
||||||
*indexed.entry(k).or_default() += 1;
|
|
||||||
union = Some(match union {
|
|
||||||
None => ab,
|
|
||||||
Some(u) => [
|
|
||||||
u[0].min(ab[0]),
|
|
||||||
u[1].min(ab[1]),
|
|
||||||
u[2].max(ab[2]),
|
|
||||||
u[3].max(ab[3]),
|
|
||||||
],
|
|
||||||
});
|
|
||||||
}
|
|
||||||
None => *unbounded.entry(k).or_default() += 1,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
println!("──── per type ────────────────────────────────────────────────");
|
|
||||||
println!(
|
|
||||||
"{:<14} {:>6} {:>6} {:>6} {:>6} {:>10} {:>10}",
|
|
||||||
"Type", "total", "idx", "unb", "skip", "top-lvl", "blk-int"
|
|
||||||
);
|
|
||||||
let mut all_keys: std::collections::BTreeSet<&str> = std::collections::BTreeSet::new();
|
|
||||||
all_keys.extend(total.keys());
|
|
||||||
for k in &all_keys {
|
|
||||||
println!(
|
|
||||||
"{:<14} {:>6} {:>6} {:>6} {:>6} {:>10} {:>10}",
|
|
||||||
k,
|
|
||||||
total.get(k).copied().unwrap_or(0),
|
|
||||||
indexed.get(k).copied().unwrap_or(0),
|
|
||||||
unbounded.get(k).copied().unwrap_or(0),
|
|
||||||
unindexable_count.get(k).copied().unwrap_or(0),
|
|
||||||
top_level.get(k).copied().unwrap_or(0),
|
|
||||||
block_internal.get(k).copied().unwrap_or(0),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
println!();
|
|
||||||
println!("union of indexed AABBs: {:?}", union);
|
|
||||||
|
|
||||||
// Sample top-level entity bboxes by type
|
|
||||||
println!();
|
|
||||||
println!("──── sample top-level entity bboxes (first 5 per type) ────────");
|
|
||||||
let mut shown: BTreeMap<&str, usize> = BTreeMap::new();
|
|
||||||
for e in doc.entities() {
|
|
||||||
let k = entity_kind(e);
|
|
||||||
if e.common().owner_handle != model_block {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
let c = shown.entry(k).or_insert(0);
|
|
||||||
if *c >= 5 {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
*c += 1;
|
|
||||||
let bb = e.as_entity().bounding_box();
|
|
||||||
println!(
|
|
||||||
" {:<12} h={:?} bb=({:.3},{:.3})→({:.3},{:.3})",
|
|
||||||
k,
|
|
||||||
e.common().handle,
|
|
||||||
bb.min.x, bb.min.y, bb.max.x, bb.max.y,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
@ -1,14 +0,0 @@
|
||||||
use acadrust::entities::EntityType;
|
|
||||||
use acadrust::io::dwg::DwgReader;
|
|
||||||
fn main() -> Result<(), Box<dyn std::error::Error>> {
|
|
||||||
let path = std::env::args().nth(1).unwrap();
|
|
||||||
let doc = DwgReader::from_file(&path)?.read()?;
|
|
||||||
for e in doc.entities() {
|
|
||||||
if let EntityType::RasterImage(img) = e {
|
|
||||||
println!("file_path: {}", img.file_path);
|
|
||||||
println!("size: {} x {}", img.size.x, img.size.y);
|
|
||||||
if let Ok(meta) = std::fs::metadata(&img.file_path) { println!("disk size: {} bytes", meta.len()); }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
@ -1,233 +0,0 @@
|
||||||
// Inspect DIMSCALE, dimstyles, text heights, and xdata to diagnose
|
|
||||||
// annotation-scale bugs in paper-space viewports.
|
|
||||||
//
|
|
||||||
// cargo run --release --example inspect_anno -- <file>
|
|
||||||
|
|
||||||
use acadrust::entities::{Dimension, EntityType};
|
|
||||||
use acadrust::io::dwg::DwgReader;
|
|
||||||
|
|
||||||
fn main() -> Result<(), Box<dyn std::error::Error>> {
|
|
||||||
let path = std::env::args().nth(1).expect("usage: inspect_anno <file>");
|
|
||||||
let doc = DwgReader::from_file(&path)?.read()?;
|
|
||||||
println!("file: {}\n", path);
|
|
||||||
|
|
||||||
println!("─── header ──────────────────────────────");
|
|
||||||
println!(" DIMSCALE (dim_scale) = {}", doc.header.dim_scale);
|
|
||||||
println!(" DIMTXT (dim_text_height)= {}", doc.header.dim_text_height);
|
|
||||||
println!(" DIMASZ (dim_arrow_size) = {}", doc.header.dim_arrow_size);
|
|
||||||
println!(" TEXTSIZE (text_height) = {}", doc.header.text_height);
|
|
||||||
println!(" LTSCALE = {}", doc.header.linetype_scale);
|
|
||||||
println!(" PSLTSCALE (psls) = {}", doc.header.paper_space_linetype_scaling);
|
|
||||||
println!(" CELTSCALE = {}", doc.header.current_entity_linetype_scale);
|
|
||||||
println!(" viewport_scale_factor = {}", doc.header.viewport_scale_factor);
|
|
||||||
println!();
|
|
||||||
println!("─── linetypes (first 10) ────────────────");
|
|
||||||
let mut shown = 0;
|
|
||||||
for lt in doc.line_types.iter() {
|
|
||||||
if shown >= 10 { break; }
|
|
||||||
println!(
|
|
||||||
" {:<24} pattern_len={:.4} elements={}",
|
|
||||||
lt.name, lt.pattern_length, lt.elements.len()
|
|
||||||
);
|
|
||||||
shown += 1;
|
|
||||||
}
|
|
||||||
println!();
|
|
||||||
|
|
||||||
println!("─── dim_styles ──────────────────────────");
|
|
||||||
println!(
|
|
||||||
" {:<24} {:>10} {:>10} {:>10} {:>10}",
|
|
||||||
"name", "dimscale", "dimtxt", "dimasz", "dimexe"
|
|
||||||
);
|
|
||||||
for s in doc.dim_styles.iter() {
|
|
||||||
println!(
|
|
||||||
" {:<24} {:>10.4} {:>10.4} {:>10.4} {:>10.4}",
|
|
||||||
s.name, s.dimscale, s.dimtxt, s.dimasz, s.dimexe
|
|
||||||
);
|
|
||||||
}
|
|
||||||
println!();
|
|
||||||
|
|
||||||
println!("─── text_styles ─────────────────────────");
|
|
||||||
for s in doc.text_styles.iter() {
|
|
||||||
println!(
|
|
||||||
" {:<24} height={:.4} last_height={:.4} font={:?}",
|
|
||||||
s.name, s.height, s.last_height, s.font_file
|
|
||||||
);
|
|
||||||
}
|
|
||||||
println!();
|
|
||||||
|
|
||||||
let mut n_text = 0u32;
|
|
||||||
let mut n_text_with_xdata = 0u32;
|
|
||||||
let mut n_text_with_acanno = 0u32;
|
|
||||||
let mut n_mtext = 0u32;
|
|
||||||
let mut n_mtext_with_xdata = 0u32;
|
|
||||||
let mut n_mtext_with_acanno = 0u32;
|
|
||||||
let mut n_dim = 0u32;
|
|
||||||
let mut n_dim_with_xdata = 0u32;
|
|
||||||
let mut n_dim_with_acanno = 0u32;
|
|
||||||
let mut xd_app_counts: std::collections::BTreeMap<String, u32> = Default::default();
|
|
||||||
|
|
||||||
let mut sample_text_h: Vec<f64> = vec![];
|
|
||||||
let mut sample_mtext_h: Vec<f64> = vec![];
|
|
||||||
let mut sample_dim_style: std::collections::BTreeMap<String, u32> = Default::default();
|
|
||||||
// For MTEXT: bucket by owner block name (paper vs model) and height range.
|
|
||||||
let mut mtext_by_owner: std::collections::BTreeMap<String, Vec<f64>> = Default::default();
|
|
||||||
|
|
||||||
let acanno_keys = ["AcAnnoPO", "AcAnnotativeData", "AcAnnoScaleObj", "AcDbXrecObject"];
|
|
||||||
|
|
||||||
for e in doc.entities() {
|
|
||||||
match e {
|
|
||||||
EntityType::Text(t) => {
|
|
||||||
n_text += 1;
|
|
||||||
let xd = &t.common.extended_data;
|
|
||||||
let has_xd = !xd.records().is_empty();
|
|
||||||
if has_xd {
|
|
||||||
n_text_with_xdata += 1;
|
|
||||||
for r in xd.records() {
|
|
||||||
*xd_app_counts.entry(r.application_name.clone()).or_default() += 1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if acanno_keys.iter().any(|k| xd.get_record(k).is_some()) {
|
|
||||||
n_text_with_acanno += 1;
|
|
||||||
}
|
|
||||||
if sample_text_h.len() < 10 {
|
|
||||||
sample_text_h.push(t.height);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
EntityType::MText(t) => {
|
|
||||||
n_mtext += 1;
|
|
||||||
let xd = &t.common.extended_data;
|
|
||||||
let has_xd = !xd.records().is_empty();
|
|
||||||
if has_xd {
|
|
||||||
n_mtext_with_xdata += 1;
|
|
||||||
for r in xd.records() {
|
|
||||||
*xd_app_counts.entry(r.application_name.clone()).or_default() += 1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if acanno_keys.iter().any(|k| xd.get_record(k).is_some()) {
|
|
||||||
n_mtext_with_acanno += 1;
|
|
||||||
}
|
|
||||||
if sample_mtext_h.len() < 10 {
|
|
||||||
sample_mtext_h.push(t.height);
|
|
||||||
}
|
|
||||||
// Look up the owning block-record by handle and label as
|
|
||||||
// "*Model_Space" / "*Paper_Space*" / "<other>".
|
|
||||||
let owner_h = t.common.owner_handle;
|
|
||||||
let owner_label = doc
|
|
||||||
.block_records
|
|
||||||
.iter()
|
|
||||||
.find_map(|br: &acadrust::BlockRecord| {
|
|
||||||
if br.handle == owner_h {
|
|
||||||
Some(br.name.clone())
|
|
||||||
} else {
|
|
||||||
None
|
|
||||||
}
|
|
||||||
})
|
|
||||||
.unwrap_or_else(|| format!("h{}", owner_h.value()));
|
|
||||||
mtext_by_owner
|
|
||||||
.entry(owner_label)
|
|
||||||
.or_default()
|
|
||||||
.push(t.height);
|
|
||||||
}
|
|
||||||
EntityType::Dimension(d) => {
|
|
||||||
n_dim += 1;
|
|
||||||
let base = match d {
|
|
||||||
Dimension::Aligned(d) => &d.base,
|
|
||||||
Dimension::Linear(d) => &d.base,
|
|
||||||
Dimension::Radius(d) => &d.base,
|
|
||||||
Dimension::Diameter(d) => &d.base,
|
|
||||||
Dimension::Angular2Ln(d) => &d.base,
|
|
||||||
Dimension::Angular3Pt(d) => &d.base,
|
|
||||||
Dimension::Ordinate(d) => &d.base,
|
|
||||||
};
|
|
||||||
let xd = &base.common.extended_data;
|
|
||||||
let has_xd = !xd.records().is_empty();
|
|
||||||
if has_xd {
|
|
||||||
n_dim_with_xdata += 1;
|
|
||||||
for r in xd.records() {
|
|
||||||
*xd_app_counts.entry(r.application_name.clone()).or_default() += 1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if acanno_keys.iter().any(|k| xd.get_record(k).is_some()) {
|
|
||||||
n_dim_with_acanno += 1;
|
|
||||||
}
|
|
||||||
*sample_dim_style.entry(base.style_name.clone()).or_default() += 1;
|
|
||||||
}
|
|
||||||
_ => {}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
println!("─── annotation entity counts ────────────");
|
|
||||||
println!(
|
|
||||||
" TEXT total={:<5} xdata={:<5} AcAnno={}",
|
|
||||||
n_text, n_text_with_xdata, n_text_with_acanno
|
|
||||||
);
|
|
||||||
println!(
|
|
||||||
" MTEXT total={:<5} xdata={:<5} AcAnno={}",
|
|
||||||
n_mtext, n_mtext_with_xdata, n_mtext_with_acanno
|
|
||||||
);
|
|
||||||
println!(
|
|
||||||
" DIMENSION total={:<5} xdata={:<5} AcAnno={}",
|
|
||||||
n_dim, n_dim_with_xdata, n_dim_with_acanno
|
|
||||||
);
|
|
||||||
println!();
|
|
||||||
|
|
||||||
println!("─── xdata application names seen ────────");
|
|
||||||
for (k, v) in &xd_app_counts {
|
|
||||||
println!(" {:>5} {}", v, k);
|
|
||||||
}
|
|
||||||
println!();
|
|
||||||
|
|
||||||
if !sample_text_h.is_empty() {
|
|
||||||
println!("─── first 10 TEXT heights ───────────────");
|
|
||||||
for h in &sample_text_h {
|
|
||||||
println!(" {:.4}", h);
|
|
||||||
}
|
|
||||||
println!();
|
|
||||||
}
|
|
||||||
if !sample_mtext_h.is_empty() {
|
|
||||||
println!("─── first 10 MTEXT heights ──────────────");
|
|
||||||
for h in &sample_mtext_h {
|
|
||||||
println!(" {:.4}", h);
|
|
||||||
}
|
|
||||||
println!();
|
|
||||||
}
|
|
||||||
if !mtext_by_owner.is_empty() {
|
|
||||||
println!("─── MTEXT by owner ──────────────────────");
|
|
||||||
for (owner, heights) in &mtext_by_owner {
|
|
||||||
let mut h = heights.clone();
|
|
||||||
h.sort_by(|a, b| a.partial_cmp(b).unwrap());
|
|
||||||
let min = h.first().copied().unwrap_or(0.0);
|
|
||||||
let max = h.last().copied().unwrap_or(0.0);
|
|
||||||
let median = h[h.len() / 2];
|
|
||||||
let n_below_10 = h.iter().filter(|&&x| x < 10.0).count();
|
|
||||||
let n_above_100 = h.iter().filter(|&&x| x > 100.0).count();
|
|
||||||
println!(
|
|
||||||
" {:<20} n={:<4} min={:.2} med={:.2} max={:.2} <10:{} >100:{}",
|
|
||||||
owner,
|
|
||||||
h.len(),
|
|
||||||
min,
|
|
||||||
median,
|
|
||||||
max,
|
|
||||||
n_below_10,
|
|
||||||
n_above_100
|
|
||||||
);
|
|
||||||
}
|
|
||||||
println!();
|
|
||||||
}
|
|
||||||
|
|
||||||
if !sample_dim_style.is_empty() {
|
|
||||||
println!("─── dimension styles used (count) ───────");
|
|
||||||
for (k, v) in &sample_dim_style {
|
|
||||||
let used_style = doc.dim_styles.iter().find(|s| s.name == *k);
|
|
||||||
let ds = used_style.map(|s| s.dimscale).unwrap_or(f64::NAN);
|
|
||||||
let dt = used_style.map(|s| s.dimtxt).unwrap_or(f64::NAN);
|
|
||||||
let da = used_style.map(|s| s.dimasz).unwrap_or(f64::NAN);
|
|
||||||
println!(
|
|
||||||
" {:>5}× style=\"{}\" dimscale={:.4} dimtxt={:.4} dimasz={:.4}",
|
|
||||||
v, k, ds, dt, da
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
@ -1,128 +0,0 @@
|
||||||
// List entities inside a named block, showing their layer + resolved linetype.
|
|
||||||
//
|
|
||||||
// cargo run --release --example inspect_block -- <file> <block_name>
|
|
||||||
|
|
||||||
use acadrust::entities::EntityType;
|
|
||||||
use acadrust::io::dwg::DwgReader;
|
|
||||||
|
|
||||||
fn main() -> Result<(), Box<dyn std::error::Error>> {
|
|
||||||
let path = std::env::args().nth(1).expect("usage: inspect_block <file> [block]");
|
|
||||||
let block_name = std::env::args().nth(2);
|
|
||||||
let doc = DwgReader::from_file(&path)?.read()?;
|
|
||||||
|
|
||||||
if block_name.is_none() {
|
|
||||||
let mut blocks: Vec<_> = doc
|
|
||||||
.block_records
|
|
||||||
.iter()
|
|
||||||
.map(|br| {
|
|
||||||
let n = doc
|
|
||||||
.entities()
|
|
||||||
.filter(|e| e.common().owner_handle == br.handle)
|
|
||||||
.count();
|
|
||||||
(br.name.clone(), n)
|
|
||||||
})
|
|
||||||
.collect();
|
|
||||||
blocks.sort_by_key(|(_, n)| std::cmp::Reverse(*n));
|
|
||||||
println!("─── top 30 blocks by entity count ───────");
|
|
||||||
for (name, n) in blocks.iter().take(30) {
|
|
||||||
println!(" {:>5} {}", n, name);
|
|
||||||
}
|
|
||||||
return Ok(());
|
|
||||||
}
|
|
||||||
let block_name = block_name.unwrap();
|
|
||||||
|
|
||||||
// Find the block record by name.
|
|
||||||
let br = doc
|
|
||||||
.block_records
|
|
||||||
.iter()
|
|
||||||
.find(|br| br.name.eq_ignore_ascii_case(&block_name))
|
|
||||||
.ok_or_else(|| format!("block '{}' not found", block_name))?;
|
|
||||||
|
|
||||||
println!("block: \"{}\" handle={:?}", br.name, br.handle);
|
|
||||||
println!();
|
|
||||||
|
|
||||||
let mut counts: std::collections::BTreeMap<&'static str, u32> = Default::default();
|
|
||||||
let mut sample_by_kind: std::collections::BTreeMap<&'static str, Vec<String>> =
|
|
||||||
Default::default();
|
|
||||||
|
|
||||||
for e in doc.entities() {
|
|
||||||
if e.common().owner_handle != br.handle {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
let kind: &'static str = match e {
|
|
||||||
EntityType::Line(_) => "Line",
|
|
||||||
EntityType::LwPolyline(_) => "LwPolyline",
|
|
||||||
EntityType::Polyline(_) => "Polyline",
|
|
||||||
EntityType::Polyline2D(_) => "Polyline2D",
|
|
||||||
EntityType::Arc(_) => "Arc",
|
|
||||||
EntityType::Circle(_) => "Circle",
|
|
||||||
EntityType::Spline(_) => "Spline",
|
|
||||||
EntityType::Ellipse(_) => "Ellipse",
|
|
||||||
EntityType::Text(_) => "Text",
|
|
||||||
EntityType::MText(_) => "MText",
|
|
||||||
EntityType::Insert(_) => "Insert",
|
|
||||||
EntityType::Hatch(_) => "Hatch",
|
|
||||||
EntityType::Solid(_) => "Solid",
|
|
||||||
_ => "Other",
|
|
||||||
};
|
|
||||||
*counts.entry(kind).or_default() += 1;
|
|
||||||
|
|
||||||
let c = e.common();
|
|
||||||
let extra = match e {
|
|
||||||
EntityType::Insert(i) => format!(" → block=\"{}\"", i.block_name),
|
|
||||||
_ => String::new(),
|
|
||||||
};
|
|
||||||
let line = format!(
|
|
||||||
" [{:?}] layer=\"{}\" lt=\"{}\" lt_scale={:.4}{}",
|
|
||||||
e.common().handle, c.layer, c.linetype, c.linetype_scale, extra
|
|
||||||
);
|
|
||||||
let bucket = sample_by_kind.entry(kind).or_default();
|
|
||||||
if bucket.len() < 4 {
|
|
||||||
bucket.push(line);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
println!("─── entity counts in block ──────────────");
|
|
||||||
for (k, v) in &counts {
|
|
||||||
println!(" {:<12} {}", k, v);
|
|
||||||
}
|
|
||||||
println!();
|
|
||||||
for (k, samples) in &sample_by_kind {
|
|
||||||
println!("─── sample {} (first {}) ────────", k, samples.len());
|
|
||||||
for s in samples {
|
|
||||||
println!("{}", s);
|
|
||||||
}
|
|
||||||
println!();
|
|
||||||
}
|
|
||||||
|
|
||||||
println!("─── relevant layers ─────────────────────");
|
|
||||||
let mut seen: std::collections::BTreeSet<String> = Default::default();
|
|
||||||
for e in doc.entities() {
|
|
||||||
if e.common().owner_handle == br.handle {
|
|
||||||
seen.insert(e.common().layer.clone());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
for layer_name in &seen {
|
|
||||||
if let Some(l) = doc.layers.get(layer_name) {
|
|
||||||
println!(
|
|
||||||
" \"{}\" lt=\"{}\" off={} frozen={}",
|
|
||||||
l.name, l.line_type, l.flags.off, l.flags.frozen
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
println!();
|
|
||||||
println!("─── linetype table (dashed only) ────────");
|
|
||||||
for lt in doc.line_types.iter() {
|
|
||||||
if lt.elements.is_empty() || lt.is_continuous() {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
let elems: Vec<f64> = lt.elements.iter().map(|el| el.length).collect();
|
|
||||||
println!(
|
|
||||||
" {:<24} pattern_len={:.4} elements={:?}",
|
|
||||||
lt.name, lt.pattern_length, elems
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
@ -1,43 +0,0 @@
|
||||||
// Lists every Insert in the doc (after xref resolve) with sub-entity counts.
|
|
||||||
// cargo run --release --example inspect_insert -- <dwg>
|
|
||||||
use acadrust::entities::EntityType;
|
|
||||||
use acadrust::io::dwg::DwgReader;
|
|
||||||
|
|
||||||
fn main() -> Result<(), Box<dyn std::error::Error>> {
|
|
||||||
let path = std::env::args().nth(1).unwrap();
|
|
||||||
let mut doc = DwgReader::from_file(&path)?.read()?;
|
|
||||||
if let Some(base) = std::path::Path::new(&path).parent() {
|
|
||||||
let infos = H7CAD::io::xref::resolve_xrefs(&mut doc, base);
|
|
||||||
for i in &infos {
|
|
||||||
println!("XREF: {} -> {:?}", i.name, i.status);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
println!("total entities: {}", doc.entities().count());
|
|
||||||
|
|
||||||
let inserts: Vec<_> = doc
|
|
||||||
.entities()
|
|
||||||
.filter_map(|e| if let EntityType::Insert(i) = e { Some(i.clone()) } else { None })
|
|
||||||
.collect();
|
|
||||||
println!("total inserts: {}", inserts.len());
|
|
||||||
|
|
||||||
let mut by_block: std::collections::HashMap<String, usize> = std::collections::HashMap::new();
|
|
||||||
for i in &inserts {
|
|
||||||
*by_block.entry(i.block_name.clone()).or_insert(0) += 1;
|
|
||||||
}
|
|
||||||
let mut by_block: Vec<_> = by_block.into_iter().collect();
|
|
||||||
by_block.sort_by(|a, b| b.1.cmp(&a.1));
|
|
||||||
|
|
||||||
println!("\ninserts grouped by block_name:");
|
|
||||||
for (name, n) in &by_block {
|
|
||||||
let sub_count = inserts.iter().find(|i| &i.block_name == name)
|
|
||||||
.map(|i| i.explode_from_document(&doc).len())
|
|
||||||
.unwrap_or(0);
|
|
||||||
let nested = doc.block_records.get(name)
|
|
||||||
.map(|br| br.entity_handles.iter()
|
|
||||||
.filter(|h| matches!(doc.get_entity(**h), Some(EntityType::Insert(_))))
|
|
||||||
.count())
|
|
||||||
.unwrap_or(0);
|
|
||||||
println!(" {:>4} × {} (sub={}, nested-inserts={})", n, name, sub_count, nested);
|
|
||||||
}
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
@ -1,116 +0,0 @@
|
||||||
// Summarise linetype usage in a DWG/DXF: which layers use which linetype,
|
|
||||||
// and which entities carry an explicit (non-bylayer) dashed linetype.
|
|
||||||
//
|
|
||||||
// cargo run --release --example inspect_lt -- <file>
|
|
||||||
|
|
||||||
use acadrust::entities::EntityType;
|
|
||||||
use acadrust::io::dwg::DwgReader;
|
|
||||||
use std::collections::BTreeMap;
|
|
||||||
|
|
||||||
fn main() -> Result<(), Box<dyn std::error::Error>> {
|
|
||||||
let path = std::env::args().nth(1).expect("usage: inspect_lt <file>");
|
|
||||||
let doc = DwgReader::from_file(&path)?.read()?;
|
|
||||||
|
|
||||||
println!("─── layers (linetype) ───────────────────");
|
|
||||||
let dashed_lts: std::collections::BTreeSet<String> = doc
|
|
||||||
.line_types
|
|
||||||
.iter()
|
|
||||||
.filter(|lt| !lt.elements.is_empty() && !lt.is_continuous())
|
|
||||||
.map(|lt| lt.name.clone())
|
|
||||||
.collect();
|
|
||||||
println!(" dashed linetypes in table: {:?}", dashed_lts);
|
|
||||||
println!();
|
|
||||||
|
|
||||||
let mut dashed_layers: BTreeMap<String, String> = BTreeMap::new();
|
|
||||||
for l in doc.layers.iter() {
|
|
||||||
if dashed_lts.contains(&l.line_type) {
|
|
||||||
dashed_layers.insert(l.name.clone(), l.line_type.clone());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
println!(" layers using a dashed linetype:");
|
|
||||||
if dashed_layers.is_empty() {
|
|
||||||
println!(" (none)");
|
|
||||||
}
|
|
||||||
for (n, lt) in &dashed_layers {
|
|
||||||
println!(" \"{}\" → \"{}\"", n, lt);
|
|
||||||
}
|
|
||||||
println!();
|
|
||||||
|
|
||||||
// Per-entity explicit dashed override (lt field non-empty and non-byLayer/byBlock/Continuous).
|
|
||||||
let mut explicit_dashed: u32 = 0;
|
|
||||||
let mut by_layer_in_dashed_layer: u32 = 0;
|
|
||||||
let mut by_layer_in_continuous: u32 = 0;
|
|
||||||
let mut by_kind: BTreeMap<&'static str, u32> = BTreeMap::new();
|
|
||||||
let mut sample_explicit: Vec<String> = vec![];
|
|
||||||
let mut sample_layer_inh: Vec<String> = vec![];
|
|
||||||
|
|
||||||
for e in doc.entities() {
|
|
||||||
let c = e.common();
|
|
||||||
let kind: &'static str = match e {
|
|
||||||
EntityType::Line(_) => "Line",
|
|
||||||
EntityType::LwPolyline(_) => "LwPolyline",
|
|
||||||
EntityType::Polyline(_) => "Polyline",
|
|
||||||
EntityType::Polyline2D(_) => "Polyline2D",
|
|
||||||
EntityType::Arc(_) => "Arc",
|
|
||||||
EntityType::Circle(_) => "Circle",
|
|
||||||
EntityType::Spline(_) => "Spline",
|
|
||||||
EntityType::Ellipse(_) => "Ellipse",
|
|
||||||
EntityType::Insert(_) => "Insert",
|
|
||||||
_ => "Other",
|
|
||||||
};
|
|
||||||
let lt = c.linetype.trim();
|
|
||||||
let lt_norm = lt.to_ascii_lowercase();
|
|
||||||
let is_bylayer = lt.is_empty() || lt_norm == "bylayer";
|
|
||||||
let is_explicit_dashed = !is_bylayer
|
|
||||||
&& lt_norm != "byblock"
|
|
||||||
&& lt_norm != "continuous"
|
|
||||||
&& dashed_lts.iter().any(|d| d.eq_ignore_ascii_case(lt));
|
|
||||||
if is_explicit_dashed {
|
|
||||||
explicit_dashed += 1;
|
|
||||||
*by_kind.entry(kind).or_default() += 1;
|
|
||||||
if sample_explicit.len() < 6 {
|
|
||||||
sample_explicit.push(format!(
|
|
||||||
" [{:?}] {} layer=\"{}\" lt=\"{}\" lt_scale={:.4}",
|
|
||||||
c.handle, kind, c.layer, lt, c.linetype_scale
|
|
||||||
));
|
|
||||||
}
|
|
||||||
} else if is_bylayer && dashed_layers.contains_key(&c.layer) {
|
|
||||||
by_layer_in_dashed_layer += 1;
|
|
||||||
if sample_layer_inh.len() < 6 {
|
|
||||||
sample_layer_inh.push(format!(
|
|
||||||
" [{:?}] {} layer=\"{}\" (inherits \"{}\") lt_scale={:.4}",
|
|
||||||
c.handle,
|
|
||||||
kind,
|
|
||||||
c.layer,
|
|
||||||
dashed_layers.get(&c.layer).unwrap(),
|
|
||||||
c.linetype_scale
|
|
||||||
));
|
|
||||||
}
|
|
||||||
} else if is_bylayer {
|
|
||||||
by_layer_in_continuous += 1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
println!("─── entities w/ explicit dashed lt ──────");
|
|
||||||
println!(" total: {}", explicit_dashed);
|
|
||||||
for (k, v) in &by_kind {
|
|
||||||
println!(" {}: {}", k, v);
|
|
||||||
}
|
|
||||||
for s in &sample_explicit {
|
|
||||||
println!("{}", s);
|
|
||||||
}
|
|
||||||
println!();
|
|
||||||
|
|
||||||
println!("─── entities inheriting dashed via layer:");
|
|
||||||
println!(" total: {}", by_layer_in_dashed_layer);
|
|
||||||
for s in &sample_layer_inh {
|
|
||||||
println!("{}", s);
|
|
||||||
}
|
|
||||||
println!();
|
|
||||||
println!(
|
|
||||||
"─── entities bylayer/continuous: {}",
|
|
||||||
by_layer_in_continuous
|
|
||||||
);
|
|
||||||
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
@ -1,174 +0,0 @@
|
||||||
// One-off inspector for diagnosing a DWG: dumps every LwPolyline/Polyline2D/
|
|
||||||
// Polyline3D with its elevation, normal vector, and per-vertex coordinates.
|
|
||||||
// Run with: cargo run --example inspect_polyline -- <path-to-dwg>
|
|
||||||
|
|
||||||
use acadrust::entities::EntityType;
|
|
||||||
use acadrust::io::dwg::DwgReader;
|
|
||||||
use acadrust::DxfReader;
|
|
||||||
use std::env;
|
|
||||||
|
|
||||||
fn main() -> Result<(), Box<dyn std::error::Error>> {
|
|
||||||
let path = env::args().nth(1).ok_or("usage: inspect_polyline <file>")?;
|
|
||||||
let mut doc = if path.to_lowercase().ends_with(".dxf") {
|
|
||||||
DxfReader::from_file(&path)?.read()?
|
|
||||||
} else {
|
|
||||||
DwgReader::from_file(&path)?.read()?
|
|
||||||
};
|
|
||||||
|
|
||||||
// Cheap inline corruption check — mirrors the rules in src/io/mod.rs so the
|
|
||||||
// example reports the same drop count without needing a library crate.
|
|
||||||
let total = doc.entities().count();
|
|
||||||
let bad = doc
|
|
||||||
.entities()
|
|
||||||
.filter(|e| {
|
|
||||||
use acadrust::entities::EntityType as E;
|
|
||||||
const MAX: usize = 100_000;
|
|
||||||
let bad_normal = |n: &acadrust::types::Vector3| {
|
|
||||||
!n.x.is_finite()
|
|
||||||
|| !n.y.is_finite()
|
|
||||||
|| !n.z.is_finite()
|
|
||||||
|| ((n.x * n.x + n.y * n.y + n.z * n.z) - 1.0).abs() >= 0.21
|
|
||||||
};
|
|
||||||
match e {
|
|
||||||
E::LwPolyline(p) => {
|
|
||||||
bad_normal(&p.normal)
|
|
||||||
|| p.vertices.len() > MAX
|
|
||||||
|| !p.elevation.is_finite()
|
|
||||||
|| p.elevation.abs() > 1e10
|
|
||||||
}
|
|
||||||
E::Polyline2D(p) => {
|
|
||||||
bad_normal(&p.normal)
|
|
||||||
|| p.vertices.len() > MAX
|
|
||||||
|| !p.elevation.is_finite()
|
|
||||||
|| p.elevation.abs() > 1e10
|
|
||||||
}
|
|
||||||
_ => false,
|
|
||||||
}
|
|
||||||
})
|
|
||||||
.count();
|
|
||||||
println!("Entities: total={} corrupt={}", total, bad);
|
|
||||||
println!();
|
|
||||||
let _ = &mut doc; // silence unused-mut warning if we don't mutate below
|
|
||||||
|
|
||||||
let h = &doc.header;
|
|
||||||
println!(
|
|
||||||
"Header extents: min=({:.3},{:.3},{:.3}) max=({:.3},{:.3},{:.3})",
|
|
||||||
h.model_space_extents_min.x,
|
|
||||||
h.model_space_extents_min.y,
|
|
||||||
h.model_space_extents_min.z,
|
|
||||||
h.model_space_extents_max.x,
|
|
||||||
h.model_space_extents_max.y,
|
|
||||||
h.model_space_extents_max.z,
|
|
||||||
);
|
|
||||||
println!();
|
|
||||||
|
|
||||||
let mut lwp = 0usize;
|
|
||||||
let mut p2d = 0usize;
|
|
||||||
let mut p3d = 0usize;
|
|
||||||
let mut pl = 0usize;
|
|
||||||
|
|
||||||
for (i, e) in doc.entities().enumerate() {
|
|
||||||
match e {
|
|
||||||
EntityType::LwPolyline(p) => {
|
|
||||||
lwp += 1;
|
|
||||||
if p.common.layer != "PLN_Sev" {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
let nx_abs = p.normal.x.abs();
|
|
||||||
let ny_abs = p.normal.y.abs();
|
|
||||||
let corrupt = !nx_abs.is_finite()
|
|
||||||
|| nx_abs > 1.0
|
|
||||||
|| ny_abs > 1.0
|
|
||||||
|| p.vertices.len() > 1000;
|
|
||||||
if corrupt {
|
|
||||||
println!(
|
|
||||||
"[{}] CORRUPT handle={:?} verts={} elev={:.3e} normal=({:.2e},{:.2e},{:.2e})",
|
|
||||||
i,
|
|
||||||
p.common.handle,
|
|
||||||
p.vertices.len(),
|
|
||||||
p.elevation,
|
|
||||||
p.normal.x,
|
|
||||||
p.normal.y,
|
|
||||||
p.normal.z,
|
|
||||||
);
|
|
||||||
} else {
|
|
||||||
println!(
|
|
||||||
"[{}] OK handle={:?} verts={} elev={:.3} normal=({:.4},{:.4},{:.4})",
|
|
||||||
i,
|
|
||||||
p.common.handle,
|
|
||||||
p.vertices.len(),
|
|
||||||
p.elevation,
|
|
||||||
p.normal.x,
|
|
||||||
p.normal.y,
|
|
||||||
p.normal.z,
|
|
||||||
);
|
|
||||||
for (vi, v) in p.vertices.iter().take(3).enumerate() {
|
|
||||||
println!(" v{}: ({:.3}, {:.3}) bulge={}", vi, v.location.x, v.location.y, v.bulge);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
EntityType::Polyline2D(p) => {
|
|
||||||
p2d += 1;
|
|
||||||
if p2d <= 5 {
|
|
||||||
println!(
|
|
||||||
"[{}] Polyline2D handle={:?} layer={:?}",
|
|
||||||
i, p.common.handle, p.common.layer
|
|
||||||
);
|
|
||||||
println!(
|
|
||||||
" elevation={} thickness={} normal=({},{},{})",
|
|
||||||
p.elevation, p.thickness, p.normal.x, p.normal.y, p.normal.z
|
|
||||||
);
|
|
||||||
println!(" closed={} verts={}", p.is_closed(), p.vertices.len());
|
|
||||||
for (vi, v) in p.vertices.iter().enumerate() {
|
|
||||||
println!(
|
|
||||||
" v{}: ({}, {}, {}) bulge={}",
|
|
||||||
vi, v.location.x, v.location.y, v.location.z, v.bulge
|
|
||||||
);
|
|
||||||
}
|
|
||||||
println!();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
EntityType::Polyline3D(p) => {
|
|
||||||
p3d += 1;
|
|
||||||
if p3d <= 5 {
|
|
||||||
println!(
|
|
||||||
"[{}] Polyline3D handle={:?} layer={:?}",
|
|
||||||
i, p.common.handle, p.common.layer
|
|
||||||
);
|
|
||||||
println!(" closed={} verts={}", p.is_closed(), p.vertices.len());
|
|
||||||
for (vi, v) in p.vertices.iter().enumerate() {
|
|
||||||
println!(
|
|
||||||
" v{}: ({}, {}, {})",
|
|
||||||
vi, v.position.x, v.position.y, v.position.z
|
|
||||||
);
|
|
||||||
}
|
|
||||||
println!();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
EntityType::Polyline(p) => {
|
|
||||||
pl += 1;
|
|
||||||
if pl <= 5 {
|
|
||||||
println!(
|
|
||||||
"[{}] Polyline (heavy 3D) handle={:?} layer={:?}",
|
|
||||||
i, p.common.handle, p.common.layer
|
|
||||||
);
|
|
||||||
println!(" closed={} verts={}", p.flags.is_closed(), p.vertices.len());
|
|
||||||
for (vi, v) in p.vertices.iter().enumerate() {
|
|
||||||
println!(
|
|
||||||
" v{}: ({}, {}, {})",
|
|
||||||
vi, v.location.x, v.location.y, v.location.z
|
|
||||||
);
|
|
||||||
}
|
|
||||||
println!();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
_ => {}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
println!(
|
|
||||||
"Totals: LwPolyline={} Polyline2D={} Polyline3D={} Polyline={}",
|
|
||||||
lwp, p2d, p3d, pl
|
|
||||||
);
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
@ -1,126 +0,0 @@
|
||||||
// Dumps every paper-space viewport's view fields from a DWG/DXF.
|
|
||||||
// cargo run --release --example inspect_viewports -- <path>
|
|
||||||
use acadrust::entities::EntityType;
|
|
||||||
use acadrust::io::dwg::DwgReader;
|
|
||||||
|
|
||||||
fn main() -> Result<(), Box<dyn std::error::Error>> {
|
|
||||||
let path = std::env::args().nth(1).expect("usage: inspect_viewports <file>");
|
|
||||||
let doc = DwgReader::from_file(&path)?.read()?;
|
|
||||||
println!("file: {}", path);
|
|
||||||
println!("total entities: {}", doc.entities().count());
|
|
||||||
let unit_name = match doc.header.insertion_units {
|
|
||||||
0 => "Unitless",
|
|
||||||
1 => "Inches",
|
|
||||||
2 => "Feet",
|
|
||||||
3 => "Miles",
|
|
||||||
4 => "Millimeters",
|
|
||||||
5 => "Centimeters",
|
|
||||||
6 => "Meters",
|
|
||||||
7 => "Kilometers",
|
|
||||||
_ => "Other",
|
|
||||||
};
|
|
||||||
println!(
|
|
||||||
"header.insertion_units = {} ({})",
|
|
||||||
doc.header.insertion_units, unit_name
|
|
||||||
);
|
|
||||||
println!(
|
|
||||||
"header.model_space_extents min=({:.3}, {:.3}, {:.3}) max=({:.3}, {:.3}, {:.3})",
|
|
||||||
doc.header.model_space_extents_min.x,
|
|
||||||
doc.header.model_space_extents_min.y,
|
|
||||||
doc.header.model_space_extents_min.z,
|
|
||||||
doc.header.model_space_extents_max.x,
|
|
||||||
doc.header.model_space_extents_max.y,
|
|
||||||
doc.header.model_space_extents_max.z,
|
|
||||||
);
|
|
||||||
println!();
|
|
||||||
|
|
||||||
let viewports: Vec<_> = doc
|
|
||||||
.entities()
|
|
||||||
.filter_map(|e| {
|
|
||||||
if let EntityType::Viewport(v) = e {
|
|
||||||
Some(v.clone())
|
|
||||||
} else {
|
|
||||||
None
|
|
||||||
}
|
|
||||||
})
|
|
||||||
.collect();
|
|
||||||
println!("viewports: {}\n", viewports.len());
|
|
||||||
|
|
||||||
for (i, vp) in viewports.iter().enumerate() {
|
|
||||||
let scale_from_view_height = if vp.view_height.abs() > 1e-9 {
|
|
||||||
vp.height / vp.view_height
|
|
||||||
} else {
|
|
||||||
f64::NAN
|
|
||||||
};
|
|
||||||
println!(
|
|
||||||
"[{}] id={} handle={:?}",
|
|
||||||
i, vp.id, vp.common.handle
|
|
||||||
);
|
|
||||||
println!(
|
|
||||||
" paper center=({:.3}, {:.3}) size={:.3} × {:.3}",
|
|
||||||
vp.center.x, vp.center.y, vp.width, vp.height
|
|
||||||
);
|
|
||||||
println!(
|
|
||||||
" view_target=({:.3}, {:.3}, {:.3})",
|
|
||||||
vp.view_target.x, vp.view_target.y, vp.view_target.z
|
|
||||||
);
|
|
||||||
println!(
|
|
||||||
" view_direction=({:.3}, {:.3}, {:.3})",
|
|
||||||
vp.view_direction.x, vp.view_direction.y, vp.view_direction.z
|
|
||||||
);
|
|
||||||
println!(
|
|
||||||
" view_center=({:.3}, {:.3})",
|
|
||||||
vp.view_center.x, vp.view_center.y
|
|
||||||
);
|
|
||||||
println!(
|
|
||||||
" view_height={:.6} == vp.height? {} ⇒ height/view_height = {:.6}",
|
|
||||||
vp.view_height,
|
|
||||||
(vp.view_height - vp.height).abs() < 1e-6,
|
|
||||||
scale_from_view_height
|
|
||||||
);
|
|
||||||
println!(
|
|
||||||
" custom_scale={:.6} twist={:.4} lens={:.2}",
|
|
||||||
vp.custom_scale, vp.twist_angle, vp.lens_length
|
|
||||||
);
|
|
||||||
println!(
|
|
||||||
" status: on={}, locked={}, perspective={}",
|
|
||||||
vp.status.is_on, vp.status.locked, vp.status.perspective
|
|
||||||
);
|
|
||||||
println!();
|
|
||||||
}
|
|
||||||
|
|
||||||
// Summary stats
|
|
||||||
let n_with_view_eq_height = viewports
|
|
||||||
.iter()
|
|
||||||
.filter(|v| (v.view_height - v.height).abs() < 1e-6)
|
|
||||||
.count();
|
|
||||||
let n_with_target_zero = viewports
|
|
||||||
.iter()
|
|
||||||
.filter(|v| {
|
|
||||||
v.view_target.x.abs() < 1e-9
|
|
||||||
&& v.view_target.y.abs() < 1e-9
|
|
||||||
&& v.view_target.z.abs() < 1e-9
|
|
||||||
})
|
|
||||||
.count();
|
|
||||||
let unique_view_heights: std::collections::BTreeSet<u64> = viewports
|
|
||||||
.iter()
|
|
||||||
.map(|v| v.view_height.to_bits())
|
|
||||||
.collect();
|
|
||||||
|
|
||||||
println!("─────── summary ───────");
|
|
||||||
println!(
|
|
||||||
" view_height == vp.height : {}/{}",
|
|
||||||
n_with_view_eq_height,
|
|
||||||
viewports.len()
|
|
||||||
);
|
|
||||||
println!(
|
|
||||||
" view_target == (0, 0, 0) : {}/{}",
|
|
||||||
n_with_target_zero,
|
|
||||||
viewports.len()
|
|
||||||
);
|
|
||||||
println!(
|
|
||||||
" unique view_height values : {}",
|
|
||||||
unique_view_heights.len()
|
|
||||||
);
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
@ -1,87 +0,0 @@
|
||||||
// Times each open phase: parse / purge / derived caches.
|
|
||||||
// Run with: cargo run --release --example time_open -- <path-to-dwg>
|
|
||||||
|
|
||||||
use acadrust::entities::EntityType;
|
|
||||||
use std::env;
|
|
||||||
use std::path::Path;
|
|
||||||
use std::time::Instant;
|
|
||||||
use H7CAD::io::{load_file, purge_corrupt_entities};
|
|
||||||
use H7CAD::scene::{build_derived_caches, Scene};
|
|
||||||
|
|
||||||
fn main() -> Result<(), Box<dyn std::error::Error>> {
|
|
||||||
let path = env::args().nth(1).ok_or("usage: time_open <file>")?;
|
|
||||||
|
|
||||||
let t0 = Instant::now();
|
|
||||||
let mut doc = load_file(Path::new(&path))?;
|
|
||||||
let parse_ms = t0.elapsed().as_millis();
|
|
||||||
|
|
||||||
let t1 = Instant::now();
|
|
||||||
let dropped = purge_corrupt_entities(&mut doc);
|
|
||||||
let purge_ms = t1.elapsed().as_millis();
|
|
||||||
|
|
||||||
let t2 = Instant::now();
|
|
||||||
let caches = build_derived_caches(&doc);
|
|
||||||
let caches_ms = t2.elapsed().as_millis();
|
|
||||||
|
|
||||||
let mut counts = std::collections::HashMap::<&'static str, usize>::new();
|
|
||||||
let mut total = 0usize;
|
|
||||||
for e in doc.entities() {
|
|
||||||
total += 1;
|
|
||||||
let tag: &'static str = match e {
|
|
||||||
EntityType::Line(_) => "Line",
|
|
||||||
EntityType::Arc(_) => "Arc",
|
|
||||||
EntityType::Circle(_) => "Circle",
|
|
||||||
EntityType::LwPolyline(_) => "LwPolyline",
|
|
||||||
EntityType::Polyline2D(_) => "Polyline2D",
|
|
||||||
EntityType::Polyline3D(_) => "Polyline3D",
|
|
||||||
EntityType::Polyline(_) => "Polyline",
|
|
||||||
EntityType::Hatch(_) => "Hatch",
|
|
||||||
EntityType::Solid(_) => "Solid",
|
|
||||||
EntityType::RasterImage(_) => "RasterImage",
|
|
||||||
EntityType::Solid3D(_) => "Solid3D",
|
|
||||||
EntityType::Region(_) => "Region",
|
|
||||||
EntityType::Body(_) => "Body",
|
|
||||||
EntityType::Text(_) => "Text",
|
|
||||||
EntityType::MText(_) => "MText",
|
|
||||||
EntityType::Insert(_) => "Insert",
|
|
||||||
EntityType::Dimension(_) => "Dimension",
|
|
||||||
EntityType::Spline(_) => "Spline",
|
|
||||||
EntityType::Ellipse(_) => "Ellipse",
|
|
||||||
_ => "_other",
|
|
||||||
};
|
|
||||||
*counts.entry(tag).or_insert(0) += 1;
|
|
||||||
}
|
|
||||||
let mut counts: Vec<_> = counts.into_iter().collect();
|
|
||||||
counts.sort_by(|a, b| b.1.cmp(&a.1));
|
|
||||||
|
|
||||||
// Set up a Scene as the UI thread does and time first wire tessellation.
|
|
||||||
let mut scene = Scene::new();
|
|
||||||
scene.document = doc;
|
|
||||||
scene.world_offset = caches.world_offset;
|
|
||||||
scene.local_extent_max = caches.local_extent_max;
|
|
||||||
scene.hatches = caches.hatches.clone();
|
|
||||||
scene.images = caches.images.clone();
|
|
||||||
scene.meshes = caches.meshes.clone();
|
|
||||||
scene.bump_geometry();
|
|
||||||
scene.current_layout = "Model".to_string();
|
|
||||||
H7CAD::linetypes::populate_document(&mut scene.document);
|
|
||||||
|
|
||||||
let t3 = Instant::now();
|
|
||||||
let wires = scene.entity_wires();
|
|
||||||
let wires_ms = t3.elapsed().as_millis();
|
|
||||||
|
|
||||||
println!("file: {}", path);
|
|
||||||
println!("parse: {} ms", parse_ms);
|
|
||||||
println!("purge: {} ms (dropped {})", purge_ms, dropped);
|
|
||||||
println!("derived caches: {} ms", caches_ms);
|
|
||||||
println!("first wires: {} ms ({} wires)", wires_ms, wires.len());
|
|
||||||
println!(" hatches: {}", caches.hatches.len());
|
|
||||||
println!(" images: {}", caches.images.len());
|
|
||||||
println!(" meshes: {}", caches.meshes.len());
|
|
||||||
println!();
|
|
||||||
println!("entities: {}", total);
|
|
||||||
for (tag, n) in counts.iter().take(20) {
|
|
||||||
println!(" {:>14} {}", tag, n);
|
|
||||||
}
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
@ -1,23 +0,0 @@
|
||||||
// Dump every xref block_record's raw is_loaded bit before any resolve.
|
|
||||||
// cargo run --release --example xref_states -- <dwg>
|
|
||||||
use acadrust::io::dwg::DwgReader;
|
|
||||||
|
|
||||||
fn main() -> Result<(), Box<dyn std::error::Error>> {
|
|
||||||
let path = std::env::args().nth(1).unwrap();
|
|
||||||
let doc = DwgReader::from_file(&path)?.read()?;
|
|
||||||
|
|
||||||
for br in doc.block_records.iter() {
|
|
||||||
if br.flags.is_xref || br.flags.is_xref_overlay {
|
|
||||||
println!(
|
|
||||||
"name={:<30} is_xref={} overlay={} is_loaded={:?} xref_path={:?} entity_handles={}",
|
|
||||||
br.name,
|
|
||||||
br.flags.is_xref,
|
|
||||||
br.flags.is_xref_overlay,
|
|
||||||
br.is_loaded,
|
|
||||||
br.xref_path,
|
|
||||||
br.entity_handles.len(),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
@ -5,17 +5,17 @@
|
||||||
<key>CFBundleDevelopmentRegion</key>
|
<key>CFBundleDevelopmentRegion</key>
|
||||||
<string>en</string>
|
<string>en</string>
|
||||||
<key>CFBundleDisplayName</key>
|
<key>CFBundleDisplayName</key>
|
||||||
<string>H7CAD</string>
|
<string>Open CAD Studio</string>
|
||||||
<key>CFBundleExecutable</key>
|
<key>CFBundleExecutable</key>
|
||||||
<string>H7CAD</string>
|
<string>OpenCADStudio</string>
|
||||||
<key>CFBundleIconFile</key>
|
<key>CFBundleIconFile</key>
|
||||||
<string>AppIcon</string>
|
<string>AppIcon</string>
|
||||||
<key>CFBundleIdentifier</key>
|
<key>CFBundleIdentifier</key>
|
||||||
<string>io.github.HakanSeven12.H7CAD</string>
|
<string>io.github.HakanSeven12.OpenCadStudio</string>
|
||||||
<key>CFBundleInfoDictionaryVersion</key>
|
<key>CFBundleInfoDictionaryVersion</key>
|
||||||
<string>6.0</string>
|
<string>6.0</string>
|
||||||
<key>CFBundleName</key>
|
<key>CFBundleName</key>
|
||||||
<string>H7CAD</string>
|
<string>Open CAD Studio</string>
|
||||||
<key>CFBundlePackageType</key>
|
<key>CFBundlePackageType</key>
|
||||||
<string>APPL</string>
|
<string>APPL</string>
|
||||||
<key>CFBundleShortVersionString</key>
|
<key>CFBundleShortVersionString</key>
|
||||||
|
|
|
||||||
|
|
@ -1,11 +1,11 @@
|
||||||
[Desktop Entry]
|
[Desktop Entry]
|
||||||
Name=H7CAD
|
Name=Open CAD Studio
|
||||||
Comment=A CAD application for 2D/3D drawing and design
|
Comment=A CAD application for 2D/3D drawing and design
|
||||||
Exec=H7CAD
|
Exec=OpenCADStudio
|
||||||
Icon=io.github.HakanSeven12.H7CAD
|
Icon=io.github.HakanSeven12.OpenCadStudio
|
||||||
Terminal=false
|
Terminal=false
|
||||||
Type=Application
|
Type=Application
|
||||||
Categories=Graphics;Engineering;
|
Categories=Graphics;Engineering;
|
||||||
MimeType=image/vnd.dxf;
|
MimeType=image/vnd.dxf;
|
||||||
Keywords=CAD;DXF;drawing;design;engineering;2D;3D;
|
Keywords=CAD;DXF;drawing;design;engineering;2D;3D;
|
||||||
StartupWMClass=H7CAD
|
StartupWMClass=OpenCADStudio
|
||||||
|
|
@ -1,9 +1,9 @@
|
||||||
use super::{Message, H7CAD};
|
use super::{Message, OpenCADStudio};
|
||||||
use crate::command::CmdResult;
|
use crate::command::CmdResult;
|
||||||
use acadrust::Handle;
|
use acadrust::Handle;
|
||||||
use iced::Task;
|
use iced::Task;
|
||||||
|
|
||||||
impl H7CAD {
|
impl OpenCADStudio {
|
||||||
pub(super) fn apply_cmd_result(&mut self, result: CmdResult) -> Task<Message> {
|
pub(super) fn apply_cmd_result(&mut self, result: CmdResult) -> Task<Message> {
|
||||||
let i = self.active_tab;
|
let i = self.active_tab;
|
||||||
match result {
|
match result {
|
||||||
|
|
|
||||||
|
|
@ -1,10 +1,10 @@
|
||||||
use super::{Message, H7CAD};
|
use super::{Message, OpenCADStudio};
|
||||||
use crate::command::CadCommand;
|
use crate::command::CadCommand;
|
||||||
use crate::scene::Scene;
|
use crate::scene::Scene;
|
||||||
use iced::Task;
|
use iced::Task;
|
||||||
use std::path::PathBuf;
|
use std::path::PathBuf;
|
||||||
|
|
||||||
impl H7CAD {
|
impl OpenCADStudio {
|
||||||
pub(super) fn dispatch_command(&mut self, cmd: &str) -> Task<Message> {
|
pub(super) fn dispatch_command(&mut self, cmd: &str) -> Task<Message> {
|
||||||
let i = self.active_tab;
|
let i = self.active_tab;
|
||||||
// Cancel any running command before starting a new one.
|
// Cancel any running command before starting a new one.
|
||||||
|
|
@ -2524,7 +2524,7 @@ impl H7CAD {
|
||||||
}
|
}
|
||||||
|
|
||||||
"REPORT" => {
|
"REPORT" => {
|
||||||
let _ = open::that("https://github.com/HakanSeven12/H7CAD/issues/new");
|
let _ = open::that("https://github.com/HakanSeven12/open-cad-studio/issues/new");
|
||||||
self.command_line.push_info("Opening GitHub issue page...");
|
self.command_line.push_info("Opening GitHub issue page...");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -2533,7 +2533,7 @@ impl H7CAD {
|
||||||
}
|
}
|
||||||
|
|
||||||
"CHANGELOG" => {
|
"CHANGELOG" => {
|
||||||
let _ = open::that("https://github.com/HakanSeven12/H7CAD/releases");
|
let _ = open::that("https://github.com/HakanSeven12/open-cad-studio/releases");
|
||||||
self.command_line.push_info("Opening release notes...");
|
self.command_line.push_info("Opening release notes...");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -4831,7 +4831,7 @@ impl H7CAD {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
"SET" => {
|
"SET" => {
|
||||||
let app = parts.get(1).copied().unwrap_or("H7CAD");
|
let app = parts.get(1).copied().unwrap_or("OpenCADStudio");
|
||||||
let val = parts.get(2).copied().unwrap_or("");
|
let val = parts.get(2).copied().unwrap_or("");
|
||||||
self.push_undo_snapshot(i, "XDATA SET");
|
self.push_undo_snapshot(i, "XDATA SET");
|
||||||
for sh in &selected_handles {
|
for sh in &selected_handles {
|
||||||
|
|
|
||||||
|
|
@ -229,7 +229,7 @@ pub(super) fn title_case_word(value: &str) -> String {
|
||||||
|
|
||||||
// ── Window icon ────────────────────────────────────────────────────────────
|
// ── Window icon ────────────────────────────────────────────────────────────
|
||||||
|
|
||||||
/// Builds a 32×32 RGBA icon: red background with H7 drawn in white pixels.
|
/// Builds a 32×32 RGBA icon: red background with OCS drawn in white pixels.
|
||||||
pub(super) fn build_window_icon() -> Vec<u8> {
|
pub(super) fn build_window_icon() -> Vec<u8> {
|
||||||
const W: usize = 32;
|
const W: usize = 32;
|
||||||
const SZ: usize = W * W * 4;
|
const SZ: usize = W * W * 4;
|
||||||
|
|
@ -261,14 +261,21 @@ pub(super) fn build_window_icon() -> Vec<u8> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// H
|
// O
|
||||||
stroke(&mut px, 4, 5, 4, 26, fg);
|
stroke(&mut px, 3, 6, 9, 6, fg);
|
||||||
stroke(&mut px, 13, 5, 13, 26, fg);
|
stroke(&mut px, 3, 25, 9, 25, fg);
|
||||||
stroke(&mut px, 4, 15, 13, 15, fg);
|
stroke(&mut px, 3, 6, 3, 25, fg);
|
||||||
// 7
|
stroke(&mut px, 9, 6, 9, 25, fg);
|
||||||
stroke(&mut px, 17, 5, 27, 5, fg);
|
// C
|
||||||
stroke(&mut px, 27, 5, 20, 26, fg);
|
stroke(&mut px, 12, 6, 18, 6, fg);
|
||||||
stroke(&mut px, 20, 16, 26, 16, fg);
|
stroke(&mut px, 12, 25, 18, 25, fg);
|
||||||
|
stroke(&mut px, 12, 6, 12, 25, fg);
|
||||||
|
// S
|
||||||
|
stroke(&mut px, 21, 6, 27, 6, fg);
|
||||||
|
stroke(&mut px, 21, 6, 21, 15, fg);
|
||||||
|
stroke(&mut px, 21, 15, 27, 15, fg);
|
||||||
|
stroke(&mut px, 27, 15, 27, 25, fg);
|
||||||
|
stroke(&mut px, 21, 25, 27, 25, fg);
|
||||||
|
|
||||||
px
|
px
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,7 @@
|
||||||
use super::{document::HistorySnapshot, H7CAD};
|
use super::{document::HistorySnapshot, OpenCADStudio};
|
||||||
use std::collections::HashSet;
|
use std::collections::HashSet;
|
||||||
|
|
||||||
impl H7CAD {
|
impl OpenCADStudio {
|
||||||
pub(super) fn history_label_from_active_cmd(&self, i: usize, fallback: &'static str) -> String {
|
pub(super) fn history_label_from_active_cmd(&self, i: usize, fallback: &'static str) -> String {
|
||||||
self.tabs[i]
|
self.tabs[i]
|
||||||
.active_cmd
|
.active_cmd
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,7 @@
|
||||||
use super::H7CAD;
|
use super::OpenCADStudio;
|
||||||
use crate::ui;
|
use crate::ui;
|
||||||
|
|
||||||
impl H7CAD {
|
impl OpenCADStudio {
|
||||||
pub(super) fn sync_ribbon_layers(&mut self) {
|
pub(super) fn sync_ribbon_layers(&mut self) {
|
||||||
let i = self.active_tab;
|
let i = self.active_tab;
|
||||||
let active = self.tabs[i].active_layer.clone();
|
let active = self.tabs[i].active_layer.clone();
|
||||||
|
|
|
||||||
|
|
@ -44,7 +44,7 @@ pub struct OpenProgress {
|
||||||
|
|
||||||
// ── Application state ──────────────────────────────────────────────────────
|
// ── Application state ──────────────────────────────────────────────────────
|
||||||
|
|
||||||
pub(super) struct H7CAD {
|
pub(super) struct OpenCADStudio {
|
||||||
start: Instant,
|
start: Instant,
|
||||||
tabs: Vec<DocumentTab>,
|
tabs: Vec<DocumentTab>,
|
||||||
active_tab: usize,
|
active_tab: usize,
|
||||||
|
|
@ -697,7 +697,7 @@ pub enum Message {
|
||||||
ObjImportPath(Option<std::path::PathBuf>),
|
ObjImportPath(Option<std::path::PathBuf>),
|
||||||
}
|
}
|
||||||
|
|
||||||
impl H7CAD {
|
impl OpenCADStudio {
|
||||||
fn new() -> Self {
|
fn new() -> Self {
|
||||||
// Boot with only the Welcome/Start tab. The user creates drawings
|
// Boot with only the Welcome/Start tab. The user creates drawings
|
||||||
// explicitly (File → New); we never auto-spawn Drawing1.
|
// explicitly (File → New); we never auto-spawn Drawing1.
|
||||||
|
|
@ -855,9 +855,9 @@ impl H7CAD {
|
||||||
use std::path::PathBuf;
|
use std::path::PathBuf;
|
||||||
|
|
||||||
pub fn run() -> iced::Result {
|
pub fn run() -> iced::Result {
|
||||||
iced::daemon(H7CAD::boot, H7CAD::update, H7CAD::view)
|
iced::daemon(OpenCADStudio::boot, OpenCADStudio::update, OpenCADStudio::view)
|
||||||
.subscription(H7CAD::subscription)
|
.subscription(OpenCADStudio::subscription)
|
||||||
.title(|state: &H7CAD, window_id: window::Id| {
|
.title(|state: &OpenCADStudio, window_id: window::Id| {
|
||||||
if Some(window_id) == state.layer_window {
|
if Some(window_id) == state.layer_window {
|
||||||
return "Layer Properties Manager".into();
|
return "Layer Properties Manager".into();
|
||||||
}
|
}
|
||||||
|
|
@ -886,7 +886,7 @@ pub fn run() -> iced::Result {
|
||||||
return "Keyboard Shortcuts".into();
|
return "Keyboard Shortcuts".into();
|
||||||
}
|
}
|
||||||
if Some(window_id) == state.about_window {
|
if Some(window_id) == state.about_window {
|
||||||
return "About H7CAD".into();
|
return "About Open CAD Studio".into();
|
||||||
}
|
}
|
||||||
if Some(window_id) == state.update_notice_window {
|
if Some(window_id) == state.update_notice_window {
|
||||||
return "Update Available".into();
|
return "Update Available".into();
|
||||||
|
|
@ -900,11 +900,11 @@ pub fn run() -> iced::Result {
|
||||||
if let Some(tab) = state.tabs.get(state.active_tab) {
|
if let Some(tab) = state.tabs.get(state.active_tab) {
|
||||||
let dot = if tab.dirty { "● " } else { "" };
|
let dot = if tab.dirty { "● " } else { "" };
|
||||||
let name = tab.tab_display_name();
|
let name = tab.tab_display_name();
|
||||||
format!("{}H7CAD — {}", dot, name)
|
format!("{}Open CAD Studio — {}", dot, name)
|
||||||
} else {
|
} else {
|
||||||
"H7CAD".to_string()
|
"Open CAD Studio".to_string()
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
.theme(|state: &H7CAD, _| state.active_theme.clone())
|
.theme(|state: &OpenCADStudio, _| state.active_theme.clone())
|
||||||
.run()
|
.run()
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,11 +1,11 @@
|
||||||
use super::helpers::{entity_type_key, entity_type_label, title_case_word};
|
use super::helpers::{entity_type_key, entity_type_label, title_case_word};
|
||||||
use super::{H7CAD, VARIES_LABEL};
|
use super::{OpenCADStudio, VARIES_LABEL};
|
||||||
use crate::linetypes;
|
use crate::linetypes;
|
||||||
use crate::scene::dispatch;
|
use crate::scene::dispatch;
|
||||||
use crate::ui;
|
use crate::ui;
|
||||||
use acadrust::{EntityType, Handle};
|
use acadrust::{EntityType, Handle};
|
||||||
|
|
||||||
impl H7CAD {
|
impl OpenCADStudio {
|
||||||
/// Rebuild the PropertiesPanel from the current entity selection.
|
/// Rebuild the PropertiesPanel from the current entity selection.
|
||||||
/// Preserves UI state (open pickers, edit buffer) across refreshes.
|
/// Preserves UI state (open pickers, edit buffer) across refreshes.
|
||||||
pub(super) fn refresh_properties(&mut self) {
|
pub(super) fn refresh_properties(&mut self) {
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,7 @@
|
||||||
use super::helpers::{
|
use super::helpers::{
|
||||||
angle_close, ortho_constrain, parse_coord, polar_constrain, ucs_to_wcs, ucs_z_axis,
|
angle_close, ortho_constrain, parse_coord, polar_constrain, ucs_to_wcs, ucs_z_axis,
|
||||||
};
|
};
|
||||||
use super::{Message, H7CAD, POLY_START_DELAY_MS};
|
use super::{Message, OpenCADStudio, POLY_START_DELAY_MS};
|
||||||
use crate::modules::ModuleEvent;
|
use crate::modules::ModuleEvent;
|
||||||
use crate::scene::grip::{find_hit_grip, find_hit_grip_paper, GripEdit};
|
use crate::scene::grip::{find_hit_grip, find_hit_grip_paper, GripEdit};
|
||||||
use crate::scene::object::GripApply;
|
use crate::scene::object::GripApply;
|
||||||
|
|
@ -31,7 +31,7 @@ fn format_size(bytes: u64) -> String {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl H7CAD {
|
impl OpenCADStudio {
|
||||||
pub fn update(&mut self, msg: Message) -> Task<Message> {
|
pub fn update(&mut self, msg: Message) -> Task<Message> {
|
||||||
match msg {
|
match msg {
|
||||||
Message::Tick(t) => {
|
Message::Tick(t) => {
|
||||||
|
|
@ -3414,7 +3414,7 @@ impl H7CAD {
|
||||||
|
|
||||||
Message::AboutCopyInfo => {
|
Message::AboutCopyInfo => {
|
||||||
let info = format!(
|
let info = format!(
|
||||||
"H7CAD v{}\nOS: {}\nArch: {}",
|
"Open CAD Studio v{}\nOS: {}\nArch: {}",
|
||||||
env!("CARGO_PKG_VERSION"),
|
env!("CARGO_PKG_VERSION"),
|
||||||
std::env::consts::OS,
|
std::env::consts::OS,
|
||||||
std::env::consts::ARCH,
|
std::env::consts::ARCH,
|
||||||
|
|
@ -4826,7 +4826,7 @@ impl H7CAD {
|
||||||
|
|
||||||
// ── DimStyle dialog helpers ─────────────────────────────────────────────────
|
// ── DimStyle dialog helpers ─────────────────────────────────────────────────
|
||||||
|
|
||||||
impl H7CAD {
|
impl OpenCADStudio {
|
||||||
/// Populate all edit buffers from the currently selected dim style.
|
/// Populate all edit buffers from the currently selected dim style.
|
||||||
fn load_dimstyle_bufs(&mut self, tab: usize) {
|
fn load_dimstyle_bufs(&mut self, tab: usize) {
|
||||||
let doc = &self.tabs[tab].scene.document;
|
let doc = &self.tabs[tab].scene.document;
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,7 @@
|
||||||
use super::document::DocumentTab;
|
use super::document::DocumentTab;
|
||||||
use super::helpers::grid_plane_from_camera;
|
use super::helpers::grid_plane_from_camera;
|
||||||
use super::history::history_dropdown_labels;
|
use super::history::history_dropdown_labels;
|
||||||
use super::{Message, H7CAD};
|
use super::{Message, OpenCADStudio};
|
||||||
use crate::scene::grip::{grips_to_screen, grips_to_screen_paper};
|
use crate::scene::grip::{grips_to_screen, grips_to_screen_paper};
|
||||||
use crate::scene::paper_canvas::PaperCanvas;
|
use crate::scene::paper_canvas::PaperCanvas;
|
||||||
use crate::scene::viewport_pane::{PaperViewportPane, ViewportPane};
|
use crate::scene::viewport_pane::{PaperViewportPane, ViewportPane};
|
||||||
|
|
@ -16,7 +16,7 @@ use iced::{keyboard, Background, Border, Color, Element, Fill, Subscription, Tas
|
||||||
|
|
||||||
const VIEWCUBE_HIT_SIZE: f32 = VIEWCUBE_DRAW_PX;
|
const VIEWCUBE_HIT_SIZE: f32 = VIEWCUBE_DRAW_PX;
|
||||||
|
|
||||||
impl H7CAD {
|
impl OpenCADStudio {
|
||||||
pub fn view(&self, window_id: window::Id) -> Element<'_, Message> {
|
pub fn view(&self, window_id: window::Id) -> Element<'_, Message> {
|
||||||
// ── Floating panel windows ─────────────────────────────────────────
|
// ── Floating panel windows ─────────────────────────────────────────
|
||||||
if Some(window_id) == self.layer_window {
|
if Some(window_id) == self.layer_window {
|
||||||
|
|
@ -1594,7 +1594,7 @@ fn unsaved_changes_dialog_window(name: &str) -> Element<'static, Message> {
|
||||||
// is the public welcome screen and stays consistent across locales.
|
// is the public welcome screen and stays consistent across locales.
|
||||||
//
|
//
|
||||||
// The page picks up the application icon's red-brown (#B03020) as a tint so
|
// The page picks up the application icon's red-brown (#B03020) as a tint so
|
||||||
// it visually belongs to H7CAD without overpowering the dark workspace.
|
// it visually belongs to OpenCADStudio without overpowering the dark workspace.
|
||||||
|
|
||||||
const BRAND: Color = Color { r: 0.690, g: 0.188, b: 0.125, a: 1.0 }; // #B03020
|
const BRAND: Color = Color { r: 0.690, g: 0.188, b: 0.125, a: 1.0 }; // #B03020
|
||||||
const BRAND_DARK: Color = Color { r: 0.45, g: 0.12, b: 0.08, a: 1.0 };
|
const BRAND_DARK: Color = Color { r: 0.45, g: 0.12, b: 0.08, a: 1.0 };
|
||||||
|
|
@ -1605,16 +1605,16 @@ pub(super) fn start_page_view<'a>() -> Element<'a, Message> {
|
||||||
const CARD_BG: Color = Color { r: 0.12, g: 0.12, b: 0.13, a: 1.0 };
|
const CARD_BG: Color = Color { r: 0.12, g: 0.12, b: 0.13, a: 1.0 };
|
||||||
const CARD_BORDER: Color = Color { r: 0.20, g: 0.20, b: 0.22, a: 1.0 };
|
const CARD_BORDER: Color = Color { r: 0.20, g: 0.20, b: 0.22, a: 1.0 };
|
||||||
|
|
||||||
// Brand-tinted "Welcome to" — the "H7CAD" word takes the accent colour
|
// Brand-tinted "Welcome to" — the "OpenCADStudio" word takes the accent colour
|
||||||
// (Thunderbird-style coloured headline split).
|
// (Thunderbird-style coloured headline split).
|
||||||
let headline = row![
|
let headline = row![
|
||||||
text("Welcome to ").size(40).color(TEXT),
|
text("Welcome to ").size(40).color(TEXT),
|
||||||
text("H7CAD").size(40).color(BRAND),
|
text("Open CAD Studio").size(40).color(BRAND),
|
||||||
]
|
]
|
||||||
.align_y(iced::Center);
|
.align_y(iced::Center);
|
||||||
|
|
||||||
let subtitle = text(
|
let subtitle = text(
|
||||||
"H7CAD is an open-source CAD viewer and editor — a gift from contributors like you. \
|
"Open CAD Studio is an open-source CAD viewer and editor — a gift from contributors like you. \
|
||||||
Open a DWG/DXF file, start a new drawing, or help shape what comes next.",
|
Open a DWG/DXF file, start a new drawing, or help shape what comes next.",
|
||||||
)
|
)
|
||||||
.size(13)
|
.size(13)
|
||||||
|
|
@ -1728,9 +1728,9 @@ pub(super) fn start_page_view<'a>() -> Element<'a, Message> {
|
||||||
let cards = row![
|
let cards = row![
|
||||||
card_section(
|
card_section(
|
||||||
"Free and open source",
|
"Free and open source",
|
||||||
"H7CAD belongs to its users.",
|
"Open CAD Studio belongs to its users.",
|
||||||
"Source is openly developed and distributed under a permissive licence. \
|
"Source is openly developed and distributed under a permissive licence. \
|
||||||
You are free to use, study, modify, and share H7CAD.",
|
You are free to use, study, modify, and share Open CAD Studio.",
|
||||||
),
|
),
|
||||||
card_section(
|
card_section(
|
||||||
"Community driven",
|
"Community driven",
|
||||||
|
|
|
||||||
|
|
@ -27,7 +27,7 @@ pub fn parse_obj(src: &str, color: [f32; 4]) -> Option<MeshModel> {
|
||||||
"v" => {
|
"v" => {
|
||||||
let vals: Vec<f32> = parts.filter_map(|s| s.parse().ok()).collect();
|
let vals: Vec<f32> = parts.filter_map(|s| s.parse().ok()).collect();
|
||||||
if vals.len() >= 3 {
|
if vals.len() >= 3 {
|
||||||
// OBJ: Y-up right-handed. H7CAD viewport is also Y-up → keep as-is.
|
// OBJ: Y-up right-handed. OpenCADStudio viewport is also Y-up → keep as-is.
|
||||||
positions.push([vals[0], vals[1], vals[2]]);
|
positions.push([vals[0], vals[1], vals[2]]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -78,7 +78,7 @@ fn build_pdf(
|
||||||
rotation_deg: i32,
|
rotation_deg: i32,
|
||||||
plot_style: Option<&PlotStyleTable>,
|
plot_style: Option<&PlotStyleTable>,
|
||||||
) -> Vec<u8> {
|
) -> Vec<u8> {
|
||||||
let mut doc = PdfDocument::new("H7CAD Export");
|
let mut doc = PdfDocument::new("Open CAD Studio Export");
|
||||||
let mut ops: Vec<Op> = Vec::new();
|
let mut ops: Vec<Op> = Vec::new();
|
||||||
|
|
||||||
// White page background.
|
// White page background.
|
||||||
|
|
|
||||||
|
|
@ -28,7 +28,7 @@ pub async fn print_wires(
|
||||||
plot_style: Option<PlotStyleTable>,
|
plot_style: Option<PlotStyleTable>,
|
||||||
) -> Result<String, String> {
|
) -> Result<String, String> {
|
||||||
// ── 1. Write to a named temp file ─────────────────────────────────────
|
// ── 1. Write to a named temp file ─────────────────────────────────────
|
||||||
let tmp_path = std::env::temp_dir().join("h7cad_print.pdf");
|
let tmp_path = std::env::temp_dir().join("open_cad_studio_print.pdf");
|
||||||
pdf_export::export_pdf(
|
pdf_export::export_pdf(
|
||||||
&wires,
|
&wires,
|
||||||
&hatches,
|
&hatches,
|
||||||
|
|
|
||||||
|
|
@ -199,7 +199,7 @@ pub fn build_step(meshes: &[&MeshModel]) -> Option<String> {
|
||||||
let msb_id = alloc();
|
let msb_id = alloc();
|
||||||
writeln!(
|
writeln!(
|
||||||
data,
|
data,
|
||||||
"#{} = MANIFOLD_SOLID_BREP('H7CAD_Solid',#{});",
|
"#{} = MANIFOLD_SOLID_BREP('OpenCADStudio_Solid',#{});",
|
||||||
msb_id, shell_id
|
msb_id, shell_id
|
||||||
)
|
)
|
||||||
.ok();
|
.ok();
|
||||||
|
|
@ -217,7 +217,7 @@ pub fn build_step(meshes: &[&MeshModel]) -> Option<String> {
|
||||||
writeln!(data, "#{} = GEOMETRIC_REPRESENTATION_CONTEXT(3);", gc_id).ok();
|
writeln!(data, "#{} = GEOMETRIC_REPRESENTATION_CONTEXT(3);", gc_id).ok();
|
||||||
writeln!(
|
writeln!(
|
||||||
data,
|
data,
|
||||||
"#{sr_id} = SHAPE_REPRESENTATION('H7CAD_Shape',(#{}),#{gc_id});",
|
"#{sr_id} = SHAPE_REPRESENTATION('OpenCADStudio_Shape',(#{}),#{gc_id});",
|
||||||
msb_id
|
msb_id
|
||||||
)
|
)
|
||||||
.ok();
|
.ok();
|
||||||
|
|
@ -227,7 +227,7 @@ pub fn build_step(meshes: &[&MeshModel]) -> Option<String> {
|
||||||
let file = format!(
|
let file = format!(
|
||||||
"ISO-10303-21;\n\
|
"ISO-10303-21;\n\
|
||||||
HEADER;\n\
|
HEADER;\n\
|
||||||
FILE_DESCRIPTION(('H7CAD STEP export'),'2;1');\n\
|
FILE_DESCRIPTION(('Open CAD Studio STEP export'),'2;1');\n\
|
||||||
FILE_NAME('{ts}','','',(''),'',' ',' ');\n\
|
FILE_NAME('{ts}','','',(''),'',' ',' ');\n\
|
||||||
FILE_SCHEMA(('CONFIG_CONTROL_DESIGN'));\n\
|
FILE_SCHEMA(('CONFIG_CONTROL_DESIGN'));\n\
|
||||||
ENDSEC;\n\
|
ENDSEC;\n\
|
||||||
|
|
|
||||||
|
|
@ -67,7 +67,7 @@ pub fn build_stl(meshes: &[&MeshModel]) -> Option<Vec<u8>> {
|
||||||
|
|
||||||
// 80-byte header.
|
// 80-byte header.
|
||||||
let mut header = [0u8; 80];
|
let mut header = [0u8; 80];
|
||||||
let title = b"H7CAD STL export";
|
let title = b"Open CAD Studio STL export";
|
||||||
header[..title.len()].copy_from_slice(title);
|
header[..title.len()].copy_from_slice(title);
|
||||||
buf.extend_from_slice(&header);
|
buf.extend_from_slice(&header);
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
//! H7CAD linetype catalog — loaded from `assets/linetypes/H7CAD.lin`.
|
//! OpenCADStudio linetype catalog — loaded from `assets/linetypes/OpenCADStudio.lin`.
|
||||||
//!
|
//!
|
||||||
//! Call [`populate_document`] to add all standard linetypes to a new document.
|
//! Call [`populate_document`] to add all standard linetypes to a new document.
|
||||||
//! Linetypes that already exist in the document are skipped.
|
//! Linetypes that already exist in the document are skipped.
|
||||||
|
|
@ -68,7 +68,7 @@ pub fn complex_lt(name: &str) -> Option<&'static ComplexLt> {
|
||||||
.get(&name.to_ascii_uppercase())
|
.get(&name.to_ascii_uppercase())
|
||||||
}
|
}
|
||||||
|
|
||||||
const LIN_SOURCE: &str = include_str!("../assets/linetypes/H7CAD.lin");
|
const LIN_SOURCE: &str = include_str!("../assets/linetypes/OpenCADStudio.lin");
|
||||||
|
|
||||||
// ── Pattern art extraction ────────────────────────────────────────────────
|
// ── Pattern art extraction ────────────────────────────────────────────────
|
||||||
|
|
||||||
|
|
@ -93,7 +93,7 @@ pub fn extract_pattern(desc: &str) -> String {
|
||||||
|
|
||||||
// ── Public API ────────────────────────────────────────────────────────────
|
// ── Public API ────────────────────────────────────────────────────────────
|
||||||
|
|
||||||
/// Add all standard H7CAD linetypes to `doc`, skipping existing ones.
|
/// Add all standard OpenCADStudio linetypes to `doc`, skipping existing ones.
|
||||||
pub fn populate_document(doc: &mut CadDocument) {
|
pub fn populate_document(doc: &mut CadDocument) {
|
||||||
for mut lt in parse(LIN_SOURCE) {
|
for mut lt in parse(LIN_SOURCE) {
|
||||||
if !doc.line_types.contains(<.name) {
|
if !doc.line_types.contains(<.name) {
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
// Arc tool — ribbon dropdown + all H7CAD arc creation methods.
|
// Arc tool — ribbon dropdown + all OpenCADStudio arc creation methods.
|
||||||
//
|
//
|
||||||
// Methods:
|
// Methods:
|
||||||
// ARC — Center, Start, End (CSE — default)
|
// ARC — Center, Start, End (CSE — default)
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
// Circle tool — ribbon dropdown + all H7CAD circle creation methods.
|
// Circle tool — ribbon dropdown + all OpenCADStudio circle creation methods.
|
||||||
//
|
//
|
||||||
// Methods:
|
// Methods:
|
||||||
// CIRCLE — Center, Radius (default)
|
// CIRCLE — Center, Radius (default)
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
// Ellipse tool — ribbon dropdown + all H7CAD ellipse creation methods.
|
// Ellipse tool — ribbon dropdown + all OpenCADStudio ellipse creation methods.
|
||||||
//
|
//
|
||||||
// Commands:
|
// Commands:
|
||||||
// ELLIPSE — Center, Axes (center → major endpoint → minor distance)
|
// ELLIPSE — Center, Axes (center → major endpoint → minor distance)
|
||||||
|
|
|
||||||
|
|
@ -1,11 +1,11 @@
|
||||||
// Hatch/Gradient/Boundary commands — H7CAD Home > Draw > Hatch dropdown.
|
// Hatch/Gradient/Boundary commands — OpenCADStudio Home > Draw > Hatch dropdown.
|
||||||
//
|
//
|
||||||
// Commands:
|
// Commands:
|
||||||
// HATCH — ANSI31: 45° hatch lines (pick inside or type S for manual)
|
// HATCH — ANSI31: 45° hatch lines (pick inside or type S for manual)
|
||||||
// GRADIENT — Linear gradient fill (pick inside or type S for manual)
|
// GRADIENT — Linear gradient fill (pick inside or type S for manual)
|
||||||
// BOUNDARY — Traces the enclosing boundary as a closed LwPolyline
|
// BOUNDARY — Traces the enclosing boundary as a closed LwPolyline
|
||||||
//
|
//
|
||||||
// Primary workflow (matches H7CAD):
|
// Primary workflow (matches OpenCADStudio):
|
||||||
// Click a point INSIDE a closed region → boundary auto-detected.
|
// Click a point INSIDE a closed region → boundary auto-detected.
|
||||||
// Type "S" to switch to manual vertex-picking mode (HATCH/GRADIENT only).
|
// Type "S" to switch to manual vertex-picking mode (HATCH/GRADIENT only).
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
// Line tool — ribbon definition + interactive command.
|
// Line tool — ribbon definition + interactive command.
|
||||||
//
|
//
|
||||||
// Command: LINE — H7CAD behaviour:
|
// Command: LINE — OpenCADStudio behaviour:
|
||||||
// 1. First click → stores start point, prompts for next point
|
// 1. First click → stores start point, prompts for next point
|
||||||
// 2. Each further click → immediately commits an acadrust::Line entity
|
// 2. Each further click → immediately commits an acadrust::Line entity
|
||||||
// (start→end) to the document; end becomes the new start point
|
// (start→end) to the document; end becomes the new start point
|
||||||
|
|
|
||||||
|
|
@ -9,7 +9,7 @@
|
||||||
// the mesh because the ACIS data is empty. Full ACIS generation requires
|
// the mesh because the ACIS data is empty. Full ACIS generation requires
|
||||||
// a separate step (out of scope here).
|
// a separate step (out of scope here).
|
||||||
//
|
//
|
||||||
// Coordinate convention in H7CAD: the viewport is Y-up (OpenGL style),
|
// Coordinate convention in OpenCADStudio: the viewport is Y-up (OpenGL style),
|
||||||
// so screen X→DXF X, screen Z→DXF Y, screen Y→DXF Z (height).
|
// so screen X→DXF X, screen Z→DXF Y, screen Y→DXF Z (height).
|
||||||
// truck works in standard math coordinates; we map accordingly.
|
// truck works in standard math coordinates; we map accordingly.
|
||||||
|
|
||||||
|
|
@ -134,7 +134,7 @@ impl CadCommand for BoxCommand {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn commit_box(p1: Vec3, p2: Vec3, height: f32, color: [f32; 4]) -> CmdResult {
|
fn commit_box(p1: Vec3, p2: Vec3, height: f32, color: [f32; 4]) -> CmdResult {
|
||||||
// Map H7CAD coords to truck: x→x, z→y, y→z
|
// Map OpenCADStudio coords to truck: x→x, z→y, y→z
|
||||||
let x0 = p1.x.min(p2.x) as f64;
|
let x0 = p1.x.min(p2.x) as f64;
|
||||||
let y0 = p1.z.min(p2.z) as f64;
|
let y0 = p1.z.min(p2.z) as f64;
|
||||||
let x1 = p1.x.max(p2.x) as f64;
|
let x1 = p1.x.max(p2.x) as f64;
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
//! H7CAD hatch pattern catalog — loaded from `assets/patterns/H7CAD.pat`.
|
//! OpenCADStudio hatch pattern catalog — loaded from `assets/patterns/OpenCADStudio.pat`.
|
||||||
//!
|
//!
|
||||||
//! PAT line format: `angle, x0, y0, dx, dy [, dash1, dash2, ...]`
|
//! PAT line format: `angle, x0, y0, dx, dy [, dash1, dash2, ...]`
|
||||||
//! - `angle` line direction in degrees
|
//! - `angle` line direction in degrees
|
||||||
|
|
@ -8,7 +8,7 @@
|
||||||
|
|
||||||
use std::sync::OnceLock;
|
use std::sync::OnceLock;
|
||||||
|
|
||||||
const PAT_SRC: &str = include_str!("../assets/patterns/H7CAD.pat");
|
const PAT_SRC: &str = include_str!("../assets/patterns/OpenCADStudio.pat");
|
||||||
|
|
||||||
// ── Public types ──────────────────────────────────────────────────────────
|
// ── Public types ──────────────────────────────────────────────────────────
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -9,7 +9,7 @@
|
||||||
// Zoom: adjusts `distance` (exponential feel).
|
// Zoom: adjusts `distance` (exponential feel).
|
||||||
// Snap: directly assigns yaw+pitch encoded as a quaternion (for ViewCube).
|
// Snap: directly assigns yaw+pitch encoded as a quaternion (for ViewCube).
|
||||||
//
|
//
|
||||||
// Coordinate convention: Z-up world space (same as the rest of H7CAD).
|
// Coordinate convention: Z-up world space (same as the rest of OpenCADStudio).
|
||||||
|
|
||||||
use glam::{vec3, Mat4, Quat, Vec3};
|
use glam::{vec3, Mat4, Quat, Vec3};
|
||||||
use iced::{Point, Rectangle};
|
use iced::{Point, Rectangle};
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
//! H7CAD-style grip editing.
|
//! OpenCADStudio-style grip editing.
|
||||||
|
|
||||||
use acadrust::Handle;
|
use acadrust::Handle;
|
||||||
use glam::{Mat4, Vec3};
|
use glam::{Mat4, Vec3};
|
||||||
|
|
@ -13,7 +13,7 @@ pub const GRIP_HALF_PX: f32 = 5.0;
|
||||||
|
|
||||||
// ── Active drag state ─────────────────────────────────────────────────────
|
// ── Active drag state ─────────────────────────────────────────────────────
|
||||||
|
|
||||||
/// Stored on `H7CAD` while a grip is being dragged.
|
/// Stored on `OpenCADStudio` while a grip is being dragged.
|
||||||
#[derive(Clone, Debug)]
|
#[derive(Clone, Debug)]
|
||||||
pub struct GripEdit {
|
pub struct GripEdit {
|
||||||
/// Handle of the entity being edited.
|
/// Handle of the entity being edited.
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
// H7CAD hatch pattern catalog — built from `assets/patterns/H7CAD.pat`.
|
// OpenCADStudio hatch pattern catalog — built from `assets/patterns/OpenCADStudio.pat`.
|
||||||
//
|
//
|
||||||
// Each `PatternEntry` wraps a parsed PAT pattern with:
|
// Each `PatternEntry` wraps a parsed PAT pattern with:
|
||||||
// - `gpu` — `HatchPattern::Pattern(families)` for the shader
|
// - `gpu` — `HatchPattern::Pattern(families)` for the shader
|
||||||
|
|
|
||||||
|
|
@ -3914,7 +3914,7 @@ impl Scene {
|
||||||
.document
|
.document
|
||||||
.views
|
.views
|
||||||
.iter()
|
.iter()
|
||||||
.find(|v| v.name == "H7CAD_Camera_Model")
|
.find(|v| v.name == "OpenCADStudio_Camera_Model")
|
||||||
.cloned();
|
.cloned();
|
||||||
if let Some(view) = saved_view {
|
if let Some(view) = saved_view {
|
||||||
return self.apply_camera_from_view_entry(&view, true);
|
return self.apply_camera_from_view_entry(&view, true);
|
||||||
|
|
@ -3978,7 +3978,7 @@ impl Scene {
|
||||||
/// Returns true if a valid sheet viewport was found and the camera was set.
|
/// Returns true if a valid sheet viewport was found and the camera was set.
|
||||||
fn apply_sheet_viewport_camera(&mut self) -> bool {
|
fn apply_sheet_viewport_camera(&mut self) -> bool {
|
||||||
// Prefer our named View entry — survives DWG save without being overridden.
|
// Prefer our named View entry — survives DWG save without being overridden.
|
||||||
let view_name = format!("H7CAD_Camera_{}", self.current_layout);
|
let view_name = format!("OpenCADStudio_Camera_{}", self.current_layout);
|
||||||
let saved_view = self
|
let saved_view = self
|
||||||
.document
|
.document
|
||||||
.views
|
.views
|
||||||
|
|
@ -4094,7 +4094,7 @@ impl Scene {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Also write to View table — survives DWG save without override.
|
// Also write to View table — survives DWG save without override.
|
||||||
self.write_camera_view_entry("H7CAD_Camera_Model", target_wcs, vd3, view_height);
|
self.write_camera_view_entry("OpenCADStudio_Camera_Model", target_wcs, vd3, view_height);
|
||||||
true
|
true
|
||||||
} else {
|
} else {
|
||||||
let target_wcs = acadrust::types::Vector3 {
|
let target_wcs = acadrust::types::Vector3 {
|
||||||
|
|
@ -4102,7 +4102,7 @@ impl Scene {
|
||||||
y: cam.target.y as f64,
|
y: cam.target.y as f64,
|
||||||
z: cam.target.z as f64,
|
z: cam.target.z as f64,
|
||||||
};
|
};
|
||||||
let view_name = format!("H7CAD_Camera_{}", self.current_layout);
|
let view_name = format!("OpenCADStudio_Camera_{}", self.current_layout);
|
||||||
|
|
||||||
// Write back to the sheet viewport entity.
|
// Write back to the sheet viewport entity.
|
||||||
let layout_block = self.current_layout_block_handle();
|
let layout_block = self.current_layout_block_handle();
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
// ViewCube wgpu pipeline — H7CAD-style interactive 3D navigation cube.
|
// ViewCube wgpu pipeline — OpenCADStudio-style interactive 3D navigation cube.
|
||||||
//
|
//
|
||||||
// 26 selectable regions: 6 faces + 12 edges + 8 corners.
|
// 26 selectable regions: 6 faces + 12 edges + 8 corners.
|
||||||
// Phong shading + hover highlight passed as uniform.
|
// Phong shading + hover highlight passed as uniform.
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
//! H7CAD-style object snap (OSNAP) engine.
|
//! OpenCADStudio-style object snap (OSNAP) engine.
|
||||||
//!
|
//!
|
||||||
//! Implemented modes:
|
//! Implemented modes:
|
||||||
//! Endpoint, Midpoint, Center, Node, Quadrant, Intersection,
|
//! Endpoint, Midpoint, Center, Node, Quadrant, Intersection,
|
||||||
|
|
@ -13,7 +13,7 @@ use crate::ui::overlay::CROSSHAIR_ARM;
|
||||||
|
|
||||||
// ── Snap type ─────────────────────────────────────────────────────────────
|
// ── Snap type ─────────────────────────────────────────────────────────────
|
||||||
|
|
||||||
/// Every OSNAP mode — mirrors the H7CAD list.
|
/// Every OSNAP mode — mirrors the OpenCADStudio list.
|
||||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
|
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
|
||||||
pub enum SnapType {
|
pub enum SnapType {
|
||||||
Endpoint,
|
Endpoint,
|
||||||
|
|
|
||||||
|
|
@ -57,7 +57,7 @@ pub fn view_window<'a>() -> Element<'a, Message> {
|
||||||
|
|
||||||
let logo = container(
|
let logo = container(
|
||||||
column![
|
column![
|
||||||
text("H7CAD").size(32).color(ACCENT),
|
text("Open CAD Studio").size(32).color(ACCENT),
|
||||||
text("CAD application for Architecture & Engineering")
|
text("CAD application for Architecture & Engineering")
|
||||||
.size(11)
|
.size(11)
|
||||||
.color(DIM),
|
.color(DIM),
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
//! Application menu — H7CAD-style dropdown that opens when the H7 logo
|
//! Application menu — OpenCADStudio-style dropdown that opens when the OCS logo
|
||||||
//! button is clicked. Overlays the entire viewport as a floating panel.
|
//! button is clicked. Overlays the entire viewport as a floating panel.
|
||||||
//!
|
//!
|
||||||
//! Layout:
|
//! Layout:
|
||||||
|
|
@ -235,7 +235,7 @@ impl AppMenu {
|
||||||
let bottom = container(
|
let bottom = container(
|
||||||
row![
|
row![
|
||||||
iced::widget::Space::new().width(Length::Fill),
|
iced::widget::Space::new().width(Length::Fill),
|
||||||
button(text("Exit H7CAD").size(11).color(Color::WHITE))
|
button(text("Exit Open CAD Studio").size(11).color(Color::WHITE))
|
||||||
.on_press(Message::Command("EXIT".into()))
|
.on_press(Message::Command("EXIT".into()))
|
||||||
.style(|_: &Theme, status| button::Style {
|
.style(|_: &Theme, status| button::Style {
|
||||||
background: Some(Background::Color(match status {
|
background: Some(Background::Color(match status {
|
||||||
|
|
@ -299,7 +299,7 @@ impl AppMenu {
|
||||||
// Backdrop — clicking outside closes the menu.
|
// Backdrop — clicking outside closes the menu.
|
||||||
let backdrop = button(
|
let backdrop = button(
|
||||||
container(panel)
|
container(panel)
|
||||||
// Offset so the panel sits just below the ribbon H7 button.
|
// Offset so the panel sits just below the ribbon OCS button.
|
||||||
.padding(iced::Padding {
|
.padding(iced::Padding {
|
||||||
top: 56.0,
|
top: 56.0,
|
||||||
left: 0.0,
|
left: 0.0,
|
||||||
|
|
@ -521,7 +521,7 @@ fn recents_file_path() -> Option<PathBuf> {
|
||||||
p
|
p
|
||||||
};
|
};
|
||||||
let mut p = base;
|
let mut p = base;
|
||||||
p.push("H7CAD");
|
p.push("OpenCADStudio");
|
||||||
Some(p.join("recent.txt"))
|
Some(p.join("recent.txt"))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
//! H7CAD-style command line — bottom panel with input and history
|
//! OpenCADStudio-style command line — bottom panel with input and history
|
||||||
|
|
||||||
use crate::app::Message;
|
use crate::app::Message;
|
||||||
use iced::widget::{column, container, row, text, text_input};
|
use iced::widget::{column, container, row, text, text_input};
|
||||||
|
|
@ -41,7 +41,7 @@ pub enum EntryKind {
|
||||||
impl CommandLine {
|
impl CommandLine {
|
||||||
pub fn new() -> Self {
|
pub fn new() -> Self {
|
||||||
let mut cl = Self::default();
|
let mut cl = Self::default();
|
||||||
cl.push_info("H7CAD ready.");
|
cl.push_info("Open CAD Studio ready.");
|
||||||
cl.push_info("Type a command or use the ribbon. Open OBJ: INSERT tab.");
|
cl.push_info("Type a command or use the ribbon. Open OBJ: INSERT tab.");
|
||||||
cl
|
cl
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
//! Properties panel — H7CAD-style editable object properties.
|
//! Properties panel — OpenCADStudio-style editable object properties.
|
||||||
//!
|
//!
|
||||||
//! Shows two sections (General + Geometry) for the selected entity.
|
//! Shows two sections (General + Geometry) for the selected entity.
|
||||||
//! • Layer → combo_box (options from document layer table)
|
//! • Layer → combo_box (options from document layer table)
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
//! H7CAD-style OSNAP dropdown popup panel.
|
//! OpenCADStudio-style OSNAP dropdown popup panel.
|
||||||
//!
|
//!
|
||||||
//! Rendered as a floating overlay above the status bar. The popup is only
|
//! Rendered as a floating overlay above the status bar. The popup is only
|
||||||
//! inserted into the view stack when `snap_popup_open` is true.
|
//! inserted into the view stack when `snap_popup_open` is true.
|
||||||
|
|
|
||||||
|
|
@ -140,7 +140,7 @@ pub fn view_window<'a>(latest: &'a str, body: &'a str) -> Element<'a, Message> {
|
||||||
let header = container(
|
let header = container(
|
||||||
column![
|
column![
|
||||||
text("New Release Available").size(20).color(ACCENT),
|
text("New Release Available").size(20).color(ACCENT),
|
||||||
text("A newer H7CAD version is published on GitHub.")
|
text("A newer Open CAD Studio version is published on GitHub.")
|
||||||
.size(11)
|
.size(11)
|
||||||
.color(DIM),
|
.color(DIM),
|
||||||
]
|
]
|
||||||
|
|
|
||||||
|
|
@ -7,9 +7,9 @@
|
||||||
// `None` when up to date / on network failure / on parse error.
|
// `None` when up to date / on network failure / on parse error.
|
||||||
|
|
||||||
const RELEASES_API: &str =
|
const RELEASES_API: &str =
|
||||||
"https://api.github.com/repos/HakanSeven12/H7CAD/releases/latest";
|
"https://api.github.com/repos/HakanSeven12/open-cad-studio/releases/latest";
|
||||||
pub const RELEASES_PAGE: &str =
|
pub const RELEASES_PAGE: &str =
|
||||||
"https://github.com/HakanSeven12/H7CAD/releases/latest";
|
"https://github.com/HakanSeven12/open-cad-studio/releases/latest";
|
||||||
|
|
||||||
/// What `check_for_update` reports when a newer release exists.
|
/// What `check_for_update` reports when a newer release exists.
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
|
|
@ -33,7 +33,7 @@ fn fetch_latest_if_outdated() -> Option<UpdateInfo> {
|
||||||
.timeout(std::time::Duration::from_secs(5))
|
.timeout(std::time::Duration::from_secs(5))
|
||||||
.build()
|
.build()
|
||||||
.get(RELEASES_API)
|
.get(RELEASES_API)
|
||||||
.set("User-Agent", concat!("h7cad/", env!("CARGO_PKG_VERSION")))
|
.set("User-Agent", concat!("OpenCADStudio/", env!("CARGO_PKG_VERSION")))
|
||||||
.set("Accept", "application/vnd.github+json")
|
.set("Accept", "application/vnd.github+json")
|
||||||
.call()
|
.call()
|
||||||
.ok()?
|
.ok()?
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue