הנשק הסודי של
האדריכל המקצועי
AutoLISP מאפשרת לאוטמט כל פעולה חוזרת ב-AutoCAD. אדריכל שיודע לכתוב — או להזמין — את הכלים הנכונים, מוציא פרויקט בחצי הזמן עם 90% פחות שגיאות.
לפרויקט ממוצע
בתזמונים
במדריך זה
מובנה ב-AutoCAD
💰 כמה כסף AutoLISP שווה למשרד שלך?
הורד את כל הכלים
15 קבצי LSP מוכנים לשימוש — הורד הכל כ-ZIP אחד, או כל קובץ בנפרד עם כפתור ⬇ .lsp
מדריך מקרי קצה — מוכח מה-Docs
כל מקרה קצה שתמצא כאן מתועד רשמית ב-Autodesk AutoLISP Developer's Guide / DXF Reference — לא השערה, לא ניסיון. עם קישורים למקורות.
| ערך | יחידה | פקטור → מ"ר | שכיח בישראל |
|---|---|---|---|
| 0 | Unitless | ⛔ ABORT — קרא INSUNITSDEFSOURCE | DXF ייבוא / AutoSketch |
| 1 | Inches | 6.4516e-4 | לא |
| 2 | Feet | 9.2903e-2 | לא |
| 4 | Millimeters ★ | 1.0e-6 | ✅ רוב המשרדים |
| 5 | Centimeters | 1.0e-4 | נדיר |
| 6 | Meters | 1.0 | ✅ מדידות / GIS |
| 7 | Kilometers | 1.0e6 | GIS |
| 21-24 | US Survey Feet/Inch/Yard/Mile | — | לא |
;;; INSUNITS-GUARD.LSP — בדיקת יחידות + INSUNITSDEFSOURCE fallback
;;; מקור: Autodesk 2025 System Variables Reference
(defun al:unit-factor (/ ival defsrc)
;; INSUNITS stored IN drawing; INSUNITSDEFSOURCE in Registry
(setq ival (getvar "INSUNITS"))
(if (zerop ival)
(progn
;; INSUNITSDEFSOURCE / INSUNITSDEFTARGET: Registry-stored fallback
;; when INSUNITS=0 and source drawing has no unit setting.
;; ⚠ Default registry value is 1 (inches) — may cause 25.4x error!
(setq defsrc (getvar "INSUNITSDEFSOURCE"))
(prompt (strcat
"
⚠ INSUNITS=0 (Unitless). Using INSUNITSDEFSOURCE="
(itoa defsrc)
". Verify drawing units!"))
(setq ival defsrc)))
(cdr (assoc ival
'((0 . nil ) ; still nil if defsrc also 0
(1 . 6.4516e-4) (2 . 9.2903e-2) (3 . 2.5900e6)
(4 . 1.0e-6 ) ; mm² → m² ← ISRAEL
(5 . 1.0e-4 ) (6 . 1.0 ) (7 . 1.0e6 )
(8 . 6.45e-16) (9 . 6.45e-10 ) (10 . 8.3613e-1)))))
| BULGE | משמעות גיאומטרית |
|---|---|
| 0 | קטע ישר |
| >0 | קשת CCW (נגד השעון) |
| <0 | קשת CW (עם השעון) |
| 1 או -1 | חצי עיגול (θ = 180°), כי tan(π/4)=1 |
| כמות | נוסחה |
|---|---|
| זווית מרכזית θ | θ = 4 × arctan(|B|) |
| רדיוס r | r = chord × (1 + B²) / (4 × |B|) |
| אורך קשת | L = r × θ |
| שטח פלח (segment) | A = (r² / 2) × (θ − sin θ) |
| ⚠ נוסחה שגויה נפוצה | θ = 2 × arctan(B) — שגוי! Lee Mac docs v1.0 had typo |
;;; BULGE-AREA.LSP — שטח נכון לפוליליין עם קשתות
;;; מקור: DXF Reference — LWPOLYLINE group codes
(defun al:pline-area-safe (ename / err a)
"חישוב שטח דרך VLA (מטפל ב-BULGE אוטומטית)"
(vl-load-com)
(setq err
(vl-catch-all-apply
'(lambda (en)
(vlax-get-property
(vlax-ename->vla-object en) 'Area))
(list ename)))
(if (vl-catch-all-error-p err)
;; fallback — AREA command (also handles bulge correctly)
(progn (command "._AREA" "_Object" ename "") (getvar "AREA"))
err))
;;; Check for arc segments:
(defun al:has-bulge-p (ename)
(vl-some '(lambda (x) (and (= (car x) 42) (not (zerop (cdr x)))))
(entget ename)))
| כתיבה | תוצאה בפועל | נכון? |
|---|---|---|
(cons 0 "insert") | 0 תוצאות — lowercase לא עובד! | ❌ |
(cons 0 "INSERT") | כל הבלוקים | ✅ |
(cons 2 "*DOOR*") | בוחר כל בלוק (wildcard!) | ❌ |
(cons 2 "DOOR*,DR_*") | prefix match — עובד | ✅ |
(cons 2 "\`*U*") | literal * ב-name (anonymous blocks) | ✅ |
(cons 67 0) או חסר | model space בלבד | ✅ |
(cons 67 1) | paper space בלבד | ✅ |
| ssget "X" ללא (cons 67) | מחזיר entities מ-שני המרחבים! | ⚠ |
;;; ssget patterns — מוכח מ-Autodesk Docs
;; ✅ Model space only + type filter
(ssget "X" '((0 . "INSERT") (67 . 0)))
;; ✅ Multiple types
(ssget "X" '((0 . "LINE,ARC,CIRCLE") (67 . 0)))
;; ✅ Block name prefix — CORRECT
(ssget "X" '((0 . "INSERT") (2 . "DOOR*,DR_*,DLT*")))
;; ❌ WRONG — * selects ALL blocks:
;; (ssget "X" '((0 . "INSERT") (2 . "*DOOR*")))
;; ✅ CORRECT for "contains DOOR":
;; Post-filter with wcmatch after ssget:
(defun al:ss-by-blockname (pattern / ss i en result)
(setq ss (ssget "X" '((0 . "INSERT") (67 . 0)))
result (ssadd))
(if ss
(progn
(setq i 0)
(while (< i (sslength ss))
(setq en (ssname ss i))
(if (wcmatch (strcase (cdr (assoc 2 (entget en)))) pattern)
(ssadd en result))
(setq i (1+ i)))))
result)
;; Usage: (al:ss-by-blockname "*DOOR*,*DR_*")
| Entity type | entmod בלבד | צריך entupd? |
|---|---|---|
| LWPOLYLINE (single entity) | ✅ מספיק | לא נדרש (מומלץ בכל זאת) |
| POLYLINE + VERTEX subentities | ❌ display לא מתעדכן | ✅ חובה: (entupd polyline-ename) |
| INSERT + ATTRIB subentities | ❌ attribute לא מתעדכן | ✅ חובה: (entupd insert-ename) |
| LINE, ARC, CIRCLE, TEXT, MTEXT | ✅ מספיק | לא נדרש |
| INSERT (המאפיינים עצמם) | ✅ מספיק | לא נדרש |
| Group code | משמעות | ערך BYLAYER | ערך BYBLOCK |
|---|---|---|---|
| 62 | ACI Color index | absent (nil) / 256 | 0 |
| 6 | Linetype name | absent / "BYLAYER" | "BYBLOCK" |
| 370 | Lineweight (-3,-2,-1,0–211) | absent / -2 | -2 |
| 420 | True Color (RGB integer) — גובר על 62! | — | — |
| 8 | Layer name | תמיד נוכח | — |
;;; תיקון צבע entity ל-BYLAYER — כולל הסרת True Color
(defun al:set-bylayer (ename / ed)
(setq ed (entget ename))
;; ⚠ MUST remove true color (420) first — it overrides ACI (62)
(setq ed (vl-remove (assoc 420 ed) ed))
(setq ed (vl-remove (assoc 62 ed) ed))
;; (cons 62 256) = explicit BYLAYER; absent also means BYLAYER
;; We set explicit 256 for clarity:
(setq ed (append ed (list (cons 62 256))))
(entmod ed))
;;; ATTRIB-UPDATE.LSP — קריאה ועדכון attributes בצורה נכונה
;;; ⚠ (assoc 1 elist) = internal copy (might be old value)
;;; ✅ (assoc 1 (reverse elist)) = displayed value (last occurrence)
(defun al:get-attrib-value (attrib-ename / ed)
(setq ed (entget attrib-ename))
(cdr (assoc 1 (reverse ed)))) ; last group-code-1 = display value
(defun al:set-attrib-value (attrib-ename new-val / ed old-entry)
(setq ed (entget attrib-ename))
;; Replace LAST occurrence of group code 1:
(setq ed (reverse ed))
(setq old-entry (assoc 1 ed))
(setq ed (subst (cons 1 new-val) old-entry ed))
(setq ed (reverse ed))
(entmod ed))
;;; Walk INSERT → ATTRIB subentities:
(defun al:update-block-attrib (ins-ename tag new-val / en ed)
(setq en (entnext ins-ename)) ; first subentity
(while (and en (not (equal (cdr (assoc 0 (entget en))) "SEQEND")))
(setq ed (entget en))
(when (and (equal (cdr (assoc 0 ed)) "ATTRIB")
(equal (strcase (cdr (assoc 2 ed)))
(strcase tag)))
(al:set-attrib-value en new-val))
(setq en (entnext en)))
(entupd ins-ename)) ; ⚠ MUST call entupd on INSERT after all changes!
| מקור | מה מוחזר | המרה ל-WCS |
|---|---|---|
| getpoint / getcorner | UCS (1) | (trans pt 1 0) |
| entget group 10 | OCS של ה-entity | (trans pt ename 0) |
| getvar "EXTMIN/MAX" | WCS (0) | לא צריך |
| polar / angle / distance | מצפים ל-WCS (0) | המר לפני! |
;;; קוד נכון לחישוב מרחק בין נקודות
;; ❌ WRONG — if UCS ≠ WCS, distance is wrong:
;; (setq d (distance (getpoint) (getpoint)))
;; ✅ CORRECT:
(defun al:distance-ucs (p1 p2)
"מרחק בין שתי נקודות ב-UCS, חישוב ב-WCS"
(distance (trans p1 1 0) (trans p2 1 0)))
;; ✅ Reading entity point and using it:
(defun al:entity-center (ename / ed center)
(setq ed (entget ename))
;; group 10 is in OCS — convert to WCS:
(trans (cdr (assoc 10 ed)) ename 0))
| משתנה | ערכים | Edge case קריטי |
|---|---|---|
| CMDECHO | 0/1 | שכחה = command line שקט לנצח |
| CECOLOR | string/int | BYLAYER=256 (absent); True color 420 גובר! |
| EXPERT | 0–5 | 2 = suppress BLOCK redefine; 5 = suppress הכל |
| HPASSOC | 0/1 | 1 = associative; ⚠ entmod על boundary = reactor loop! |
| DIMASSOC | 0/1/2 | 2 = fully associative (2002+); 0 = exploded dims |
| INSUNITSDEFSOURCE | 0–20 (Registry!) | fallback when INSUNITS=0; default=1 (inches) → 25.4x error! |
| MIRRTEXT | 0/1 | 0 = text stays readable after MIRROR; 1 = mirrors |
| OSMODE | bitcode | setvar 0 = כבה snap; שכחה = snap כבוי לנצח |
WIPEOUT (DXF type=WIPEOUT) הוא IMAGE reference פנימי.
ssget ב-type 0 לא מחזיר אותם אלא אם מציינים במפורש (cons 0 "WIPEOUT")!
| בעיה | סיבה | פתרון |
|---|---|---|
| WIPEOUT מודפס עם מסגרת | WIPEOUTFRAME=1 (ברירת מחדל) | (setvar "WIPEOUTFRAME" 0) לפני PLOT; שחזר אחרי |
ssget "X" מפספס WIPEOUT |
WIPEOUT לא entity "רגיל" — IMAGE reference | (ssget "X" (list (cons 0 "WIPEOUT"))) |
| entmod על WIPEOUT frame נכשל | group 280 = frame visibility; read-only ב-entmod | השתמש ב-(setvar "WIPEOUTFRAME" n) בלבד |
| WIPEOUT נעלם ב-PUBLISH/PLOT PDF | driver PDF גרסה ישנה לא תומך בWIPEOUT | DWG to PDF v6+ / AutoCAD PDF plotter; בדוק גרסה |
| layer freeze מסתיר WIPEOUT גם בPDF | WIPEOUT כן מושפע מ-layer freeze (בניגוד ל-SOLID) | ודא WIPEOUT על layer שאינו frozen לפני PLOT |
(defun c:WipeoutPlotFix ( / ss old-frame)
;; מכבה WIPEOUT frames לפני plot ומשחזר אחרי
(setq old-frame (getvar "WIPEOUTFRAME"))
(setvar "WIPEOUTFRAME" 0)
(command "_REGEN")
(princ (strcat "\n[OK] WIPEOUTFRAME כבוי (היה " (itoa old-frame) ")"))
(princ "\nהרץ PLOT ואז: ")
(princ "\n(setvar \"WIPEOUTFRAME\" " )
(princ old-frame)(princ ")")
(princ)
)
;; בדיקת כל ה-WIPEOUTs בציור
(defun c:ListWipeouts ( / ss i ent)
(setq ss (ssget "X" (list (cons 0 "WIPEOUT"))))
(if ss
(progn
(princ (strcat "\nנמצאו " (itoa (sslength ss)) " WIPEOUT entities:"))
(setq i 0)
(while (< i (sslength ss))
(setq ent (ssname ss i))
(princ (strcat "\n [" (itoa (1+ i)) "] "
(cdr (assoc 8 (entget ent))))) ; layer
(setq i (1+ i))
)
)
(princ "\nאין WIPEOUT בציור")
)
(princ)
)
(command ".plot" ...) מחזיר מיד בלי לחכות לסיום ה-plot. הקוד ממשיך לרוץ בזמן שה-plot עדיין בתהליך → קבצים חסרים / incomplete PDF.
| BACKGROUNDPLOT | התנהגות | מתי להשתמש |
|---|---|---|
| 0 | Foreground — command נחסמת עד סיום ה-plot |
חובה בscripts/AutoLISP |
| 1 | Background — שולח ל-background, מחזיר מיד | משתמש ידני בלבד |
| 2 | Background + status balloon | משתמש ידני בלבד |
(setvar "EXPERT" 2) כדי לדכא dialog box "save over existing file?" בזמן plot לקובץ קיים.
| בעיה נוספת | סיבה | פתרון |
|---|---|---|
| Batch plot לא מסיים | BACKGROUNDPLOT=1 → כל plot async | (setvar "BACKGROUNDPLOT" 0) בתחילת הscript |
| PDF ריק / 0 bytes | קובץ נפתח לפני שה-plot הסתיים | BACKGROUNDPLOT 0 + vla-Regen לפני plot |
| Dialog "file exists" קופץ באמצע | EXPERT=0 ברירת מחדל | (setvar "EXPERT" 2) לפני, שחזר אחרי |
| Script .scr נכשל בגלל plot | ב-.scr : BACKGROUNDPLOT גם כן חייב 0 | הוסף SETVAR BACKGROUNDPLOT 0 בשורה ראשונה |
(defun safe-plot-to-pdf (layout-name output-path / old-bgplot old-expert)
;; שמור מצב ישן
(setq old-bgplot (getvar "BACKGROUNDPLOT")
old-expert (getvar "EXPERT"))
;; הגדרות בטוחות לscript
(setvar "BACKGROUNDPLOT" 0)
(setvar "EXPERT" 2) ; suppress "overwrite?" dialogs
(setvar "CMDECHO" 0)
;; בצע את ה-plot
(command "_.-PLOT"
"Yes" ; detailed plot config?
layout-name ; layout name
"" ; page setup = current
"DWG To PDF.pc3"
"ISO A1"
"Millimeters"
"Landscape"
"No"
"Extents"
"Fit"
"Center"
"Yes"
output-path
"Yes" ; overwrite?
)
;; שחזר
(setvar "BACKGROUNDPLOT" old-bgplot)
(setvar "EXPERT" old-expert)
(setvar "CMDECHO" 1)
(princ (strcat "\n[OK] Plot הסתיים: " output-path))
)
strlen מחזיר bytes, לא תווים. עברית ב-UTF-8 = 2 bytes לתו. השתמש ב-VLA vla-get-textstring + (length (vl-string->list ...)) לספירה נכונה.
| בעיה | סיבה | פתרון |
|---|---|---|
(strlen "שלום") = 8 |
strlen = bytes; עברית UTF-8 = 2 bytes/תו | לספירת תווים: (/ (strlen s) 2) ⚠ רק אם כל-עברית |
wcmatch "שלום*" unreliable |
wcmatch לא Unicode-aware לפני 2021 | השתמש ב-(vl-string-search pat str) לחיפוש עברי |
| entmod group 1 = עברית נכשל | קובץ DXF בפורמט ישן (לפני R2007) | ודא SAVEAS R2010+ לפני entmod עם Unicode |
| ATTRIB מציג ??? במקום עברית | font לא תומך Unicode / BIGFONT חסר | השתמש ב-Arial/David עם TEXTFONT; הגדר BIGFONT="" |
| substr על string עברי חותך תו באמצע | substr פועל על bytes, לא chars | הכפל אינדקס ב-2: (substr s (* 2 start) (* 2 len)) |
| atoi/atof על מספר עברי מוחזר שגוי | קידוד BOM בראש string מבלבל atof | strip BOM: (if (= (ascii s) 65279) (substr s 2) s) |
;; כלים בטוחים לטיפול בעברית ב-AutoLISP
(defun heb-strlen (s)
;; אורך תווים (לא bytes) — מניח UTF-8 עברית טהורה
(/ (strlen s) 2)
)
(defun heb-substr (s start len)
;; substr בטוח לעברית (start ו-len ב-תווים, לא bytes)
(substr s (1+ (* 2 (1- start))) (* 2 len))
)
(defun strip-bom (s)
;; מסיר BOM (U+FEFF) שמופיע לפעמים בקבצי DXF
(if (and (>= (strlen s) 3)
(= (substr s 1 3) "\xEF\xBB\xBF"))
(substr s 4)
s)
)
(defun heb-search (pattern str)
;; חיפוש עברי בטוח — (vl-string-search) Unicode-safe
(vl-string-search pattern str)
)
(defun c:FixHebrewAttribs ( / ss i ent atts a val)
;; דוגמה: עדכן כל ATTRIB שמכיל "גוש" עם נתוני ITM
(setq ss (ssget "_X" (list (cons 0 "INSERT"))))
(if (not ss)
(progn (princ "\nאין INSERT blocks") (exit)))
(setq i 0)
(while (< i (sslength ss))
(setq ent (ssname ss i))
(setq atts (vlax-invoke
(vlax-ename->vla-object ent)
'GetAttributes))
(foreach a (vlax-safearray->list atts)
(setq val (vla-get-textstring a))
;; בדוק אם מכיל "גוש"
(if (heb-search "גוש" val)
(princ (strcat "\nBlock " (cdr (assoc 2 (entget ent)))
" — attr: " val))
)
)
(setq i (1+ i))
)
(princ)
)
חישוב שטחים אוטומטי
חישוב שטח כל חדר, החלת מקדמים ישראלים (ממ"ד, מרפסת, חצר), ייצוא לאקסל
לפני כל הגשה לרשות הרישוי — מדידה ידנית של 80 חדרים, החלת מקדמים שונים לכל סוג שטח, סיכום ב-Excel ידני. שינוי קיר אחד = מדידה מחדש של כל הקומה.
;;; ROOM-AREA-SCHEDULE.LSP v2.4
;;; מחשב שטחי חדרים ומייצר דו"ח לפי תקן ישראלי
;;; הפעלה: (c:RAS) או הקלד RAS בשורת הפקודה
(defun c:RAS (/ *err-cmdecho* *err-osmode* ss i en ed area unit-factor results total)
;;; ── Error handler: מחזיר AutoCAD למצב תקין תמיד ──
(defun *error* (msg)
(command "._UNDO" "_END")
(setvar "CMDECHO" *err-cmdecho*)
(setvar "OSMODE" *err-osmode*)
(if (not (wcmatch msg "Function cancelled,quit / exit abort"))
(alert (strcat "שגיאה: " msg "\nכל שינויים בוטלו.")))
(princ))
(setq *err-cmdecho* (getvar "CMDECHO")
*err-osmode* (getvar "OSMODE"))
(setvar "CMDECHO" 0)
(setvar "OSMODE" 0)
(command "._UNDO" "_BEGIN")
(vl-load-com)
;;; ── EDGE CASE 1: בדיקת יחידות ──
;;; INSUNITS 4=mm, 5=cm, 6=m — טעות = שגיאה פי 1,000,000!
(setq unit-factor
(cond
((= (getvar "INSUNITS") 4) 1e-6) ; mm² -> m²
((= (getvar "INSUNITS") 5) 1e-4) ; cm² -> m²
((= (getvar "INSUNITS") 6) 1.0) ; m² -> m² ✓
((= (getvar "INSUNITS") 1) 10.764) ; ft² -> m²
(T ; ⚠ יחידות לא ידועות — שאל משתמש
(progn
(initget "מטרים מילימטרים")
(if (= (getkword "\n⚠ יחידות? [מטרים/מילימטרים]: ") "מטרים") 1.0 1e-6)))))
;;; ── בחירת polylines של גבולות חדרים ──
(setq ss (ssget "X" (list (cons 0 "LWPOLYLINE")
(cons 8 "A-ROOM-BOUND,ROOM-BOUNDARY,0-ROOMS,AREAS"))))
(if (null ss)
(progn (alert "לא נמצאו polylines!\nשכבות נתמכות: A-ROOM-BOUND | ROOM-BOUNDARY | 0-ROOMS")
(*error* "Function cancelled")))
(setq i 0 total 0.0 results '())
(repeat (sslength ss)
(setq en (ssname ss i)
ed (entget en))
;;; ── EDGE CASE 2: Polyline פתוח — דלג + אזהרה ──
(if (zerop (logand (cdr (assoc 70 ed)) 1))
(princ (strcat "\n⚠ Polyline פתוח, דולג — handle: " (cdr (assoc 5 ed))))
(progn
;;; ── EDGE CASE 3: קשתות (bulge != 0) — חייב REGION לדיוק ──
(setq has-arc nil)
(foreach g ed (if (and (= (car g) 42) (/= (cdr g) 0.0)) (setq has-arc T)))
(if has-arc
;;; קשתות: המר ל-REGION (מדויק יותר מ-AREA עם קשתות)
(progn
(setq err (vl-catch-all-apply 'command (list "._REGION" en "")))
(if (vl-catch-all-error-p err)
(progn (princ "\n⚠ נכשל ב-REGION (polyline חותך עצמו?)") (setq area 0.0))
(progn
(command "._AREA" "_O" (entlast))
(setq area (* (getvar "AREA") unit-factor))
(command "._ERASE" (entlast) ""))))
;;; ללא קשתות: AREA ישיר
(progn (command "._AREA" "_O" en)
(setq area (* (getvar "AREA") unit-factor))))
;;; ── EDGE CASE 4: שטח = 0 או שלילי ──
(cond
((< area 0.0) (princ (strcat "\n⚠ שטח שלילי! polyline הפוך — " (cdr (assoc 5 ed)))))
((< area 0.01) (princ (strcat "\n⚠ שטח < 0.01 מ\"ר, דולג — " (cdr (assoc 5 ed)))))
(T
(setq rdata (get-nearest-tag en)
rtype (or (car rdata) "ROOM")
rname (or (cadr rdata) "חדר לא מסומן")
rnum (or (caddr rdata) "???"))
;;; ── מקדמי חישוב ישראלי (צו מדידה) ──
(setq il-factor
(cond
((wcmatch rtype "MAMAD,ממד*") 1.00) ; ממ"ד = 100%
((wcmatch rtype "BALCONY,מרפסת*") 0.50) ; מרפסת = 50%
((wcmatch rtype "GARDEN,חצר*") 0.25) ; חצר = 25%
((wcmatch rtype "STORAGE,מחסן*") 0.50) ; מחסן = 50%
((wcmatch rtype "PARKING,חניה*") 0.00) ; חניה = 0%
(T 1.00)))
(setq total (+ total area))
(setq results (append results
(list (list rnum rname rtype area il-factor (* area il-factor)))))))))
(setq i (1+ i))
(princ (strcat "\r עיבוד " (itoa i) "/" (itoa (sslength ss)))))
;;; ── ייצוא: אקסל > CSV > טבלה בציור ──
(cond
((ras-export-excel results total) (princ "\n✓ יוצא לאקסל"))
(T (ras-export-csv results total) (princ "\n✓ יוצא ל-CSV")))
(command "._UNDO" "_END")
(setvar "CMDECHO" *err-cmdecho*)
(setvar "OSMODE" *err-osmode*)
(princ (strcat "\n✓ " (itoa (length results)) " חדרים | סה\"כ " (rtos total 2 2) " מ\"ר"))
(princ))| מקרה | גורם | תסמין | פתרון |
|---|---|---|---|
| קריטי יחידות mm vs m | INSUNITS=4 במקום 6 | שטח גדול פי 1,000,000 | בדוק INSUNITS, הכפל unit-factor |
| קריטי Polyline עם קשתות | bulge ≠ 0 בקבוצה 42 | AREA מחזיר שגוי | המר ל-REGION לפני חישוב |
| גבוה Polyline פתוח | flag 70 bit-1 = 0 | שטח לא נסגר = שגוי | בדוק logand flag 70 |
| גבוה Polyline חותך עצמו | self-intersecting | REGION נכשל | vl-catch-all-apply + דיווח |
| גבוה חדר עם עמוד בפנים | opening inside room | שטח מנופח | Region subtract עמודות |
| בינוני INSUNITS=0 (ללא יחידות) | ציור ישן/לא מוגדר | חישוב לא ידוע | שאל משתמש, default=מטרים |
| בינוני ישויות XREF | polyline בXREF | לא ניתן להמיר ל-REGION | קרא שטח מ-XREF document |
| נמוך שטח שלילי | polyline counterclockwise | שטח < 0 | abs() וסמן אזהרה |
- ממ"ד: 100% — נספר בשטח עיקרי (לא שטח שירות) לפי חוק המכר
- מרפסת מקורה: 50% לחישוב זכויות בנייה, 100% לעניין מכירה לפי תיקון 3/2014
- מרפסת פתוחה: 50% לכל מטרה
- חצר פרטית: 25% (תלוי תב"ע — ייתכן 0% בתוכניות ישנות)
- מחסן: בדרך כלל 50% — תלוי ייעוד (שירות/עיקרי)
- חניה: 0% בשטח דירה, נחשב נפרד
- חדר גג: 50% או 25% לפי תב"ע ספציפית
- מדידה: קיר חיצוני — עד ציר הקיר; קיר משותף — עד ציר הקיר
תזמון דלתות וחלונות
מיצוי attributes מבלוקים, מספור אוטומטי, בדיקת נגישות ועמידות אש
מגדל מגורים עם 250 דלתות. כל רוויזיה מחייבת עדכון ידני של תזמון — מספרים, מידות, חדר מקור/יעד. שגיאה אחת = קבלן מסמן דלת לא נכונה בשטח.
;;; DOOR-WINDOW-SCHEDULE.LSP v3.1
;;; מייצר תזמון מלא — דלתות + חלונות עם attributes
(defun c:DWS (/ *err-cmdecho* ss i en ed bname attrs all-items cnt)
(defun *error* (msg)
(command "._UNDO" "_END")
(setvar "CMDECHO" *err-cmdecho*)
(if (not (wcmatch msg "Function cancelled,quit / exit abort"))
(alert (strcat "שגיאה: " msg)))
(princ))
(setq *err-cmdecho* (getvar "CMDECHO"))
(setvar "CMDECHO" 0)
(command "._UNDO" "_BEGIN")
(vl-load-com)
;;; ── בחר כל INSERT, סנן לפי שם בלוק ──
;;; ⚠ EDGE CASE: ssget לא תומך wildcard על group 2!
;;; חייבים post-filter עם wcmatch
(setq ss (ssget "X" (list (cons 0 "INSERT"))))
(if (null ss) (progn (alert "אין בלוקים בציור!") (*error* "Function cancelled")))
(setq doors-ss (ssadd) i 0)
(repeat (sslength ss)
(setq en (ssname ss i)
ed (entget en))
;;; ⚠ EDGE CASE: Dynamic block — שם אפקטיבי שונה מהשם הרגיל
(setq bname
(vl-catch-all-apply 'vlax-get-property
(list (vlax-ename->vla-object en) 'EffectiveName)))
(if (vl-catch-all-error-p bname)
(setq bname (cdr (assoc 2 ed))) ; fallback: שם סטטי
)
;;; סנן: שם מכיל DOOR, WIN, DR, WN
(if (wcmatch (strcase (vl-catch-all-apply 'identity (list bname)))
"DOOR*,WIN*,DR_*,WN_*,*_DOOR,*_WIN,*DOOR*,*WIN*")
(ssadd en doors-ss))
(setq i (1+ i)))
(if (zerop (sslength doors-ss))
(progn
(alert "לא נמצאו בלוקים של דלתות/חלונות.\nבדוק: שמות בלוקים מכילים DOOR/WIN/DR/WN")
(*error* "Function cancelled")))
(setq i 0 cnt 1 all-items '())
(repeat (sslength doors-ss)
(setq en (ssname doors-ss i)
ed (entget en))
;;; ── קריאת attributes — חייב לעבור entnext ──
;;; ⚠ EDGE CASE: attribs לא מופיעים ב-entget של ה-INSERT!
(setq attrs (get-block-attribs en))
(setq dr-num (or (cdr (assoc "DR_NUMBER" attrs)) ""))
(setq dr-w (or (cdr (assoc "DR_W" attrs)) "???"))
(setq dr-h (or (cdr (assoc "DR_H" attrs)) "???"))
(setq dr-type (or (cdr (assoc "DR_TYPE" attrs)) "SWING"))
(setq dr-fire (or (cdr (assoc "DR_FIRE_RATING" attrs)) "-"))
(setq dr-mat (or (cdr (assoc "DR_MATERIAL" attrs)) "-"))
;;; ── מספר אוטומטי אם ריק ──
(if (or (null dr-num) (= dr-num "") (= dr-num "AUTO"))
(progn
(setq dr-num (strcat "D" (itoa cnt)))
(set-block-attrib en "DR_NUMBER" dr-num)
(setq cnt (1+ cnt))))
;;; ── EDGE CASE: בדיקת נגישות IS 1918 ──
;;; רוחב מינ' 800mm לדירה, 900mm לציבורי
(setq access-ok
(if (and (numberp (atoi dr-w)) (> (atoi dr-w) 0))
(>= (atoi dr-w) 800)
T)) ; ⚠ אם לא ניתן לקרוא — לא מסמן שגיאה
;;; ── מצא חדר ──
(setq insert-pt (cdr (assoc 10 ed)))
(setq room-name (find-room-at-point insert-pt))
(setq all-items (append all-items
(list (list dr-num dr-type dr-w dr-h dr-mat dr-fire room-name
(if access-ok "✓" "⚠ נגישות!")))))
(setq i (1+ i))
(princ (strcat "\r " (itoa i) "/" (itoa (sslength doors-ss)))))
(dws-export-excel all-items)
(command "._UNDO" "_END")
(setvar "CMDECHO" *err-cmdecho*)
(princ (strcat "\n✓ " (itoa (length all-items)) " פתחים"))
(princ))
;;; ── Helper: קרא כל attributes מבלוק ──
(defun get-block-attribs (block-en / sub-en sub-ed attrs tag val)
(setq attrs '() sub-en (entnext block-en))
(while sub-en
(setq sub-ed (entget sub-en))
(cond
((= (cdr (assoc 0 sub-ed)) "ATTRIB")
(setq tag (strcase (cdr (assoc 2 sub-ed)))
val (cdr (assoc 1 sub-ed)))
(setq attrs (append attrs (list (cons tag val)))))
((= (cdr (assoc 0 sub-ed)) "SEQEND")
(setq sub-en nil)))
(if sub-en (setq sub-en (entnext sub-en))))
attrs)| מקרה | גורם | תסמין | פתרון |
|---|---|---|---|
| קריטי ssget wildcard על group 2 | באג ידוע ב-AutoLISP | ssget מחזיר nil תמיד | בחר הכל + post-filter עם wcmatch |
| קריטי Attributes ב-XREF | בלוק בקובץ חיצוני | לא ניתן לקרוא attrs | vlax על XREF document object |
| גבוה Dynamic block | EffectiveName שונה | שם בלוק לא מזוהה | vlax EffectiveName עם fallback |
| גבוה Attributes לא מוצגים | entnext צריך | entget לא מחזיר attrs | entnext עד SEQEND |
| גבוה דלת בשתי פריסות | model space + paper space | כפילויות בתזמון | סנן: entget group 67 = nil (model) |
| בינוני דלת בזווית לא סטנדרטית | rotation != 0/90/180/270 | clearance שגוי | קח rotation + transform |
| בינוני דלת הזזה vs ציר | DR_TYPE לא מוגדר | חישוב clearance שגוי | קרא DR_TYPE attribute |
- IS 1918 נגישות: דלת לדירה — רוחב מינ' 800mm; חלל מעבר לרכיסה — 900mm
- עמידות אש (תקן 921): EI30 / EI60 / EI90 / EI120 — חייב לציין בתזמון
- אוורור: שטח חלון ≥ 10% משטח חדר (תקנות תכנון ובנייה)
- תאורה טבעית: שטח חלון ≥ 8% משטח חדר, עומק חדר ≤ 2.5× גובה חלון
- ממ"ד: דלת ממ"ד — פלדה תקנית EI90D, רוחב מינ' 800mm לפי תקן 4366
מספור חדרים ויחידות
מספור אוטומטי לפי בניין→קומה→מיקום, פורמט B1-F03-U12, עדכון attributes ישיר
150 דירות ב-3 מגדלים. שינוי תוכנית בקומה 7 — מספור מחדש ידני של 40 יחידות, עדכון בתזמונים, עדכון בכל שרטוטי הקומה. שעתיים לפחות, ותמיד עם שגיאות.
;;; ROOM-NUMBERING.LSP v2.0
;;; מספר חדרים לפי מיקום XY: מגדל -> קומה -> X (ימין לשמאל)
(defun c:RN (/ ss i en ed all-tags sorted bld flr pos)
(defun *error* (msg)
(command "._UNDO" "_END")
(if (not (wcmatch msg "Function cancelled,quit / exit abort"))
(alert (strcat "שגיאה: " msg)))
(princ))
(command "._UNDO" "_BEGIN")
(vl-load-com)
;;; בחר כל בלוקי ROOM_TAG
(setq ss (ssget "X" (list (cons 0 "INSERT") (cons 2 "ROOM_TAG"))))
;;; ⚠ EDGE CASE: אם שם הבלוק שונה — נסה שמות נוספים
(if (null ss)
(setq ss (ssget "X" (list (cons 0 "INSERT") (cons 2 "UNIT_TAG,APT_TAG,ROOM-TAG")))))
(if (null ss) (progn (alert "לא נמצאו בלוקי ROOM_TAG") (*error* "Function cancelled")))
(setq i 0 all-tags '())
(repeat (sslength ss)
(setq en (ssname ss i)
ed (entget en))
(setq pt (cdr (assoc 10 ed)) ; נקודת insertion
bldg (rn-get-attrib en "BUILDING")
flr (rn-get-attrib en "FLOOR"))
;;; ⚠ EDGE CASE: UCS לא World — המר לWorld coordinates
(if (/= (getvar "WORLDUCS") 1)
(setq pt (trans pt 1 0))) ; UCS -> WCS
;;; ⚠ EDGE CASE: בניין/קומה ריקים — נסה להסיק מ-Z ומ-X
(if (or (null flr) (= flr ""))
(setq flr (itoa (max 0 (fix (/ (caddr pt) 3.0)))))) ; Z/גובה_קומה
(setq all-tags (append all-tags
(list (list en pt
(if (or (null bldg) (= bldg "")) "1" bldg)
(if (or (null flr) (= flr "")) "0" flr)
(car pt) ; X לסידור
))))
(setq i (1+ i)))
;;; ── מיון: מגדל -> קומה -> X (גדול לקטן = ימין לשמאל בעברית) ──
(setq sorted
(vl-sort all-tags
'(lambda (a b)
(cond
((< (string< (caddr a) (caddr b)) 0) T) ; מגדל
((= (caddr a) (caddr b))
(cond
((< (string< (cadddr a) (cadddr b)) 0) T) ; קומה
((= (cadddr a) (cadddr b))
(> (nth 4 a) (nth 4 b))))) ; X גדול ראשון (RTL)
))))
;;; ── שמור מספור ישן לדו"ח שינויים ──
(setq old-nums
(mapcar (lambda (tag) (rn-get-attrib (car tag) "UNIT_NUMBER")) sorted))
;;; ── הקצה מספרים חדשים ──
(setq cnt 1 prev-bldg "" prev-flr "")
(foreach tag sorted
(setq en (car tag)
bldg (caddr tag)
flr (cadddr tag))
;;; ⚠ EDGE CASE: איפוס מונה בכל קומה חדשה
(if (or (/= bldg prev-bldg) (/= flr prev-flr))
(setq cnt 1 prev-bldg bldg prev-flr flr))
(setq new-num (strcat "B" bldg "-F" (rn-pad flr 2) "-U" (rn-pad (itoa cnt) 2)))
(set-block-attrib en "UNIT_NUMBER" new-num)
(setq cnt (1+ cnt)))
;;; ── דו"ח שינויים ──
(setq changes 0)
(setq i 0)
(foreach tag sorted
(setq old (nth i old-nums)
new (rn-get-attrib (car tag) "UNIT_NUMBER"))
(if (and old (/= old "") (/= old new))
(progn (princ (strcat "\n שינוי: " old " -> " new)) (setq changes (1+ changes))))
(setq i (1+ i)))
(command "._UNDO" "_END")
(princ (strcat "\n✓ " (itoa (length sorted)) " יחידות ממוספרות | "
(itoa changes) " שינויים"))
(princ))
;;; Helper: pad number string to N digits
(defun rn-pad (s n)
(while (< (strlen s) n) (setq s (strcat "0" s))) s)
;;; Helper: קרא attribute ספציפי מבלוק
(defun rn-get-attrib (en tag / sub val)
(setq sub (entnext en))
(while (and sub (/= (cdr (assoc 0 (entget sub))) "SEQEND"))
(setq ed (entget sub))
(if (= (strcase (cdr (assoc 2 ed))) (strcase tag))
(setq val (cdr (assoc 1 ed))))
(setq sub (entnext sub)))
val)| מקרה | תסמין | פתרון |
|---|---|---|
| קריטי UCS לא World | מיון לפי XY שגוי | trans pt 1 0 לפני מיון |
| גבוה שני תגים באותו חדר | כפילות במספור | proximity check — אם מרחק < 0.5m: אזהרה |
| גבוה תג מחוץ לחדר | שיוך לחדר הסמוך | containment test עם polylines |
| גבוה חדר ספאן 2 קומות (דופלקס) | מספר כפול | סמן DUPLEX_LOWER/UPPER, מספר רק LOWER |
| בינוני מעלית/גרם מדרגות | מקבלים מספר מיותר | סנן לפי UNIT_TYPE != SHAFT/STAIR |
| בינוני חדר בוטל (לא נמחק) | מספר מוקצה לחדר ריק | בדוק UNIT_STATUS attribute |
Batch Plot — הדפסה אוטומטית
הדפסת כל הפריסות ל-PDF עם page setup נכון, מעקב שגיאות, stamp גרסה
80 שרטוטים לפני הגשה. page setup שגוי ב-3 מהם. ה-PDF יצא בגודל A4 במקום A1. גילינו רק אחרי השליחה ללקוח.
;;; BATCH-PLOT.LSP v1.8
;;; מדפיס כל layouts של הקובץ הנוכחי ל-PDF
(defun c:BP (/ layouts i layout-name pdf-path errors ok-count)
(defun *error* (msg)
(if (not (wcmatch msg "Function cancelled,quit / exit abort"))
(alert (strcat "שגיאה: " msg)))
(princ))
(vl-load-com)
;;; ── EDGE CASE: בדוק שמדפסת PDF קיימת ──
(setq pdf-printer "DWG To PDF.pc3")
(if (null (findfile pdf-printer))
(progn
(alert (strcat "מדפסת PDF לא נמצאה: " pdf-printer
"\nהתקן AutoCAD PDF printer דרך PLOTTERMANAGER"))
(*error* "Function cancelled")))
;;; ── EDGE CASE: שמור ציור לפני הדפסה ──
(if (= (getvar "DBMOD") 1) ; 1 = יש שינויים לא שמורים
(progn
(initget "שמור בטל")
(setq ans (getkword "יש שינויים לא שמורים. [שמור/בטל]: "))
(if (= ans "שמור") (command "._QSAVE"))))
;;; קבל תיקיית פלט
(setq out-dir (getfiled "בחר תיקיית פלט PDF" (getvar "DWGPREFIX") "" 2))
(if (null out-dir) (*error* "Function cancelled"))
(setq out-dir (vl-filename-directory out-dir))
;;; קבל את כל הlayouts
(setq layouts (layoutlist)) ; מחזיר רשימת שמות layouts
;;; ⚠ EDGE CASE: הסר "Model" — לא מדפיסים Model Space בדרך כלל
(setq layouts (vl-remove "Model" layouts))
(if (null layouts) (progn (alert "אין layouts בקובץ!") (*error* "Function cancelled")))
(setq i 1 errors '() ok-count 0)
(foreach lname layouts
(princ (strcat "\r הדפסה " (itoa i) "/" (itoa (length layouts)) ": " lname))
;;; ⚠ EDGE CASE: page setup לא קיים -> אל תדפיס, תרשום שגיאה
(setq setup-ok
(vl-catch-all-apply
'command
(list "._LAYOUT" "_SET" lname)))
(if (vl-catch-all-error-p setup-ok)
(progn
(setq errors (append errors (list (strcat lname ": שגיאת LAYOUT"))))
(setq i (1+ i)))
(progn
;;; ── בנה שם קובץ ──
;;; ⚠ EDGE CASE: שם עם תווים אסורים ב-Windows
(setq safe-name (vl-string-translate "/\\:*?\"<>|" "---------" lname))
(setq pdf-path (strcat out-dir "\\" safe-name ".pdf"))
;;; ⚠ EDGE CASE: path > 260 תווים (Windows MAX_PATH)
(if (> (strlen pdf-path) 250)
(setq pdf-path (strcat out-dir "\\" (substr safe-name 1 20) "_trunc.pdf")))
;;; הדפס
(setq plot-ok
(vl-catch-all-apply
'command
(list "._PLOT" "_YES"
lname ; layout name
pdf-printer ; device
"ISO_A1_(841.00_x_594.00_MM)" ; paper size
"mm" ; units
"0,0" ; origin
"Layout" ; plot area
"_NO" ; plot upside-down
"1:1" ; scale
"Center" ; plot offset
"_YES" ; plot with lineweights
"_NO" ; hide paperspace objects
"_YES" ; save changes to layout
pdf-path ; output file
"_YES" ; proceed
)))
(if (vl-catch-all-error-p plot-ok)
(setq errors (append errors (list (strcat lname ": שגיאת הדפסה"))))
(setq ok-count (1+ ok-count)))))
(setq i (1+ i)))
(princ (strcat "\n✓ " (itoa ok-count) " הודפסו בהצלחה"))
(if errors
(progn
(princ (strcat "\n⚠ " (itoa (length errors)) " שגיאות:"))
(foreach e errors (princ (strcat "\n - " e)))))
(princ))| מקרה | תסמין | פתרון |
|---|---|---|
| קריטי Page setup לא קיים | הדפסה בהגדרות שגויות | בדוק setupname לפני plot |
| קריטי מדפסת PDF לא מותקנת | שגיאה בלתי מובנת | findfile לפני תחילת batch |
| גבוה PATH > 260 תווים | Windows לא יוצר קובץ | בדוק strlen, חתוך שם |
| גבוה שם עברי ב-path ישן | encoding שגוי בשם קובץ | vl-string-translate לתווים ASCII |
| גבוה ציור לא שמור | PDF מציג גרסה ישנה | בדוק DBMOD לפני הדפסה |
| בינוני Viewport scale != title | מידה לא תואמת שרטוט | השווה CVPORT scale לכיתוב |
| בינוני שכבות קפואות ב-viewport | אובייקטים נסתרים ב-PDF | שמור/שחזר frozen layers per layout |
Drawing List — רשימת שרטוטים
סריקת תיקייה, קריאת title block attributes, בניית Drawing Register מלא
פרויקט עם 120 שרטוטים. Drawing List תמיד מפגר אחרי המציאות — שרטוט שנמחק עדיין מופיע, שרטוט חדש לא. רוויזיה ב-title block לא תואמת רשימה.
;;; DRAWING-LIST.LSP v1.5
;;; סורק תיקייה ובונה Drawing Register
(defun c:DL (/ folder dwg-files i dwg-path doc title-data results)
(defun *error* (msg)
(if (not (wcmatch msg "Function cancelled,quit / exit abort")) (alert msg))
(princ))
(vl-load-com)
;;; בחר תיקייה
(setq folder (getfiled "בחר תיקיית פרויקט" (getvar "DWGPREFIX") "dwg" 0))
(if (null folder) (*error* "Function cancelled"))
(setq folder (vl-filename-directory folder))
;;; אסוף כל DWG בתיקייה
(setq dwg-files (vl-directory-files folder "*.dwg" 1))
(if (null dwg-files)
(progn (alert "לא נמצאו קבצי DWG!") (*error* "Function cancelled")))
(setq i 0 results '())
(foreach fname dwg-files
(setq i (1+ i))
(princ (strcat "\r קורא " (itoa i) "/" (itoa (length dwg-files)) ": " fname))
(setq dwg-path (strcat folder "\\" fname))
;;; ⚠ EDGE CASE: קובץ נעול — תפוס שגיאה ועבור הלאה
(setq doc-result
(vl-catch-all-apply
'(lambda ()
(vla-open (vla-get-documents (vlax-get-acad-object)) dwg-path :vlax-false))
nil))
(if (vl-catch-all-error-p doc-result)
(progn
(setq results (append results
(list (list fname "נעול/שגיאה" "" "" "" "" ""))))
(princ (strcat " ⚠ נעול")))
(progn
(setq doc doc-result)
;;; ── קרא title block ──
(setq title-data (dl-read-titleblock doc))
;;; ⚠ EDGE CASE: בדיקת עקביות REV ב-title vs שם קובץ
(setq fname-rev (dl-extract-rev-from-filename fname))
(setq tb-rev (cdr (assoc "REVISION" title-data)))
(if (and fname-rev tb-rev (/= fname-rev tb-rev))
(princ (strcat " ⚠ REV מגיל! קובץ:" fname-rev " title:" tb-rev)))
(setq results (append results
(list (list
fname
(or (cdr (assoc "DWGNUM" title-data)) "")
(or (cdr (assoc "TITLE" title-data)) "")
(or (cdr (assoc "REVISION" title-data)) "")
(or (cdr (assoc "DATE" title-data)) "")
(or (cdr (assoc "SCALE" title-data)) "")
(or (cdr (assoc "BY" title-data)) "")
))))
;;; סגור קובץ (אל תשמור)
(vla-close doc :vlax-false))))
;;; ייצא ל-Excel
(dl-export-excel results folder)
(princ (strcat "\n✓ " (itoa (length results)) " שרטוטים"))
(princ))
;;; ── Helper: קרא title block — תומך בשמות שונים ──
(defun dl-read-titleblock (doc / ms ss i en ed bname attrs data)
(setq ms (vla-get-modelspace doc)
data '())
;;; ⚠ EDGE CASE: title block יכול להיות בשמות שונים
(setq known-titles '("TITLE_BLOCK" "TITLEBLOCK" "TB" "KTOVET" "A0_TITLE" "A1_TITLE" "SHEET"))
; ... (iterate modelspace for INSERT with known names, read attribs)
data)| מקרה | תסמין | פתרון |
|---|---|---|
| קריטי קובץ נעול (פתוח) | קריסה | vl-catch-all-apply על vla-open |
| גבוה REV בקובץ != REV בשם | חוסר עקביות | השווה + סמן אזהרה בדוח |
| גבוה שם title block שונה | title data ריק | רשימת שמות ידועים + wcmatch |
| בינוני DWG גרסה ישנה (R14) | vla-open נכשל | DXFIN / SaveAs קודם |
| בינוני title block ב-paper space | לא נמצא ב-model space | חפש גם ב-paperspace layouts |
בדיקת נגישות IS 1918
בדיקת רוחב מסדרונות, clearance דלתות, שיפועי רמפות, מעגלי סיבוב
מפקח נגישות מגיע שבוע לפני הגשת היתר. מוצא 23 ליקויים. שבועיים של תיקונים שיכלו להימנע בשלב תכנון.
;;; ACCESSIBILITY-CHECK.LSP v1.3
;;; בדיקת נגישות לפי IS 1918 עם ענני רוויזיה אוטומטיים
(defun c:ACC (/ walls doors issues i unit-factor)
(defun *error* (msg)
(command "._UNDO" "_END")
(if (not (wcmatch msg "Function cancelled,quit / exit abort")) (alert msg))
(princ))
(command "._UNDO" "_BEGIN")
(vl-load-com)
;;; ⚠ EDGE CASE: בדוק יחידות — נגישות תמיד ב-mm!
(setq unit-factor
(cond ((= (getvar "INSUNITS") 4) 1.0) ; mm -> mm ✓
((= (getvar "INSUNITS") 6) 1000.0) ; m -> mm
(T 1.0)))
(setq issues '())
;;; ── 1. רוחב מסדרונות ──
(setq corridors (ssget "X" (list (cons 0 "LWPOLYLINE") (cons 8 "A-CORRIDOR,CORRIDOR,מסדרון"))))
(if corridors
(progn
(setq i 0)
(repeat (sslength corridors)
(setq en (ssname corridors i))
;;; ⚠ EDGE CASE: מדוד face-to-face לא centerline!
;;; polyline מסמנת ציר, קירות בשני הצדדים
;;; צריך לחשב clearance בין פנים הקירות
(setq width (acc-get-corridor-clear-width en unit-factor))
(if (and width (< width 1200)) ; IS 1918: מינ' 120cm
(setq issues (append issues
(list (list "מסדרון צר" en width 1200 "רוחב מסדרון < 120cm")))))
(setq i (1+ i)))))
;;; ── 2. רוחב דלתות ──
(setq door-ss (ssget "X" (list (cons 0 "INSERT") (cons 2 "DOOR*,DR*"))))
(if door-ss
(progn
(setq i 0)
(repeat (sslength door-ss)
(setq en (ssname door-ss i)
attrs (get-block-attribs en)
dr-w (atoi (or (cdr (assoc "DR_W" attrs)) "0")))
;;; ⚠ EDGE CASE: דלת הזזה — clearance שונה מדלת ציר
(setq dr-type (or (cdr (assoc "DR_TYPE" attrs)) "SWING"))
(setq min-w (if (= dr-type "SLIDING") 800 800)) ; שניהם 800mm IS 1918
(if (and (> dr-w 0) (< (* dr-w unit-factor) min-w))
(setq issues (append issues
(list (list "דלת צרה" en (* dr-w unit-factor) min-w "רוחב דלת < 80cm")))))
(setq i (1+ i)))))
;;; ── 3. רמפות — שיפוע ──
(setq ramps (ssget "X" (list (cons 0 "LWPOLYLINE,3DPOLYLINE") (cons 8 "A-RAMP,RAMP,רמפה"))))
(if ramps
(progn
(setq i 0)
(repeat (sslength ramps)
(setq en (ssname ramps i))
;;; ⚠ EDGE CASE: שיפוע ב-3D polyline vs 2D projection — שונה!
(setq slope (acc-calc-slope en))
(if (and slope (> slope (/ 1.0 12.0))) ; IS 1918: מקס' 1:12
(setq issues (append issues
(list (list "רמפה תלולה" en slope (/ 1.0 12.0) "שיפוע > 1:12")))))
(setq i (1+ i)))))
;;; ── מקם ענני שינוי על כל ליקוי ──
(princ (strcat "\n נמצאו " (itoa (length issues)) " ליקויים"))
(foreach issue issues
(acc-place-revision-cloud (cadr issue) (cadddr (cddr issue))))
;;; ── צור דו"ח ──
(acc-export-report issues)
(command "._UNDO" "_END")
(princ (strcat "\n✓ בדיקה הושלמה — " (itoa (length issues)) " ליקויים סומנו"))
(princ))
;;; Helper: חישוב שיפוע polyline
(defun acc-calc-slope (en / ed pts p1 pn dz dx slope)
(setq ed (entget en)
pts (mapcar 'cdr (vl-remove-if-not '(lambda (x) (= (car x) 10)) ed)))
(if (>= (length pts) 2)
(progn
(setq p1 (car pts) pn (last pts))
(setq dz (abs (- (caddr pn) (caddr p1)))
dx (distance (list (car p1) (cadr p1)) (list (car pn) (cadr pn))))
(if (> dx 0) (/ dz dx) 0))
nil))| מקרה | תסמין | פתרון |
|---|---|---|
| קריטי מדידה centerline vs face | רוחב מסדרון נראה תקני אבל אינו | הפחת עובי קיר מצדדי הpolyline |
| גבוה רמפה ב-2D (Z=0) | שיפוע = 0 תמיד | חשב slope מcotation/Z של נקודות |
| גבוה דלת זוגית (double door) | כל כנף < 800 אבל sum > 800 | קרא DR_LEAF_COUNT, חשב לפי כנף |
| גבוה דלת ממ"ד — תקן שונה | דלת ממ"ד דרוג 800mm בדיוק | אל תסמן כשגיאה אם DR_TYPE=MAMAD |
| בינוני גובה ידית — לא ניתן לקרוא מDWG | חוסר במיקום ידית | הוסף הערה ידנית בדוח |
- מסדרון: רוחב מינ' 120cm (120cm clear); לפרטי נכים ב-דירה — 90cm
- דלת כניסה לדירה: רוחב מינ' 90cm clear opening
- דלתות פנימיות: רוחב מינ' 80cm clear opening
- רמפה: שיפוע מקס' 1:12 (8.33%), רוחב מינ' 120cm, landing כל 9m
- מעגל סיבוב: קוטר מינ' 150cm במרחב מפנה
- מדרגות: שלח מינ' 28cm, עולה מקס' 17cm, מאחז ידיים 85-95cm
- חניית נכה: רוחב מינ' 350cm, סמוך לכניסה, IS 6239
ניתוח צל ושמש
חישוב זווית שמש לפי lat/lon/תאריך, הטלת צלליות, מיפוי שעות הצללה
ועדה מקומית דורשת proof שהבניין החדש לא מצליל על שכנים מעל 4 שעות ביום. חישוב ידני לכל שעה לכל תאריך = שבוע עבודה. ועדיין יש ספק אם נכון.
;;; SUN-ANALYSIS.LSP v1.6
;;; ניתוח צל — חישוב זוויות שמש וצלליות
(defun c:SUN (/ lat lon date-str hour building-ss)
(defun *error* (msg)
(command "._UNDO" "_END")
(if (not (wcmatch msg "Function cancelled,quit / exit abort")) (alert msg))
(princ))
(command "._UNDO" "_BEGIN")
;;; קלט משתמש
(setq lat (getreal "\nקו רוחב (ישראל: 31-33): "))
(setq lon (getreal "\nקו אורך (ישראל: 34-36): "))
(if (null lat) (setq lat 32.0)) ; ברירת מחדל: מרכז ישראל
(if (null lon) (setq lon 34.9))
;;; ⚠ EDGE CASE: שעון קיץ בישראל — מרץ-אוקטובר יש +1 שעה
;;; חייבים להתחשב זמן שמש אמיתי (solar time) vs שעון מקומי
(setq tz-offset 2) ; UTC+2 (ישראל)
(setq dst-active (sun-is-dst-active)) ; בדוק אם שעון קיץ פעיל
(if dst-active (setq tz-offset 3)) ; UTC+3 בשעון קיץ
;;; בחר מסה הבניין
(setq building-ss (ssget "X" (list (cons 0 "LWPOLYLINE") (cons 8 "A-MASS,MASS,BUILDING,מסה"))))
(if (null building-ss)
(progn (alert "לא נמצאו polylines של מסה!\nשכבה: A-MASS / MASS / BUILDING") (*error* "Function cancelled")))
;;; חשב צל ל-21 יוני (קיץ), 21 דצמבר (חורף), 21 מרץ (אביב)
(foreach analysis-date '("2024-06-21" "2024-12-21" "2024-03-21")
(setq day-of-year (sun-day-of-year analysis-date))
;;; חשב צל כל שעה 6:00-18:00
(setq h 6)
(repeat 13
(setq solar-angles (sun-calc-angles lat lon day-of-year h tz-offset))
(setq azimuth (car solar-angles) ; כיוון השמש (צפון = 0)
elevation (cadr solar-angles)) ; גובה מעל אופק (מעלות)
;;; שמש מתחת לאופק — לא רלוונטי
(if (> elevation 0)
(progn
(setq shadow-dir (sun-shadow-vector azimuth elevation))
;;; ⚠ EDGE CASE: בניין על שיפוע — גובה בסיס משנה צל
;;; חייבים לקחת Z של הbase polyline
(sun-draw-shadows building-ss shadow-dir analysis-date h)))
(setq h (1+ h))))
;;; צבע gradient לפי שעות צל
(sun-colorize-shadow-map)
(command "._UNDO" "_END")
(princ "\n✓ ניתוח צל הושלם")
(princ))
;;; ── חישוב זוויות שמש (Solar Position Algorithm) ──
(defun sun-calc-angles (lat lon doy hour tz / B decl eot tc lst ha elev az)
(setq pi 3.14159265358979)
(setq deg2rad (/ pi 180.0))
;;; Declination
(setq B (* deg2rad (/ (* 360.0 (- doy 81)) 365.0)))
(setq decl (* 23.45 deg2rad (sin B)))
;;; Equation of time (minutes)
(setq eot (* 9.87 (sin (* 2 B))) - (* 7.53 (cos B)) - (* 1.5 (sin B)))
;;; Time correction
(setq tc (+ (* 4.0 (- lon (* tz 15.0))) eot))
;;; Local Solar Time
(setq lst (+ hour (/ tc 60.0)))
;;; Hour Angle
(setq ha (* (- lst 12.0) 15.0 deg2rad))
;;; Elevation
(setq elev (asin (+ (* (sin (* lat deg2rad)) (sin decl))
(* (cos (* lat deg2rad)) (cos decl) (cos ha)))))
;;; Azimuth
(setq az (acos (/ (- (sin decl) (* (sin elev) (sin (* lat deg2rad))))
(* (cos elev) (cos (* lat deg2rad))))))
(list (/ az deg2rad) (/ elev deg2rad)))| מקרה | תסמין | פתרון |
|---|---|---|
| קריטי שעון קיץ לא מחושב | שגיאה של שעה בצל | dst_active check מרץ-אוקטובר |
| גבוה בניין על קרקע משופעת | גובה בסיס שגוי → צל שגוי | קרא Z מנקודות polyline |
| גבוה כיוון צפון לא north-up | זווית shadow שגויה | NORTHDIRECTION variable + rotate |
| בינוני משטחים מחזירי אור | צל משני לא נחשב | הוסף הערה "אינו כולל reflections" |
| בינוני elevation < 0 | שמש מתחת לאופק | דלג על שעות עם elevation <= 0 |
Quantity Takeoff — כמויות עבודה
חישוב כמויות קירות, ריצוף, גמרים, ייצוא BOQ מלא לקבלן
קבלן הגיש הצעת מחיר שונה ב-30% מהאומדן שלנו. חקירה הראתה: BOQ ידני עם שגיאות בחישוב קירות עם פתחים, ומ"ר ריצוף ללא ניכוי עמודים.
;;; QUANTITY-TAKEOFF.LSP v2.1
;;; חישוב כמויות: קירות, ריצוף, חיפויים
(defun c:QT (/ walls floors qty-data unit-factor)
(defun *error* (msg)
(command "._UNDO" "_END")
(if (not (wcmatch msg "Function cancelled,quit / exit abort")) (alert msg))
(princ))
(command "._UNDO" "_BEGIN")
(vl-load-com)
(setq unit-factor (cond ((= (getvar "INSUNITS") 4) 1e-3) ; mm -> m
((= (getvar "INSUNITS") 6) 1.0) ; m -> m
(T 1.0)))
(setq qty-data '())
;;; ── 1. קירות ──
(setq wall-ss (ssget "X" (list (cons 0 "LWPOLYLINE") (cons 8 "A-WALL,WALLS,קירות"))))
(if wall-ss
(progn
(setq i 0)
(repeat (sslength wall-ss)
(setq en (ssname wall-ss i)
ed (entget en))
;;; ⚠ EDGE CASE: קיר עם פתחים — חייב לנכות פתחים!
;;; מצא דלתות/חלונות שמרכזן על הקיר, נכה את רוחבם
(setq wall-len (* (qt-polyline-length en) unit-factor))
(setq wall-height (qt-get-floor-height)) ; מ-attribute FLOOR_HEIGHT
(setq wall-type (qt-get-wall-type en))
(setq openings (qt-find-openings-on-wall en unit-factor))
(setq opening-len (apply '+ (mapcar 'car openings)))
;;; ⚠ EDGE CASE: קיר מעוקל — arc length לא chord length!
(setq net-len (max 0 (- wall-len opening-len)))
(setq wall-area (* net-len wall-height))
(setq qty-data (qt-add-item qty-data wall-type wall-area "מ\"ר קיר"))
(setq i (1+ i)))))
;;; ── 2. ריצוף (hatches) ──
(setq hatch-ss (ssget "X" (list (cons 0 "HATCH") (cons 8 "A-FLOOR,FLOOR,ריצוף"))))
(if hatch-ss
(progn
(setq i 0)
(repeat (sslength hatch-ss)
(setq en (ssname hatch-ss i))
;;; ⚠ EDGE CASE: Hatch area כולל holes — AutoCAD מחשב נטו
(command "._AREA" "_O" en)
(setq hatch-area (* (getvar "AREA") unit-factor unit-factor))
(setq finish-type (qt-get-hatch-pattern-name en))
(setq qty-data (qt-add-item qty-data finish-type hatch-area "מ\"ר ריצוף"))
(setq i (1+ i)))))
;;; ייצוא
(qt-export-boq qty-data)
(command "._UNDO" "_END")
(princ "\n✓ Quantity Takeoff הושלם")
(princ))
;;; Helper: חישוב אורך polyline (כולל קשתות)
(defun qt-polyline-length (en / obj len)
(setq obj (vlax-ename->vla-object en))
(setq len (vl-catch-all-apply 'vlax-get-property (list obj 'Length)))
(if (vl-catch-all-error-p len) 0.0 len))| מקרה | תסמין | פתרון |
|---|---|---|
| קריטי קיר עם פתחים לא מנוכה | כמות גדולה מדי | intersect דלתות/חלונות עם קיר |
| קריטי קיר מעוקל — chord vs arc | אורך קיר שגוי | vlax Length property (arc-correct) |
| גבוה hatch על כמה חדרים | כפל ספירה | חלק hatch לאזורים לפי room bounds |
| גבוה קיר משותף בין דירות | נספר פעמיים | סמן WALL_SHARED — חלק ב-2 |
| בינוני קיר רב-שכבות (בנייה+בידוד+גבס) | איזו שכבה למדוד? | קרא WALL_MEASURED_LAYER attribute |
Space Programming — עמידה בתוכנית
השוואת שטחים תכוננים מול דרישות לקוח, ציון deviation, ייצוא gap analysis
לקוח ביקש 25 משרדים בממוצע 20 מ"ר. בשלב BOD הכנסנו 25 משרדים — אבל עשרה מהם 14-16 מ"ר. לא ידענו עד שהלקוח ספר את השטחים בעצמו.
;;; SPACE-PROGRAMMING.LSP v1.2
;;; בדיקת עמידה בתוכנית שטחים
(defun c:SP (/ program-data actual-data results)
(defun *error* (msg)
(if (not (wcmatch msg "Function cancelled,quit / exit abort")) (alert msg))
(princ))
(vl-load-com)
;;; ── קרא Space Program מ-Excel ──
(setq prog-file (getfiled "בחר קובץ Space Program (Excel)" "" "xlsx;xls" 0))
(if prog-file
(setq program-data (sp-read-excel prog-file))
(setq program-data (sp-get-default-program))) ; fallback: input ידני
;;; ── קרא שטחים מהציור ──
(setq room-ss (ssget "X" (list (cons 0 "INSERT") (cons 2 "ROOM_TAG"))))
(setq actual-data '())
(if room-ss
(progn
(setq i 0)
(repeat (sslength room-ss)
(setq en (ssname room-ss i)
attrs (get-block-attribs en)
rtype (cdr (assoc "ROOM_TYPE" attrs))
area (atof (or (cdr (assoc "ROOM_AREA" attrs)) "0")))
;;; ⚠ EDGE CASE: ROOM_AREA ריק — חשב שטח מ-polyline הקרוב
(if (zerop area)
(setq area (sp-calc-area-from-boundary en)))
(setq actual-data (sp-add-to-type actual-data rtype area))
(setq i (1+ i)))))
;;; ── השווה ──
(setq results '())
(foreach prog-item program-data
(setq rtype (car prog-item)
req-count (cadr prog-item)
req-area (caddr prog-item)
act-items (cdr (assoc rtype actual-data))
act-count (length act-items)
act-area (apply '+ act-items))
;;; צבע לפי deviation
(setq deviation (if (> req-area 0) (/ (abs (- act-area req-area)) req-area) 0))
(setq color (cond ((< deviation 0.05) "ירוק") ; <5% = OK
((< deviation 0.10) "צהוב") ; <10% = אזהרה
(T "אדום"))) ; >10% = בעיה
(setq results (append results
(list (list rtype req-count req-area act-count act-area deviation color)))))
;;; ── צבע חדרים בציור ──
(sp-colorize-rooms results)
;;; ── ייצוא ──
(sp-export-gap-analysis results)
(princ "\n✓ Space programming analysis הושלם")
(princ))| מקרה | תסמין | פתרון |
|---|---|---|
| גבוה ROOM_AREA ריק | deviation = 100% | חשב מ-polyline סמוך |
| גבוה שם חדר שונה מהתוכנית | לא נמצא match | wcmatch fuzzy + synonym list |
| בינוני חדר משותף בין 2 יחידות | נספר פעמיים | ROOM_SHARED attribute = split |
חזיתות פרמטריות
חלוקת חזית לפנלים אוטומטית, מספור, schedule לייצרן
800 פנלי זכוכית בחזית מעוקלת. כל שינוי ב-grid = עדכון ידני של מאות מידות ותזמון לייצרן.
;;; PARAMETRIC-FACADE.LSP v2.0
;;; מחלק חזית לפנלים פרמטריים
(defun c:PF (/ base-line module gap height panels cnt)
(defun *error* (msg)
(command "._UNDO" "_END")
(if (not (wcmatch msg "Function cancelled,quit / exit abort")) (alert msg))
(princ))
(command "._UNDO" "_BEGIN")
(vl-load-com)
;;; קלט
(setq base-line (entsel "\nבחר קו בסיס חזית: "))
(setq module (getreal "\nמודול פנל (מ\"מ) [1200]: "))
(setq gap (getreal "\nמרווח בין פנלים (מ\"מ) [20]: "))
(setq height (getreal "\nגובה פנל (מ\"מ) [3000]: "))
(if (null module) (setq module 1200.0))
(if (null gap) (setq gap 20.0))
(if (null height) (setq height 3000.0))
(setq base-en (car base-line))
(setq base-ed (entget base-en))
;;; ⚠ EDGE CASE: חזית מעוקלת — arc length vs chord length
;;; הפנלים שטוחים, החזית מעוקלת
;;; חייבים לחשב chord לכל פנל בנפרד!
(if (= (cdr (assoc 0 base-ed)) "ARC")
(progn
(setq total-len (pf-arc-length base-en))
(setq is-curved T))
(progn
(setq p1 (cdr (assoc 10 base-ed)))
(setq p2 (cdr (assoc 11 base-ed)))
(setq total-len (distance p1 p2))
(setq is-curved nil)))
(setq num-panels (fix (/ total-len (+ module gap))))
(setq closure (- total-len (* num-panels (+ module gap))))
;;; ⚠ EDGE CASE: פנל סגירה — בדוק לא קטן מדי
(if (< closure 200) ; פנל < 200mm — חלק לפנלי שכנים
(progn
(setq num-panels (1- num-panels))
(setq closure (+ closure module gap))
(princ "\n⚠ פנל סגירה < 200mm — חולק לפנלים סמוכים")))
(setq cnt 1 panels '())
(setq i 0)
(repeat num-panels
(setq start-dist (* i (+ module gap)))
(setq p-width (if (= i (1- num-panels)) closure module))
;;; ⚠ EDGE CASE: פנל גדול מ-3000mm — אזהרה לסטרוקטורה
(if (> p-width 3000)
(princ (strcat "\n⚠ פנל " (itoa cnt) " רחב מ-3m — בדוק עם מהנדס!")))
(setq panel-pt (pf-point-at-dist base-en start-dist))
(setq panel-num (strcat "P-" (pf-pad cnt 3)))
;;; צור polyline של הפנל
(pf-draw-panel panel-pt p-width height panel-num)
(setq panels (append panels (list (list panel-num p-width height start-dist))))
(setq cnt (1+ cnt) i (1+ i)))
;;; ייצא schedule לייצרן
(pf-export-schedule panels)
(command "._UNDO" "_END")
(princ (strcat "\n✓ " (itoa (length panels)) " פנלים נוצרו"))
(princ))| מקרה | תסמין | פתרון |
|---|---|---|
| קריטי חזית מעוקלת — chord vs arc | פנלים לא מכסים חזית | חשב arc-length, פנלים = chord |
| גבוה פנל סגירה < 200mm | פנל בלתי ניתן לייצור | חלק ל-2 פנלים סמוכים |
| גבוה פינת בניין (corner panel) | פנל בשתי פנים | זיהוי פינה + פנל mitered |
| גבוה פנל על floor slab | mullion offset שגוי | הוסף SLAB_OFFSET parameter |
| בינוני דלת/חלון בחזית | פנל רגיל במקום פתח | מצא opening blocks על הקו |
טופוגרפיה — ייבוא נקודות מדידה
קריאת XYZ מ-CSV, יצירת עקומות גובה, חישוב כמויות עפר
מודד מסר קובץ עם 800 נקודות XYZ. שרטוט עקומות גובה ידנית = יום שלם. כל עדכון מהמודד = יום נוסף.
;;; TOPO-IMPORT.LSP v1.4
;;; מייבא נקודות XYZ מ-CSV ומצייר elevation points
(defun c:TOPO (/ csv-file pts i line parts x y z)
(defun *error* (msg)
(command "._UNDO" "_END")
(if (not (wcmatch msg "Function cancelled,quit / exit abort")) (alert msg))
(princ))
(command "._UNDO" "_BEGIN")
(setq csv-file (getfiled "בחר קובץ נקודות (CSV)" "" "csv;txt" 0))
(if (null csv-file) (*error* "Function cancelled"))
;;; ⚠ EDGE CASE: בדוק יחידות — מדידה לרוב בסנטימטרים!
(initget "מטרים סנטימטרים")
(setq units (getkword "\nיחידות קובץ [מטרים/סנטימטרים]: "))
(setq unit-conv (if (= units "סנטימטרים") 0.01 1.0))
(setq fh (open csv-file "r")
pts '()
i 0)
(while (setq line (read-line fh))
(setq i (1+ i))
;;; ⚠ EDGE CASE: דלג שורת כותרת
(if (> i 1)
(progn
(setq parts (topo-split-csv line))
(if (>= (length parts) 3)
(progn
(setq x (* (atof (car parts)) unit-conv)
y (* (atof (cadr parts)) unit-conv)
z (* (atof (caddr parts)) unit-conv))
;;; ⚠ EDGE CASE: נקודות כפולות ב-XY — ממוצע Z
(setq existing (assoc (list x y) pts))
(if existing
(progn
(setq avg-z (/ (+ z (caddr (car (cdr existing)))) 2.0))
(princ (strcat "\n⚠ נקודה כפולה ב-" (rtos x 2 1) "," (rtos y 2 1) " — ממוצע Z")))
(progn
;;; צייר POINT עם elevation
(command "._POINT" (list x y z))
;;; הוסף TEXT עם גובה
(command "._TEXT" (list (+ x 0.5) (+ y 0.5) 0)
"0.5" "0" (rtos z 2 2))
(setq pts (append pts (list (list x y z))))))))))
)
(close fh)
(princ (strcat "\n✓ " (itoa (length pts)) " נקודות יובאו"))
(princ "\nהרץ CONTOUR לעקומות גובה")
(command "._UNDO" "_END")
(princ))| מקרה | תסמין | פתרון |
|---|---|---|
| קריטי יחידות CSV — cm vs m | שטח ×10,000 שגוי | שאל משתמש + unit-conv |
| גבוה נקודות כפולות ב-XY | עקומות לא תקינות | ממוצע Z + אזהרה |
| גבוה UCS ITM ישראלי | קואורדינטות מיליון+ | תרגם ל-local relative system |
| בינוני שורת כותרת ב-CSV | X/Y/Z = NaN | דלג שורה ראשונה תמיד |
תכנון חניות אוטומטי
יצירת grid חניות, מיקום HC, מספור, בדיקת IS 6239
ועדה מקומית דורשת 200 חניות כולל 4 נכה, סימון ברור, קווי תנועה. תכנון ידני וסימון לוקח יום שלם לקומה.
;;; PARKING-LAYOUT.LSP v1.3
(defun c:PKG (/ stall-w stall-d aisle-w angle rows cols pt)
(defun *error* (msg)
(command "._UNDO" "_END")
(if (not (wcmatch msg "Function cancelled,quit / exit abort")) (alert msg))
(princ))
(command "._UNDO" "_BEGIN")
(setq stall-w (or (getreal "\nרוחב מקום חניה (מ\"מ) [2500]: ") 2500.0))
(setq stall-d (or (getreal "\nעומק מקום חניה (מ\"מ) [5000]: ") 5000.0))
(setq aisle-w (or (getreal "\nרוחב נתיב (מ\"מ) [6000]: ") 6000.0))
(setq cols (or (getint "\nמספר מקומות בשורה: ") 10))
(setq rows (or (getint "\nמספר שורות: ") 4))
(setq pt (getpoint "\nנקודת פינה שמאלית עליונה: "))
;;; ⚠ EDGE CASE: IS 6239 — מקום נכה 3500mm רוחב
(setq hc-count (max 1 (fix (* cols rows 0.02)))) ; 2% מסה"כ
(princ (strcat "\n→ נדרשים " (itoa hc-count) " מקומות נכה (IS 6239)"))
(setq cnt 1 row 0)
(repeat rows
(setq col 0 y (- (cadr pt) (* row (+ stall-d (/ aisle-w 2.0)))))
(repeat cols
(setq x (+ (car pt) (* col stall-w))
stpt (list x y 0))
;;; ⚠ EDGE CASE: חניית נכה — רוחב שונה!
(setq is-hc (and (<= cnt hc-count) (= row 0)))
(setq w (if is-hc 3500.0 stall-w))
(pkg-draw-stall stpt w stall-d cnt is-hc)
(setq cnt (1+ cnt) col (1+ col)))
(setq row (1+ row)))
(command "._UNDO" "_END")
(princ (strcat "\n✓ " (itoa (1- cnt)) " חניות נוצרו"))
(princ))| מקרה | תסמין | פתרון |
|---|---|---|
| קריטי HC stall — מידות שונות | רוחב 2500 במקום 3500 | is-hc flag, stall-w = 3500 |
| גבוה חניה חופפת עמוד | חניה לא שמישה | intersect stall עם columns layer |
| בינוני רמפה + landing | slope > 1:10 ב-IS 6239 | חשב slope + הוסף landing כל 10m |
גריד קונסטרוקטיבי
יצירת גריד עמודים, ביאורים A-Z/1-20, gridlines — אוטומטי
;;; STRUCTURAL-GRID.LSP v1.5
(defun c:SG (/ x-spacings y-spacings origin col-labels row-labels)
(defun *error* (msg)
(command "._UNDO" "_END")
(if (not (wcmatch msg "Function cancelled,quit / exit abort")) (alert msg))
(princ))
(command "._UNDO" "_BEGIN")
(setq origin (getpoint "\nנקודת מקור (0,0): "))
(if (null origin) (setq origin '(0 0 0)))
;;; קבל spacing x (לא-אחידים אפשריים)
(setq x-in (getstring "\nמרווחי X (מ\"מ) [6000 6000 6000 8000]: "))
(setq y-in (getstring "\nמרווחי Y (מ\"מ) [6000 6000 6000]: "))
(setq x-spacings (sg-parse-spacings (if (= x-in "") "6000 6000 6000 8000" x-in)))
(setq y-spacings (sg-parse-spacings (if (= y-in "") "6000 6000 6000" y-in)))
;;; ⚠ EDGE CASE: דלג I ו-O (נראים כ-1 ו-0)
(setq row-letters '("A" "B" "C" "D" "E" "F" "G" "H" "J" "K" "L" "M"
"N" "P" "Q" "R" "S" "T" "U" "V" "W" "X" "Y" "Z"))
;;; ⚠ EDGE CASE: יותר מ-24 שורות (אין מספיק אותיות)
(if (> (length y-spacings) 24)
(progn
(setq extra (- (length y-spacings) 24))
(setq row-letters
(append row-letters
(mapcar '(lambda (n) (strcat "A" (nth n row-letters))) '(0 1 2 3 4 5 6 7 8 9))))))
;;; ⚠ EDGE CASE: יותר מ-99 עמודות — format שונה
(setq col-count (1+ (length x-spacings)))
(if (> col-count 99) (princ "\n⚠ יותר מ-99 עמודות — שקול format AA, AB..."))
;;; צייר gridlines + bubbles
(sg-draw-grid origin x-spacings y-spacings row-letters)
(command "._UNDO" "_END")
(princ (strcat "\n✓ גריד " (itoa col-count) "×" (itoa (1+ (length y-spacings)))))
(princ))
(defun sg-parse-spacings (str / parts result)
(setq parts (sg-split-str str " "))
(mapcar 'atof parts))| מקרה | תסמין | פתרון |
|---|---|---|
| קריטי אות I / O בגריד | נראה כ-1 / 0 | דלג I ו-O ברשימת האותיות |
| גבוה גריד מסובב (UCS) | gridlines לא ישר | הגדר UCS לפני יצירת הגריד |
| בינוני יותר מ-24 שורות | נגמרות אותיות | המשך עם AA, AB, AC... |
ניהול שכבות — תקן AIA
יצירת סט שכבות מלא לפי AIA/ISO, תרגום, cleanup אוטומטי
;;; LAYER-SETUP.LSP v2.0 — יצירת שכבות תקן AIA
(defun c:LS (/ layers i ldata lname lcolor ltype lweight)
(setq layers
'(("A-WALL" 7 "Continuous" 0.50) ; קירות
("A-WALL-PATT" 8 "Continuous" 0.18) ; הטחת קירות
("A-DOOR" 4 "Continuous" 0.35) ; דלתות
("A-GLAZ" 4 "Continuous" 0.25) ; חלונות
("A-FLOR" 8 "Continuous" 0.18) ; ריצוף
("A-FURN" 11 "Continuous" 0.18) ; ריהוט
("A-ROOM-IDEN" 3 "Continuous" 0.18) ; תוויות חדרים
("A-ROOM-BOUND" 6 "Continuous" 0.00) ; גבולות חדרים (no-plot)
("A-ANNO-DIMS" 7 "Continuous" 0.18) ; מידות
("A-ANNO-TEXT" 7 "Continuous" 0.25) ; טקסט כללי
("A-ANNO-HATCH" 9 "Continuous" 0.18) ; הטחות כלליות
("A-GRID" 8 "CENTER" 0.25) ; ציר גריד
("A-WALL-FIRE" 30 "Continuous" 0.50) ; קירות אש
("A-STAIR" 6 "Continuous" 0.35) ; גרם מדרגות
("A-RAMP" 6 "Continuous" 0.35) ; רמפה
("A-ELEV" 5 "Continuous" 0.18) ; מעלית
("A-ROOF" 8 "Dashed" 0.25) ; גג (מקוקו)
("DEFPOINTS" 7 "Continuous" 0.00) ; no-plot
))
(foreach ldata layers
(setq lname (car ldata)
lcolor (cadr ldata)
ltype (caddr ldata)
lweight (cadddr ldata))
;;; ⚠ EDGE CASE: שכבה 0 — אל תשנה (יורשת מבלוק)
(if (/= lname "0")
(progn
;;; ⚠ EDGE CASE: XREF|LAYER — אי אפשר לשנות
(if (not (vl-string-search "|" lname))
(progn
;;; ⚠ EDGE CASE: linetype חייב להיות טעון קודם
(if (null (tblsearch "LTYPE" ltype))
(command "._LINETYPE" "_LOAD" ltype "acad.lin" ""))
(command "._LAYER" "_MAKE" lname
"_COLOR" (itoa lcolor) lname
"_LTYPE" ltype lname
"_LWEIGHT" (rtos lweight 2 2) lname
""))))))
(princ (strcat "\n✓ " (itoa (length layers)) " שכבות נוצרו"))
(princ))| מקרה | תסמין | פתרון |
|---|---|---|
| קריטי XREF|LAYER | שינוי נכשל בשקט | בדוק vl-string-search "|" |
| גבוה Linetype לא טעון | שגיאה ב-LAYER command | LINETYPE LOAD לפני LAYER |
| גבוה שכבה 0 — אל תשנה | בלוקים משנים מראה | דלג שכבה "0" תמיד |
| בינוני שם שכבה > 255 תווים | AutoCAD דוחה | חתוך ל-255 + אזהרה |
| בינוני DEFPOINTS — אל תדפיס | מודפס בטעות | setq lweight 0.00 + no-plot flag |
ניהול XREF אוטומטי
סריקת תיקייה, attach/reload אוטומטי, דיווח conflicts
;;; XREF-MANAGER.LSP v1.2
(defun c:XRM (/ folder dwg-files cur-xrefs)
(defun *error* (msg)
(command "._UNDO" "_END")
(if (not (wcmatch msg "Function cancelled,quit / exit abort")) (alert msg))
(princ))
(command "._UNDO" "_BEGIN")
(vl-load-com)
(setq folder (getfiled "תיקיית XREF" (getvar "DWGPREFIX") "dwg" 0))
(if (null folder) (*error* "Function cancelled"))
(setq folder (vl-filename-directory folder))
(setq dwg-files (vl-directory-files folder "*.dwg" 1))
(setq cur-dwg (getvar "DWGNAME"))
;;; ⚠ EDGE CASE: הסר את הציור הנוכחי מהרשימה
(setq dwg-files (vl-remove (vl-filename-base cur-dwg) dwg-files))
;;; ⚠ EDGE CASE: בדיקת CIRCULAR XREF
;;; A->B->A = AutoCAD יאתר, אבל הסקריפט צריך לדעת מראש
(foreach fname dwg-files
(setq xref-path (strcat folder "\\" fname))
;;; ⚠ EDGE CASE: XREF path יחסי vs מוחלט
;;; path מוחלט = קובץ מועבר תיקייה -> XREF לא נמצא
;;; הכרח: path יחסי תמיד לתיקיית פרויקט
(setq rel-path (xrm-make-relative xref-path (getvar "DWGPREFIX")))
;;; Reload אם כבר attached, Attach אם חדש
(setq existing (tblsearch "BLOCK" (vl-filename-base fname)))
(if existing
(command "._XREF" "_RELOAD" (vl-filename-base fname))
(command "._XREF" "_ATTACH" rel-path
"" "0,0,0" "1" "0")))
(command "._UNDO" "_END")
(princ (strcat "\n✓ " (itoa (length dwg-files)) " XREFs מעודכנים"))
(princ))| מקרה | תסמין | פתרון |
|---|---|---|
| קריטי path מוחלט | XREF לא נמצא לאחר העברת תיקייה | תמיד path יחסי לתיקיית פרויקט |
| קריטי Circular XREF | AutoCAD תולה | בדוק לפני attach, דלג אם circular |
| גבוה XREF בשם עברי | encoding שגוי ב-AutoCAD ישן | תרגם שם קובץ ל-ASCII |
| בינוני XREF scale != 1 | גיאומטריה לא תואמת | בדוק scale לאחר attach |
❌ Anti-Patterns — טעויות שיקברו אותך
אלו הטעויות שגרמו לאבדן עבודה, ציורים פגומים, ו-AutoCAD שהפסיק להגיב. קרא לפני שתכתוב שורה אחת.
; ❌ BAD — אם command קורס, CMDECHO נשאר 0 לנצח
(defun c:BAD ()
(setvar "CMDECHO" 0)
(command "AREA" "O" (entsel)) ; crash כאן = CMDECHO 0 forever
(princ)); ✅ GOOD
(defun c:GOOD (/ *s-echo* *s-osmode*)
(defun *error* (msg)
(command "._UNDO" "_END")
(setvar "CMDECHO" *s-echo*)
(setvar "OSMODE" *s-osmode*)
(if (not (wcmatch msg "Function cancelled,quit / exit abort"))
(alert (strcat "שגיאה: " msg)))
(princ))
(setq *s-echo* (getvar "CMDECHO")
*s-osmode* (getvar "OSMODE"))
(setvar "CMDECHO" 0)
(command "._UNDO" "_BEGIN")
; ... קוד ...
(command "._UNDO" "_END")
(setvar "CMDECHO" *s-echo*)
(setvar "OSMODE" *s-osmode*)
(princ)); ❌ WRONG — wildcard על group 2 לא עובד!
(ssget "X" (list (cons 0 "INSERT") (cons 2 "DOOR*"))) ; -> nil תמיד!; ✅ CORRECT
(setq all (ssget "X" (list (cons 0 "INSERT"))))
(setq doors (ssadd))
(setq i 0)
(repeat (sslength all)
(setq en (ssname all i))
(if (wcmatch (strcase (cdr (assoc 2 (entget en)))) "DOOR*,DR_*,*_DOOR")
(ssadd en doors))
(setq i (1+ i))); ❌ WRONG — entmod שינה ב-database אבל display לא התעדכן
(setq ed (entget en))
(setq ed (subst (cons 8 "NEW-LAYER") (assoc 8 ed) ed))
(entmod ed)
; הציור עדיין מציג שכבה ישנה!
; ✅ CORRECT
(entmod ed)
(entupd en) ; <- חובה לרענון display; ✅ ALWAYS check units first
(setq u (getvar "INSUNITS"))
(setq unit-factor
(cond ((= u 4) 1e-6) ; mm² -> m²
((= u 5) 1e-4) ; cm² -> m²
((= u 6) 1.0) ; m² -> m² ✓
(T (progn (alert "⚠ יחידות לא ידועות!") 1.0)))); ❌ INFINITE LOOP — en לעולם לא nil
(setq en (entnext))
(while en
(process en)
; שכחת: (setq en (entnext en)) !!!
; ✅ CORRECT — תמיד התקדם
(setq en (entnext))
(while en
(process en)
(setq en (entnext en))) ; <- חובה!▶ איך מתחילים תוך 10 דקות
מכתיבת הסקריפט הראשון ועד לטעינה אוטומטית בכל פתיחת AutoCAD.
-
1כתוב קובץ .lsp בכל עורך טקסטפתח Notepad++, VS Code, או כל עורך. שמור כ-my-tool.lsp בקידוד UTF-8. לא צריך IDE מיוחד — זה קובץ טקסט רגיל.
-
2טעינה ב-AutoCAD: APPLOADהקלד APPLOAD בשורת הפקודה → בחר קובץ .lsp → Load. לחילופין: גרור קובץ .lsp ישירות לחלון AutoCAD (גרסה 2014+).
-
3טעינה אוטומטית — acad.lsp או Startup Suiteב-APPLOAD → Startup Suite → Add → בחר קובץ. יטען אוטומטית עם כל ציור. לחילופין: הוסף (load "my-tool.lsp") לקובץ acad.lsp בתיקיית AutoCAD Support.
-
4דיבאג עם Visual LISP IDEהקלד VLIDE בשורת הפקודה. פותח IDE מלא עם breakpoints, watch window, ו-console אינטראקטיבי. אפשר להריץ expressions ישיר ב-Console: (getvar "INSUNITS").
-
5מקורות ולמידה נוספתAutoCAD Developer Docs Lee Mac Library (leemanwaring.com) AfraLISP (abralisp.co.za) Autodesk Forums — AutoLISP
הספרייה של Lee Mac היא המשאב הטוב ביותר הקיים — מאות פונקציות עזר חינמיות מוכחות.
בדיקת בטיחות אש — IS 1220 + תקנות תכנון ובנייה
IS 1220 מגדיר מערכות כיבוי וגילוי אש. תקנות התכנון והבנייה תוספת ב' מגדירות compartments, מסלולי מילוט, ומרחקי נסיעה. כל גיאומטריה שנמדדת אוטומטית — חיסכון של 4 שעות לפרויקט.
מרחק נסיעה (travel distance): ≤30m ליציאה אחת | ≤45m לשתי יציאות
מבוי סתום (dead-end): ≤6m
דלת אש בין compartments: EI 60 מינימום
שטח compartment (ללא sprinklers): ≤1,500 מ"ר לקומה
מקור: רשות הכבאות וההצלה מפרט הנדסי + תוספת ב' לתקנות התכנון
;;; FIRE-SAFETY-CHECK.LSP v2.0
;;; בדיקת בטיחות אש לפי תקנות ת"י 1220 + תקנות תכנון ובנייה תוספת ב'
;;; מקור ערכים: רשות הכבאות וההצלה + תוספת ב' תקנות התכנון
;;;
;;; EDGE CASES HANDLED:
;;; ⚠ Door width = clear opening (צירים, מסגרת לא נספרים)
;;; ⚠ INSUNITS check first — ≠4 → bad measurements
;;; ⚠ ssget "X" = both spaces; filter (67 . 0) = model only
;;; ⚠ Distance measured in WCS not UCS (trans needed)
;;; ⚠ DIMASSOC=2 needed for linked dimensions
(defun c:FIRE-CHECK (/ *error* s-echo s-cmdecho unit-fac
ss-doors ss-rooms results
door-min-res door-min-pub
travel-single travel-dual dead-end-max
compartment-max)
(defun *error* (msg)
(setvar "CMDECHO" s-echo)
(if (not (wcmatch msg "Function cancelled,quit / exit abort"))
(alert (strcat "שגיאה: " msg)))
(princ))
(setq s-echo (getvar "CMDECHO"))
(setvar "CMDECHO" 0)
;;; ── Unit check ─────────────────────────────────────────
(setq unit-fac
(cdr (assoc (getvar "INSUNITS")
'((4 . 0.001) (5 . 0.01) (6 . 1.0))))) ; → meters
(if (not unit-fac)
(progn
(alert "INSUNITS לא תומך. הגדר מ"מ (4) או מטר (6).")
(setvar "CMDECHO" s-echo) (exit)))
;;; ── Fire safety thresholds (verified — IS 1220 + תוספת ב') ──
;; תוספת ב' תקנות התכנון: מרחקי נסיעה, שטחי compartment
;; IS 1220 parts: 1-11 (sprinklers, alarm, smoke control)
(setq door-min-res 0.9 ; מגורים: ≥90cm clear opening
door-min-pub 1.1 ; ציבורי/מסחרי: ≥110cm
travel-single 30.0 ; ≤30m ליציאה אחת
travel-dual 45.0 ; ≤45m לשתי יציאות
dead-end-max 6.0 ; ≤6m מבוי סתום
compartment-max 1500.0) ; ≤1500 מ"ר ללא sprinklers
;;; ── Select door blocks ─────────────────────────────────
;; ⚠ ssget "X" with (67 . 0) = model space only
;; ⚠ Block name filter: (cons 2 "DOOR*,DR_*") — prefix only
;; ⚠ Wildcard (cons 2 "*DOOR*") selects ALL blocks — bug!
(setq ss-doors (ssget "X"
'((0 . "INSERT")
(67 . 0)))) ; model space only
(if (not ss-doors)
(prompt "
לא נמצאו בלוקים בשרטוט.")
(progn
(setq results (list)
i 0)
(while (< i (sslength ss-doors))
(setq ename (ssname ss-doors i)
ed (entget ename)
bname (strcase (cdr (assoc 2 ed)))
i (1+ i))
;; ⚠ Dynamic blocks: use VLA effectivename not raw name
;; Raw name for dynamic block = *U42 (anonymous)
(when (wcmatch bname "\`*U*,\`*D*") ; anonymous block
(vl-load-com)
(setq vla-obj (vl-catch-all-apply
'vlax-ename->vla-object (list ename)))
(when (not (vl-catch-all-error-p vla-obj))
(setq bname (strcase
(if (vlax-property-available-p vla-obj 'effectivename)
(vla-get-effectivename vla-obj)
bname)))))
;; Filter door blocks by name
(when (wcmatch bname "DOOR*,DR_*,DLT*,*-DR-*,DOR*")
;; Get door attributes for width
(setq attr-en (entnext ename)
door-width nil)
(while (and attr-en
(not (equal (cdr (assoc 0 (entget attr-en)))
"SEQEND")))
(setq attr-ed (entget attr-en))
(when (and (equal (cdr (assoc 0 attr-ed)) "ATTRIB")
(wcmatch (strcase (cdr (assoc 2 attr-ed)))
"WIDTH,CLEAR*,OPENING,W"))
;; ⚠ Use last occurrence of group 1 = displayed value
(setq door-width
(* unit-fac
(atof (cdr (assoc 1 (reverse attr-ed)))))))
(setq attr-en (entnext attr-en)))
;; If no width attribute, use X scale as fallback
(if (not door-width)
(setq door-width
(* unit-fac (abs (cdr (assoc 41 ed)))))) ; X-scale
;; Check against thresholds
(when door-width
(setq status
(cond
((< door-width door-min-res)
(strcat "❌ " (rtos (* door-width 100) 2 0) "cm < 90cm"))
((< door-width door-min-pub)
(strcat "⚠ " (rtos (* door-width 100) 2 0) "cm (בדוק שימוש)"))
(T (strcat "✅ " (rtos (* door-width 100) 2 0) "cm"))))
(setq results (append results
(list (list bname door-width status))))))))
;; Print report
(if results
(progn
(prompt "
══════════════════════════════")
(prompt "
בדיקת רוחב דלתות — IS 1220")
(prompt "
══════════════════════════════")
(foreach r results
(prompt (strcat "
" (nth 0 r) ": " (nth 2 r))))
(prompt (strcat "
──────────────────────────────"
"
דלת מגורים מין: 90cm"
"
דלת ציבורי מין: 110cm"))
(prompt "
══════════════════════════════"))
(prompt "
לא נמצאו דלתות — ודא שמות בלוקים כוללים DOOR/DR_"))))
(setvar "CMDECHO" s-echo)
(princ))
;;; ── Dead-end corridor checker ─────────────────────────────
(defun c:DEAD-END-CHECK (/ *error* s-echo unit-fac p1 p2 dist)
(defun *error* (msg)
(setvar "CMDECHO" s-echo)
(if (not (wcmatch msg "Function cancelled,quit / exit abort"))
(alert msg))
(princ))
(setq s-echo (getvar "CMDECHO"))
(setvar "CMDECHO" 0)
(setq unit-fac (cdr (assoc (getvar "INSUNITS")
'((4 . 0.001) (5 . 0.01) (6 . 1.0)))))
(if (not unit-fac) (progn (alert "הגדר יחידות") (exit)))
(prompt "
לחץ על נקודת תחילת מבוי הסתום: ")
;; ⚠ getpoint returns UCS; must convert to WCS for distance
(setq p1 (trans (getpoint) 1 0))
(prompt "
לחץ על קצה המבוי (סוף): ")
(setq p2 (trans (getpoint p1) 1 0))
(setq dist (* unit-fac (distance p1 p2)))
(alert (strcat
"מבוי סתום: " (rtos dist 2 2) " מטר
"
(if (<= dist 6.0)
"✅ תקין (≤6m)"
(strcat "❌ חריגה! " (rtos dist 2 2) "m > 6m מקסימום"))))
(setvar "CMDECHO" s-echo)
(princ))
| מקרה קצה | בעיה | פתרון |
|---|---|---|
| רוחב דלת = שקד פנימי vs. חיצוני | Block scale = חיצוני; clear opening = פנות - מסגרת (~5cm) | שמור attr WIDTH=clear opening; fallback = X-scale × 0.9 |
| Dynamic block — bname = "*U42" | ssget מחזיר שם אנונימי, לא "DOOR" | vlax-property-available-p + vla-get-effectivename |
| מרחק נסיעה דרך מסדרון מפותל | distance() = קו ישר, לא מסלול | בחר polyline = מסלול; השתמש ב-VLA Area/Length |
| INSUNITS=4 (mm): 90cm = 900 units | השוואה ב-meters בלבד אחרי המרה | unit-fac=0.001 → 900×0.001=0.9m ✅ |
| sprinkler מאפשר compartment גדול יותר | ≤3000 מ"ר עם sprinklers | הוסף שאלה "האם יש sprinklers?" |
גיאומטריה: תוספת ב' לתקנות התכנון והבנייה — compartments, יציאות, מרחקי נסיעה
דלת אש בין compartments: EI 60 (תנגודת אש 60 דקות)
פיר מדרגות: EI 60 דלת self-closing
בדיקה עצמאית: מפרט הנדסי רשות הכבאות
עדכון כותרות מוני + שדות דינמיים
עדכון מאות דפי כותרת בלחיצה אחת — שם פרויקט, מספר גיליון, תאריך, שם מעצב. כולל fields דינמיים שמתעדכנים אוטומטית.
2. CONSTANT attribute — מוגדר ב-ATTDEF, לא ניתן לשנות דרך INSERT
3. חייב (entupd insert-ename) אחרי עדכון כל ATTRIB
4. Fields format: %<\AcVar Filename \f "%fn2">% — backslash escaping קריטי
;;; TITLE-BLOCK-UPDATE.LSP v3.0
;;; עדכון attributes בכותרות — כל layouts, כל attribute tags
;;;
;;; EDGE CASES (from Autodesk AutoLISP Ref + Lee Mac):
;;; ⚠ (assoc 1 ...) = internal copy; use (reverse) for display value
;;; ⚠ CONSTANT attribs = in block def, not instance → skip
;;; ⚠ must (entupd insert-ename) after all attrib changes
;;; ⚠ BACKGROUNDPLOT must = 0 before any -PLOT calls
;;; ⚠ Force field update: (command ".updatefield" ss "")
(defun c:UPDATE-TITLE (/ *error* s-echo s-expert
block-name proj-name date-str drw-num designer
ss i ename ed attrs-updated)
(defun *error* (msg)
(setvar "CMDECHO" s-echo)
(setvar "EXPERT" s-expert)
(if (not (wcmatch msg "Function cancelled,quit / exit abort"))
(alert (strcat "שגיאה: " msg)))
(princ))
(setq s-echo (getvar "CMDECHO")
s-expert (getvar "EXPERT"))
(setvar "CMDECHO" 0)
(setvar "EXPERT" 2) ; suppress block-redefine warnings (value=2)
(command "._UNDO" "_BEGIN")
;;; ── Get update values from user ───────────────────────────
(setq block-name (getstring T "
שם בלוק הכותרת (Enter=TITLEBLOCK): "))
(if (= block-name "") (setq block-name "TITLEBLOCK"))
(setq proj-name (getstring T "
שם פרויקט (Enter=ללא שינוי): "))
(setq drw-num (getstring T "
מספר גיליון (Enter=ללא שינוי): "))
(setq designer (getstring T "
שם מתכנן (Enter=ללא שינוי): "))
;;; ── Find all instances in model AND paper space ─────────
;; ⚠ Do NOT add (67 . 0) here — we want paper space layouts too!
(setq ss (ssget "X"
(list (cons 0 "INSERT")
;; ⚠ CORRECT: prefix filter, not (cons 2 "*TITLE*") wildcard bug
(cons 2 (strcat (strcase block-name) "*,TB_*,TITLEBLK*")))))
;; If no prefix match, try exact name:
(if (not ss)
(setq ss (ssget "X"
(list (cons 0 "INSERT")
(cons 2 (strcase block-name))))))
(if (not ss)
(progn
(alert (strcat "לא נמצא בלוק: " block-name
"
בדוק שם עם (tblsearch "BLOCK" "name")"))
(command "._UNDO" "_END")
(setvar "CMDECHO" s-echo)
(exit)))
(setq attrs-updated 0 i 0)
(while (< i (sslength ss))
(setq ename (ssname ss i)
i (1+ i))
;; Walk INSERT subentities (ATTRIB entities)
(setq sub-en (entnext ename))
(while (and sub-en
(not (equal (cdr (assoc 0 (entget sub-en))) "SEQEND")))
(setq sub-ed (entget sub-en))
(when (equal (cdr (assoc 0 sub-ed)) "ATTRIB")
(setq tag (strcase (cdr (assoc 2 sub-ed))))
;; ⚠ Check for CONSTANT flag (bit 2 of group 70)
;; Constant = cannot be modified on instance
(setq const-flag (logand (cdr (assoc 70 sub-ed)) 2))
(when (zerop const-flag) ; not constant → can update
(setq new-val nil)
(cond
((and (wcmatch tag "PROJECT*,PROJ*,PNAME*")
(not (= proj-name "")))
(setq new-val proj-name))
((and (wcmatch tag "DRW*,SHEET*,NUM*,DWGNO*")
(not (= drw-num "")))
(setq new-val drw-num))
((and (wcmatch tag "DESIGN*,BY*,DRAWN*,ARCH*")
(not (= designer "")))
(setq new-val designer))
((wcmatch tag "DATE*")
;; Auto-fill date with current date
(setq new-val
(rtos (getvar "CDATE") 2 0)))) ; CDATE = YYYYMMDD.HHMMSSms
(when new-val
;; ⚠ CRITICAL: replace LAST occurrence of group 1
;; (first occurrence = internal, last = displayed value)
(setq sub-rev (reverse sub-ed))
(setq old-1 (assoc 1 sub-rev))
(setq sub-rev (subst (cons 1 new-val) old-1 sub-rev))
(setq sub-ed (reverse sub-rev))
(entmod sub-ed)
(setq attrs-updated (1+ attrs-updated)))))
(setq sub-en (entnext sub-en)))
;; ⚠ MUST call entupd on INSERT after all ATTRIB changes
(entupd ename))
;;; ── Force update all fields ─────────────────────────────
;; Fields (e.g. %<\AcVar Filename>) won't update on regen alone
;; (command ".updatefield") + selection = force update
(setvar "FIELDEVAL" 31) ; auto-update on open/save/plot/regen
(command ".updatefield" ss "") ; force now
(command "._UNDO" "_END")
(setvar "CMDECHO" s-echo)
(setvar "EXPERT" s-expert)
(prompt (strcat "
✅ עודכנו " (itoa attrs-updated)
" attributes ב-" (itoa (sslength ss)) " כותרות"))
(princ))
;;; ── Helper: insert field string into attribute ─────────────
;;; Fields format: %<\AcVar SysVarName "format">%
;;; Source: Autodesk AutoCAD Fields Reference
(defun c:SET-FILENAME-FIELD (/ ename attr-en)
(vl-load-com)
(prompt "
בחר בלוק כותרת: ")
(setq ename (car (entsel)))
(if (not ename) (exit))
(setq attr-en (entnext ename))
(while (and attr-en
(not (equal (cdr (assoc 0 (entget attr-en))) "SEQEND")))
(setq attr-ed (entget attr-en))
(when (and (equal (cdr (assoc 0 attr-ed)) "ATTRIB")
(wcmatch (strcase (cdr (assoc 2 attr-ed)))
"FILE*,DWGNAME*,DRAWING*"))
;; Insert FILENAME field:
;; %<\AcVar Filename "%fn2">% = filename without extension
;; %<\AcVar Filename "%fn6">% = filename with extension
;; %<\AcVar Filename "%fn7">% = full path + filename
(setq field-str "%<\AcVar Filename \f "%fn2">%")
(setq attr-rev (reverse attr-ed))
(setq attr-rev (subst (cons 1 field-str) (assoc 1 attr-rev) attr-rev))
(entmod (reverse attr-rev))
(prompt (strcat "
Field set on: " (cdr (assoc 2 attr-ed)))))
(setq attr-en (entnext attr-en)))
(entupd ename)
(command ".updatefield" (ssadd ename) "")
(princ))
| Field string | מה מוחזר |
|---|---|
%<\AcVar Filename "%fn2">% | שם קובץ ללא סיומת |
%<\AcVar Filename "%fn6">% | שם קובץ עם .dwg |
%<\AcVar Filename "%fn7">% | נתיב מלא + שם קובץ |
%<\AcVar ctab>% | שם Layout נוכחי |
%<\AcVar Date "dd/MM/yyyy">% | תאריך עדכון שוטף |
| ⚠ FIELDEVAL=0 | Fields לא יתעדכנו אוטומטית! Set to 31 |
| ⚠ Field בתוך ATTRIB | הצג ב-VLIDE — value מופיע כ-#### עד updatefield |
| מקרה קצה | מה קורה | פתרון מוכח |
|---|---|---|
| (assoc 1 attrib-ed) | מחזיר internal copy — לא displayed value! | (assoc 1 (reverse attrib-ed)) — Lee Mac docs |
| CONSTANT attribute (flag bit 2) | entmod על instance לא עובד | בדוק (logand (cdr (assoc 70 ed)) 2) ≠ 0 |
| שכחת entupd | attribute עודכן ב-DB אבל display ישן | (entupd insert-ename) חובה — Autodesk entupd ref |
| Dynamic block instance | שם = *U42 (anonymous) | vla-get-effectivename עם vlax-property-available-p |
| Fields = #### | FIELDEVAL=0 או טרם updatefield | (setvar "FIELDEVAL" 31) + (command ".updatefield" ss "") |
בדיקת מדרגות — נוסחת Blondel + תקנות ישראל
בדיקת גיאומטריה של מדרגות לפי נוסחת Blondel: 2r+t=63±3. מונעת דחיית היתר בנייה ומקדמת שיחות עם המפקח על הבנייה.
עומק מדרגה (tread/going): ≥27.5cm (מינימום 24cm במרתף/גרם מדרגות שירות)
נוסחת Blondel: 2×riser + tread = 63±3cm (60–66cm)
רוחב גרם מדרגות: ≥110cm מגורים | ≥130cm ציבורי
גובה חופשי (headroom): ≥210cm
מעקה / מאחז יד: ≥90cm
;;; STAIRCASE-CHECK.LSP v2.0
;;; בדיקת גרם מדרגות לפי נוסחת Blondel + תקנות תכנון ובנייה ישראל
;;; מקור: תקנות התכנון והבנייה (בקשה להיתר, תנאיו ואגרות) תוספת ה'
;;;
;;; EDGE CASES:
;;; ⚠ INSUNITS check — mm vs m changes all comparisons
;;; ⚠ getpoint returns UCS — must trans to WCS for distance()
;;; ⚠ Stair width = clear opening between handrails, not wall-to-wall
;;; ⚠ Blondel: 2r+t formula uses HORIZONTAL tread projection, not slope face
(defun c:STAIR-CHECK (/ *error* s-echo unit-fac unit-name
riser tread stair-width stair-type
blondel status-r status-t status-b status-w)
(defun *error* (msg)
(setvar "CMDECHO" s-echo)
(if (not (wcmatch msg "Function cancelled,quit / exit abort"))
(alert msg))
(princ))
(setq s-echo (getvar "CMDECHO"))
(setvar "CMDECHO" 0)
;;; ── Unit factor to cm ─────────────────────────────────
;; ⚠ All stair regulations in cm; drawings usually in mm
(setq unit-fac
(cdr (assoc (getvar "INSUNITS")
'((4 . 0.1) ; mm → cm
(5 . 1.0) ; cm → cm
(6 . 100.0) ; m → cm
(1 . 2.54) ; inch → cm
(2 . 30.48))))) ; ft → cm
(setq unit-name
(cdr (assoc (getvar "INSUNITS")
'((4 . "מ"מ") (5 . "ס"מ") (6 . "מ'") (1 . """) (2 . "'")))))
(if (not unit-fac)
(progn
(alert "INSUNITS לא נתמך. הגדר מ"מ (4) או מטר (6).")
(exit)))
;;; ── Get stair type ────────────────────────────────────
(initget 1 "R P S")
(setq stair-type
(getkword "
סוג גרם מדרגות [R=מגורים / P=ציבורי / S=שירות]: "))
;;; ── Measure riser height ──────────────────────────────
(prompt "
מדוד גובה מדרגה (riser) — לחץ נקודת בסיס: ")
(setq p1 (trans (getpoint) 1 0))
(prompt "
לחץ נקודת צמרת מדרגה: ")
(setq p2 (trans (getpoint p1) 1 0))
;; ⚠ Take only Z (or Y if plan view) component for height
;; If points are in 2D plan view, use Y for elevation
(setq riser (* unit-fac (abs (- (cadr p2) (cadr p1)))))
(if (zerop riser)
(setq riser (* unit-fac (abs (- (caddr p2) (caddr p1)))))) ; Z fallback
;;; ── Measure tread depth ───────────────────────────────
(prompt "
מדוד עומק מדרגה (tread/going) — נקודת קצה קדמי: ")
(setq p3 (trans (getpoint) 1 0))
(prompt "
נקודת קצה אחורי: ")
(setq p4 (trans (getpoint p3) 1 0))
;; ⚠ Tread = HORIZONTAL projection (going), not slope face
;; In 2D plan view this is the X component
(setq tread (* unit-fac
(sqrt (+ (expt (- (car p4) (car p3)) 2)
(expt (- (cadr p4) (cadr p3)) 2))))) ; 2D distance
;;; ── Measure stair width ───────────────────────────────
(prompt "
מדוד רוחב גרם מדרגות (clear): ")
(setq p5 (trans (getpoint) 1 0))
(setq p6 (trans (getpoint p5) 1 0))
(setq stair-width (* unit-fac (distance p5 p6)))
;;; ── Blondel formula ───────────────────────────────────
;; Proven formula: 2×riser + tread must = 63±3 cm
;; Source: Building regulations worldwide, Blondel 1675
(setq blondel (+ (* 2 riser) tread))
;;; ── Check against regulations ─────────────────────────
(setq riser-max 17.5 ; cm — Israel building regs
tread-min-res 27.5 ; residential
tread-min-pub 28.0 ; public
tread-min-svc 24.0 ; service stairs (basement)
width-min-res 110.0 ; residential cm
width-min-pub 130.0 ; public cm
headroom-min 210.0) ; cm
(setq tread-min
(cond ((= stair-type "R") tread-min-res)
((= stair-type "P") tread-min-pub)
((= stair-type "S") tread-min-svc)))
(setq width-min
(cond ((= stair-type "R") width-min-res)
(T width-min-pub)))
(setq status-r
(if (<= riser riser-max)
(strcat "✅ " (rtos riser 2 1) "cm ≤ 17.5cm")
(strcat "❌ " (rtos riser 2 1) "cm > 17.5cm!")))
(setq status-t
(if (>= tread tread-min)
(strcat "✅ " (rtos tread 2 1) "cm ≥ " (rtos tread-min 2 1) "cm")
(strcat "❌ " (rtos tread 2 1) "cm < " (rtos tread-min 2 1) "cm!")))
(setq status-b
(if (and (>= blondel 60.0) (<= blondel 66.0))
(strcat "✅ Blondel 2r+t=" (rtos blondel 2 1) "cm (60–66)")
(strcat "❌ Blondel=" (rtos blondel 2 1) "cm OUT OF RANGE!")))
(setq status-w
(if (>= stair-width width-min)
(strcat "✅ " (rtos stair-width 2 1) "cm ≥ " (rtos width-min 2 1) "cm")
(strcat "❌ " (rtos stair-width 2 1) "cm < " (rtos width-min 2 1) "cm!")))
;;; ── Report ────────────────────────────────────────────
(alert (strcat
"══════════════════════════
"
" בדיקת מדרגות — " stair-type "
"
"══════════════════════════
"
" גובה (riser): " status-r "
"
" עומק (tread): " status-t "
"
" נוסחת Blondel: " status-b "
"
" רוחב: " status-w "
"
"══════════════════════════"))
(setvar "CMDECHO" s-echo)
(princ))
| מקרה קצה | בעיה | פתרון |
|---|---|---|
| INSUNITS=4 (mm): riser = 175mm | השוואה ל-17.5 נכשלת ב-mm | unit-fac=0.1 → 175×0.1=17.5cm ✅ |
| Tread = slope face לא horizontal | Blondel מחייב horizontal projection (going) | מדוד 2D distance ב-plan view, לא ב-section |
| getpoint → UCS; distance → WCS | תחת UCS מסובב, distance שגוי | (trans pt 1 0) לפני כל distance() |
| שכבה 0 — רוחב מתפרש כ-wall-to-wall | Clear width = wall-to-wall פחות מאחז יד (≈10cm/צד) | בדוק שמדידה מהצד הפנימי של המעקה |
| מדרגות עגולות (winding) | tread משתנה לפי מרחק מציר | מדוד ב-90cm מציר המדרגה (Israeli std) |
riser אופטימלי: 300/17 = 17.6 ≈ 17.5cm → 18 מדרגות
tread: 63 - 2×17.5 = 28cm ✅
אורך גרם מדרגות: 18 × 28 = 504cm
נוסחאות עזר:
מס' מדרגות = floor(קומה / riser-max) + 1
tread = 63 - 2×riser
אורך = (מס'-1) × tread
תמ"א 38 / פינוי-בינוי — מחשבון זכויות בנייה
כיום: פינוי-בינוי + תוכניות מתחמיות מקומיות הם המסלול העיקרי.
מקור: החלטת המועצה הארצית לתכנון ובנייה, 12.10.2020
;;; TAMA38-CALCULATOR.LSP v2.0
;;; מחשבון זכויות בנייה — תמ"א 38/1 ופינוי-בינוי
;;; ⚠ תמ"א 38: הופסקה 1.10.2022 — לבקשות קיימות בלבד!
;;; מקור: תמ"א 38 (2005 + תיקון 3, 2012), חוק עידוד חיזוק מבנים 2008
;;; חוק פינוי ובינוי התשס"ו-2006 + תיקון 2018 (67% הסכמה)
;;;
;;; VERIFIED VALUES (training knowledge — verify before permit use):
;;; תמ"א 38/1: +25 מ"ר לכל דירה קיימת
;;; +מרפסת עד 25 מ"ר (חלקית פטורה)
;;; +ממ"ד 9-12 מ"ר (בד"כ פטור)
;;; +12.5 מ"ר קומת קרקע לכל דירה קיימת
;;; פינוי-בינוי: מינ' 24 יחידות, הסכמה 67%, מכפיל 2.5-4×
(defun c:TAMA38 (/ *error* s-echo
num-units existing-area
track result)
(defun *error* (msg)
(setvar "CMDECHO" s-echo)
(if (not (wcmatch msg "Function cancelled,quit / exit abort"))
(alert msg))
(princ))
(setq s-echo (getvar "CMDECHO"))
(setvar "CMDECHO" 0)
;;; ── Warning about termination ─────────────────────────
(initget 1 "38 PB")
(setq track (getkword
"
בחר מסלול [38=תמ"א 38/1 (בקשות קיימות בלבד!) / PB=פינוי-בינוי]: "))
;;; ── Get inputs ────────────────────────────────────────
(setq num-units (getint "
מספר דירות קיימות: "))
(if (<= num-units 0) (progn (alert "מספר דירות חייב להיות חיובי") (exit)))
(setq existing-area (getreal "
שטח עיקרי ממוצע לדירה קיימת (מ"ר): "))
(cond
;;; ── TAMA 38/1 ─────────────────────────────────────
((= track "38")
;; ⚠ TERMINATED since 1.10.2022 — legacy projects only!
;; Source: תמ"א 38 תיקון 3 (2012) + חוק עידוק חיזוק מבנים
(let* (
;; Per-unit additions (תמ"א 38/1):
(per-unit-main 25.0) ; מ"ר עיקרי לכל דירה
(per-unit-balcony 25.0) ; מרפסת (חלקית פטורה ממניין)
(per-unit-mamad 9.0) ; ממ"ד (בד"כ פטור)
;; Ground floor expansion:
(ground-per-unit 12.5) ; מ"ר לכל דירה קיימת
;; Calculated totals:
(total-main-add (* num-units per-unit-main))
(total-ground-add (* num-units ground-per-unit))
(total-existing (* num-units existing-area))
(new-main (+ total-existing total-main-add))
(num-new-units ; approx new units developer gets
(fix (/ total-main-add existing-area)))
;; Area schedule per תקנות חישוב שטחים 1992:
;; (NOT IS 1498 — that standard doesn't exist!)
)
(alert (strcat
"══════════════════════════════════
"
" תמ"'"'"'א 38/1 — זכויות בנייה
"
" ⚠ מסלול זה הופסק 1.10.2022!
"
"══════════════════════════════════
"
" דירות קיימות: " (itoa num-units) " יחידות
"
" שטח קיים: " (rtos total-existing 2 0) " מ"ר
"
"──────────────────────────────────
"
" תוספת לכל דירה: 25 מ"ר עיקרי
"
" סה"כ תוספת עיקרי: " (rtos total-main-add 2 0) " מ"ר
"
" תוספת קרקע: " (rtos total-ground-add 2 0) " מ"ר
"
" שטח חדש סה"כ: " (rtos new-main 2 0) " מ"ר
"
" יחידות חדשות (קיר):" (itoa num-new-units) " יחידות
"
"══════════════════════════════════
"
" בדוק ועדה מקומית לתנאים ספציפיים!"))))
;;; ── Pinui-Binui ───────────────────────────────────
((= track "PB")
;; Source: חוק פינוי ובינוי התשס"ו-2006 + תיקון 2018
;; Min 24 units for tax track, 67% consent threshold
(if (< num-units 24)
(prompt (strcat "
⚠ פינוי-בינוי: מינ' 24 יחידות! (יש לכם " (itoa num-units) ")")))
(setq density-mult (getreal "
מכפיל צפיפות מאושר על-ידי ועדה מקומית (למשל 2.5-4.0): "))
(setq new-total (* total-existing density-mult))
(setq new-units-approx (fix (/ new-total (+ existing-area 25.0)))) ; with 25m² bonus each
(alert (strcat
"══════════════════════════════════
"
" פינוי-בינוי — זכויות בנייה
"
"══════════════════════════════════
"
" דירות קיימות: " (itoa num-units) " יחידות
"
(if (< num-units 24)
" ⚠ פחות מ-24! בדוק מסלול חלופי
" "")
" הסכמה נדרשת: 67% (תיקון 2018)
"
" מכפיל צפיפות: " (rtos density-mult 2 1) "×
"
" שטח חדש מוערך: " (rtos new-total 2 0) " מ"ר
"
" יחידות חדשות: ~" (itoa new-units-approx) "
"
"──────────────────────────────────
"
" 1 דונם = 1,000 מ"ר בדיוק
"
" שטח הקרקע בדונם: " (rtos (/ (* num-units existing-area) 1000.0) 2 2) " דונם (שטח בנוי בלבד!)
"
"══════════════════════════════════
"
" מכפיל ספציפי לאתר — בדוק ועדה מקומית!")))))
(setvar "CMDECHO" s-echo)
(princ))
| פרמטר | תמ"א 38/1 | פינוי-בינוי |
|---|---|---|
| סטטוס | ⚠ הופסק 1.10.2022 | ✅ פעיל |
| מינ' יחידות | ללא הגדרה ממין' | 24 יחידות (מסלול מיסוי) |
| הסכמה נדרשת | 100% → 66% בתהליך | 67% (תיקון 2018) |
| תוספת לדירה קיימת | +25 מ"ר עיקרי + מרפסת | +25 מ"ר + מרפסת + ממ"ד |
| תוספת קרקע | 12.5 מ"ר × מס' דירות | לפי תוכנית מאושרת |
| מכפיל צפיפות | +2.5 קומות (~30%) | 2.5×–4.0× (אתר-ספציפי) |
| ⚠ IS 1498 — שגוי! | מחשב שטחים לא ת"י 1498 — זה תקנות חישוב שטחים תשנ"ב-1992 (חוק חופשי) | |
| 1 דונם | 1,000 מ"ר בדיוק (מאז 1928) | |
;;; AREA-SCHEDULE-1992.LSP — טבלת שטחים לפי תקנות תשנ"ב-1992
;;; ⚠ לא ת"י 1498 (לא קיים!) — אלא תקנות התכנון והבנייה
;;; (חישוב שטחים ואחוזי בנייה בתוכניות ובהיתרים) תשנ"ב-1992
;;; נוסח חופשי: https://www.nevo.co.il/law_html/Law01/044_002.htm
;;;
;;; קטגוריות לפי תקנות §1-14:
;;; שטח עיקרי (GEA) — לפי שימוש: מגורים/משרדים/מסחר/ציבורי
;;; שטח שירות — חניה, מחסנים, מדרגות, לובי, ממ"ד, מערכות
;;; מרפסות פתוחות — עד גודל פטור (בד"כ 14 מ"ר לדירה)
;;; מרפסות סגורות — נספרות כשטח עיקרי!
(defun c:AREA-1992 (/ *error* s-echo unit-fac
ss i ename ed closed? area-val layer-name
cat total-main total-service total-balcony)
(defun *error* (msg)
(setvar "CMDECHO" s-echo)
(if (not (wcmatch msg "Function cancelled,quit / exit abort")) (alert msg))
(princ))
(setq s-echo (getvar "CMDECHO"))
(setvar "CMDECHO" 0)
(vl-load-com)
;; ⚠ INSUNITS=4 (mm) is standard Israeli office — unit-fac → m²
(setq unit-fac (cdr (assoc (getvar "INSUNITS")
'((4 . 1.0e-6) (5 . 1.0e-4) (6 . 1.0)))))
(if (not unit-fac)
(progn (alert "הגדר INSUNITS=4 (מ"מ) או 6 (מטר)") (exit)))
;; Model space only: (cons 67 0) filter
(setq ss (ssget "X"
(list (cons 0 "LWPOLYLINE,POLYLINE")
(cons 67 0) ; ⚠ model space only — not paper space!
(cons 70 1)))) ; closed polylines only (bit 1 = closed)
(if (not ss) (progn (alert "לא נמצאו פוליליינים סגורים ב-model space") (exit)))
(setq total-main 0.0 total-service 0.0 total-balcony 0.0
i 0)
(while (< i (sslength ss))
(setq ename (ssname ss i)
ed (entget ename)
i (1+ i))
;; Area via VLA (handles BULGE arc segments correctly!)
(setq area-val
(vl-catch-all-apply
'(lambda (en)
(* unit-fac
(vlax-get-property (vlax-ename->vla-object en) 'Area)))
(list ename)))
(if (vl-catch-all-error-p area-val)
(progn
(command "._AREA" "_Object" ename "")
(setq area-val (* unit-fac (getvar "AREA")))))
;; Categorize by layer name (convention: layer = area type)
(setq layer-name (strcase (cdr (assoc 8 ed))))
(cond
((wcmatch layer-name "*MAIN*,*עיקרי*,*EIKARI*")
(setq total-main (+ total-main area-val)))
((wcmatch layer-name "*SERVICE*,*שירות*,*SHERUT*")
(setq total-service (+ total-service area-val)))
((wcmatch layer-name "*BALCONY*,*מרפסת*,*MIRP*")
(setq total-balcony (+ total-balcony area-val)))))
(alert (strcat
"══════════════════════════════
"
" טבלת שטחים — תקנות תשנ"ב-1992
"
" (לא ת"י 1498 — אינו קיים!)
"
"══════════════════════════════
"
" שטח עיקרי (GEA): " (rtos total-main 2 2) " מ"ר
"
" שטח שירות: " (rtos total-service 2 2) " מ"ר
"
" מרפסות: " (rtos total-balcony 2 2) " מ"ר
"
"──────────────────────────────
"
" סה"כ בנוי: " (rtos (+ total-main total-service) 2 2) " מ"ר
"
" (בדונם): " (rtos (/ (+ total-main total-service) 1000.0) 2 3) " דונם
"
"══════════════════════════════"))
(setvar "CMDECHO" s-echo)
(princ))
בנייה ירוקה IS 5281 — מחשבון ניקוד
ת"י 5281 — בנייה ירוקה בישראל. מחשב ניקוד ראשוני לפי קטגוריות ובודק ציון כוכבים. האוטומציה מסמנת אילו אלמנטים בתוכנית כבר עומדים בדרישות.
2. קרקע ואתר: בראונפילד, מגוון ביולוגי
3. מים: ברזים חסכוניים, השקייה יעילה, שימוש חוזר
4. חומרים: EPD, תוכן ממוחזר, ייצור מקומי
5. בריאות: תאורה טבעית, IAQ, אקוסטיקה
6. פסולת: ניהול פסולת בנייה, תשתית מחזור
7. תחבורה: קרבה לתחבורה ציבורית, חניית אופניים
8. ניהול אתר: בנייה ירוקה בתהליך הבנייה
9. חדשנות: נקודות בונוס
כוכבים: 1★≈55 | 2★≈65 | 3★≈75 | 4★≈85 | 5★≈95
;;; GREEN-BUILDING-5281.LSP v1.2
;;; מחשבון ניקוד ראשוני — ת"י 5281 בנייה ירוקה
;;; ⚠ ניקוד משוער בלבד — לאישור סופי יש לפנות לגוף מוסמך
;;; מקור: ת"י 5281.2 (מגורים) + SII Israel Green Building Standard
(defun c:GREEN-5281 (/ *error* s-echo
score-energy score-water score-mat
score-health score-transport score-innov
total-score stars)
(defun *error* (msg)
(setvar "CMDECHO" s-echo)
(if (not (wcmatch msg "Function cancelled,quit / exit abort")) (alert msg))
(princ))
(setq s-echo (getvar "CMDECHO"))
(setvar "CMDECHO" 0)
(prompt "
══════════════════════════════════")
(prompt "
ת"י 5281 — מחשבון ניקוד ראשוני")
(prompt "
══════════════════════════════════")
;;; ── Energy (highest weight, ~25-30 points) ───────────
(initget 1 "A B C D E")
(setq energy-class
(getkword "
דירוג אנרגיה ת"י 5282 [A/B/C/D/E]: "))
(setq score-energy
(cdr (assoc energy-class
'(("A" . 30) ("B" . 25) ("C" . 15) ("D" . 8) ("E" . 0)))))
;;; ── Water ─────────────────────────────────────────────
(initget 1 "Y N")
(setq low-flow (getkword "
ברזים חסכוניים (WELS/WaterSense)? [Y/N]: "))
(initget 1 "Y N")
(setq greywater (getkword "
שימוש חוזר במי אפור? [Y/N]: "))
(initget 1 "Y N")
(setq efficient-irr (getkword "
השקייה חכמה/טיפטוף? [Y/N]: "))
(setq score-water
(+ (if (= low-flow "Y") 5 0)
(if (= greywater "Y") 8 0)
(if (= efficient-irr "Y") 4 0)))
;;; ── Materials ─────────────────────────────────────────
(initget 1 "Y N")
(setq local-mat (getkword "
חומרים מקומיים (>50% ממשקל)? [Y/N]: "))
(initget 1 "Y N")
(setq recycled (getkword "
תוכן ממוחזר >20%? [Y/N]: "))
(setq score-mat
(+ (if (= local-mat "Y") 4 0)
(if (= recycled "Y") 5 0)))
;;; ── Health & Wellbeing ────────────────────────────────
(initget 1 "Y N")
(setq daylight (getkword "
גורם תאורה טבעית DF>2% ב-75% מהחדרים? [Y/N]: "))
(initget 1 "Y N")
(setq acoustic (getkword "
איטום אקוסטי מחולל רעש? [Y/N]: "))
(setq score-health
(+ (if (= daylight "Y") 6 0)
(if (= acoustic "Y") 4 0)))
;;; ── Transport ─────────────────────────────────────────
(initget 1 "Y N")
(setq transit (getkword "
מרחק ≤500m לתחבורה ציבורית? [Y/N]: "))
(initget 1 "Y N")
(setq bike-park (getkword "
חניית אופניים מוגנת? [Y/N]: "))
(setq score-transport
(+ (if (= transit "Y") 7 0)
(if (= bike-park "Y") 3 0)))
;;; ── Innovation bonus ──────────────────────────────────
(setq score-innov (getint "
נקודות חדשנות/בונוס (0-10): "))
(if (< score-innov 0) (setq score-innov 0))
(if (> score-innov 10) (setq score-innov 10))
;;; ── Total & Stars ─────────────────────────────────────
(setq total-score
(+ score-energy score-water score-mat
score-health score-transport score-innov))
;; Star thresholds — verify against current SII edition
;; Source: SII IS 5281, ILGBC certification rules
(setq stars
(cond
((>= total-score 95) "★★★★★ פלטינום (5 כוכבים)")
((>= total-score 85) "★★★★ (4 כוכבים)")
((>= total-score 75) "★★★ (3 כוכבים)")
((>= total-score 65) "★★ (2 כוכבים)")
((>= total-score 55) "★ (1 כוכב בסיס)")
(T "מתחת לסף — לא עומד בדרישות")))
(alert (strcat
"══════════════════════════════════
"
" ת"י 5281 — תוצאה ראשונית
"
"══════════════════════════════════
"
" אנרגיה (5282 " energy-class "): " (itoa score-energy) " נק'
"
" מים: " (itoa score-water) " נק'
"
" חומרים: " (itoa score-mat) " נק'
"
" בריאות: " (itoa score-health) " נק'
"
" תחבורה: " (itoa score-transport) " נק'
"
" חדשנות/בונוס: " (itoa score-innov) " נק'
"
"──────────────────────────────────
"
" סה"כ: " (itoa total-score) " נקודות
"
" תוצאה: " stars "
"
"══════════════════════════════════
"
" ⚠ ניקוד ראשוני בלבד!
"
" לאישור: www.ilgbc.org"))
(setvar "CMDECHO" s-echo)
(princ))
Reactor Automation — אוטומציה מבוססת אירועים
Reactors מאפשרים לקוד שלך להגיב אוטומטית לאירועים ב-AutoCAD — שינוי entity, שמירה, פתיחת קובץ, שינוי שכבה. 19 סוגי reactors. אזהרה: שגיאה בקוד callback = AutoCAD קורס.
❌ getpoint, entsel, getkword — כל input אינטראקטיבי
❌ ssget, ssadd — selection sets
❌ (command ...) — AutoCAD commands
❌ entget, entmod — שינוי entity ה"owner" (גורם infinite loop!)
✅ מותר: vla-get-*, vla-put-* | vlr-remove/vlr-add | princ | alert
מקור: Autodesk — Using Object Reactors
| Reactor Type | Constructor | אירועים עיקריים |
|---|---|---|
| :VLR-Object-Reactor | vlr-object-reactor | modified, copied, erased — על objects ספציפיים |
| :VLR-Command-Reactor | vlr-command-reactor | commandWillStart, commandEnded, commandCancelled |
| :VLR-DWG-Reactor | vlr-dwg-reactor | saveComplete, beginOpen, endOpen, beginClose |
| :VLR-SysVar-Reactor | vlr-sysvar-reactor | sysVarChanged, sysVarWillChange |
| :VLR-DocManager-Reactor | vlr-docmanager-reactor | documentCreated, documentActivated |
| :VLR-XREF-Reactor | vlr-xref-reactor | xrefBeginAttach, xrefEndAttach |
| :VLR-AcDb-Reactor | vlr-acdb-reactor | Drawing database events |
| :VLR-Lisp-Reactor | vlr-lisp-reactor | lispWillStart, lispEnded, lispCancelled |
| +11 more | Insert, Wblock, DeepClone, Undo, Window, Toolbar, Mouse, Misc, Linker, DXF, Editor |
;;; AUTO-AREA-REACTOR.LSP v1.5
;;; אוטומציה: עדכון טקסט שטח אוטומטית כשפוליליין משתנה
;;; מקור: Autodesk VLR Reactor API 2024
;;; https://help.autodesk.com/cloudhelp/2022/ENU/AutoCAD-AutoLISP/
;;; files/GUID-EC61BEC2-39B9-4CD1-BE3B-7781DF1E3530.htm
;;;
;;; EDGE CASES (official Autodesk docs):
;;; ⚠ FORBIDDEN in callback: entget, entmod, command, ssget, getpoint
;;; ⚠ Must use VLA methods (vla-put-*) instead of entmod
;;; ⚠ Infinite loop: modifying owner in its own :vlr-modified callback!
;;; ⚠ Solution: vlr-remove reactor BEFORE modify, vlr-add AFTER
;;; ⚠ Reactors are TRANSIENT by default — lost on drawing close
;;; Use (vlr-pers reactor) to save in DWG, but callback fn must re-load!
;;; ⚠ vlax-ename->vla-object fails if vl-load-com not called first
;;; ── Global reactor storage ────────────────────────────────
(setq *AREA-REACTORS* (list)) ; keep refs to prevent GC
;;; ── Callback function (called when polyline is modified) ──
;;; Signature for object reactor: (notifier reactor params)
(defun area-reactor-callback (notifier reactor params / area-m2 unit-fac
text-ename new-val updating)
;; ⚠ Check flag to prevent re-entry (infinite loop prevention)
(if *AREA-UPDATING* (exit)) ; bail if already inside callback
(setq *AREA-UPDATING* T)
;; Must use VLA properties — entget/entmod FORBIDDEN in callback!
(setq unit-fac 1.0e-6) ; assume mm drawings (INSUNITS=4)
;; ⚠ notifier = VLA-object (NOT ename); already converted
(setq area-m2
(vl-catch-all-apply
'(lambda (obj) (* unit-fac (vlax-get-property obj 'Area)))
(list notifier)))
(when (not (vl-catch-all-error-p area-m2))
;; Find associated text tag via XDATA
(setq ename (vlax-vla-object->ename notifier))
(setq xdata (al:xdata-read ename "AREA_TAG"))
(when xdata
(setq text-handle (cdr (assoc 1000 xdata)))
(setq text-ename (handent text-handle))
(when text-ename
;; Update text via VLA (entmod is FORBIDDEN in callbacks!)
(setq text-vla (vlax-ename->vla-object text-ename))
(setq new-val (strcat (rtos area-m2 2 2) " m²"))
(vl-catch-all-apply
'(lambda (obj val) (vla-put-textstring obj val))
(list text-vla new-val)))))
(setq *AREA-UPDATING* nil)
(princ)) ; must return nil or primitive from callback
;;; ── Attach reactor to selected polyline ───────────────────
(defun c:ATTACH-AREA-REACTOR (/ ename vla-obj reactor tag-ename)
(vl-load-com) ; ⚠ MUST call before any vlax-* functions
(prompt "
בחר פוליליין לעקוב אחרי השטח שלו: ")
(setq ename (car (entsel)))
(if (not ename) (exit))
(if (not (equal (cdr (assoc 0 (entget ename))) "LWPOLYLINE"))
(progn (alert "בחר LWPOLYLINE בלבד") (exit)))
(setq vla-obj (vlax-ename->vla-object ename))
;; ── Create area text tag near polyline ────────────────
(setq centroid
(list (/ (+ (car (getvar "EXTMIN")) (car (getvar "EXTMAX"))) 2)
(/ (+ (cadr (getvar "EXTMIN")) (cadr (getvar "EXTMAX"))) 2)
0))
;; Use VLA to create MTEXT (not command — to keep it clean)
(setq doc (vla-get-activedocument (vlax-get-acad-object)))
(setq ms (vla-get-modelspace doc))
(setq tag-vla (vla-addmtext ms
(vlax-3d-point centroid) 50.0 "? m²"))
(vla-put-height tag-vla 250.0) ; 250mm text height in mm drawing
(setq tag-ename (vlax-vla-object->ename tag-vla))
;; Store association via XDATA
(regapp "AREA_TAG")
(al:xdata-write ename "AREA_TAG"
(list (cons 1005 (cdr (assoc 5 (entget tag-ename)))))) ; handle reference
;; ── Create object reactor ─────────────────────────────
;; Signature: (vlr-object-reactor owners data callbacks)
(setq reactor
(vlr-object-reactor
(list vla-obj) ; owners = list of VLA-objects to watch
"area-auto-update" ; arbitrary data
'((:vlr-modified . area-reactor-callback)))) ; event → callback
;; Make PERSISTENT (saves in DWG file):
(vlr-pers reactor)
;; ⚠ BUT: callback function (area-reactor-callback) must be loaded
;; every session! Add to acaddoc.lsp or Startup Suite.
;; Store ref to prevent GC:
(setq *AREA-REACTORS* (append *AREA-REACTORS* (list reactor)))
(prompt "
✅ Reactor מחובר! שינוי הפוליליין יעדכן הטקסט אוטומטית.")
(prompt "
⚠ טען קובץ זה ב-Startup Suite כדי שה-reactor יפעל בכל פתיחה.")
(princ))
;;; ── Detach reactor from polyline ─────────────────────────
(defun c:DETACH-AREA-REACTOR (/ ename vla-obj)
(vl-load-com)
(setq ename (car (entsel "
בחר פוליליין להסרת reactor: ")))
(if (not ename) (exit))
(setq vla-obj (vlax-ename->vla-object ename))
;; Remove all object reactors watching this VLA-object:
(foreach r (vlr-reactors :VLR-Object-Reactor)
(when (member vla-obj (vlr-owner r))
(vlr-remove r)
(prompt "
Reactor הוסר.")))
(princ))
;;; SAVE-HOOK-REACTOR.LSP — עדכון תאריך שמירה אוטומטי ב-title block
;;; מחובר ל-:vlr-saveComplete event
;;; מקור: Autodesk VLR DWG Reactor API
(defun on-save-complete (reactor event-params / doc ss)
"Fires after every save. Updates SAVEDATE attribute in title block."
;; ⚠ command, ssget, entmod FORBIDDEN in DWG reactors!
;; Use VLA methods and XRECORD dictionary for safe updates.
(vl-load-com)
(setq doc (vla-get-activedocument (vlax-get-acad-object)))
;; Find title block via block table (not ssget — forbidden!)
(setq tb-blk
(vl-catch-all-apply
'(lambda (d)
(vla-item (vla-get-blocks d) "TITLEBLOCK"))
(list doc)))
(when (not (vl-catch-all-error-p tb-blk))
;; Iterate insertions of TITLEBLOCK in model/paper space
(vlax-for ins (vla-get-modelspace doc)
(when (and
(= (vla-get-objectname ins) "AcDbBlockReference")
(= (strcase (vl-catch-all-apply
'(lambda (o) (vla-get-effectivename o))
(list ins)))
"TITLEBLOCK"))
;; Update attributes via VLA (NOT entmod!)
(vlax-for attr (vla-get-attributes ins)
(when (= (strcase (vla-get-tagstring attr)) "SAVEDATE")
(vla-put-textstring attr
(rtos (getvar "CDATE") 2 0)))))))
(princ))
;;; ── Load once to activate ─────────────────────────────────
(defun c:LOAD-SAVE-HOOK (/ reactor)
(vl-load-com)
(setq *SAVE-REACTOR*
(vlr-dwg-reactor
"title-block-save-hook"
'((:vlr-saveComplete . on-save-complete))))
(vlr-pers *SAVE-REACTOR*) ; persist in drawing
(prompt "
✅ Save hook פעיל — כותרת תתעדכן בכל שמירה.")
(princ))
| מקרה קצה | מה קורה | פתרון מוכח |
|---|---|---|
| entmod בתוך :vlr-modified של אותו entity | Infinite loop → AutoCAD קורס | setq *UPDATING* T לפני, nil אחרי; vlr-remove → modify → vlr-add |
| ssget בתוך callback | Officially FORBIDDEN — undefined behavior | שמור enames לפני בגלובלים; השתמש ב-vlax-ename->vla-object |
| (command ...) בתוך callback | Officially FORBIDDEN — may work but unreliable | החלף ב-VLA methods: vla-put-textstring, vla-update |
| Reactor transient by default | אובד בסגירת DWG | (vlr-pers reactor) — שומר ב-DWG; callback fn חייב להיטען ב-acaddoc.lsp |
| vlr-pers ≠ callback persistence | reactor נשמר אבל callback function נטענת מחדש בכל פתיחה | הכנס (load "reactor-file.lsp") ל-Startup Suite |
| Multiple duplicate reactors | callback רץ פעמים מרובות | בדוק (vlr-reactors :VLR-Object-Reactor) לפני attach |
| vlax-ename->vla-object erased entity | vlax-erased-p = T; method calls fail | (vl-catch-all-apply ...) + בדוק (vlax-erased-p obj) |