7e7fe11a71e6969a5b990d64f3a3b8ad85c70955
10
Commits
| Author | SHA1 | Message | Date | |
|---|---|---|---|---|
|
|
4c5c8227ef |
merge: track F group C
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01FKFStjEFzpGyLq5bMpWxTd |
||
|
|
18db0b46c0 |
merge: track F group R
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01FKFStjEFzpGyLq5bMpWxTd |
||
|
|
cd1ae3529c |
feat(arch): SECTION 25 opens - walls, openings, stairs and exterior chains as tools
engineering/arch/draw.py is the only module of the track that touches a backend. It draws through draw_prims(role_layer=ARCH_ROLE_LAYER) and the generic contract members, so it runs on both engines as written, and it writes the plan onto the drawing: each element's ACADMCP_ARCH record on one of its own entities (a wall's first face line, a door's leaf, a window's frame, a stair's first riser) and, beside it under ACADMCP_ARCH_H, the handles that element owns. read_plan reads the records back and never modifies the drawing. arch_wall computes the whole network before and after the new walls and redraws only the drawn walls whose outline changes - erasing exactly the entities they own - and says which under redrawn. arch_opening hosts a door or a window in a drawn wall found by id and redraws that wall cut; its tag defaults to the next D1/W1 (K1/P1 in Turkish). A new wall that would leave a drawn opening uncuttable, and an opening that runs into a junction, are refused before anything is erased. arch_stair reports the Blondel value; it does not enforce it. exterior_chains gives each side up to three rows, nearest first: every jamb in that facade, both faces of every wall meeting it, and the overall - from the wall engine's own faces. A row that would only restate a farther one is not emitted (ISO 129-1: each measurement once). The 10 mm / 8 mm row spacing is a declared drafting default times the plot scale, not a standard value. The arch tag is ranked after mech so the four tools file under a new architecture group; alias records carry WALLADD/MLINE, DOORADD/WINDOWADD and STAIRADD with English and Turkish shop words. Inventory regenerated; README snapshot 233 tools (228 advertised) in 26 groups; registry snapshot architecture: 4. No new ActiveX member is called. Measured on AutoCAD 2026 in a scratch document through ComBackend: shell + teed partition, D1 + W1, a straight 17-riser stair and the bottom chains gave the same 20 wall LINEs (identical endpoints), 9 poche HATCHes, 1+1 door, 4 window lines, 11 dimensions and a read_plan round trip as the headless engine. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01FKFStjEFzpGyLq5bMpWxTd |
||
|
|
9168d61397 |
feat(task-6): door, window and room schedules read from the drawing
engineering/arch/schedule.py turns the plan records into schedule rows: doors by tag with width, height, swing, hand and wall; windows by tag with width, height, sill and wall; rooms by number with name and the measured area the label carries, plus a total. Tags compare as numbers (D2 before D10). An opening without a tag gets the next free D1 / W1 - K1 / P1 in Turkish - in wall order; an explicit tag is never renumbered and a generated one never repeats it. A value the model does not carry is an empty cell, never a default, and Turkish output writes decimal commas. draw_schedule reads the ACADMCP_ARCH records off the drawing through the one reader rooms.py already has and draws a real TABLE the way the ISO 7573 parts list does - top-left insertion, the table growing down, the parts list's 7 mm rows and 2.5 mm text at the plot scale, the engine's representation reported. Nothing to schedule, an unknown kind or language and more rows than one TABLE takes are refused before anything is drawn. arch_schedule joins arch_room and arch_rooms_detect under SECTION 25's rooms marker (banner 3 tools), shares the TABLE command with entity_create_table and bom_table, and the counts follow: 232 tools in 25 groups. Live fix in com_backend.entity_create_table. MEASURED on AutoCAD 2026: IAcadTable has no TextHeight property. pywin32 raised AttributeError on `table.TextHeight = h`, the old code swallowed it, and every live table kept the Standard style's heights (title 6.0, header and data 4.5), which is 0.1 mm on paper at 1:50. The member that works is SetCellTextHeight(row, col, h), probed on a scratch document: every cell read back 125.0 via GetCellTextHeight. The row heights stayed 350.0 and GetBoundingBox matched the reported bbox (10000,3250)-(15750,5000). Then draw_schedule ran end to end through ComBackend on a scratch document: a native TABLE on A-SYMB-T-N for doors (5 rows) and rooms/tr (6 rows, total 45,00 m²), and the document was closed afterwards. The new fake in tests/test_table_leader.py has only the measured members. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01FKFStjEFzpGyLq5bMpWxTd |
||
|
|
b68fe7ef66 |
feat(arch): door and window symbols, straight/L/U stairs and the Blondel check
openings.py draws what stands in the gap the wall engine cuts, from the same numbers - the wall axis, its face offsets and model.segment_of - so the symbol cannot drift off its gap. A door is drawn open at 90 degrees: its leaf as long as the opening is wide, square to the wall from the hinge on the swing-side face, and a quarter arc from the closed position to the open one. swing=in is the left of the axis (the inside of a CCW perimeter); hand names the hinge jamb seen from the swing side facing the wall. All four swing/hand pairs are tested against hand-computed hinges and arc angles. A window is a frame line on each face and two glazing lines at the thirds of the thickness. stairs.py draws every riser once as a line across its flight, the outline, a walking line up the middle with an open arrow at the top, and the language's 'up' word on the first tread at 2.5 mm times the plot scale. An L or a U turns on a square landing; its first flight carries (risers + 1) // 2 risers. The riser count and every dimension are refused by name before any geometry. blondel() reports 2R + G against 600-650 mm - the step-length rule attributed to Francois Blondel (1675), reported as a design rule and never as a building code: no national stair regulation is checked. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01FKFStjEFzpGyLq5bMpWxTd |
||
|
|
f5f11178a9 |
feat(task-8): SECTION 25 grid and symbols - arch_grid, arch_symbol
engineering/arch/symbols.py is pure: grid_prims() draws each structural axis on the grid role - its CENTER linetype comes from the A-GRID-E-N row, no primitive sets one - with a 5 mm (paper) bubble and its label touching both ends, on the symbol role so the circle does not print dashed. Numbers run along x and letters along y (A..Z, AA, ... none skipped) unless labels names them; positions are sorted, duplicates and label-count mismatches refused by path. symbol_prims() draws the four plan symbols: the north arrow (a circle, an arrow turned CCW from +Y, the vocab(lang) north letter upright beyond the tip), the section mark (the cut line, a labelled bubble past each end and a viewing arrow at each end, p1/p2 relative to the origin), the level mark (the triangle, its line and the value formatted +3.00 / ±0.00, with a decimal comma in Turkish) and the elevation mark (a circle whose pointers are the tangents from r*sqrt(2) touching it at +/-45 degrees). An unknown kind is refused with the list, a parameter the kind does not take with the ones it does. Every size is a declared paper-mm drafting choice times the plot scale, not a standards value. arch_grid and arch_symbol draw through draw_prims with ARCH_ROLE_LAYER; alias records (COLUMNGRIDADD, English and Turkish synonyms), a regenerated inventory, the README snapshot at 233 tools and the branch-local group snapshot (entity_creation 21) complete the registration. Beyond the plan: grid_axes refuses a labels value that is not a dict and a labels.x / labels.y that is not a list (a string was split into one label per character, a number raised a bare TypeError). No new ActiveX member: a live run on AutoCAD 2026 (scratch document, closed after) drew the grid and all four symbols through draw_prims - A-GRID-E-N created with CENTER, bubbles, texts and symbols on A-SYMB-T-N, the Turkish north letter K and the level "+3,00". Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01FKFStjEFzpGyLq5bMpWxTd |
||
|
|
afd42356b5 |
feat(task-7): SECTION 25 catalogue - arch_catalogue_list, arch_catalogue_insert
engineering/arch/catalogue.py is the standard-parts pattern of track B for an architectural plan: twenty authored plan symbols in two families - thirteen furniture items (single and double bed, wardrobe, three-seat sofa, armchair, chair, dining table with four chairs, desk, kitchen counter run, fridge, bookshelf, nightstand, coffee table) and seven sanitary items (WC, bidet, wall basin, shower tray, bathtub, kitchen sink, washing machine). Every size is a nominal catalogue dimension and says so in SIZE_BASIS, in each row and in both tool docstrings; no standard is cited because none fixes these sizes. Each item is typed block_define primitives on layer 0 inside [0, w] x [0, d], origin at the back-left corner with the wall side along +X - a test holds all twenty to their footprint. catalogue() searches name, family and the English and Turkish labels, ignoring case and Turkish diacritics (buzdolabi finds buzdolabı). insert_item() defines ARCH_<NAME> once, creates the furniture or sanitary layer from its ARCH_LAYERS row when missing (live ActiveX refuses a missing layer), inserts with an invisible ITEM attribute, and refuses an unknown name with the nearest catalogue names before any write. SECTION 25 opens on this branch with the two tools, alias records (INSERT, ADCENTER, TOOLPALETTES and English/Turkish synonyms), a regenerated inventory, the README snapshot at 231 tools, and the branch-local group snapshot (entity_query 10, entity_creation 19) that the track F merge recomputes. No new ActiveX call: insert_item composes the existing layer_list, layer_create, block_list, block_define and block_insert members. Run once live through ComBackend on AutoCAD 2026 (scratch document, closed after): all twenty items defined and inserted, a second double_bed insert reported defined=false, ITEM read back as double_bed, 20 ARCH_* blocks, 21 INSERTs on A-FURN-E-N / A-SANR-E-N, both layers created at ACI 5 / 0.18 mm. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01FKFStjEFzpGyLq5bMpWxTd |
||
|
|
67d578e09a |
feat(task-5): planar faces, measured room labels and the foreign-plan reader
engineering/arch/faces.py finds the closed faces a set of straight segments bounds: endpoints within tol merge, every segment splits where another crosses or touches it (the T-junction a partition makes), dangling edges are pruned, and the half-edge walk turns to the next edge clockwise so every bounded face comes out counter-clockwise. A piece nested inside a face of another piece is a hole in it, so a ring of walls measures its body and a room with a column measures its floor. Areas come from engineering.measure.polygon_area_perimeter, the arithmetic analysis_measure_entity reports. engineering/arch/rooms.py labels a room with the area of the face its point lies in - a 4000 x 5000 room between 200 mm walls reads 20.00 m2, never the axis area - and writes the room record onto the name text. Opening records close door and window gaps along the wall faces, so two rooms either side of a door stay two rooms. rooms_detect reads any plan's lines, drops faces under min_area and thin wall bodies (mean width under 600 mm, a declared heuristic), reports confidence 1.0 for faces enclosed only by our wall layer and 0.6 otherwise, lists arcs as skipped rather than flattening them, and never modifies the drawing. Two hardenings beyond the plan text, each with a test: read_records pages the current space once and filters to the arch layers locally (the plan paged it once per arch layer - twelve full scans, each a cross-process call per entity on a live seat), keeping the same layer-then-drawing order; and an opening record whose offset falls outside its wall (a stale record) is listed in omitted instead of raising out of the read-only detector. No new ActiveX member is called: both tools compose entity_list, entity_get/set_xdata, layer_list and draw_prims, whose COM paths are already live-verified. SECTION 25 opens on this branch with arch_room and arch_rooms_detect under the rooms marker; until Task 9 gives arch its group they file under entity_creation and entity_query. Alias records, a regenerated inventory, the README snapshot at 231 tools in 25 groups and the frozen group-size snapshot follow. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01FKFStjEFzpGyLq5bMpWxTd |
||
|
|
dae48d9037 |
feat(arch): the wall engine - L/T/X junctions, opening gaps and material poche
engineering/arch/walls.py turns a wall network - axis polylines with a thickness, a justification and a material - into clean outlines. One rule resolves every junction: at each node the segment ends are sorted by angle and the left face of each meets the right face of the next one counter-clockwise. That mitres an L, stops a T's stem on the crossing wall's near face (cut there, while the far face stays one line) and cuts all four faces of an X. A T is an axis end lying inside another wall: it is carried onto the crossing axis and the crossing segment is split. The hub a three- or four-way node leaves is a region of its own, so the poche has no hole. Openings remove their width from both faces and are closed by jambs; a free end is closed by a jamb. Each solid piece is one CCW region and one HatchArea in the wall's material. The two face lines across each opening are kept as thresholds - never drawn - so room_segments hands the face finder closed rooms instead of rooms merged through their doorways. Refused by name before any geometry: a duplicate id, an unknown material or justification, a zero-length segment, a junction below 5 degrees, a corner inside another wall off its axis, an end whose axis meets the other wall past its end, a segment lying wholly inside the wall it joins, and one too short for its junctions (engineering.measure.is_self_intersecting on its outline). An opening that runs into a junction is returned in omitted, never dropped. Every corner in the tests is hand-computed: two 200 mm walls at the origin meet at (100, 100) inside and (-100, -100) outside. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01FKFStjEFzpGyLq5bMpWxTd |
||
|
|
d5c5e90406 |
feat(arch): the plan model and its foundation - layers, materials, EN/TR, two generalisations
Track F starts here; the three group branches are cut from this commit. - engineering/arch/model.py: Wall, Opening, Stair and Room as frozen dataclasses, and every refusal of spec 4 before anything is drawn, by path (walls[2].thickness): finite positive numbers, an axis of at least two distinct points with no zero-length segment, an arc segment refused by name rather than approximated, an opening on an unknown wall, past its wall, across an axis vertex or overlapping another (both ids named), an unknown material, and a typed room area - areas are measured, never typed. The plan travels as ACADMCP_ARCH XDATA; an opening's door/window kind and a stair's straight/l/u kind ride beside the payload kind as opening_kind / stair_kind, because one key cannot hold both. - engineering/arch/layers.py: the arch set - thirteen ISO 13567-style layers with ISO 128 lineweights only (0.05 mm on the construction scratch layer, as every set in this repository) - and ARCH_ROLE_LAYER, the one place a role becomes a layer. Registered in engineering/layers.py last, which is what keeps the engineering.layers <-> engineering.arch import cycle closed. - engineering/arch/materials.py: eight wall materials, one pattern each, every row citing its source: ANSI31 for brick (ISO 128-50 general hatching, acadiso.pat's own "Iron, Brick, Stone masonry"), AAC as ANSI31 at twice the spacing (a repository convention, and it says so), AR-CONC / AR-SAND as AutoCAD patterns, and the JIS A 0150 RC / stone / wood symbols. Reinforced concrete cannot be ANSI31 and AR-CONC in one HATCH, so it takes the one symbol that names it. Periods on a 1:50 sheet are recomputed from ezdxf's own pattern definitions by the tests. normalise_material is the one reading of a material name, shared by the model and wall_hatch, so the model never refuses "Reinforced Concrete" that the hatch table resolves. - engineering/arch/lang.py: EN / TR vocabulary, D/W and K/P tag prefixes, and the decimal comma for lang="tr" (24,50 m2). - draw_prims(role_layer=) and the XDATA codec's app_id= / kinds=: both generalised with the mechanical defaults, and the mechanical suite passes unchanged against them - that is the regression gate. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01FKFStjEFzpGyLq5bMpWxTd |