;;; DWGLAB_EN.lsp (English prompts, pure ASCII) - dwg-lab icon pack tools for AutoCAD 2013+ (plain AutoLISP: no ObjectARX, no .NET, no vl- functions).
;;; Should also run in BricsCAD and ZWCAD (untested there).
;;; GENERATED by kit/make_kit.py from iconpack/pack.py: edit kit/src/DWGLAB.lsp.in, not this file.
;;;
;;; Load: APPLOAD -> DWGLAB.lsp (add it to the Startup Suite to load it in every drawing).
;;; Commands:
;;;   DL              picture menu of every pack block (click one to place it)
;;;   DL_SOCKET       socket family   [1/2/Ip/32/Tv/Data/Usb]
;;;   DL_SWITCH       switch family   [1/2/Pass/Dimmer]
;;;   DL_LIGHT        light family    [Spot/Ceiling/Pendant/Wall/Track]
;;;   DL_WATER        plumbing family [Cold/Hot/Drain/Toilet/Floor/Machine]
;;;   DL_HEAT         heating family  [Radiator/Dryer/Ac/Vent/Thermostat]
;;;   DL_<BLOCK>      one block directly, e.g. DL_WATER_COLD, DL_SOCKET_TV, DL_RADIATOR, DL_DOOR, DL_WINDOW, DL_TILE_TAG
;;;   DL_ROOM         room outline on A-ROOM (draw or select) + ROOM_TAG (name, ceiling, floor)
;;;   DL_PROJECT      the PROJECT block (slab height), once per drawing
;;;   DL_CHECK        quick local check (missing H, points off the walls / outside rooms, duplicate IDs, tags, layers)
;;;   DL_FIXLAYERS    moves every pack block to its pack layer
;;;   DL_KIT          tell the tools where DWGLAB_ICONPACK.dwg is
;;;
;;; How a point is placed: pick it on the wall. The tool finds the nearest room outline (A-ROOM, within 300 mm), moves the
;;; point onto it and turns the symbol to face into that room. Then it asks H (Enter = the pack default or your last value).
;;; Doors and windows get the next free ID (D1, D2... / W1, W2...). Points (sockets, water...) have no ID in the drawing:
;;; the portal numbers them and keeps the numbers between versions.

;;; ------------------------------------------------------------------ data (generated)
;; layers: (name . ACI colour)
(setq *DL-LAYERS* '(("A-WALL" . 7) ("A-ROOM" . 140) ("A-DOOR" . 3) ("A-WINDOW" . 150) ("A-DEMO" . 1) ("A-NEW" . 4) ("E-POWER" . 1) ("E-LIGHT" . 6) ("E-LED" . 6) ("E-TRACK" . 6) ("P-WATER" . 5) ("M-HEAT" . 30) ("M-FLOORHEAT" . 30) ("C-CEILING" . 8) ("F-FLOOR" . 9) ("W-TILE" . 140) ("F-FURN" . 8)))
;; blocks: (name layer kind label attributes prompts H-reference)
;; kind: W wall point, C ceiling / floor point, R radiator, D door, N window, M room tag, P project, T zone / tile tag
(setq *DL-BLOCKS* '(
  ("SOCKET" "E-POWER" "W" "Socket, single" (("H" . "300") ("NOTE" . "")) ("H") "box centre")
  ("SOCKET_2" "E-POWER" "W" "Socket, double" (("H" . "300") ("NOTE" . "")) ("H") "box centre")
  ("SOCKET_IP44" "E-POWER" "W" "Socket, waterproof IP44" (("H" . "1100") ("NOTE" . "")) ("H") "box centre")
  ("SOCKET_32A" "E-POWER" "W" "Power socket (oven, hob) 32A" (("H" . "100") ("NOTE" . "")) ("H") "box centre")
  ("SOCKET_TV" "E-POWER" "W" "TV socket" (("H" . "1100") ("NOTE" . "")) ("H") "box centre")
  ("SOCKET_DATA" "E-POWER" "W" "Data / internet socket" (("H" . "300") ("NOTE" . "")) ("H") "box centre")
  ("SOCKET_USB" "E-POWER" "W" "Socket with USB" (("H" . "300") ("NOTE" . "")) ("H") "box centre")
  ("SWITCH_1" "E-POWER" "W" "Switch, 1 key" (("H" . "900") ("GROUP" . "a")) ("H" "GROUP") "box centre")
  ("SWITCH_2" "E-POWER" "W" "Switch, 2 keys" (("H" . "900") ("GROUP" . "a,b")) ("H" "GROUP") "box centre")
  ("SWITCH_PASS" "E-POWER" "W" "Two-way switch (pass-through)" (("H" . "900") ("GROUP" . "a")) ("H" "GROUP") "box centre")
  ("DIMMER" "E-POWER" "W" "Dimmer" (("H" . "900") ("GROUP" . "a")) ("H" "GROUP") "box centre")
  ("PANEL" "E-POWER" "W" "Electrical panel" (("H" . "1600") ("NOTE" . "")) ("H") "centre")
  ("THERMOSTAT" "M-HEAT" "W" "Thermostat (floor heating)" (("H" . "900") ("NOTE" . "")) ("H") "box centre")
  ("SPOT" "E-LIGHT" "C" "Recessed spot" (("GROUP" . "a") ("MODEL" . "")) ("GROUP") nil)
  ("CEILING_LIGHT" "E-LIGHT" "C" "Ceiling light" (("GROUP" . "a") ("MODEL" . "")) ("GROUP") nil)
  ("PENDANT" "E-LIGHT" "C" "Pendant" (("GROUP" . "a") ("MODEL" . "") ("DROP" . "700")) ("GROUP") nil)
  ("WALL_LIGHT" "E-LIGHT" "W" "Wall light (sconce)" (("H" . "1800") ("GROUP" . "a") ("MODEL" . "")) ("H" "GROUP") "box centre")
  ("TRACK_SPOT" "E-LIGHT" "C" "Track head (on E-TRACK rail)" (("GROUP" . "a") ("MODEL" . "")) ("GROUP") nil)
  ("WATER_COLD" "P-WATER" "W" "Cold water point" (("H" . "550") ("FOR" . "")) ("H") "pipe axis")
  ("WATER_HOT" "P-WATER" "W" "Hot water point" (("H" . "550") ("FOR" . "")) ("H") "pipe axis")
  ("DRAIN" "P-WATER" "W" "Drain / sewer outlet" (("H" . "450") ("D" . "50")) ("H") "pipe axis")
  ("WC_DRAIN" "P-WATER" "W" "Toilet outlet 110" (("H" . "200") ("D" . "110")) ("H") "pipe axis")
  ("FLOOR_DRAIN" "P-WATER" "C" "Floor drain (trap)" (("D" . "50")) () nil)
  ("WASHER" "P-WATER" "W" "Washing machine point" (("H" . "600")) ("H") "centre")
  ("RADIATOR" "M-HEAT" "R" "Radiator (stretch X to length)" (("H" . "150") ("HEIGHT" . "600") ("MODEL" . "")) ("H") "bottom edge")
  ("TOWEL_DRYER" "M-HEAT" "W" "Towel dryer" (("H" . "900") ("HEIGHT" . "800")) ("H") "centre")
  ("AC_INDOOR" "M-HEAT" "W" "AC indoor unit" (("H" . "2400") ("MODEL" . "")) ("H") "centre")
  ("VENT" "M-HEAT" "W" "Exhaust vent" (("H" . "2500") ("D" . "100")) ("H") "centre")
  ("DOOR" "A-DOOR" "D" "Door (scale = width/900)" (("ID" . "D1") ("H" . "2100") ("TYPE" . "")) ("H") nil)
  ("WINDOW" "A-WINDOW" "N" "Window (stretch X to length)" (("ID" . "W1") ("SILL" . "850") ("TOP" . "2350")) ("SILL" "TOP") nil)
  ("ROOM_TAG" "A-ROOM" "M" "Room tag: name, ceiling, floor" (("NAME" . "Room") ("CEIL" . "2700") ("FLOOR" . "")) ("NAME" "CEIL" "FLOOR") nil)
  ("CEIL_TAG" "C-CEILING" "T" "Ceiling zone level" (("LEVEL" . "2600")) ("LEVEL") nil)
  ("FLOOR_TAG" "F-FLOOR" "T" "Floor zone material" (("MATERIAL" . "")) ("MATERIAL") nil)
  ("TILE_TAG" "W-TILE" "T" "Wall tile: material, from / to height" (("MATERIAL" . "") ("FROM" . "0") ("H" . "2400")) ("MATERIAL" "FROM" "H") "top of the tiles")
  ("PROJECT" "A-ROOM" "P" "Project data, once per drawing" (("SLAB" . "2850") ("CLIENT" . "") ("ADDRESS" . "") ("DESIGNER" . "")) ("SLAB" "CLIENT" "ADDRESS" "DESIGNER") nil)
))
(setq *DL-TAGTXT* '(("H" . "H, mm above the finished floor") ("GROUP" . "GROUP (a light and its switch share a letter; SWITCH_2: a,b)") ("SILL" . "Sill, mm") ("TOP" . "Window top, mm") ("LEVEL" . "Ceiling level here, mm") ("MATERIAL" . "Material") ("FROM" . "Tiles from, mm") ("NAME" . "Room name") ("CEIL" . "Ceiling height, mm") ("FLOOR" . "Floor finish") ("SLAB" . "SLAB: finished floor to slab, mm") ("CLIENT" . "Client") ("ADDRESS" . "Address") ("DESIGNER" . "Designer")))
(setq *DL-MM-TAGS* '("H" "SILL" "TOP" "LEVEL" "FROM" "CEIL" "SLAB"))
;; picture menu icons: (name (x1 y1 x2 y2 colour) ...), 0..100, y up
(setq *DL-ICONS* '(
  ("SOCKET" (5 29 95 29 253) (50 29 50 45 1) (24 45 76 45 1) (76 45 75 52 1) (75 52 73 58 1) (73 58 69 63 1) (69 63 63 67 1) (63 67 57 70 1) (57 70 50 71 1) (50 71 43 70 1) (43 70 37 67 1) (37 67 31 63 1) (31 63 27 58 1) (27 58 25 52 1) (25 52 24 45 1) (47 29 53 29 1) (50 26 50 32 1))
  ("SOCKET_2" (5 29 95 29 253) (50 29 50 45 1) (24 45 76 45 1) (76 45 75 52 1) (75 52 73 58 1) (73 58 69 63 1) (69 63 63 67 1) (63 67 57 70 1) (57 70 50 71 1) (50 71 43 70 1) (43 70 37 67 1) (37 67 31 63 1) (31 63 27 58 1) (27 58 25 52 1) (25 52 24 45 1) (47 29 53 29 1) (50 26 50 32 1))
  ("SOCKET_IP44" (5 29 95 29 253) (50 29 50 45 1) (24 45 76 45 1) (76 45 75 52 1) (75 52 73 58 1) (73 58 69 63 1) (69 63 63 67 1) (63 67 57 70 1) (57 70 50 71 1) (50 71 43 70 1) (43 70 37 67 1) (37 67 31 63 1) (31 63 27 58 1) (27 58 25 52 1) (25 52 24 45 1) (47 29 53 29 1) (50 26 50 32 1))
  ("SOCKET_32A" (5 29 95 29 253) (50 29 50 45 1) (24 45 76 45 1) (76 45 75 52 1) (75 52 73 58 1) (73 58 69 63 1) (69 63 63 67 1) (63 67 57 70 1) (57 70 50 71 1) (50 71 43 70 1) (43 70 37 67 1) (37 67 31 63 1) (31 63 27 58 1) (27 58 25 52 1) (25 52 24 45 1) (47 29 53 29 1) (50 26 50 32 1))
  ("SOCKET_TV" (5 29 95 29 253) (50 29 50 45 1) (24 45 76 45 1) (76 45 75 52 1) (75 52 73 58 1) (73 58 69 63 1) (69 63 63 67 1) (63 67 57 70 1) (57 70 50 71 1) (50 71 43 70 1) (43 70 37 67 1) (37 67 31 63 1) (31 63 27 58 1) (27 58 25 52 1) (25 52 24 45 1) (47 29 53 29 1) (50 26 50 32 1))
  ("SOCKET_DATA" (5 29 95 29 253) (50 29 50 45 1) (24 45 76 45 1) (76 45 75 52 1) (75 52 73 58 1) (73 58 69 63 1) (69 63 63 67 1) (63 67 57 70 1) (57 70 50 71 1) (50 71 43 70 1) (43 70 37 67 1) (37 67 31 63 1) (31 63 27 58 1) (27 58 25 52 1) (25 52 24 45 1) (47 29 53 29 1) (50 26 50 32 1))
  ("SOCKET_USB" (5 29 95 29 253) (50 29 50 45 1) (24 45 76 45 1) (76 45 75 52 1) (75 52 73 58 1) (73 58 69 63 1) (69 63 63 67 1) (63 67 57 70 1) (57 70 50 71 1) (50 71 43 70 1) (43 70 37 67 1) (37 67 31 63 1) (31 63 27 58 1) (27 58 25 52 1) (25 52 24 45 1) (47 29 53 29 1) (50 26 50 32 1))
  ("SWITCH_1" (5 16 95 16 253) (50 16 50 37 1) (66 53 65 57 1) (65 57 64 61 1) (64 61 61 64 1) (61 64 58 66 1) (58 66 54 68 1) (54 68 50 68 1) (50 68 46 68 1) (46 68 42 66 1) (42 66 39 64 1) (39 64 36 61 1) (36 61 35 57 1) (35 57 34 53 1) (34 53 35 49 1) (35 49 36 45 1) (36 45 39 41 1) (39 41 42 39 1) (42 39 46 37 1) (46 37 50 37 1) (50 37 54 37 1) (54 37 58 39 1) (58 39 61 41 1) (61 41 64 45 1) (64 45 65 49 1) (65 49 66 53 1) (61 63 74 84 1) (47 16 53 16 1) (50 13 50 19 1))
  ("SWITCH_2" (5 16 95 16 253) (50 16 50 37 1) (66 53 65 57 1) (65 57 64 61 1) (64 61 61 64 1) (61 64 58 66 1) (58 66 54 68 1) (54 68 50 68 1) (50 68 46 68 1) (46 68 42 66 1) (42 66 39 64 1) (39 64 36 61 1) (36 61 35 57 1) (35 57 34 53 1) (34 53 35 49 1) (35 49 36 45 1) (36 45 39 41 1) (39 41 42 39 1) (42 39 46 37 1) (46 37 50 37 1) (50 37 54 37 1) (54 37 58 39 1) (58 39 61 41 1) (61 41 64 45 1) (64 45 65 49 1) (65 49 66 53 1) (61 63 74 84 1) (71 63 84 84 1) (47 16 53 16 1) (50 13 50 19 1))
  ("SWITCH_PASS" (5 16 95 16 253) (50 16 50 37 1) (66 53 65 57 1) (65 57 64 61 1) (64 61 61 64 1) (61 64 58 66 1) (58 66 54 68 1) (54 68 50 68 1) (50 68 46 68 1) (46 68 42 66 1) (42 66 39 64 1) (39 64 36 61 1) (36 61 35 57 1) (35 57 34 53 1) (34 53 35 49 1) (35 49 36 45 1) (36 45 39 41 1) (39 41 42 39 1) (42 39 46 37 1) (46 37 50 37 1) (50 37 54 37 1) (54 37 58 39 1) (58 39 61 41 1) (61 41 64 45 1) (64 45 65 49 1) (65 49 66 53 1) (61 63 74 84 1) (47 16 53 16 1) (50 13 50 19 1))
  ("DIMMER" (5 16 95 16 253) (50 16 50 37 1) (66 53 65 57 1) (65 57 64 61 1) (64 61 61 64 1) (61 64 58 66 1) (58 66 54 68 1) (54 68 50 68 1) (50 68 46 68 1) (46 68 42 66 1) (42 66 39 64 1) (39 64 36 61 1) (36 61 35 57 1) (35 57 34 53 1) (34 53 35 49 1) (35 49 36 45 1) (36 45 39 41 1) (39 41 42 39 1) (42 39 46 37 1) (46 37 50 37 1) (50 37 54 37 1) (54 37 58 39 1) (58 39 61 41 1) (61 41 64 45 1) (64 45 65 49 1) (65 49 66 53 1) (61 63 74 84 1) (47 16 53 16 1) (50 13 50 19 1))
  ("PANEL" (12 37 88 37 253) (5 37 95 37 1) (95 37 95 63 1) (95 63 5 63 1) (5 63 5 37 1) (47 37 53 37 1) (50 34 50 40 1))
  ("THERMOSTAT" (5 34 95 34 253) (34 34 66 34 30) (66 34 66 66 30) (66 66 34 66 30) (34 66 34 34 30) (47 34 53 34 1) (50 31 50 37 1))
  ("SPOT" (68 50 67 55 6) (67 55 65 59 6) (65 59 63 63 6) (63 63 59 65 6) (59 65 55 67 6) (55 67 50 68 6) (50 68 45 67 6) (45 67 41 65 6) (41 65 37 63 6) (37 63 35 59 6) (35 59 33 55 6) (33 55 32 50 6) (32 50 33 45 6) (33 45 35 41 6) (35 41 37 37 6) (37 37 41 35 6) (41 35 45 33 6) (45 33 50 32 6) (50 32 55 33 6) (55 33 59 35 6) (59 35 63 37 6) (63 37 65 41 6) (65 41 67 45 6) (67 45 68 50 6) (25 50 75 50 6) (50 25 50 75 6) (47 50 53 50 1) (50 47 50 53 1))
  ("CEILING_LIGHT" (95 50 93 62 6) (93 62 89 72 6) (89 72 82 82 6) (82 82 72 89 6) (72 89 62 93 6) (62 93 50 95 6) (50 95 38 93 6) (38 93 28 89 6) (28 89 18 82 6) (18 82 11 72 6) (11 72 7 62 6) (7 62 5 50 6) (5 50 7 38 6) (7 38 11 28 6) (11 28 18 18 6) (18 18 28 11 6) (28 11 38 7 6) (38 7 50 5 6) (50 5 62 7 6) (62 7 72 11 6) (72 11 82 18 6) (82 18 89 28 6) (89 28 93 38 6) (93 38 95 50 6) (72 50 72 56 6) (72 56 69 61 6) (69 61 66 66 6) (66 66 61 69 6) (61 69 56 72 6) (56 72 50 72 6) (50 72 44 72 6) (44 72 39 69 6) (39 69 34 66 6) (34 66 31 61 6) (31 61 28 56 6) (28 56 28 50 6) (28 50 28 44 6) (28 44 31 39 6) (31 39 34 34 6) (34 34 39 31 6) (39 31 44 28 6) (44 28 50 28 6) (50 28 56 28 6) (56 28 61 31 6) (61 31 66 34 6) (66 34 69 39 6) (69 39 72 44 6) (72 44 72 50 6) (47 50 53 50 1) (50 47 50 53 1))
  ("PENDANT" (95 50 93 62 6) (93 62 89 72 6) (89 72 82 82 6) (82 82 72 89 6) (72 89 62 93 6) (62 93 50 95 6) (50 95 38 93 6) (38 93 28 89 6) (28 89 18 82 6) (18 82 11 72 6) (11 72 7 62 6) (7 62 5 50 6) (5 50 7 38 6) (7 38 11 28 6) (11 28 18 18 6) (18 18 28 11 6) (28 11 38 7 6) (38 7 50 5 6) (50 5 62 7 6) (62 7 72 11 6) (72 11 82 18 6) (82 18 89 28 6) (89 28 93 38 6) (93 38 95 50 6) (63 63 82 82 6) (37 63 18 82 6) (37 37 18 18 6) (63 37 82 18 6) (47 50 53 50 1) (50 47 50 53 1))
  ("WALL_LIGHT" (5 34 95 34 253) (18 34 82 34 6) (82 34 80 42 6) (80 42 77 50 6) (77 50 72 57 6) (72 57 66 62 6) (66 62 58 65 6) (58 65 50 66 6) (50 66 42 65 6) (42 65 34 62 6) (34 62 28 57 6) (28 57 23 50 6) (23 50 20 42 6) (20 42 18 34 6) (50 34 50 66 6) (47 34 53 34 1) (50 31 50 37 1))
  ("TRACK_SPOT" (29 31 71 31 6) (71 31 50 69 6) (50 69 29 31 6) (47 46 53 46 1) (50 43 50 49 1))
  ("WATER_COLD" (5 29 95 29 253) (50 29 50 39 5) (66 55 65 59 5) (65 59 64 63 5) (64 63 61 66 5) (61 66 58 69 5) (58 69 54 70 5) (54 70 50 71 5) (50 71 46 70 5) (46 70 42 69 5) (42 69 39 66 5) (39 66 36 63 5) (36 63 35 59 5) (35 59 34 55 5) (34 55 35 51 5) (35 51 36 47 5) (36 47 39 44 5) (39 44 42 42 5) (42 42 46 40 5) (46 40 50 39 5) (50 39 54 40 5) (54 40 58 42 5) (58 42 61 44 5) (61 44 64 47 5) (64 47 65 51 5) (65 51 66 55 5) (47 29 53 29 1) (50 26 50 32 1))
  ("WATER_HOT" (5 29 95 29 253) (50 29 50 39 5) (66 55 65 59 5) (65 59 64 63 5) (64 63 61 66 5) (61 66 58 69 5) (58 69 54 70 5) (54 70 50 71 5) (50 71 46 70 5) (46 70 42 69 5) (42 69 39 66 5) (39 66 36 63 5) (36 63 35 59 5) (35 59 34 55 5) (34 55 35 51 5) (35 51 36 47 5) (36 47 39 44 5) (39 44 42 42 5) (42 42 46 40 5) (46 40 50 39 5) (50 39 54 40 5) (54 40 58 42 5) (58 42 61 44 5) (61 44 64 47 5) (64 47 65 51 5) (65 51 66 55 5) (47 29 53 29 1) (50 26 50 32 1))
  ("DRAIN" (5 28 95 28 253) (68 54 68 59 5) (68 59 66 63 5) (66 63 63 67 5) (63 67 59 70 5) (59 70 55 72 5) (55 72 50 72 5) (50 72 45 72 5) (45 72 41 70 5) (41 70 37 67 5) (37 67 34 63 5) (34 63 32 59 5) (32 59 32 54 5) (32 54 32 49 5) (32 49 34 45 5) (34 45 37 41 5) (37 41 41 38 5) (41 38 45 36 5) (45 36 50 36 5) (50 36 55 36 5) (55 36 59 38 5) (59 38 63 41 5) (63 41 66 45 5) (66 45 68 49 5) (68 49 68 54 5) (37 41 63 67 5) (37 67 63 41 5) (47 28 53 28 1) (50 25 50 31 1))
  ("WC_DRAIN" (5 24 95 24 253) (24 24 76 24 5) (76 24 76 76 5) (76 76 24 76 5) (24 76 24 24 5) (47 24 53 24 1) (50 21 50 27 1))
  ("FLOOR_DRAIN" (71 50 70 55 5) (70 55 68 60 5) (68 60 65 65 5) (65 65 60 68 5) (60 68 55 70 5) (55 70 50 71 5) (50 71 45 70 5) (45 70 40 68 5) (40 68 35 65 5) (35 65 32 60 5) (32 60 30 55 5) (30 55 29 50 5) (29 50 30 45 5) (30 45 32 40 5) (32 40 35 35 5) (35 35 40 32 5) (40 32 45 30 5) (45 30 50 29 5) (50 29 55 30 5) (55 30 60 32 5) (60 32 65 35 5) (65 35 68 40 5) (68 40 70 45 5) (70 45 71 50 5) (35 35 65 65 5) (35 65 65 35 5) (80 50 79 58 5) (79 58 76 65 5) (76 65 71 71 5) (71 71 65 76 5) (65 76 58 79 5) (58 79 50 80 5) (50 80 42 79 5) (42 79 35 76 5) (35 76 29 71 5) (29 71 24 65 5) (24 65 21 58 5) (21 58 20 50 5) (20 50 21 42 5) (21 42 24 35 5) (24 35 29 29 5) (29 29 35 24 5) (35 24 42 21 5) (42 21 50 20 5) (50 20 58 21 5) (58 21 65 24 5) (65 24 71 29 5) (71 29 76 35 5) (76 35 79 42 5) (79 42 80 50 5) (47 20 53 20 1) (50 17 50 23 1))
  ("WASHER" (5 24 95 24 253) (24 24 76 24 5) (76 24 76 76 5) (76 76 24 76 5) (24 76 24 24 5) (47 24 53 24 1) (50 21 50 27 1))
  ("RADIATOR" (35 46 65 46 253) (5 47 95 47 30) (95 47 95 54 30) (95 54 5 54 30) (5 54 5 47 30) (14 47 14 54 30) (23 47 23 54 30) (32 47 32 54 30) (41 47 41 54 30) (50 47 50 54 30) (59 47 59 54 30) (68 47 68 54 30) (77 47 77 54 30) (86 47 86 54 30) (47 46 53 46 1) (50 43 50 49 1))
  ("TOWEL_DRYER" (20 43 80 43 253) (5 43 95 43 30) (95 43 95 57 30) (95 57 5 57 30) (5 57 5 43 30) (47 43 53 43 1) (50 40 50 46 1))
  ("AC_INDOOR" (33 39 67 39 253) (5 39 95 39 30) (95 39 95 61 30) (95 61 5 61 30) (5 61 5 39 30) (47 39 53 39 1) (50 36 50 42 1))
  ("VENT" (5 21 95 21 253) (76 53 75 59 30) (75 59 73 66 30) (73 66 69 71 30) (69 71 63 75 30) (63 75 57 78 30) (57 78 50 79 30) (50 79 43 78 30) (43 78 37 75 30) (37 75 31 71 30) (31 71 27 66 30) (27 66 25 59 30) (25 59 24 53 30) (24 53 25 46 30) (25 46 27 39 30) (27 39 31 34 30) (31 34 37 30 30) (37 30 43 27 30) (43 27 50 26 30) (50 26 57 27 30) (57 27 63 30 30) (63 30 69 34 30) (69 34 73 39 30) (73 39 75 46 30) (75 46 76 53 30) (32 42 68 42 30) (32 53 68 53 30) (32 63 68 63 30) (47 21 53 21 1) (50 18 50 24 1))
  ("DOOR" (5 5 5 95 3) (95 5 92 28 3) (92 28 83 50 3) (83 50 68 68 3) (68 68 50 83 3) (50 83 28 92 3) (28 92 5 95 3) (5 5 95 5 3) (2 5 8 5 1) (5 2 5 8 1))
  ("WINDOW" (5 59 95 59 150) (5 41 95 41 150) (5 50 95 50 150) (5 59 5 41 150) (95 59 95 41 150) (2 59 8 59 1) (5 56 5 62 1))
  ("ROOM_TAG" (5 35 95 35 140) (95 35 95 65 140) (95 65 5 65 140) (5 65 5 35 140) (47 45 53 45 1) (50 42 50 48 1))
  ("CEIL_TAG" (5 35 95 35 250) (95 35 95 65 250) (95 65 5 65 250) (5 65 5 35 250) (47 45 53 45 1) (50 42 50 48 1))
  ("FLOOR_TAG" (5 35 95 35 9) (95 35 95 65 9) (95 65 5 65 9) (5 65 5 35 9) (47 45 53 45 1) (50 42 50 48 1))
  ("TILE_TAG" (5 35 95 35 140) (95 35 95 65 140) (95 65 5 65 140) (5 65 5 35 140) (47 45 53 45 1) (50 42 50 48 1))
  ("PROJECT" (5 38 95 38 140) (95 38 95 62 140) (95 62 5 62 140) (5 62 5 38 140) (47 46 53 46 1) (50 43 50 49 1))
))
(setq *DL-DCL* '(
  "dwglab : dialog {"
  "  label = \"dwg-lab icon pack: click a block\";"
  "  : boxed_column { label = \"Electrical\";"
  "    : row {"
  "      : column { : image_button { key = \"b_SOCKET\"; width = 11; height = 5; fixed_width = true; fixed_height = true; color = dialog_background; } : text { label = \"SOCKET\"; width = 14; alignment = centered; } }"
  "      : column { : image_button { key = \"b_SOCKET_2\"; width = 11; height = 5; fixed_width = true; fixed_height = true; color = dialog_background; } : text { label = \"SOCKET_2\"; width = 14; alignment = centered; } }"
  "      : column { : image_button { key = \"b_SOCKET_IP44\"; width = 11; height = 5; fixed_width = true; fixed_height = true; color = dialog_background; } : text { label = \"SOCKET_IP44\"; width = 14; alignment = centered; } }"
  "      : column { : image_button { key = \"b_SOCKET_32A\"; width = 11; height = 5; fixed_width = true; fixed_height = true; color = dialog_background; } : text { label = \"SOCKET_32A\"; width = 14; alignment = centered; } }"
  "      : column { : image_button { key = \"b_SOCKET_TV\"; width = 11; height = 5; fixed_width = true; fixed_height = true; color = dialog_background; } : text { label = \"SOCKET_TV\"; width = 14; alignment = centered; } }"
  "      : column { : image_button { key = \"b_SOCKET_DATA\"; width = 11; height = 5; fixed_width = true; fixed_height = true; color = dialog_background; } : text { label = \"SOCKET_DATA\"; width = 14; alignment = centered; } }"
  "      : column { : image_button { key = \"b_SOCKET_USB\"; width = 11; height = 5; fixed_width = true; fixed_height = true; color = dialog_background; } : text { label = \"SOCKET_USB\"; width = 14; alignment = centered; } }"
  "    }"
  "    : row {"
  "      : column { : image_button { key = \"b_SWITCH_1\"; width = 11; height = 5; fixed_width = true; fixed_height = true; color = dialog_background; } : text { label = \"SWITCH_1\"; width = 14; alignment = centered; } }"
  "      : column { : image_button { key = \"b_SWITCH_2\"; width = 11; height = 5; fixed_width = true; fixed_height = true; color = dialog_background; } : text { label = \"SWITCH_2\"; width = 14; alignment = centered; } }"
  "      : column { : image_button { key = \"b_SWITCH_PASS\"; width = 11; height = 5; fixed_width = true; fixed_height = true; color = dialog_background; } : text { label = \"SWITCH_PASS\"; width = 14; alignment = centered; } }"
  "      : column { : image_button { key = \"b_DIMMER\"; width = 11; height = 5; fixed_width = true; fixed_height = true; color = dialog_background; } : text { label = \"DIMMER\"; width = 14; alignment = centered; } }"
  "      : column { : image_button { key = \"b_PANEL\"; width = 11; height = 5; fixed_width = true; fixed_height = true; color = dialog_background; } : text { label = \"PANEL\"; width = 14; alignment = centered; } }"
  "      : column { : image_button { key = \"b_THERMOSTAT\"; width = 11; height = 5; fixed_width = true; fixed_height = true; color = dialog_background; } : text { label = \"THERMOSTAT\"; width = 14; alignment = centered; } }"
  "    }"
  "  }"
  "  : boxed_column { label = \"Lighting\";"
  "    : row {"
  "      : column { : image_button { key = \"b_SPOT\"; width = 11; height = 5; fixed_width = true; fixed_height = true; color = dialog_background; } : text { label = \"SPOT\"; width = 14; alignment = centered; } }"
  "      : column { : image_button { key = \"b_CEILING_LIGHT\"; width = 11; height = 5; fixed_width = true; fixed_height = true; color = dialog_background; } : text { label = \"CEILING_LIGHT\"; width = 14; alignment = centered; } }"
  "      : column { : image_button { key = \"b_PENDANT\"; width = 11; height = 5; fixed_width = true; fixed_height = true; color = dialog_background; } : text { label = \"PENDANT\"; width = 14; alignment = centered; } }"
  "      : column { : image_button { key = \"b_WALL_LIGHT\"; width = 11; height = 5; fixed_width = true; fixed_height = true; color = dialog_background; } : text { label = \"WALL_LIGHT\"; width = 14; alignment = centered; } }"
  "      : column { : image_button { key = \"b_TRACK_SPOT\"; width = 11; height = 5; fixed_width = true; fixed_height = true; color = dialog_background; } : text { label = \"TRACK_SPOT\"; width = 14; alignment = centered; } }"
  "    }"
  "  }"
  "  : boxed_column { label = \"Plumbing\";"
  "    : row {"
  "      : column { : image_button { key = \"b_WATER_COLD\"; width = 11; height = 5; fixed_width = true; fixed_height = true; color = dialog_background; } : text { label = \"WATER_COLD\"; width = 14; alignment = centered; } }"
  "      : column { : image_button { key = \"b_WATER_HOT\"; width = 11; height = 5; fixed_width = true; fixed_height = true; color = dialog_background; } : text { label = \"WATER_HOT\"; width = 14; alignment = centered; } }"
  "      : column { : image_button { key = \"b_DRAIN\"; width = 11; height = 5; fixed_width = true; fixed_height = true; color = dialog_background; } : text { label = \"DRAIN\"; width = 14; alignment = centered; } }"
  "      : column { : image_button { key = \"b_WC_DRAIN\"; width = 11; height = 5; fixed_width = true; fixed_height = true; color = dialog_background; } : text { label = \"WC_DRAIN\"; width = 14; alignment = centered; } }"
  "      : column { : image_button { key = \"b_FLOOR_DRAIN\"; width = 11; height = 5; fixed_width = true; fixed_height = true; color = dialog_background; } : text { label = \"FLOOR_DRAIN\"; width = 14; alignment = centered; } }"
  "      : column { : image_button { key = \"b_WASHER\"; width = 11; height = 5; fixed_width = true; fixed_height = true; color = dialog_background; } : text { label = \"WASHER\"; width = 14; alignment = centered; } }"
  "    }"
  "  }"
  "  : boxed_column { label = \"Heating & air\";"
  "    : row {"
  "      : column { : image_button { key = \"b_RADIATOR\"; width = 11; height = 5; fixed_width = true; fixed_height = true; color = dialog_background; } : text { label = \"RADIATOR\"; width = 14; alignment = centered; } }"
  "      : column { : image_button { key = \"b_TOWEL_DRYER\"; width = 11; height = 5; fixed_width = true; fixed_height = true; color = dialog_background; } : text { label = \"TOWEL_DRYER\"; width = 14; alignment = centered; } }"
  "      : column { : image_button { key = \"b_AC_INDOOR\"; width = 11; height = 5; fixed_width = true; fixed_height = true; color = dialog_background; } : text { label = \"AC_INDOOR\"; width = 14; alignment = centered; } }"
  "      : column { : image_button { key = \"b_VENT\"; width = 11; height = 5; fixed_width = true; fixed_height = true; color = dialog_background; } : text { label = \"VENT\"; width = 14; alignment = centered; } }"
  "    }"
  "  }"
  "  : boxed_column { label = \"Doors, windows, tags\";"
  "    : row {"
  "      : column { : image_button { key = \"b_DOOR\"; width = 11; height = 5; fixed_width = true; fixed_height = true; color = dialog_background; } : text { label = \"DOOR\"; width = 14; alignment = centered; } }"
  "      : column { : image_button { key = \"b_WINDOW\"; width = 11; height = 5; fixed_width = true; fixed_height = true; color = dialog_background; } : text { label = \"WINDOW\"; width = 14; alignment = centered; } }"
  "      : column { : image_button { key = \"b_ROOM_TAG\"; width = 11; height = 5; fixed_width = true; fixed_height = true; color = dialog_background; } : text { label = \"ROOM_TAG\"; width = 14; alignment = centered; } }"
  "      : column { : image_button { key = \"b_CEIL_TAG\"; width = 11; height = 5; fixed_width = true; fixed_height = true; color = dialog_background; } : text { label = \"CEIL_TAG\"; width = 14; alignment = centered; } }"
  "      : column { : image_button { key = \"b_FLOOR_TAG\"; width = 11; height = 5; fixed_width = true; fixed_height = true; color = dialog_background; } : text { label = \"FLOOR_TAG\"; width = 14; alignment = centered; } }"
  "      : column { : image_button { key = \"b_TILE_TAG\"; width = 11; height = 5; fixed_width = true; fixed_height = true; color = dialog_background; } : text { label = \"TILE_TAG\"; width = 14; alignment = centered; } }"
  "      : column { : image_button { key = \"b_PROJECT\"; width = 11; height = 5; fixed_width = true; fixed_height = true; color = dialog_background; } : text { label = \"PROJECT\"; width = 14; alignment = centered; } }"
  "    }"
  "  }"
  "  : row {"
  "    : button { key = \"c_check\"; label = \"DL_CHECK\"; width = 16; }"
  "    : button { key = \"c_fix\"; label = \"DL_FIXLAYERS\"; width = 16; }"
  "    cancel_button;"
  "  }"
  "}"
))

;;; ------------------------------------------------------------------ small helpers
(defun dl-msg (s) (princ (strcat "\n" s)) (princ))
(defun dl-b (name) (assoc (strcase name) *DL-BLOCKS*))
(defun dl-layer-of (name) (nth 1 (dl-b name)))
(defun dl-kind (name) (nth 2 (dl-b name)))
(defun dl-label (name) (nth 3 (dl-b name)))
(defun dl-attrs (name) (nth 4 (dl-b name)))
(defun dl-prompts (name) (nth 5 (dl-b name)))
(defun dl-href (name) (nth 6 (dl-b name)))

(defun dl-v- (a b) (list (- (car a) (car b)) (- (cadr a) (cadr b))))
(defun dl-v+ (a b) (list (+ (car a) (car b)) (+ (cadr a) (cadr b))))
(defun dl-vk (a k) (list (* k (car a)) (* k (cadr a))))
(defun dl-dot (a b) (+ (* (car a) (car b)) (* (cadr a) (cadr b))))
(defun dl-crs (a b) (- (* (car a) (cadr b)) (* (cadr a) (car b))))
(defun dl-len (a) (sqrt (dl-dot a a)))
(defun dl-2d (p) (list (car p) (cadr p)))
(defun dl-wcs (p) (dl-2d (trans p 1 0)))                          ; picked point (UCS) -> WCS
(defun dl-ucs (p) (trans (list (car p) (cadr p) 0.0) 0 1))         ; WCS -> UCS, for command input
(defun dl-xy (p) (strcat (rtos (car p) 2 0) "," (rtos (cadr p) 2 0)))
(defun dl-proj (p a b / ab)                                         ; P projected on the line A-B
  (setq ab (dl-v- b a))
  (dl-v+ a (dl-vk ab (/ (dl-dot (dl-v- p a) ab) (dl-dot ab ab)))))
;; a command is still waiting for input (bit 1 only: bit 4 = a script is running, set for the whole .scr)
(defun dl-busy () (= 1 (logand 1 (getvar "CMDACTIVE"))))
(defun dl-ring (pts) (append (cdr pts) (list (car pts))))           ; next vertex of each vertex

;;; ------------------------------------------------------------------ start / end / errors (sysvars restored, one UNDO step)
(setq *DL-VARS* '("CMDECHO" "ATTREQ" "ATTDIA" "CLAYER" "AUNITS" "ANGBASE" "ANGDIR" "INSUNITS"))
(defun dl-start ()
  (setq *dl-saved* (mapcar '(lambda (v) (cons v (getvar v))) *DL-VARS*))
  (setq *dl-olderr* *error* *error* dl-error)
  (setvar "CMDECHO" 0)
  (command "_.UNDO" "_BE")
  (setq *dl-undo* T))
(defun dl-restore ()
  (foreach p *dl-saved* (if (cdr p) (setvar (car p) (cdr p))))
  (if *dl-saved* (setq *error* *dl-olderr*))
  (setq *dl-saved* nil))
(defun dl-end ()
  (if *dl-undo* (command "_.UNDO" "_E"))
  (setq *dl-undo* nil)
  (dl-restore)
  (princ))
(defun dl-error (m)
  (if (not (wcmatch (strcase m) "*CANCEL*,*QUIT*,*EXIT*,*BREAK*"))
    (princ (strcat "\nDWGLAB: " m)))
  (if *dl-undo* (if command-s (command-s "_.UNDO" "_E") (command "_.UNDO" "_E")))  ; command-s: AutoCAD 2015+
  (setq *dl-undo* nil)
  (dl-restore)
  (princ))
(defun dl-paper-p ()
  (if (and (= 0 (getvar "TILEMODE")) (= 1 (getvar "CVPORT")))
    (progn (dl-msg "Switch to model space first.") T)))

;;; ------------------------------------------------------------------ kit: block definitions and layers
(defun dl-kit-path (/ f u)
  (cond ((and (setq f (getenv "DWGLAB_KIT")) (/= f "") (findfile f)) (findfile f))
        ((findfile "DWGLAB_ICONPACK.dwg"))
        ((and (setq u (getenv "USERPROFILE")) (findfile (strcat u "\\Downloads\\DWGLAB_ICONPACK.dwg"))))
        ((setq f (getfiled "DWGLAB_ICONPACK.dwg" "DWGLAB_ICONPACK.dwg" "dwg" 0)) (setenv "DWGLAB_KIT" f) f)))
;; Inserts the kit drawing as block DWGLAB_KIT and erases that insert: every pack block definition (and the pack
;; layers) comes in, nothing stays drawn. DWGLAB_KIT stays defined on purpose: it holds the pack definitions, so PURGE
;; does not remove a pack block you have not placed yet.
(defun dl-load-kit (/ f nm i e0 e)
  (if (setq f (dl-kit-path))
    (progn
      (dl-msg (strcat "Loading blocks from " f))
      (setq nm "DWGLAB_KIT" i 1)
      (while (tblsearch "BLOCK" nm) (setq i (1+ i) nm (strcat "DWGLAB_KIT" (itoa i))))  ; never the "redefine?" question
      ;; inserted at 0,0 and erased again: no cancel, a cancel would also stop a running script (.scr)
      (setvar "ATTREQ" 0)
      (setq e0 (entlast))
      (command "_.-INSERT" (strcat nm "=" f) "_non" '(0.0 0.0 0.0) "1" "1" "0")
      (while (dl-busy) (command ""))
      (setq e (entlast))
      (if (and e (not (eq e e0)) (= "INSERT" (cdr (assoc 0 (entget e)))) (= nm (strcase (cdr (assoc 2 (entget e)))))) (entdel e))
      T)
    (progn (dl-msg "DWGLAB_ICONPACK.dwg not found. Run DL_KIT.") nil)))
(defun dl-ensure (name)
  (if (not (tblsearch "BLOCK" name)) (dl-load-kit))
  (cond ((not (tblsearch "BLOCK" name))
         (dl-msg (strcat "Block " name " is not defined. Run DL_KIT.")) nil)
        ((not (dl-def-ok name))
         (dl-msg (strcat name ": this drawing has its own block with that name (no pack attributes). Rename yours.")) nil)
        (T)))
;; the definition carries every attribute the pack block has
(defun dl-def-ok (name / e d tags ok)
  (setq e (cdr (assoc -2 (tblsearch "BLOCK" name))))
  (while e
    (setq d (entget e))
    (if (= (cdr (assoc 0 d)) "ATTDEF") (setq tags (cons (strcase (cdr (assoc 2 d))) tags)))
    (setq e (entnext e)))
  (setq ok T)
  (foreach a (dl-attrs name) (if (not (member (car a) tags)) (setq ok nil)))
  ok)
;; the layer exists, is thawed, on and unlocked (the portal skips blocks on frozen / off layers)
(defun dl-layer (name / d c f)
  (if (tblsearch "LAYER" name)
    (progn
      (setq d (entget (tblobjname "LAYER" name)) f (cdr (assoc 70 d)) c (cdr (assoc 62 d)))
      (if (or (/= 0 (logand f 5)) (minusp c))
        (entmod (subst (cons 62 (abs c)) (assoc 62 d) (subst (cons 70 (logand f (~ 5))) (assoc 70 d) d)))))
    (entmake (list '(0 . "LAYER") '(100 . "AcDbSymbolTableRecord") '(100 . "AcDbLayerTableRecord") (cons 2 name) '(70 . 0)
                   (cons 62 (cond ((cdr (assoc name *DL-LAYERS*))) (7))) '(6 . "Continuous"))))
  name)

;;; ------------------------------------------------------------------ rooms (closed LWPOLYLINEs on A-ROOM, model space)
(defun dl-closed-p (e / d pts)
  (setq d (entget e))
  (or (= 1 (logand 1 (cdr (assoc 70 d))))
      (progn (foreach g d (if (= (car g) 10) (setq pts (cons (cdr g) pts))))
             (and (> (length pts) 2) (< (distance (car pts) (last pts)) 1.0)))))
(defun dl-plpts (e / d pts el)
  (setq d (entget e) el (cdr (assoc 38 d)))
  (if (not el) (setq el 0.0))
  (foreach g d (if (= (car g) 10) (setq pts (cons (dl-2d (trans (list (cadr g) (caddr g) el) e 0)) pts))))
  (setq pts (reverse pts))
  (if (and (> (length pts) 2) (< (distance (car pts) (last pts)) 1.0)) (setq pts (reverse (cdr (reverse pts)))))
  pts)
(defun dl-rooms (/ ss i e out)
  (if (setq ss (ssget "_X" '((0 . "LWPOLYLINE") (8 . "A-ROOM") (410 . "Model"))))
    (repeat (setq i (sslength ss))
      (setq e (ssname ss (setq i (1- i))))
      (if (dl-closed-p e) (setq out (cons (cons e (dl-plpts e)) out)))))
  out)
(defun dl-area2 (pts / s)
  (setq s 0.0)
  (mapcar '(lambda (a b) (setq s (+ s (dl-crs a b)))) pts (dl-ring pts))
  s)
(defun dl-inside (p pts / in)
  (mapcar '(lambda (a b)
             (if (and (/= (> (cadr a) (cadr p)) (> (cadr b) (cadr p)))
                      (< (car p) (+ (car a) (/ (* (- (cadr p) (cadr a)) (- (car b) (car a))) (- (cadr b) (cadr a))))))
               (setq in (not in))))
          pts (dl-ring pts))
  in)
(defun dl-centroid (pts / a cx cy k)
  (setq a 0.0 cx 0.0 cy 0.0)
  (mapcar '(lambda (p q) (setq k (dl-crs p q) a (+ a k) cx (+ cx (* k (+ (car p) (car q)))) cy (+ cy (* k (+ (cadr p) (cadr q))))))
          pts (dl-ring pts))
  (if (> (abs a) 1e-6)
    (list (/ cx (* 3.0 a)) (/ cy (* 3.0 a)))
    (car pts)))
(defun dl-room-at (p rooms / hit)
  (foreach r rooms (if (and (not hit) (dl-inside p (cdr r))) (setq hit r)))
  hit)
;; nearest room outline edge to P: (distance foot inward-normal room-ename edge-start edge-end) or nil
(defun dl-near (p rooms / best pts sg ab ln u foot d)
  (foreach r rooms
    (setq pts (cdr r) sg (if (> (dl-area2 pts) 0) 1.0 -1.0))
    (mapcar '(lambda (a b)
               (setq ab (dl-v- b a) ln (dl-len ab))
               (if (> ln 1e-6)
                 (progn
                   (setq u (max 0.0 (min 1.0 (/ (dl-dot (dl-v- p a) ab) (* ln ln))))
                         foot (dl-v+ a (dl-vk ab u))
                         d (distance p foot))
                   (if (or (not best) (< d (car best)))
                     (setq best (list d foot (dl-vk (list (- (cadr ab)) (car ab)) (/ sg ln)) (car r) a b))))))
            pts (dl-ring pts)))
  best)
;; drops vertices on a straight line (within 5 mm), like the portal does before it measures walls
(defun dl-clean (pts / out n i a b c ab)
  (setq n (length pts) i 0)
  (foreach b pts
    (setq a (nth (rem (+ i n -1) n) pts) c (nth (rem (1+ i) n) pts) ab (dl-v- c a))
    (if (or (< (dl-len ab) 1e-6) (> (/ (abs (dl-crs ab (dl-v- b a))) (dl-len ab)) 5.0)) (setq out (cons b out)))
    (setq i (1+ i)))
  (reverse out))

;;; ------------------------------------------------------------------ inserting
(defun dl-getatts (e / n d out)
  (setq n (entnext e))
  (while (and n (= "ATTRIB" (cdr (assoc 0 (setq d (entget n))))))
    (setq out (cons (cons (strcase (cdr (assoc 2 d))) (cdr (assoc 1 d))) out) n (entnext n)))
  (reverse out))
(defun dl-setatts (e atts / n d v)
  (setq n (entnext e))
  (while (and n (= "ATTRIB" (cdr (assoc 0 (setq d (entget n))))))
    (if (setq v (assoc (strcase (cdr (assoc 2 d))) atts))
      (entmod (subst (cons 1 (cdr v)) (assoc 1 d) d)))
    (setq n (entnext n)))
  (entupd e)
  e)
;; NAME at WCS point P, rotation ROT (radians, WCS), scale XS / YS, on the pack layer. Returns the INSERT or nil.
(defun dl-put (name p rot xs ys / e0 e)
  (setvar "CLAYER" (dl-layer (dl-layer-of name)))
;; INSUNITS 4 while inserting: pack blocks are mm, so the unit factor is 1 whatever this drawing / profile says
  (setvar "ATTREQ" 0) (setvar "ATTDIA" 0) (setvar "AUNITS" 0) (setvar "ANGBASE" 0.0) (setvar "ANGDIR" 0) (setvar "INSUNITS" 4)
  (setq e0 (entlast))
  (command "_.-INSERT" name "_X" xs "_Y" ys "_R" (angtos (- rot (angle '(0 0 0) (getvar "UCSXDIR"))) 0 6) "_non" (dl-ucs p))
  (while (dl-busy) (command ""))
  (setq e (entlast))
  (if (and e (not (eq e e0)) (= "INSERT" (cdr (assoc 0 (entget e))))) e))
(defun dl-next-id (name prefix / ss i v mx)
  (setq mx 0)
  (if (setq ss (ssget "_X" (list '(0 . "INSERT") (cons 2 name) '(410 . "Model"))))
    (repeat (setq i (sslength ss))
      (setq v (cdr (assoc "ID" (dl-getatts (ssname ss (setq i (1- i)))))))
      (if (and v (wcmatch (strcase v) (strcat prefix "#*")))
        (setq mx (max mx (atoi (substr v (1+ (strlen prefix)))))))))
  (strcat prefix (itoa (1+ mx))))

;;; ------------------------------------------------------------------ prompts with defaults (Enter = default)
(defun dl-def (name tag / v)
  (cond ((setq v (assoc (strcat name "." tag) *dl-last*)) (cdr v))
        ((cdr (assoc tag (dl-attrs name))))
        ("")))
(defun dl-remember (name tag v) (setq *dl-last* (cons (cons (strcat name "." tag) v) *dl-last*)))
(defun dl-ask-int (msg def / v)
  (initget 4)
  (setq v (getint (strcat "\n" msg " <" def ">: ")))
  (if v (itoa v) def))
(defun dl-ask-str (msg def / v)
  (setq v (getstring T (strcat "\n" msg " <" def ">: ")))
  (if (= v "") def v))
(defun dl-tag-text (name tag / s)
  (setq s (cond ((cdr (assoc tag *DL-TAGTXT*))) (tag)))
  (if (and (= tag "H") (dl-href name)) (strcat s " (" (dl-href name) ")") s))
(defun dl-ask-atts (name / out v)
  (foreach tg (dl-prompts name)
    (setq v (if (member tg *DL-MM-TAGS*)
              (dl-ask-int (dl-tag-text name tg) (dl-def name tg))
              (dl-ask-str (dl-tag-text name tg) (dl-def name tg))))
    (dl-remember name tg v)
    (setq out (cons (cons tg v) out)))
  (reverse out))
(defun dl-atts-text (atts / s)
  (setq s "")
  (foreach a atts (setq s (strcat s " " (car a) "=" (cdr a))))
  s)

;;; ------------------------------------------------------------------ placing: wall points, ceiling points, doors, windows
(setq *DL-NOROOM* "No room outline (A-ROOM) within 300 mm: draw the rooms first (DL_ROOM).")
;; P (WCS) on a wall -> (insertion rotation): on the nearest room outline, facing into that room
(defun dl-wall-place (p / nr q)
  (setq nr (dl-near p (dl-rooms)))
  (if (and nr (<= (car nr) 300.0))
    (progn
      (if (> (car nr) 1.0)
        (dl-msg (strcat "moved " (rtos (car nr) 2 0) " mm onto the room outline")))
      (list (cadr nr) (- (angle '(0 0) (caddr nr)) (/ pi 2))))
    (progn
      (dl-msg *DL-NOROOM*)
      (if (setq q (getpoint (dl-ucs p) "\nPick a point inside the room (the symbol faces it): "))
        (list p (- (angle p (dl-wcs q)) (/ pi 2)))))))
(defun dl-deflen () (if *dl-radlen* *dl-radlen* 1000.0))
(defun dl-loop-wall (name / p pl e ln)
  (dl-msg (strcat (dl-label name) "  [" name "]"))
  (while (setq p (getpoint "\nPoint on the wall face, Enter = done: "))
    (if (setq pl (dl-wall-place (dl-wcs p)))
      (progn
        (setq ln 1000.0)
        (if (= (dl-kind name) "R")
          (progn
            (initget 6)
            (setq ln (cond ((getdist (dl-ucs (car pl)) (strcat "\nRadiator length, mm <" (rtos (dl-deflen) 2 0) ">: ")))
                           ((dl-deflen)))
                  *dl-radlen* ln)))
        (if (setq e (dl-put name (car pl) (cadr pl) (/ ln 1000.0) 1.0))
          (dl-msg (strcat name (dl-atts-text (dl-getatts (dl-setatts e (dl-ask-atts name)))))))))))
(defun dl-loop-free (name / p e)
  (dl-msg (strcat (dl-label name) "  [" name "]"))
  (while (setq p (getpoint "\nInsertion point, Enter = done: "))
    (setq p (dl-wcs p))
    (if (and (/= (dl-kind name) "T") (not (dl-room-at p (dl-rooms))))
      (dl-msg "Warning: not inside a room outline (A-ROOM)."))
    (if (setq e (dl-put name p 0.0 1.0 1.0))
      (dl-msg (strcat name (dl-atts-text (dl-getatts (dl-setatts e (dl-ask-atts name)))))))))
;; DOOR: insertion = hinge; the opening runs along block +Y (the straight leaf line), width = 900 x scale; the leaf swings to +X
(defun dl-loop-door (/ p1 p2 p3 nr ln s xs id e)
  (dl-msg (dl-label "DOOR"))
  (while (setq p1 (getpoint "\nDoor: hinge side of the opening, on the wall face, Enter = done: "))
    (setq p1 (dl-wcs p1))
    (if (setq p2 (getpoint (dl-ucs p1) "\nOther side of the opening: "))
      (progn
        (setq p2 (dl-wcs p2) nr (dl-near p1 (dl-rooms)))
        (if (and nr (<= (car nr) 300.0))
          (setq p1 (dl-proj p1 (nth 4 nr) (nth 5 nr)) p2 (dl-proj p2 (nth 4 nr) (nth 5 nr)))
          (dl-msg *DL-NOROOM*))
        (setq ln (distance p1 p2))
        (if (< ln 300.0)
          (dl-msg "Opening under 300 mm: skipped.")
          (progn
            (setq s (/ ln 900.0) xs s)
            (setq p3 (getpoint (dl-ucs p1) "\nPick a point on the side the door opens to <right>: "))
            (if (and p3 (> (dl-crs (dl-v- p2 p1) (dl-v- (dl-wcs p3) p1)) 0)) (setq xs (- s)))
            (setq id (dl-next-id "DOOR" "D"))
            (if (setq e (dl-put "DOOR" p1 (- (angle p1 p2) (/ pi 2)) xs s))
              (dl-msg (strcat id " " (rtos ln 2 0) " mm" (dl-atts-text (dl-getatts (dl-setatts e (cons (cons "ID" id) (dl-ask-atts "DOOR"))))))))))))))
;; WINDOW: insertion = one end on the room-side face; runs along block +X (length = 1000 x X scale); the room is on +Y
(defun dl-loop-window (/ p1 p2 q nr n dir ux s ins ln id e)
  (dl-msg (dl-label "WINDOW"))
  (while (setq p1 (getpoint "\nWindow: one end, on the room side wall face, Enter = done: "))
    (setq p1 (dl-wcs p1) nr (dl-near p1 (dl-rooms)) n nil)
    (if (setq p2 (getpoint (dl-ucs p1) "\nOther end: "))
      (progn
        (setq p2 (dl-wcs p2))
        (if (and nr (<= (car nr) 300.0))
          (setq n (caddr nr) p1 (dl-proj p1 (nth 4 nr) (nth 5 nr)))
          (progn
            (dl-msg *DL-NOROOM*)
            (if (and (> (distance p1 p2) 1.0)
                     (setq q (getpoint (dl-ucs p1) "\nPick a point inside the room: ")))
              (progn
                (setq dir (dl-vk (dl-v- p2 p1) (/ 1.0 (distance p1 p2))) n (list (- (cadr dir)) (car dir)))
                (if (< (dl-dot (dl-v- (dl-wcs q) p1) n) 0) (setq n (dl-vk n -1.0)))))))
        (if n
          (progn
            (setq ux (list (cadr n) (- (car n))) s (dl-dot (dl-v- p2 p1) ux))
            (if (< s 0) (setq ins (dl-v+ p1 (dl-vk ux s)) ln (- s)) (setq ins p1 ln s))
            (if (< ln 200.0)
              (dl-msg "Window under 200 mm: skipped.")
              (progn
                (setq id (dl-next-id "WINDOW" "W"))
                (if (setq e (dl-put "WINDOW" ins (angle '(0 0) ux) (/ ln 1000.0) 1.0))
                  (dl-msg (strcat id " " (rtos ln 2 0) " mm" (dl-atts-text (dl-getatts (dl-setatts e (cons (cons "ID" id) (dl-ask-atts "WINDOW"))))))))))))))))

;;; ------------------------------------------------------------------ rooms and the project block
(defun dl-tags-in (pts / ss i e out)
  (if (setq ss (ssget "_X" '((0 . "INSERT") (2 . "ROOM_TAG") (410 . "Model"))))
    (repeat (setq i (sslength ss))
      (setq e (ssname ss (setq i (1- i))))
      (if (dl-inside (dl-2d (trans (cdr (assoc 10 (entget e))) e 0)) pts) (setq out (cons e out)))))
  out)
(defun dl-room (/ k e e0 d pts c tp tags atts)
  (initget "Draw Select")
  (setq k (getkword "\nRoom outline [Draw/Select] <Draw>: "))
  (if (= k "Select")
    (progn
      (setq e (car (entsel "\nSelect the room polyline: ")))
      (if (and e (/= (cdr (assoc 0 (entget e))) "LWPOLYLINE"))
        (progn (dl-msg "Not a polyline (LWPOLYLINE).") (setq e nil))))
    (progn
      (setvar "CLAYER" (dl-layer "A-ROOM"))
      (setq e0 (entlast))
      (dl-msg "Trace the room along the finished wall faces, C = close.")
      (command "_.PLINE")
      (while (dl-busy) (command pause))
      (setq e (entlast))
      (if (or (not e) (eq e e0) (/= (cdr (assoc 0 (entget e))) "LWPOLYLINE")) (setq e nil))))
  (if e
    (progn
      (dl-layer "A-ROOM")
      (setq d (entget e))
      (setq d (subst (cons 8 "A-ROOM") (assoc 8 d) d))
      (setq d (subst (cons 70 (logior 1 (cdr (assoc 70 d)))) (assoc 70 d) d))
      (entmod d)
      (setq pts (dl-plpts e))
      (if (< (length pts) 3)
        (dl-msg "An outline needs 3 or more corners.")
        (if (setq tags (dl-tags-in pts))
          (dl-msg (strcat "This room already has a ROOM_TAG: " (cdr (assoc "NAME" (dl-getatts (car tags))))))
          (progn
            (setq c (dl-centroid pts))
            (if (not (dl-inside c pts)) (setq c nil))
            (setq tp (getpoint (strcat "\nRoom tag position" (if c " <centre>" "") ": ")))
            (setq tp (cond (tp (dl-wcs tp)) (c)))
            (if (not tp)
              (dl-msg "No tag position: run DL_ROOM again and pick it.")
              (if (setq e (dl-put "ROOM_TAG" tp 0.0 1.0 1.0))
                (progn
                  (setq atts (dl-ask-atts "ROOM_TAG"))
                  (dl-setatts e atts)
                  (dl-msg (strcat "ROOM" (dl-atts-text atts) ", " (rtos (/ (abs (dl-area2 pts)) 2e6) 2 1) " m2"))))))))))
  (princ))
(defun dl-project (/ ss p e)
  (if (setq ss (ssget "_X" '((0 . "INSERT") (2 . "PROJECT") (410 . "Model"))))
    (dl-msg (strcat "PROJECT is already in the drawing:" (dl-atts-text (dl-getatts (ssname ss 0)))
                    "  (double-click it to edit)"))
    (if (setq p (getpoint "\nPROJECT block position (outside the rooms): "))
      (if (setq e (dl-put "PROJECT" (dl-wcs p) 0.0 1.0 1.0))
        (dl-msg (strcat "PROJECT" (dl-atts-text (dl-getatts (dl-setatts e (dl-ask-atts "PROJECT"))))))))))

;;; ------------------------------------------------------------------ dispatch and commands
(defun dl-run (name / k)
  (setq name (strcase name) k (dl-kind name))
  (cond ((not (dl-b name)) (dl-msg (strcat "Not a pack block: " name)))
        ((dl-paper-p))
        (T
         (dl-start)
         (if (dl-ensure name)
           (cond ((member k '("W" "R")) (dl-loop-wall name))
                 ((member k '("C" "T")) (dl-loop-free name))
                 ((= k "D") (dl-loop-door))
                 ((= k "N") (dl-loop-window))
                 ((= k "M") (dl-room))
                 ((= k "P") (dl-project))))
         (dl-end)))
  (princ))
(defun dl-family (title kws pairs / k)
  (initget kws)
  (setq k (getkword (strcat "\n" title " [" (dl-slash kws) "] <" (car (car pairs)) ">: ")))
  (dl-run (cdr (assoc (if k k (car (car pairs))) pairs))))
(defun dl-slash (s / out i ch)
  (setq out "" i 1)
  (while (<= i (strlen s))
    (setq ch (substr s i 1) out (strcat out (if (= ch " ") "/" ch)) i (1+ i)))
  out)
(defun c:DL_SOCKET () (dl-family "Socket" "1 2 Ip 32 Tv Data Usb"
  '(("1" . "SOCKET") ("2" . "SOCKET_2") ("Ip" . "SOCKET_IP44") ("32" . "SOCKET_32A") ("Tv" . "SOCKET_TV") ("Data" . "SOCKET_DATA") ("Usb" . "SOCKET_USB"))))
(defun c:DL_SWITCH () (dl-family "Switch" "1 2 Pass Dimmer"
  '(("1" . "SWITCH_1") ("2" . "SWITCH_2") ("Pass" . "SWITCH_PASS") ("Dimmer" . "DIMMER"))))
(defun c:DL_LIGHT () (dl-family "Light" "Spot Ceiling Pendant Wall Track"
  '(("Spot" . "SPOT") ("Ceiling" . "CEILING_LIGHT") ("Pendant" . "PENDANT") ("Wall" . "WALL_LIGHT") ("Track" . "TRACK_SPOT"))))
(defun c:DL_WATER () (dl-family "Plumbing" "Cold Hot Drain Toilet Floor Machine"
  '(("Cold" . "WATER_COLD") ("Hot" . "WATER_HOT") ("Drain" . "DRAIN") ("Toilet" . "WC_DRAIN") ("Floor" . "FLOOR_DRAIN") ("Machine" . "WASHER"))))
(defun c:DL_HEAT () (dl-family "Heating, air" "Radiator Dryer Ac Vent Thermostat"
  '(("Radiator" . "RADIATOR") ("Dryer" . "TOWEL_DRYER") ("Ac" . "AC_INDOOR") ("Vent" . "VENT") ("Thermostat" . "THERMOSTAT"))))
(defun c:DL_ROOM () (dl-run "ROOM_TAG"))
(defun c:DL_PROJECT () (dl-run "PROJECT"))
(defun c:DL_SOCKET_2 () (dl-run "SOCKET_2"))
(defun c:DL_SOCKET_IP44 () (dl-run "SOCKET_IP44"))
(defun c:DL_SOCKET_32A () (dl-run "SOCKET_32A"))
(defun c:DL_SOCKET_TV () (dl-run "SOCKET_TV"))
(defun c:DL_SOCKET_DATA () (dl-run "SOCKET_DATA"))
(defun c:DL_SOCKET_USB () (dl-run "SOCKET_USB"))
(defun c:DL_SWITCH_1 () (dl-run "SWITCH_1"))
(defun c:DL_SWITCH_2 () (dl-run "SWITCH_2"))
(defun c:DL_SWITCH_PASS () (dl-run "SWITCH_PASS"))
(defun c:DL_DIMMER () (dl-run "DIMMER"))
(defun c:DL_PANEL () (dl-run "PANEL"))
(defun c:DL_THERMOSTAT () (dl-run "THERMOSTAT"))
(defun c:DL_SPOT () (dl-run "SPOT"))
(defun c:DL_CEILING_LIGHT () (dl-run "CEILING_LIGHT"))
(defun c:DL_PENDANT () (dl-run "PENDANT"))
(defun c:DL_WALL_LIGHT () (dl-run "WALL_LIGHT"))
(defun c:DL_TRACK_SPOT () (dl-run "TRACK_SPOT"))
(defun c:DL_WATER_COLD () (dl-run "WATER_COLD"))
(defun c:DL_WATER_HOT () (dl-run "WATER_HOT"))
(defun c:DL_DRAIN () (dl-run "DRAIN"))
(defun c:DL_WC_DRAIN () (dl-run "WC_DRAIN"))
(defun c:DL_FLOOR_DRAIN () (dl-run "FLOOR_DRAIN"))
(defun c:DL_WASHER () (dl-run "WASHER"))
(defun c:DL_RADIATOR () (dl-run "RADIATOR"))
(defun c:DL_TOWEL_DRYER () (dl-run "TOWEL_DRYER"))
(defun c:DL_AC_INDOOR () (dl-run "AC_INDOOR"))
(defun c:DL_VENT () (dl-run "VENT"))
(defun c:DL_DOOR () (dl-run "DOOR"))
(defun c:DL_WINDOW () (dl-run "WINDOW"))
(defun c:DL_ROOM_TAG () (dl-run "ROOM_TAG"))
(defun c:DL_CEIL_TAG () (dl-run "CEIL_TAG"))
(defun c:DL_FLOOR_TAG () (dl-run "FLOOR_TAG"))
(defun c:DL_TILE_TAG () (dl-run "TILE_TAG"))
(defun c:DL_PROJECT () (dl-run "PROJECT"))
(defun c:DL_KIT (/ f)
  (if (setq f (getfiled "DWGLAB_ICONPACK.dwg" (cond ((dl-kit-path)) ("DWGLAB_ICONPACK.dwg")) "dwg" 0))
    (progn (setenv "DWGLAB_KIT" f) (dl-start) (dl-load-kit) (dl-end)))
  (princ))

;;; ------------------------------------------------------------------ DL_FIXLAYERS
(defun dl-pack-inserts (/ ss i e out)
  (if (setq ss (ssget "_X" '((0 . "INSERT") (410 . "Model"))))
    (repeat (setq i (sslength ss))
      (setq e (ssname ss (setq i (1- i))))
      (if (dl-b (cdr (assoc 2 (entget e)))) (setq out (cons e out)))))
  out)
(defun c:DL_FIXLAYERS (/ n d lay a ad)
  (setq n 0)
  (foreach e (dl-pack-inserts)
    (setq d (entget e) lay (dl-layer-of (cdr (assoc 2 d))))
    (if (/= (strcase (cdr (assoc 8 d))) lay)
      (progn
        (dl-layer lay)
        (entmod (subst (cons 8 lay) (assoc 8 d) d))
        (setq a (entnext e))
        (while (and a (= "ATTRIB" (cdr (assoc 0 (setq ad (entget a))))))
          (entmod (subst (cons 8 lay) (assoc 8 ad) ad))
          (setq a (entnext a)))
        (entupd e)
        (setq n (1+ n)))))
  (dl-msg (strcat (itoa n) " block(s) moved to their pack layer."))
  (princ))

;;; ------------------------------------------------------------------ DL_CHECK: a quick local version of the portal check
(defun dl-e (e p msg)
  (setq *dl-ne* (1+ *dl-ne*))
  (if e (ssadd e *dl-bad*))
  (princ (strcat "\n  ERROR  " msg (if p (strcat "  @" (dl-xy p)) ""))))
(defun dl-w (e p msg)
  (setq *dl-nw* (1+ *dl-nw*))
  (if e (ssadd e *dl-bad*))
  (princ (strcat "\n  warning  " msg (if p (strcat "  @" (dl-xy p)) ""))))
(defun dl-num-p (v) (and v (/= v "") (distof v 2)))
(defun dl-ins-xy (e) (dl-2d (trans (cdr (assoc 10 (entget e))) e 0)))
;; where the symbol points (block +Y on the plan, mirroring included), WCS unit vector
(defun dl-ins-uy (e / d r sy v)
  (setq d (entget e) r (cdr (assoc 50 d)) sy (cond ((cdr (assoc 42 d))) (1.0)))
  (setq v (dl-2d (trans (list (* (- (sin r)) (if (minusp sy) -1.0 1.0)) (* (cos r) (if (minusp sy) -1.0 1.0)) 0.0) e 0 T)))
  (dl-vk v (/ 1.0 (dl-len v))))
(defun c:DL_CHECK (/ rooms ss i e d name k p a nr ids seen cnt lay ld pts nproj rr sx sy)
  (setq *dl-ne* 0 *dl-nw* 0 *dl-bad* (ssadd))
  (dl-msg "==== DWGLAB check (quick, local; the portal check is the full one) ====")
  (if (not (member (getvar "INSUNITS") '(0 4)))
    (dl-e nil nil "Drawing units are not mm (UNITS / INSUNITS)."))
  ;; room outlines
  (if (setq ss (ssget "_X" '((0 . "LWPOLYLINE") (8 . "A-ROOM") (410 . "Model"))))
    (repeat (setq i (sslength ss))
      (setq e (ssname ss (setq i (1- i))))
      (if (not (dl-closed-p e)) (dl-e e (car (dl-plpts e)) "A-ROOM polyline is not closed."))))
  (setq rooms (dl-rooms))
  (if (not rooms) (dl-e nil nil "No room outlines (closed polylines on A-ROOM). Use DL_ROOM."))
  (foreach r rooms
    (setq pts (dl-clean (cdr r)))
    (mapcar '(lambda (a b) (if (< (distance a b) 100.0)
                             (dl-e (car r) a (strcat "Room outline edge " (rtos (distance a b) 2 0) " mm: under 100 mm, simplify the outline."))))
            pts (dl-ring pts)))
  ;; room tags: exactly one per room
  (setq cnt (mapcar '(lambda (r) (cons (car r) 0)) rooms))
  (foreach e (dl-pack-inserts)
    (if (= (strcase (cdr (assoc 2 (entget e)))) "ROOM_TAG")
      (progn
        (setq p (dl-ins-xy e) rr (dl-room-at p rooms) a (dl-getatts e))
        (if rr
          (setq cnt (subst (cons (car rr) (1+ (cdr (assoc (car rr) cnt)))) (assoc (car rr) cnt) cnt))
          (dl-e e p (strcat "ROOM_TAG '" (cdr (assoc "NAME" a)) "' is not inside a room outline.")))
        (if (not (dl-num-p (cdr (assoc "CEIL" a))))
          (dl-e e p (strcat "ROOM_TAG '" (cdr (assoc "NAME" a)) "': CEIL (ceiling height, mm) is empty or not a number."))))))
  (foreach c cnt
    (cond ((= 0 (cdr c)) (dl-e (car c) (dl-centroid (dl-plpts (car c))) "Room without ROOM_TAG."))
          ((> (cdr c) 1) (dl-e (car c) (dl-centroid (dl-plpts (car c))) "Room with 2+ ROOM_TAGs."))))
  ;; every pack block
  (setq nproj 0)
  (foreach e (dl-pack-inserts)
    (setq d (entget e) name (strcase (cdr (assoc 2 d))) k (dl-kind name) p (dl-ins-xy e) a (dl-getatts e))
    (setq lay (strcase (cdr (assoc 8 d))) ld (tblsearch "LAYER" lay))
    (if (/= lay (dl-layer-of name))
      (dl-w e p (strcat name " is on layer " lay ", should be " (dl-layer-of name) " (DL_FIXLAYERS)")))
    (if (and ld (or (= 1 (logand 1 (cdr (assoc 70 ld)))) (minusp (cdr (assoc 62 ld)))))
      (dl-w e p (strcat name " is on a frozen / off layer: the portal skips it.")))
    ;; scale: X stretches RADIATOR / WINDOW, DOOR is width/900 both ways; anything else is 1 (25.4 = inch block units)
    (setq sx (abs (cond ((cdr (assoc 41 d))) (1.0))) sy (abs (cond ((cdr (assoc 42 d))) (1.0))))
    (if (cond ((= k "D") (> (abs (- sx sy)) 1e-6))
              ((member k '("R" "N")) (> (abs (- sy 1.0)) 1e-6))
              (T (or (> (abs (- sx 1.0)) 1e-6) (> (abs (- sy 1.0)) 1e-6))))
      (dl-w e p (strcat name " is scaled X " (rtos sx 2 2) " Y " (rtos sy 2 2)
                        ": insert it with DL_* or set the scale to 1.")))
    (if (assoc "H" (dl-attrs name))
      (cond ((or (not (assoc "H" a)) (= "" (cdr (assoc "H" a)))) (dl-e e p (strcat name " has no H.")))
            ((not (dl-num-p (cdr (assoc "H" a)))) (dl-e e p (strcat name " H='" (cdr (assoc "H" a)) "' is not a number.")))))
    (cond ((member k '("W" "R"))
           (setq nr (dl-near p rooms))
           (cond ((or (not nr) (> (car nr) 100.0))
                  (dl-e e p (strcat name " is not on a room outline (more than 100 mm off).")))
                 ((< (dl-dot (dl-ins-uy e) (caddr nr)) 0.7)
                  (dl-e e p (strcat name " faces out of the room (rotate it to point into the room).")))))
          ((= k "C")
           (if (not (dl-room-at p rooms)) (dl-e e p (strcat name " is outside every room."))))
          ((member k '("D" "N"))
           (setq ids (cons (strcat name ":" (strcase (cond ((cdr (assoc "ID" a))) ("")))) ids)))
          ((= k "P")
           (setq nproj (1+ nproj))
           (if (not (dl-num-p (cdr (assoc "SLAB" a)))) (dl-e e p "PROJECT: SLAB (floor to slab, mm) is empty.")))))
  (if (= nproj 0) (dl-e nil nil "No PROJECT block (slab height). Use DL_PROJECT."))
  (if (> nproj 1) (dl-w nil nil "More than one PROJECT block."))
  (setq seen nil)
  (foreach i ids
    (if (and (not (member i seen)) (member i (cdr (member i ids))))
      (dl-w nil nil (strcat "Duplicate ID " i)))
    (setq seen (cons i seen)))
  ;; a block with a pack name but not the pack definition
  (foreach b *DL-BLOCKS*
    (if (and (tblsearch "BLOCK" (car b)) (not (dl-def-ok (car b))))
      (dl-e nil nil (strcat (car b) ": the block definition is not the pack's (attributes missing)."))))
  (dl-msg (strcat "==== " (itoa *dl-ne*) " error(s), " (itoa *dl-nw*) " warning(s)"
                  (if (> (sslength *dl-bad*) 0) (strcat ". The blocks concerned are selected.") "")))
  (if (> (sslength *dl-bad*) 0) (sssetfirst nil *dl-bad*))
  (princ))

;;; ------------------------------------------------------------------ DL: picture menu (DCL written to the temp folder)
(defun dl-icon (key segs / w h s ox oy)
  (setq w (dimx_tile key) h (dimy_tile key) s (/ (min w h) 100.0) ox (/ (- w (* s 100)) 2.0) oy (/ (- h (* s 100)) 2.0))
  (start_image key)
  (fill_image 0 0 w h -15)
  (foreach g segs  ; (x1 y1 x2 y2 colour), 0..100, y up
    (vector_image (fix (+ ox (* s (nth 0 g)))) (fix (- h oy (* s (nth 1 g))))
                  (fix (+ ox (* s (nth 2 g)))) (fix (- h oy (* s (nth 3 g)))) (nth 4 g)))
  (end_image))
(defun c:DL (/ f h id r)
  (setq f (strcat (getvar "TEMPPREFIX") "dwglab_menu.dcl") h (open f "w"))
  (foreach l *DL-DCL* (write-line l h))
  (close h)
  (setq id (load_dialog f) *dl-pick* nil)
  (if (and (> id 0) (new_dialog "dwglab" id))
    (progn
      (foreach b *DL-ICONS* (dl-icon (strcat "b_" (car b)) (cdr b)))
      (foreach b *DL-BLOCKS* (action_tile (strcat "b_" (car b)) (strcat "(setq *dl-pick* \"" (car b) "\") (done_dialog 1)")))
      (action_tile "c_check" "(done_dialog 2)")
      (action_tile "c_fix" "(done_dialog 3)")
      (setq r (start_dialog))
      (unload_dialog id)
      (cond ((and (= r 1) *dl-pick*) (dl-run *dl-pick*))
            ((= r 2) (c:DL_CHECK))
            ((= r 3) (c:DL_FIXLAYERS))))
    (dl-msg "Could not open the dialog."))
  (princ))

(dl-msg "DWGLAB loaded: DL (picture menu), DL_SOCKET, DL_SWITCH, DL_LIGHT, DL_WATER, DL_HEAT, DL_DOOR, DL_WINDOW, DL_ROOM, DL_PROJECT, DL_CHECK, DL_FIXLAYERS, DL_KIT")
(princ)
