# Чертежи DXF/DWG (AutoCAD-совместимые)

Ты создаёшь и правишь векторные чертежи через библиотеку **ezdxf** в песочнице. Геометрию пишешь Python-кодом, служебные операции (превью, валидация, осмотр, экспорт, DWG-конвертация) — готовыми скриптами.

Все скрипты лежат в `/mnt/skills/cad_drafting_ru/scripts/` и уже в `sys.path` — модули `dxf_common`, `gost_layout`, `dxf_data`, `sortament` импортируются напрямую по имени.

---

## S0. КОНТЕКСТ И ПРАВИЛА

- **Формат вывода — `.dxf`.** Его открывают nanoCAD, КОМПАС-3D, Renga, AutoCAD и бесплатные вьюверы (ShareCAD). Это формат по умолчанию для отдачи пользователю.
- **`.pdf`** делай, когда пользователю нужен лист на печать/согласование/подпись — через `dxf_export.py`.
- **`.dwg` на входе** → сначала `dxf_dwg.py to-dxf`, потом правь. **`.dwg` на выходе** — только если пользователь явно просит нативный DWG (нужен ODA File Converter; если недоступен — отдавай `.dxf`).
- Единицы — миллиметры. Слои берутся из `dxf_common` со штатными типами линий и толщинами: `KONTUR` (основная, 0.5мм), `OSEVAYA` (осевая, штрихпунктир), `NEVIDIMAYA` (невидимая, штриховая), `RAZMERY`, `TEKST`, `SHTRIHOVKA`, `RAMKA`.
- Кириллический текст пиши стилем `CYR`. Размеры — стилем `GOST` (засечки, текст над линией, высота 2.5) или дефолтным — он уже настроен под ГОСТ в `new_doc`.
- Готовый файл сохраняй в `/home/user/output/` — оттуда он становится артефактом на скачивание.

---

## S1. WORKFLOW

| Задача | Шаги |
|--------|------|
| Новый чертёж (геометрия) | `new_doc` → примитивы → `save` → `dxf_validate.py` → `dxf_preview.py` |
| Оформленный лист | геометрия в модели → `gost_layout.make_sheet` → `place_view` (масштаб) → `add_title_block` (штамп) → `save` → `dxf_preview.py --layout <имя>` → при просьбе `dxf_export.py --layout <имя>` (PDF) |
| Чертёж из таблицы координат/CSV | `dxf_data.read_table` → `points_from_table`/`polyline_from_table`/`coordinate_table`/`profile_from_table` → `save` → preview |
| Деталь со стандартным профилем | `sortament.draw_section` + `sortament.mass` → `save` → preview |
| Правка `.dxf` пользователя | `dxf_inspect.py` → `open_doc` → query/изменения → `save` → validate → preview |

**ОБЯЗАТЕЛЬНО** после любого создания/изменения вызови валидацию и превью:
```bash
python /mnt/skills/cad_drafting_ru/scripts/dxf_validate.py /home/user/output/result.dxf
python /mnt/skills/cad_drafting_ru/scripts/dxf_preview.py /home/user/output/result.dxf
```
Для оформленного листа превью делай по листу: `dxf_preview.py result.dxf --layout A3`.

---

## S2. ГЕОМЕТРИЯ — ezdxf (Python в песочнице)

```python
from dxf_common import new_doc, open_doc, save

doc = new_doc()
msp = doc.modelspace()
```

Примитивы (слой — через `dxfattribs={"layer": ...}`):
```python
msp.add_line((0, 0), (100, 0), dxfattribs={"layer": "KONTUR"})
msp.add_lwpolyline([(0, 0), (100, 0), (100, 50), (0, 50)], close=True, dxfattribs={"layer": "KONTUR"})
msp.add_circle((50, 25), 10, dxfattribs={"layer": "KONTUR"})
msp.add_arc((0, 0), 20, 0, 90, dxfattribs={"layer": "KONTUR"})
```
Дуга в полилинии (скругление/сегмент) — через bulge, формат `xyseb` (x, y, start_width, end_width, bulge):
```python
msp.add_lwpolyline([(0, 0, 0, 0, 0.5), (50, 0, 0, 0, 0), (50, 50)], format="xyseb", dxfattribs={"layer": "KONTUR"})
```
Сплайн и эллипс:
```python
msp.add_spline([(0, 0), (20, 30), (60, 10), (90, 40)], dxfattribs={"layer": "KONTUR"})
msp.add_ellipse((50, 25), major_axis=(30, 0), ratio=0.5, dxfattribs={"layer": "KONTUR"})
```
Эквидистанта (offset) контура:
```python
from ezdxf.math import offset_vertices_2d
pts = [(0, 0), (100, 0), (100, 50), (0, 50)]
off = list(offset_vertices_2d(pts, offset=5, closed=True))
msp.add_lwpolyline(off, close=True, dxfattribs={"layer": "KONTUR"})
```

Текст и аннотации:
```python
msp.add_text("Деталь А", height=2.5, dxfattribs={"layer": "TEKST", "style": "CYR"}).set_placement((5, 60))
mtext = msp.add_mtext("Примечание\nв две строки", dxfattribs={"layer": "TEKST", "style": "CYR", "char_height": 2.5})
mtext.set_location((5, 70))
```

Размеры — после создания обязательно `.render()`. Дефолтный стиль уже под ГОСТ:
```python
msp.add_linear_dim(base=(0, -10), p1=(0, 0), p2=(100, 0), dxfattribs={"layer": "RAZMERY"}).render()
msp.add_aligned_dim(p1=(0, 0), p2=(60, 40), distance=12, dxfattribs={"layer": "RAZMERY"}).render()
msp.add_radius_dim(center=(50, 25), radius=10, angle=45, dimstyle="EZ_RADIUS", dxfattribs={"layer": "RAZMERY"}).render()
msp.add_diameter_dim(center=(50, 25), radius=10, angle=135, dxfattribs={"layer": "RAZMERY"}).render()
msp.add_angular_dim_2l(base=(40, 40), line1=((0, 0), (50, 0)), line2=((0, 0), (40, 30)), dxfattribs={"layer": "RAZMERY"}).render()
```

Штриховка — заливка или паттерн материала (металл/бетон):
```python
hatch = msp.add_hatch(color=9, dxfattribs={"layer": "SHTRIHOVKA"})
hatch.paths.add_polyline_path([(0, 0), (100, 0), (100, 50), (0, 50)], is_closed=True)
steel = msp.add_hatch(dxfattribs={"layer": "SHTRIHOVKA"})
steel.set_pattern_fill("ANSI31", scale=2.0)
steel.paths.add_polyline_path([(0, 0), (40, 0), (40, 20), (0, 20)], is_closed=True)
```

Блоки (повторяемые элементы, символы):
```python
mark = doc.blocks.new("METKA")
mark.add_circle((0, 0), 3, dxfattribs={"layer": "KONTUR"})
msp.add_blockref("METKA", (40, 40))
```

Правка существующего файла:
```python
doc = open_doc("/tmp/input.dxf")
msp = doc.modelspace()
for line in msp.query('LINE[layer=="KONTUR"]'):
    line.dxf.layer = "OSEVAYA"
msp.add_circle((10, 10), 5, dxfattribs={"layer": "KONTUR"})
save(doc, "result.dxf")
```
Удаление: `msp.delete_entity(entity)`. Сдвиг: `entity.translate(dx, dy, 0)`.

---

## S3. ЛИСТ, РАМКА, ШТАМП — `gost_layout`

Превращает геометрию в оформленный чертёж: лист формата ГОСТ 2.301 с рамкой (поля 20/5/5/5), основной надписью ГОСТ 2.104 форма 1 и видовым экраном в масштабе.

```python
import gost_layout as gl

w, h = gl.sheet_size("A3", "landscape")
sheet = gl.make_sheet(doc, "A3", "landscape")
gl.place_view(sheet, scale=2, model_center=(100, 60), paper_center=(180, 150), paper_size=(220, 160))
gl.add_title_block(sheet, {
    "обозначение": "СБ-001.00.00", "наименование": "Корпус",
    "материал": "Сталь 45 ГОСТ 1050", "масса": "12,4", "масштаб": "1:2",
    "лист": "1", "листов": "1", "организация": "ООО Самрешу", "разработал": "Иванов",
}, sheet=(w, h))
gl.add_revision_table(sheet, [["1", "1", "Изв.12", "—", "31.05.26"]], sheet=(w, h))
```
- `make_sheet(doc, fmt, orientation)` — формат A4…A0, `orientation` portrait/landscape. Геометрию рисуй в модели, на лист её показывает видовой экран.
- `place_view(sheet, scale, model_center, paper_center, paper_size)` — `scale=50` значит 1:50; `paper_*` в мм на листе, `model_center` — точка модели в центре окна.
- `add_title_block(sheet, fields, sheet=(w,h))` — поля по именам (обозначение/наименование/материал/масса/масштаб/лист/листов/организация/разработал/проверил).
- Поля штампа на детали: наименование+материал; на сборке: наименование без материала.

---

## S4. ДАННЫЕ → ЧЕРТЁЖ — `dxf_data`

Когда пользователь даёт таблицу координат, CSV или Excel — не вбивай точки руками, читай таблицу.

```python
import dxf_data as dd

rows = dd.read_table("/tmp/coords.csv")              # CSV или .xlsx
dd.points_from_table(msp, rows, x="X", y="Y", name="N")
dd.polyline_from_table(msp, rows, x="X", y="Y", close=True)
dd.coordinate_table(msp, rows, name="N", x="X", y="Y", at=(1200, 2030))   # ведомость координат
```
Продольный профиль (пикетаж × отметка) с вертикальным экажерейшеном:
```python
prof = dd.read_table("/tmp/profile.csv")
dd.profile_from_table(msp, prof, chainage="PK", elevation="H", v_exag=10)
```
Формат пикета: `dd.format_station(12345)` → `"ПК123+45"`.

---

## S5. СОРТАМЕНТ ГОСТ — `sortament`

Стандартные профили не рисуй вручную — бери из сортамента. Размеры и масса погонного метра встроены (двутавр ГОСТ 26020, швеллер ГОСТ 8240, уголок ГОСТ 8509).

```python
import sortament as sm

sm.draw_section(msp, "двутавр", "20Б1", at=(0, 0))        # сечение на слое KONTUR
sm.draw_section(msp, "швеллер", "24", at=(200, 0))
sm.draw_section(msp, "уголок", "63x5", at=(350, 0), rotation=0)
m = sm.mass("двутавр", "20Б1", length_mm=6000)            # кг для 6 м
```
- Типоразмера нет в таблице — передай размеры явно: `sm.draw_section(msp, "I", dims=(h, b, s, t, mass), at=...)`.
- Масса листа/произвольного: `sm.plate_mass(L, W, t)` или `sm.mass_from_volume(vol_mm3)` (плотность 7850).
- Полученную массу проставляй в штамп (`масса`) и в спецификацию.

---

## S6. СЛУЖЕБНЫЕ СКРИПТЫ

Путь `/mnt/skills/cad_drafting_ru/scripts/`, у каждого `--help`.

```bash
python scripts/dxf_inspect.py /tmp/input.dxf                       # версия, слои, сущности, блоки, габариты
python scripts/dxf_validate.py /home/user/output/result.dxf        # аудит; код возврата != 0 = ошибки
python scripts/dxf_preview.py  /home/user/output/result.dxf        # PNG модели (инлайн)
python scripts/dxf_preview.py  /home/user/output/result.dxf --layout A3   # PNG листа
python scripts/dxf_export.py   /home/user/output/result.dxf --layout A3   # PDF листа на печать
python scripts/dxf_dwg.py to-dxf /tmp/input.dwg                    # нужен ODA File Converter
```

---

## S7. ГРАНИЦЫ

- Поддержано: 2D-геометрия, слои с типами линий и толщинами, все типы размеров, текст, штриховки (заливка и паттерн), блоки, лист с рамкой и штампом ГОСТ, видовые экраны в масштабе, генерация из таблиц, сортамент, экспорт в PDF — генерация и правка.
- НЕ поддержано через ezdxf: построение/правка 3D-солидов (ACIS), авторинг параметрики динамических блоков, proxy-объекты вертикалей (AEC/MEP) — их можно прочитать, но не редактировать семантически. Если задача требует этого — честно скажи пользователю.

---

*Геометрию пиши Python-кодом, лист/штамп/данные/сортамент — модулями, служебное — скриптами. Всегда заканчивай валидацией и превью.*
