slice3d 0.1.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,10 @@
1
+ __pycache__/
2
+ *.pyc
3
+ .venv/
4
+ *.egg-info/
5
+ build/
6
+ dist/
7
+ .pytest_cache/
8
+ examples/output/
9
+ examples/slices_gui/
10
+ .DS_Store
slice3d-0.1.0/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 hanibuchi
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
slice3d-0.1.0/PKG-INFO ADDED
@@ -0,0 +1,163 @@
1
+ Metadata-Version: 2.5
2
+ Name: slice3d
3
+ Version: 0.1.0
4
+ Summary: 3Dモデルを一定間隔でスライスして断面(輪郭)を出力するライブラリ/CLI
5
+ Project-URL: Homepage, https://github.com/Hanibuchi/slice-3d
6
+ Project-URL: Issues, https://github.com/Hanibuchi/slice-3d/issues
7
+ Author-email: hanibuchi <hanitech8686@gmail.com>
8
+ License-Expression: MIT
9
+ License-File: LICENSE
10
+ Keywords: 3d,cad,cross-section,mesh,slicing,stl,trimesh
11
+ Classifier: Development Status :: 3 - Alpha
12
+ Classifier: Intended Audience :: Developers
13
+ Classifier: Intended Audience :: Science/Research
14
+ Classifier: License :: OSI Approved :: MIT License
15
+ Classifier: Programming Language :: Python :: 3
16
+ Classifier: Programming Language :: Python :: 3.9
17
+ Classifier: Programming Language :: Python :: 3.10
18
+ Classifier: Programming Language :: Python :: 3.11
19
+ Classifier: Programming Language :: Python :: 3.12
20
+ Classifier: Programming Language :: Python :: 3.13
21
+ Classifier: Programming Language :: Python :: 3.14
22
+ Classifier: Topic :: Multimedia :: Graphics :: 3D Modeling
23
+ Classifier: Topic :: Scientific/Engineering
24
+ Requires-Python: >=3.9
25
+ Requires-Dist: networkx>=3.0
26
+ Requires-Dist: numpy>=1.24
27
+ Requires-Dist: scipy>=1.10
28
+ Requires-Dist: shapely>=2.0
29
+ Requires-Dist: trimesh>=4.0.0
30
+ Provides-Extra: dev
31
+ Requires-Dist: fast-simplification>=0.1.7; extra == 'dev'
32
+ Requires-Dist: matplotlib!=3.11.*,>=3.8; extra == 'dev'
33
+ Requires-Dist: pytest>=7.0; extra == 'dev'
34
+ Provides-Extra: gui
35
+ Requires-Dist: fast-simplification>=0.1.7; extra == 'gui'
36
+ Requires-Dist: matplotlib!=3.11.*,>=3.8; extra == 'gui'
37
+ Provides-Extra: viz
38
+ Requires-Dist: matplotlib>=3.7; extra == 'viz'
39
+ Description-Content-Type: text/markdown
40
+
41
+ # slice3d
42
+
43
+ 3Dモデル(STL / OBJ / PLY / GLB / GLTF など)を一定間隔でスライスし、断面(輪郭)を SVG / DXF / PNG / CSV として出力するPythonライブラリ・CLIです。[trimesh](https://github.com/mikedh/trimesh) をベースにしています。
44
+
45
+ アンモナイトの化石モデルをX軸方向にスライスした例:
46
+
47
+ ![スライス一覧](docs/assets/ammonite_slices_grid.png)
48
+
49
+ 中心付近の断面(渦巻き状の房室がきれいに現れる):
50
+
51
+ ![中心断面](docs/assets/ammonite_center_slice.png)
52
+
53
+ ## インストール
54
+
55
+ ```bash
56
+ pip install slice3d
57
+
58
+ # PNG出力を使う場合(matplotlibが必要)
59
+ pip install "slice3d[viz]"
60
+ ```
61
+
62
+ 開発時はこのリポジトリを editable インストールしてください。
63
+
64
+ ```bash
65
+ python -m venv .venv && source .venv/bin/activate
66
+ pip install -e ".[dev]"
67
+ ```
68
+
69
+ ## CLIとして使う
70
+
71
+ ```bash
72
+ slice3d model.stl --axis z --thickness 2.0 --outdir slices --format svg
73
+ ```
74
+
75
+ | オプション | 説明 | 既定値 |
76
+ | --- | --- | --- |
77
+ | `--axis {x,y,z}` | スライスする軸 | `z` |
78
+ | `--thickness` | スライス間隔(モデル単位) | `1.0` |
79
+ | `--start` / `--end` | スライス範囲(省略時はモデルのバウンディングボックス全体) | なし |
80
+ | `--outdir` | 出力先ディレクトリ | `./slices` |
81
+ | `--format {svg,dxf,png,csv}` | 断面の出力形式 | `svg` |
82
+
83
+ PNG出力(CLI/`slice_file`/GUIいずれも共通)は、断面ごとの内容に自動フィットするのではなく、元モデル全体のバウンディングボックスを基準に表示範囲を固定して保存する。そのため、同じスライス処理で出力した画像はすべて同じピクセルサイズ・同じ縮尺になり、小さな断面ほど小さく、大きな断面ほど大きく写る(元モデルに対する実際の大きさの比率がそのまま画像に反映される)。
84
+
85
+ 交差しない位置のスライスは自動的にスキップされます。
86
+
87
+ ## GUIビューア
88
+
89
+ 体積・スライス位置を確認しながら、軸やスライス間隔(thickness)をその場で変更できる専用ウィンドウを開きます。
90
+
91
+ ```bash
92
+ pip install "slice3d[gui]"
93
+ slice3d-gui model.stl
94
+ slice3d-gui model.stl --axis x --thickness 0.01
95
+ slice3d-gui model.glb # glTF/GLBは仕様上メートル単位なので自動で"m"表示
96
+ slice3d-gui model.stl --units mm # STLなど単位不明な形式は明示的に指定
97
+ slice3d-gui model.stl --format png # Save系ボタンの初期フォーマット(既定: svg)
98
+ ```
99
+
100
+ ![GUIビューア](docs/assets/gui_screenshot.png)
101
+
102
+ ウィンドウ内の表示は(体積・軸・数値など)すべて英語で統一しています。
103
+
104
+ - ウィンドウ上部にモデル名・体積(`mesh.volume`、非watertightなら近似値である旨も表示)
105
+ - 左側にモデル全体を半透明の3D表示。現在の切断位置を赤い平面と断面の輪郭線で重ねて表示するので、どこをどの向きで切っているか一目で分かる
106
+ - 右側にその断面(切断面を真上から見た2D形状)をリアルタイム表示
107
+ - スライダーでスライス位置(index / position)を切り替え
108
+ - `Axis` ラジオボタンでスライス軸を切り替え(切り替え時はthicknessが軸の全長に応じてキリの良い値(1/2/5 × 10ⁿ、約30分割相当)に自動再設定される)
109
+ - `Thickness` テキストボックスで間隔を指定して Enter → 断面数が再計算される
110
+ - 体積・thickness・positionの表示には単位が付く。glTF/GLB(`.glb` / `.gltf`)は仕様上メートル単位と定められているため自動で`m`が付き、STL/OBJ/PLYなど単位情報を持たない形式は既定で単位なし
111
+ - `--units`(例: `mm`, `cm`)で表示単位を明示指定できる。`--units ""` で単位表示を消すことも可能
112
+ - `Format` ボタン(SVG/PNG/DXF/CSV)で保存形式を選択。選択中の形式はハイライトされ、Saveボタンのラベルにも反映される
113
+ - `Save Current Slice` ボタンで現在表示中の断面を1枚保存
114
+ - `Save All Slices Along <軸>-Axis` ボタンで、現在の軸・thickness設定のまま全断面を一括保存(交差しない位置は自動的にスキップ)
115
+ - 保存先はどちらも `<モデルと同じディレクトリ>/slices_gui/`
116
+
117
+ ## ライブラリとして使う
118
+
119
+ ```python
120
+ import slice3d
121
+
122
+ # 一括処理: モデルを読み込んでスライスし、ディレクトリへ書き出す
123
+ written = slice3d.slice_file("model.stl", "out", axis="z", thickness=1.0, fmt="svg")
124
+
125
+ # 細かく制御したい場合
126
+ mesh = slice3d.load_mesh("model.stl")
127
+ for s in slice3d.iter_slices(mesh, axis="z", thickness=1.0):
128
+ if s.is_empty:
129
+ continue
130
+ print(s.index, s.position, len(s.path_2d.discrete))
131
+ slice3d.save_section(s.path_2d, f"out/{s.index:04d}.svg")
132
+ ```
133
+
134
+ ### API
135
+
136
+ - `slice3d.load_mesh(path)` — 3Dモデルを読み込み `trimesh.Trimesh` を返す
137
+ - `slice3d.compute_heights(mesh, axis, thickness, start=None, end=None)` — スライス位置の配列を計算
138
+ - `slice3d.iter_slices(mesh, axis="z", thickness=1.0, start=None, end=None)` — `Slice` を1枚ずつ生成するイテレータ
139
+ - `slice3d.slice_mesh(...)` — `iter_slices` の結果をリストで取得
140
+ - `slice3d.save_section(path_2d, outpath, fmt=None)` — 1枚の断面を保存(`fmt` 省略時は拡張子から推定)
141
+ - `slice3d.print_volume(mesh)` — メッシュの体積を標準出力に表示し、その値を返す
142
+ - `slice3d.slice_file(model_path, outdir, axis="z", thickness=1.0, start=None, end=None, fmt="svg")` — 読み込み〜保存までを一括実行
143
+
144
+ `Slice` は `index`, `axis`, `position`, `path_2d`(`trimesh.path.Path2D | None`), `is_empty` を持つデータクラスです。
145
+
146
+ ## サンプル
147
+
148
+ ```bash
149
+ python examples/slice_ammonite.py
150
+ ```
151
+
152
+ `examples/ammonite.glb` をX軸方向にスライスし、`examples/output/` にPNGを出力します。
153
+
154
+ ## 開発
155
+
156
+ ```bash
157
+ pip install -e ".[dev]"
158
+ pytest
159
+ ```
160
+
161
+ ## ライセンス
162
+
163
+ MIT License. [LICENSE](LICENSE) を参照してください。
@@ -0,0 +1,123 @@
1
+ # slice3d
2
+
3
+ 3Dモデル(STL / OBJ / PLY / GLB / GLTF など)を一定間隔でスライスし、断面(輪郭)を SVG / DXF / PNG / CSV として出力するPythonライブラリ・CLIです。[trimesh](https://github.com/mikedh/trimesh) をベースにしています。
4
+
5
+ アンモナイトの化石モデルをX軸方向にスライスした例:
6
+
7
+ ![スライス一覧](docs/assets/ammonite_slices_grid.png)
8
+
9
+ 中心付近の断面(渦巻き状の房室がきれいに現れる):
10
+
11
+ ![中心断面](docs/assets/ammonite_center_slice.png)
12
+
13
+ ## インストール
14
+
15
+ ```bash
16
+ pip install slice3d
17
+
18
+ # PNG出力を使う場合(matplotlibが必要)
19
+ pip install "slice3d[viz]"
20
+ ```
21
+
22
+ 開発時はこのリポジトリを editable インストールしてください。
23
+
24
+ ```bash
25
+ python -m venv .venv && source .venv/bin/activate
26
+ pip install -e ".[dev]"
27
+ ```
28
+
29
+ ## CLIとして使う
30
+
31
+ ```bash
32
+ slice3d model.stl --axis z --thickness 2.0 --outdir slices --format svg
33
+ ```
34
+
35
+ | オプション | 説明 | 既定値 |
36
+ | --- | --- | --- |
37
+ | `--axis {x,y,z}` | スライスする軸 | `z` |
38
+ | `--thickness` | スライス間隔(モデル単位) | `1.0` |
39
+ | `--start` / `--end` | スライス範囲(省略時はモデルのバウンディングボックス全体) | なし |
40
+ | `--outdir` | 出力先ディレクトリ | `./slices` |
41
+ | `--format {svg,dxf,png,csv}` | 断面の出力形式 | `svg` |
42
+
43
+ PNG出力(CLI/`slice_file`/GUIいずれも共通)は、断面ごとの内容に自動フィットするのではなく、元モデル全体のバウンディングボックスを基準に表示範囲を固定して保存する。そのため、同じスライス処理で出力した画像はすべて同じピクセルサイズ・同じ縮尺になり、小さな断面ほど小さく、大きな断面ほど大きく写る(元モデルに対する実際の大きさの比率がそのまま画像に反映される)。
44
+
45
+ 交差しない位置のスライスは自動的にスキップされます。
46
+
47
+ ## GUIビューア
48
+
49
+ 体積・スライス位置を確認しながら、軸やスライス間隔(thickness)をその場で変更できる専用ウィンドウを開きます。
50
+
51
+ ```bash
52
+ pip install "slice3d[gui]"
53
+ slice3d-gui model.stl
54
+ slice3d-gui model.stl --axis x --thickness 0.01
55
+ slice3d-gui model.glb # glTF/GLBは仕様上メートル単位なので自動で"m"表示
56
+ slice3d-gui model.stl --units mm # STLなど単位不明な形式は明示的に指定
57
+ slice3d-gui model.stl --format png # Save系ボタンの初期フォーマット(既定: svg)
58
+ ```
59
+
60
+ ![GUIビューア](docs/assets/gui_screenshot.png)
61
+
62
+ ウィンドウ内の表示は(体積・軸・数値など)すべて英語で統一しています。
63
+
64
+ - ウィンドウ上部にモデル名・体積(`mesh.volume`、非watertightなら近似値である旨も表示)
65
+ - 左側にモデル全体を半透明の3D表示。現在の切断位置を赤い平面と断面の輪郭線で重ねて表示するので、どこをどの向きで切っているか一目で分かる
66
+ - 右側にその断面(切断面を真上から見た2D形状)をリアルタイム表示
67
+ - スライダーでスライス位置(index / position)を切り替え
68
+ - `Axis` ラジオボタンでスライス軸を切り替え(切り替え時はthicknessが軸の全長に応じてキリの良い値(1/2/5 × 10ⁿ、約30分割相当)に自動再設定される)
69
+ - `Thickness` テキストボックスで間隔を指定して Enter → 断面数が再計算される
70
+ - 体積・thickness・positionの表示には単位が付く。glTF/GLB(`.glb` / `.gltf`)は仕様上メートル単位と定められているため自動で`m`が付き、STL/OBJ/PLYなど単位情報を持たない形式は既定で単位なし
71
+ - `--units`(例: `mm`, `cm`)で表示単位を明示指定できる。`--units ""` で単位表示を消すことも可能
72
+ - `Format` ボタン(SVG/PNG/DXF/CSV)で保存形式を選択。選択中の形式はハイライトされ、Saveボタンのラベルにも反映される
73
+ - `Save Current Slice` ボタンで現在表示中の断面を1枚保存
74
+ - `Save All Slices Along <軸>-Axis` ボタンで、現在の軸・thickness設定のまま全断面を一括保存(交差しない位置は自動的にスキップ)
75
+ - 保存先はどちらも `<モデルと同じディレクトリ>/slices_gui/`
76
+
77
+ ## ライブラリとして使う
78
+
79
+ ```python
80
+ import slice3d
81
+
82
+ # 一括処理: モデルを読み込んでスライスし、ディレクトリへ書き出す
83
+ written = slice3d.slice_file("model.stl", "out", axis="z", thickness=1.0, fmt="svg")
84
+
85
+ # 細かく制御したい場合
86
+ mesh = slice3d.load_mesh("model.stl")
87
+ for s in slice3d.iter_slices(mesh, axis="z", thickness=1.0):
88
+ if s.is_empty:
89
+ continue
90
+ print(s.index, s.position, len(s.path_2d.discrete))
91
+ slice3d.save_section(s.path_2d, f"out/{s.index:04d}.svg")
92
+ ```
93
+
94
+ ### API
95
+
96
+ - `slice3d.load_mesh(path)` — 3Dモデルを読み込み `trimesh.Trimesh` を返す
97
+ - `slice3d.compute_heights(mesh, axis, thickness, start=None, end=None)` — スライス位置の配列を計算
98
+ - `slice3d.iter_slices(mesh, axis="z", thickness=1.0, start=None, end=None)` — `Slice` を1枚ずつ生成するイテレータ
99
+ - `slice3d.slice_mesh(...)` — `iter_slices` の結果をリストで取得
100
+ - `slice3d.save_section(path_2d, outpath, fmt=None)` — 1枚の断面を保存(`fmt` 省略時は拡張子から推定)
101
+ - `slice3d.print_volume(mesh)` — メッシュの体積を標準出力に表示し、その値を返す
102
+ - `slice3d.slice_file(model_path, outdir, axis="z", thickness=1.0, start=None, end=None, fmt="svg")` — 読み込み〜保存までを一括実行
103
+
104
+ `Slice` は `index`, `axis`, `position`, `path_2d`(`trimesh.path.Path2D | None`), `is_empty` を持つデータクラスです。
105
+
106
+ ## サンプル
107
+
108
+ ```bash
109
+ python examples/slice_ammonite.py
110
+ ```
111
+
112
+ `examples/ammonite.glb` をX軸方向にスライスし、`examples/output/` にPNGを出力します。
113
+
114
+ ## 開発
115
+
116
+ ```bash
117
+ pip install -e ".[dev]"
118
+ pytest
119
+ ```
120
+
121
+ ## ライセンス
122
+
123
+ MIT License. [LICENSE](LICENSE) を参照してください。
Binary file
@@ -0,0 +1,27 @@
1
+ """ライブラリ使用例: アンモナイトのGLBモデルをX軸方向にスライスする。
2
+
3
+ 実行方法::
4
+
5
+ python examples/slice_ammonite.py
6
+ """
7
+
8
+ from pathlib import Path
9
+
10
+ import slice3d
11
+
12
+ HERE = Path(__file__).parent
13
+
14
+
15
+ def main() -> None:
16
+ written = slice3d.slice_file(
17
+ HERE / "ammonite.glb",
18
+ outdir=HERE / "output",
19
+ axis="x",
20
+ thickness=0.007,
21
+ fmt="png",
22
+ )
23
+ print(f"{len(written)} 枚の断面を {HERE / 'output'} に出力しました")
24
+
25
+
26
+ if __name__ == "__main__":
27
+ main()
@@ -0,0 +1,44 @@
1
+ """ライブラリ使用例: アンモナイトのGLBモデルの体積を計算する。
2
+
3
+ 実行方法::
4
+
5
+ python examples/volume_ammonite.py
6
+ """
7
+
8
+ from pathlib import Path
9
+
10
+ import trimesh
11
+
12
+ import slice3d
13
+
14
+ HERE = Path(__file__).parent
15
+
16
+
17
+ def main() -> None:
18
+ mesh = slice3d.load_mesh(HERE / "ammonite.glb")
19
+
20
+ print(f"頂点数: {len(mesh.vertices)}")
21
+ print(f"面数: {len(mesh.faces)}")
22
+ print(f"バウンディングボックス(m): {mesh.extents}")
23
+
24
+ if not mesh.is_watertight:
25
+ print("警告: メッシュが閉じていません (is_watertight=False)。穴埋め・法線修正を試みます…")
26
+ mesh.merge_vertices()
27
+ mesh.update_faces(mesh.unique_faces())
28
+ mesh.remove_unreferenced_vertices()
29
+ trimesh.repair.fix_normals(mesh)
30
+ trimesh.repair.fill_holes(mesh)
31
+ if mesh.is_watertight:
32
+ print("修復に成功しました。")
33
+ else:
34
+ print("修復後も完全には閉じていません。以下の体積は近似値です。")
35
+
36
+ volume_m3 = abs(mesh.volume)
37
+ print(f"体積: {volume_m3:.6e} m^3 ({volume_m3 * 1e6:.4f} cm^3)")
38
+
39
+ hull_volume_m3 = mesh.convex_hull.volume
40
+ print(f"(参考) 凸包の体積: {hull_volume_m3:.6e} m^3 ({hull_volume_m3 * 1e6:.4f} cm^3)")
41
+
42
+
43
+ if __name__ == "__main__":
44
+ main()
@@ -0,0 +1,59 @@
1
+ [build-system]
2
+ requires = ["hatchling"]
3
+ build-backend = "hatchling.build"
4
+
5
+ [project]
6
+ name = "slice3d"
7
+ version = "0.1.0"
8
+ description = "3Dモデルを一定間隔でスライスして断面(輪郭)を出力するライブラリ/CLI"
9
+ readme = "README.md"
10
+ license = "MIT"
11
+ requires-python = ">=3.9"
12
+ authors = [{ name = "hanibuchi", email = "hanitech8686@gmail.com" }]
13
+ keywords = ["3d", "mesh", "slicing", "cross-section", "trimesh", "cad", "stl"]
14
+ classifiers = [
15
+ "Development Status :: 3 - Alpha",
16
+ "Intended Audience :: Developers",
17
+ "Intended Audience :: Science/Research",
18
+ "License :: OSI Approved :: MIT License",
19
+ "Programming Language :: Python :: 3",
20
+ "Programming Language :: Python :: 3.9",
21
+ "Programming Language :: Python :: 3.10",
22
+ "Programming Language :: Python :: 3.11",
23
+ "Programming Language :: Python :: 3.12",
24
+ "Programming Language :: Python :: 3.13",
25
+ "Programming Language :: Python :: 3.14",
26
+ "Topic :: Multimedia :: Graphics :: 3D Modeling",
27
+ "Topic :: Scientific/Engineering",
28
+ ]
29
+ dependencies = [
30
+ "trimesh>=4.0.0",
31
+ "numpy>=1.24",
32
+ "scipy>=1.10",
33
+ "networkx>=3.0",
34
+ "shapely>=2.0",
35
+ ]
36
+
37
+ [project.optional-dependencies]
38
+ viz = ["matplotlib>=3.7"]
39
+ # gui.py は RadioButtons/Slider の radio_props・label_props・handle_style
40
+ # (ウィジェットのクリック領域拡大に使用) に依存するため matplotlib>=3.8 が必要。
41
+ # matplotlib>=3.11 は TextBox の resize_event ハンドラが `_call_with_reparented_event`
42
+ # でラップされ、ResizeEvent に inaxes 属性が無いため AttributeError で落ちる回帰バグがある
43
+ # (thickness入力欄がTextBoxなのでウィンドウリサイズ時に必ず再現する)。修正版が出るまで除外する。
44
+ gui = ["matplotlib>=3.8,!=3.11.*", "fast-simplification>=0.1.7"]
45
+ dev = ["pytest>=7.0", "matplotlib>=3.8,!=3.11.*", "fast-simplification>=0.1.7"]
46
+
47
+ [project.scripts]
48
+ slice3d = "slice3d.cli:main"
49
+ slice3d-gui = "slice3d.gui:main"
50
+
51
+ [project.urls]
52
+ Homepage = "https://github.com/Hanibuchi/slice-3d"
53
+ Issues = "https://github.com/Hanibuchi/slice-3d/issues"
54
+
55
+ [tool.hatch.build.targets.wheel]
56
+ packages = ["src/slice3d"]
57
+
58
+ [tool.pytest.ini_options]
59
+ testpaths = ["tests"]
@@ -0,0 +1,46 @@
1
+ """slice3d: 3Dモデルを一定間隔でスライスして断面を出力するライブラリ。
2
+
3
+ 最小の使用例::
4
+
5
+ import slice3d
6
+
7
+ mesh = slice3d.load_mesh("model.stl")
8
+ for s in slice3d.iter_slices(mesh, axis="z", thickness=1.0):
9
+ if not s.is_empty:
10
+ slice3d.save_section(s.path_2d, f"out/{s.index:04d}.svg")
11
+
12
+ または一括処理::
13
+
14
+ import slice3d
15
+
16
+ slice3d.slice_file("model.stl", "out", axis="z", thickness=1.0, fmt="svg")
17
+ """
18
+
19
+ from .core import (
20
+ AXES,
21
+ Slice,
22
+ compute_heights,
23
+ iter_slices,
24
+ load_mesh,
25
+ polylines,
26
+ print_volume,
27
+ save_section,
28
+ slice_file,
29
+ slice_mesh,
30
+ )
31
+
32
+ __version__ = "0.1.0"
33
+
34
+ __all__ = [
35
+ "AXES",
36
+ "Slice",
37
+ "compute_heights",
38
+ "iter_slices",
39
+ "load_mesh",
40
+ "polylines",
41
+ "print_volume",
42
+ "save_section",
43
+ "slice_file",
44
+ "slice_mesh",
45
+ "__version__",
46
+ ]
@@ -0,0 +1,4 @@
1
+ from .cli import main
2
+
3
+ if __name__ == "__main__":
4
+ main()
@@ -0,0 +1,58 @@
1
+ """slice3d のコマンドラインインターフェース。"""
2
+
3
+ from __future__ import annotations
4
+
5
+ import argparse
6
+ from pathlib import Path
7
+
8
+ from .core import AXES, load_mesh, compute_heights, iter_slices, save_section
9
+
10
+
11
+ def parse_args(argv: list[str] | None = None) -> argparse.Namespace:
12
+ parser = argparse.ArgumentParser(
13
+ prog="slice3d",
14
+ description="3Dモデルを一定幅でスライスして断面を出力する",
15
+ )
16
+ parser.add_argument("model", type=Path, help="入力3Dモデルファイル (STL/OBJ/PLY/GLBなど)")
17
+ parser.add_argument("--axis", choices=tuple(AXES), default="z", help="スライスする軸 (既定: z)")
18
+ parser.add_argument("--thickness", type=float, default=1.0, help="スライス間隔 [モデル単位] (既定: 1.0)")
19
+ parser.add_argument("--start", type=float, default=None, help="開始位置 (既定: モデルの最小値)")
20
+ parser.add_argument("--end", type=float, default=None, help="終了位置 (既定: モデルの最大値)")
21
+ parser.add_argument("--outdir", type=Path, default=Path("slices"), help="出力先ディレクトリ (既定: ./slices)")
22
+ parser.add_argument(
23
+ "--format",
24
+ choices=("svg", "dxf", "png", "csv"),
25
+ default="svg",
26
+ help="断面の出力形式 (既定: svg)",
27
+ )
28
+ return parser.parse_args(argv)
29
+
30
+
31
+ def main(argv: list[str] | None = None) -> None:
32
+ args = parse_args(argv)
33
+
34
+ mesh = load_mesh(args.model)
35
+ if not mesh.is_watertight:
36
+ print(f"警告: メッシュが閉じていません (is_watertight=False)。断面が欠ける場合があります: {args.model}")
37
+
38
+ heights = compute_heights(mesh, args.axis, args.thickness, args.start, args.end)
39
+ print(f"{len(heights)} 枚の断面を axis={args.axis}, thickness={args.thickness} で生成します")
40
+
41
+ args.outdir.mkdir(parents=True, exist_ok=True)
42
+ stem = args.model.stem
43
+
44
+ n_ok = 0
45
+ for s in iter_slices(mesh, axis=args.axis, thickness=args.thickness, start=args.start, end=args.end):
46
+ if s.is_empty:
47
+ print(f" [{s.index:04d}] {args.axis}={s.position:.4f}: 交差なし (スキップ)")
48
+ continue
49
+ outpath = args.outdir / f"{stem}_{args.axis}{s.index:04d}_{s.position:.4f}.{args.format}"
50
+ save_section(s.path_2d, outpath, args.format)
51
+ print(f" [{s.index:04d}] {args.axis}={s.position:.4f}: {outpath.name}")
52
+ n_ok += 1
53
+
54
+ print(f"完了: {n_ok}/{len(heights)} 断面を {args.outdir} に出力しました")
55
+
56
+
57
+ if __name__ == "__main__":
58
+ main()