cad2d-ir 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,125 @@
1
+ Metadata-Version: 2.3
2
+ Name: cad2d-ir
3
+ Version: 0.1.0
4
+ Summary: Intermediate representation (IR) for 2D CAD and DXF converters
5
+ Keywords: cad,dxf,geometry,2d,intermediate-representation
6
+ Author: neka-nat
7
+ Author-email: neka-nat <nekanat.stock@gmail.com>
8
+ License: MIT
9
+ Classifier: Development Status :: 3 - Alpha
10
+ Classifier: Intended Audience :: Developers
11
+ Classifier: License :: OSI Approved :: MIT License
12
+ Classifier: Programming Language :: Python :: 3
13
+ Classifier: Programming Language :: Python :: 3.10
14
+ Classifier: Programming Language :: Python :: 3.11
15
+ Classifier: Programming Language :: Python :: 3.12
16
+ Classifier: Programming Language :: Python :: 3.13
17
+ Classifier: Topic :: Scientific/Engineering
18
+ Classifier: Topic :: Software Development :: Libraries
19
+ Requires-Dist: jsonschema>=4.23.0
20
+ Requires-Python: >=3.10
21
+ Project-URL: Documentation, https://github.com/neka-nat/cad2d-ir/tree/main/docs
22
+ Project-URL: Homepage, https://github.com/neka-nat/cad2d-ir
23
+ Project-URL: Issues, https://github.com/neka-nat/cad2d-ir/issues
24
+ Project-URL: Repository, https://github.com/neka-nat/cad2d-ir
25
+ Description-Content-Type: text/markdown
26
+
27
+ # cad2d-ir
28
+
29
+ `cad2d-ir` is an intermediate representation (IR) for 2D CAD data, plus converters between DXF and that IR.
30
+
31
+ The goal is to provide a stable, machine-friendly format for:
32
+ - CAD ingestion pipelines
33
+ - geometric post-processing
34
+ - round-trip conversion workflows
35
+
36
+ ## Status
37
+
38
+ The project currently supports:
39
+ - `LINE`, `CIRCLE`, `ARC`, `LWPOLYLINE`, `TEXT`
40
+ - `MTEXT`, `INSERT`, `HATCH`
41
+ - staged support for `SPLINE`, `DIMENSION`, and `constraints`
42
+
43
+ See `ir_schema.json` for the canonical schema.
44
+
45
+ ## Installation
46
+
47
+ ### With pip
48
+
49
+ ```bash
50
+ pip install cad2d-ir
51
+ ```
52
+
53
+ ### For development (uv)
54
+
55
+ ```bash
56
+ uv sync
57
+ uv run pytest
58
+ ```
59
+
60
+ ## CLI
61
+
62
+ ```bash
63
+ # Validate IR JSON
64
+ cad2d-ir validate examples/ir/minimal.json
65
+
66
+ # DXF -> IR
67
+ cad2d-ir dxf2ir examples/dxf/simple_line.dxf -o /tmp/out.json --pretty
68
+
69
+ # IR -> DXF
70
+ cad2d-ir ir2dxf examples/ir/minimal.json -o /tmp/out.dxf
71
+ ```
72
+
73
+ Warnings (for example when `constraints` cannot be represented in plain DXF) are printed to stderr.
74
+
75
+ ## Python API
76
+
77
+ ```python
78
+ from cad2d_ir import convert_dxf_text_to_ir, convert_ir_to_dxf_text
79
+
80
+ dxf_text = "0\nSECTION\n2\nENTITIES\n0\nLINE\n10\n0\n20\n0\n11\n1\n21\n1\n0\nENDSEC\n0\nEOF\n"
81
+ to_ir = convert_dxf_text_to_ir(dxf_text)
82
+ print(to_ir.document["entities"][0]["kind"]) # LINE
83
+
84
+ to_dxf = convert_ir_to_dxf_text(to_ir.document)
85
+ print(len(to_dxf.warnings))
86
+ ```
87
+
88
+ Lower-level conversion functions (`dxf_to_ir`, `ir_to_dxf`) are also available.
89
+
90
+ ## Repository layout
91
+
92
+ ```text
93
+ src/cad2d_ir/
94
+ api.py # public high-level API
95
+ cli.py # CLI entrypoint
96
+ schema.py # schema loading + validation
97
+ codecs/dxf.py # DXF <-> IR conversion
98
+ data/ir_schema.json
99
+ ir_schema.json # canonical schema source
100
+ tests/
101
+ examples/
102
+ docs/
103
+ ```
104
+
105
+ ## Compatibility and limitations
106
+
107
+ - `constraints` are preserved in IR but omitted when exporting to DXF.
108
+ - `DIMENSION` support is semantic/staged and may not preserve every CAD-system-specific detail.
109
+ - `HATCH` currently focuses on polyline-style loops.
110
+
111
+ ## Development
112
+
113
+ ```bash
114
+ uv run pytest -q
115
+ uv run cad2d-ir --help
116
+ ```
117
+
118
+ See:
119
+ - `CONTRIBUTING.md`
120
+ - `docs/API.md`
121
+ - `docs/SCHEMA_NOTES.md`
122
+
123
+ ## License
124
+
125
+ MIT (`LICENSE`)
@@ -0,0 +1,99 @@
1
+ # cad2d-ir
2
+
3
+ `cad2d-ir` is an intermediate representation (IR) for 2D CAD data, plus converters between DXF and that IR.
4
+
5
+ The goal is to provide a stable, machine-friendly format for:
6
+ - CAD ingestion pipelines
7
+ - geometric post-processing
8
+ - round-trip conversion workflows
9
+
10
+ ## Status
11
+
12
+ The project currently supports:
13
+ - `LINE`, `CIRCLE`, `ARC`, `LWPOLYLINE`, `TEXT`
14
+ - `MTEXT`, `INSERT`, `HATCH`
15
+ - staged support for `SPLINE`, `DIMENSION`, and `constraints`
16
+
17
+ See `ir_schema.json` for the canonical schema.
18
+
19
+ ## Installation
20
+
21
+ ### With pip
22
+
23
+ ```bash
24
+ pip install cad2d-ir
25
+ ```
26
+
27
+ ### For development (uv)
28
+
29
+ ```bash
30
+ uv sync
31
+ uv run pytest
32
+ ```
33
+
34
+ ## CLI
35
+
36
+ ```bash
37
+ # Validate IR JSON
38
+ cad2d-ir validate examples/ir/minimal.json
39
+
40
+ # DXF -> IR
41
+ cad2d-ir dxf2ir examples/dxf/simple_line.dxf -o /tmp/out.json --pretty
42
+
43
+ # IR -> DXF
44
+ cad2d-ir ir2dxf examples/ir/minimal.json -o /tmp/out.dxf
45
+ ```
46
+
47
+ Warnings (for example when `constraints` cannot be represented in plain DXF) are printed to stderr.
48
+
49
+ ## Python API
50
+
51
+ ```python
52
+ from cad2d_ir import convert_dxf_text_to_ir, convert_ir_to_dxf_text
53
+
54
+ dxf_text = "0\nSECTION\n2\nENTITIES\n0\nLINE\n10\n0\n20\n0\n11\n1\n21\n1\n0\nENDSEC\n0\nEOF\n"
55
+ to_ir = convert_dxf_text_to_ir(dxf_text)
56
+ print(to_ir.document["entities"][0]["kind"]) # LINE
57
+
58
+ to_dxf = convert_ir_to_dxf_text(to_ir.document)
59
+ print(len(to_dxf.warnings))
60
+ ```
61
+
62
+ Lower-level conversion functions (`dxf_to_ir`, `ir_to_dxf`) are also available.
63
+
64
+ ## Repository layout
65
+
66
+ ```text
67
+ src/cad2d_ir/
68
+ api.py # public high-level API
69
+ cli.py # CLI entrypoint
70
+ schema.py # schema loading + validation
71
+ codecs/dxf.py # DXF <-> IR conversion
72
+ data/ir_schema.json
73
+ ir_schema.json # canonical schema source
74
+ tests/
75
+ examples/
76
+ docs/
77
+ ```
78
+
79
+ ## Compatibility and limitations
80
+
81
+ - `constraints` are preserved in IR but omitted when exporting to DXF.
82
+ - `DIMENSION` support is semantic/staged and may not preserve every CAD-system-specific detail.
83
+ - `HATCH` currently focuses on polyline-style loops.
84
+
85
+ ## Development
86
+
87
+ ```bash
88
+ uv run pytest -q
89
+ uv run cad2d-ir --help
90
+ ```
91
+
92
+ See:
93
+ - `CONTRIBUTING.md`
94
+ - `docs/API.md`
95
+ - `docs/SCHEMA_NOTES.md`
96
+
97
+ ## License
98
+
99
+ MIT (`LICENSE`)
@@ -0,0 +1,49 @@
1
+ [project]
2
+ name = "cad2d-ir"
3
+ version = "0.1.0"
4
+ description = "Intermediate representation (IR) for 2D CAD and DXF converters"
5
+ readme = "README.md"
6
+ license = { text = "MIT" }
7
+ authors = [
8
+ { name = "neka-nat", email = "nekanat.stock@gmail.com" }
9
+ ]
10
+ requires-python = ">=3.10"
11
+ keywords = ["cad", "dxf", "geometry", "2d", "intermediate-representation"]
12
+ classifiers = [
13
+ "Development Status :: 3 - Alpha",
14
+ "Intended Audience :: Developers",
15
+ "License :: OSI Approved :: MIT License",
16
+ "Programming Language :: Python :: 3",
17
+ "Programming Language :: Python :: 3.10",
18
+ "Programming Language :: Python :: 3.11",
19
+ "Programming Language :: Python :: 3.12",
20
+ "Programming Language :: Python :: 3.13",
21
+ "Topic :: Scientific/Engineering",
22
+ "Topic :: Software Development :: Libraries",
23
+ ]
24
+ dependencies = [
25
+ "jsonschema>=4.23.0",
26
+ ]
27
+
28
+ [project.urls]
29
+ Homepage = "https://github.com/neka-nat/cad2d-ir"
30
+ Repository = "https://github.com/neka-nat/cad2d-ir"
31
+ Documentation = "https://github.com/neka-nat/cad2d-ir/tree/main/docs"
32
+ Issues = "https://github.com/neka-nat/cad2d-ir/issues"
33
+
34
+ [project.scripts]
35
+ cad2d-ir = "cad2d_ir.cli:main"
36
+
37
+ [build-system]
38
+ requires = ["uv_build>=0.8.15,<0.9.0"]
39
+ build-backend = "uv_build"
40
+
41
+ [dependency-groups]
42
+ dev = [
43
+ "pytest>=8.4.1",
44
+ "pytest-cov>=6.0.0",
45
+ "ruff>=0.8.0",
46
+ ]
47
+
48
+ [tool.pytest.ini_options]
49
+ testpaths = ["tests"]
@@ -0,0 +1,33 @@
1
+ from cad2d_ir.api import (
2
+ DxfToIrResult,
3
+ IrToDxfResult,
4
+ convert_dxf_file_to_ir,
5
+ convert_dxf_text_to_ir,
6
+ convert_ir_file_to_dxf,
7
+ convert_ir_to_dxf_text,
8
+ dump_ir_json,
9
+ load_ir_json,
10
+ )
11
+ from cad2d_ir.codecs.dxf import dxf_to_ir, ir_to_dxf, read_dxf_file, write_dxf_file
12
+ from cad2d_ir.schema import IRValidationError, load_schema, validate_ir
13
+
14
+ __version__ = "0.1.0"
15
+
16
+ __all__ = [
17
+ "__version__",
18
+ "DxfToIrResult",
19
+ "IRValidationError",
20
+ "IrToDxfResult",
21
+ "convert_dxf_file_to_ir",
22
+ "convert_dxf_text_to_ir",
23
+ "convert_ir_file_to_dxf",
24
+ "convert_ir_to_dxf_text",
25
+ "dxf_to_ir",
26
+ "dump_ir_json",
27
+ "ir_to_dxf",
28
+ "load_ir_json",
29
+ "load_schema",
30
+ "read_dxf_file",
31
+ "validate_ir",
32
+ "write_dxf_file",
33
+ ]
@@ -0,0 +1,89 @@
1
+ from __future__ import annotations
2
+
3
+ from dataclasses import dataclass
4
+ import json
5
+ from pathlib import Path
6
+ from typing import Any
7
+
8
+ from cad2d_ir.codecs.dxf import dxf_to_ir, ir_to_dxf
9
+ from cad2d_ir.schema import validate_ir
10
+
11
+
12
+ @dataclass(slots=True)
13
+ class DxfToIrResult:
14
+ document: dict[str, Any]
15
+ warnings: list[str]
16
+
17
+
18
+ @dataclass(slots=True)
19
+ class IrToDxfResult:
20
+ dxf_text: str
21
+ warnings: list[str]
22
+
23
+
24
+ def convert_dxf_text_to_ir(
25
+ dxf_text: str,
26
+ *,
27
+ ir_version: str = "0.1.0",
28
+ validate: bool = True,
29
+ ) -> DxfToIrResult:
30
+ warnings: list[str] = []
31
+ document = dxf_to_ir(
32
+ dxf_text,
33
+ ir_version=ir_version,
34
+ validate=validate,
35
+ warnings=warnings,
36
+ )
37
+ return DxfToIrResult(document=document, warnings=warnings)
38
+
39
+
40
+ def convert_dxf_file_to_ir(
41
+ path: str | Path,
42
+ *,
43
+ ir_version: str = "0.1.0",
44
+ validate: bool = True,
45
+ ) -> DxfToIrResult:
46
+ dxf_text = Path(path).read_text(encoding="utf-8")
47
+ return convert_dxf_text_to_ir(dxf_text, ir_version=ir_version, validate=validate)
48
+
49
+
50
+ def convert_ir_to_dxf_text(
51
+ document: dict[str, Any],
52
+ *,
53
+ validate: bool = True,
54
+ ) -> IrToDxfResult:
55
+ warnings: list[str] = []
56
+ dxf_text = ir_to_dxf(document, validate=validate, warnings=warnings)
57
+ return IrToDxfResult(dxf_text=dxf_text, warnings=warnings)
58
+
59
+
60
+ def convert_ir_file_to_dxf(
61
+ path: str | Path,
62
+ *,
63
+ validate: bool = True,
64
+ ) -> IrToDxfResult:
65
+ document = load_ir_json(path, validate=validate)
66
+ return convert_ir_to_dxf_text(document, validate=validate)
67
+
68
+
69
+ def load_ir_json(path: str | Path, *, validate: bool = True) -> dict[str, Any]:
70
+ document = json.loads(Path(path).read_text(encoding="utf-8"))
71
+ if validate:
72
+ validate_ir(document)
73
+ return document
74
+
75
+
76
+ def dump_ir_json(
77
+ document: dict[str, Any],
78
+ path: str | Path,
79
+ *,
80
+ pretty: bool = True,
81
+ validate: bool = True,
82
+ ) -> None:
83
+ if validate:
84
+ validate_ir(document)
85
+ indent = 2 if pretty else None
86
+ text = json.dumps(document, ensure_ascii=False, indent=indent)
87
+ if pretty:
88
+ text += "\n"
89
+ Path(path).write_text(text, encoding="utf-8")
@@ -0,0 +1,83 @@
1
+ from __future__ import annotations
2
+
3
+ import argparse
4
+ import json
5
+ from pathlib import Path
6
+ import sys
7
+ from typing import Sequence
8
+
9
+ from cad2d_ir.api import convert_dxf_text_to_ir, convert_ir_to_dxf_text, load_ir_json
10
+ from cad2d_ir.schema import IRValidationError, validate_ir
11
+
12
+
13
+ def _read_json(path: Path) -> dict:
14
+ return json.loads(path.read_text(encoding="utf-8"))
15
+
16
+
17
+ def _write_text(path: Path | None, text: str) -> None:
18
+ if path is None:
19
+ print(text, end="")
20
+ return
21
+ path.write_text(text, encoding="utf-8")
22
+
23
+
24
+ def build_parser() -> argparse.ArgumentParser:
25
+ parser = argparse.ArgumentParser(prog="cad2d-ir")
26
+ subparsers = parser.add_subparsers(dest="command", required=True)
27
+
28
+ validate_parser = subparsers.add_parser("validate", help="Validate an IR JSON document")
29
+ validate_parser.add_argument("input", type=Path, help="Path to IR JSON file")
30
+
31
+ dxf2ir_parser = subparsers.add_parser("dxf2ir", help="Convert DXF file to IR JSON")
32
+ dxf2ir_parser.add_argument("input", type=Path, help="Path to DXF file")
33
+ dxf2ir_parser.add_argument("-o", "--output", type=Path, help="Output IR JSON path")
34
+ dxf2ir_parser.add_argument("--pretty", action="store_true", help="Pretty-print JSON")
35
+ dxf2ir_parser.add_argument("--ir-version", default="0.1.0", help="IR version string")
36
+
37
+ ir2dxf_parser = subparsers.add_parser("ir2dxf", help="Convert IR JSON to DXF file")
38
+ ir2dxf_parser.add_argument("input", type=Path, help="Path to IR JSON file")
39
+ ir2dxf_parser.add_argument("-o", "--output", type=Path, help="Output DXF path")
40
+ return parser
41
+
42
+
43
+ def main(argv: Sequence[str] | None = None) -> int:
44
+ parser = build_parser()
45
+ args = parser.parse_args(argv)
46
+
47
+ try:
48
+ if args.command == "validate":
49
+ validate_ir(_read_json(args.input))
50
+ print("IR document is valid.")
51
+ return 0
52
+
53
+ if args.command == "dxf2ir":
54
+ result = convert_dxf_text_to_ir(
55
+ args.input.read_text(encoding="utf-8"),
56
+ ir_version=args.ir_version,
57
+ validate=True,
58
+ )
59
+ indent = 2 if args.pretty else None
60
+ text = json.dumps(result.document, ensure_ascii=False, indent=indent)
61
+ if indent is not None:
62
+ text += "\n"
63
+ _write_text(args.output, text)
64
+ for warning in result.warnings:
65
+ print(f"Warning: {warning}", file=sys.stderr)
66
+ return 0
67
+
68
+ if args.command == "ir2dxf":
69
+ result = convert_ir_to_dxf_text(load_ir_json(args.input, validate=True), validate=True)
70
+ _write_text(args.output, result.dxf_text)
71
+ for warning in result.warnings:
72
+ print(f"Warning: {warning}", file=sys.stderr)
73
+ return 0
74
+ except (IRValidationError, ValueError, json.JSONDecodeError) as exc:
75
+ print(f"Error: {exc}")
76
+ return 2
77
+
78
+ parser.error(f"Unknown command: {args.command}")
79
+ return 2
80
+
81
+
82
+ if __name__ == "__main__":
83
+ raise SystemExit(main())
@@ -0,0 +1,3 @@
1
+ from cad2d_ir.codecs.dxf import dxf_to_ir, ir_to_dxf, read_dxf_file, write_dxf_file
2
+
3
+ __all__ = ["dxf_to_ir", "ir_to_dxf", "read_dxf_file", "write_dxf_file"]