;;; ---------------------------------------------------------------------------
;;; BoxCulvert.lsp
;;; code compiled by YZ August 2026
;;; ---------------------------------------------------------------------------
;;; REINFORCED CONCRETE BOX CULVERT - PLANS, SECTIONS AND REINFORCEMENT
;;;
;;; PURPOSE
;;;   Generates the whole detail for a cast-in-place reinforced concrete box:
;;;
;;;     Sectional plan   through the walls, dimensioned inside and out
;;;     Roof plan        the slab over
;;;     Section 1        through the side walls, with reinforcement and dowels
;;;     Section 2        through the end walls, likewise
;;;     Pipe connections on any of the four sides, set out from their inverts
;;;
;;;   From thirteen numbers you get a complete, dimensioned sheet.
;;;
;;; SELF-CONTAINED
;;;   The routine this was rebuilt from inserted three blocks from hard-coded
;;;   paths - a wall shear key, a slab key and a reinforced concrete pipe, all
;;;   under /acad/strblk/. None of those blocks exist any more, so the original
;;;   could not complete a single culvert without erroring at the first key.
;;;
;;;   All three are now DRAWN AS GEOMETRY from the dimensions already to hand.
;;;   Nothing has to be installed and nothing can go missing.
;;;
;;; THE TWO SECTIONS ARE ONE FUNCTION
;;;   Section 1 cuts the long way, Section 2 the short way, but a box culvert is
;;;   symmetrical - both sections are the same drawing with a different clear
;;;   span and a different pair of dowel lengths. They were two near-identical
;;;   200-line blocks; they are one function called twice here, which is why the
;;;   reinforcement cannot drift between them.
;;;
;;; UNITS
;;;   Dimensions are entered and drawn in FEET, and dimension TEXT is written in
;;;   inches - the value times twelve - which is how culvert sheets are annotated
;;;   in imperial practice. Reinforcement cover and bar offsets are the small
;;;   decimal-foot constants below: 0.16 ft is about 2 inches, 0.08 ft about 1.
;;;
;;; WHERE IT DRAWS
;;;   At a point you pick, not at fixed world coordinates. The original always
;;;   drew at 10,5 regardless, so a second culvert landed on top of the first.
;;;
;;;   BOXCULVERT  - draw a complete reinforced concrete box culvert detail
;;; ---------------------------------------------------------------------------

;;; ---------------------------------------------------------------------------
;;; REINFORCEMENT CONSTANTS, in feet
;;; ---------------------------------------------------------------------------

(setq BoxCul:COVER  0.16    ; concrete cover to the face of a bar, ~2 inches
      BoxCul:BARGAP 0.08    ; clear gap between a dowel and the wall steel, ~1 in
      BoxCul:DOWEND 0.42    ; how far a dowel runs into the bottom slab, inside
      BoxCul:DOWEND2 0.33   ; ditto, outside face
      BoxCul:BEND   0.16    ; length of the bend where wall steel hooks a dowel
      BoxCul:BOTCVR 0.25    ; cover to the bottom mat in the bottom slab
)

;;; ---------------------------------------------------------------------------
;;; SESSION MEMORY
;;; ---------------------------------------------------------------------------

(if (null *BoxCul:Prefs*)
    (setq *BoxCul:Prefs*
        (list (cons "L"      8.0)    ; clear span, long direction
              (cons "W"      6.0)    ; clear span, short direction
              (cons "H"      6.0)    ; clear height
              (cons "WT"     1.0)    ; wall thickness
              (cons "TST"    1.0)    ; top slab thickness
              (cons "BST"    1.0)    ; bottom slab thickness
              (cons "FE"     0.5)    ; footing extension beyond the wall
              (cons "SWTOFD" 2.0)    ; side wall, outside face dowel length
              (cons "SWTIFD" 2.0)    ; side wall, inside face dowel length
              (cons "TWTOFD" 2.0)    ; end wall, outside face dowel length
              (cons "TWTIFD" 2.0)    ; end wall, inside face dowel length
              (cons "EL"     0.0)    ; elevation of the top of the bottom slab
        )
    )
)

(defun BoxCul:Get ( key ) (cdr (assoc key *BoxCul:Prefs*)))

(defun BoxCul:Put ( key val )
    (setq *BoxCul:Prefs*
        (cons (cons key val)
              (vl-remove-if '(lambda (p) (= (car p) key)) *BoxCul:Prefs*)))
    val
)

(defun BoxCul:Ask ( key prompt / v )
    (initget 4)   ; reject negative; zero is legitimate for an elevation
    (setq v (getdist (strcat "\n" prompt " <" (rtos (BoxCul:Get key) 2 3) ">: ")))
    (if v (BoxCul:Put key v) (BoxCul:Get key))
)

;;; ---------------------------------------------------------------------------
;;; LAYERS
;;;
;;; Named for what they carry rather than the original's office codes (g3, s1,
;;; soo, soh, sooo), which meant nothing outside the practice that wrote them.
;;; ---------------------------------------------------------------------------

(defun BoxCul:Layer ( name / )
    (if (tblsearch "layer" name)
        (command "_.LAYER" "_ON" name "_THAW" name "_UNLOCK" name "")
        (command "_.LAYER" "_NEW" name ""))
    (setvar "CLAYER" name)
)

(defun BoxCul:Concrete ( ) (BoxCul:Layer "BOXCUL-CONCRETE"))
(defun BoxCul:Rebar    ( ) (BoxCul:Layer "BOXCUL-REBAR"))
(defun BoxCul:Footing  ( ) (BoxCul:Layer "BOXCUL-FOOTING"))
(defun BoxCul:Hidden   ( ) (BoxCul:Layer "BOXCUL-HIDDEN"))
(defun BoxCul:Dims     ( ) (BoxCul:Layer "BOXCUL-DIMS"))

;;; ---------------------------------------------------------------------------
;;; DRAWING PRIMITIVES
;;; ---------------------------------------------------------------------------

(defun BoxCul:XY ( ox oy x y ) (list (+ ox x) (+ oy y) 0.0))

(defun BoxCul:Line ( pts close )
    (command "_.LINE")
    (foreach p pts (command p))
    (if close (command "_C") (command ""))
    (princ)
)

;;; Linear dimension with an explicit text override, which is how the original
;;; annotated in inches while drawing in feet.
(defun BoxCul:Dim ( p1 p2 loc txt vertical )
    (BoxCul:Dims)
    (vl-catch-all-apply
        '(lambda ( )
             (command "_.DIMLINEAR" p1 p2
                      (if vertical "_V" "_H")
                      "_T" txt loc)))
    (princ)
)

;;; Dimension text: feet in, inches out, to one decimal.
(defun BoxCul:Inches ( ft ) (rtos (* 12.0 ft) 4 1))

;;; ---------------------------------------------------------------------------
;;; SHEAR KEY - drawn, not inserted
;;;
;;; A trapezoidal key cast into a construction joint so the wall and slab
;;; interlock in shear. Proportioned from the wall thickness: half as wide as the
;;; wall, a quarter as deep, with tapered sides so the formwork strikes.
;;;
;;;   base   centre of the joint face
;;;   wt     wall thickness, which sets the key size
;;;   up     T to key upwards into the member above, nil to key downwards
;;; ---------------------------------------------------------------------------

(defun BoxCul:Key ( base wt up / w1 w2 d s )
    (setq w1 (* wt 0.50)      ; width at the joint face
          w2 (* wt 0.30)      ; width at the bottom of the key
          d  (* wt 0.25)      ; depth
          s  (if up 1.0 -1.0))
    (BoxCul:Line
        (list (list (- (car base) (/ w1 2.0)) (cadr base))
              (list (- (car base) (/ w2 2.0)) (+ (cadr base) (* s d)))
              (list (+ (car base) (/ w2 2.0)) (+ (cadr base) (* s d)))
              (list (+ (car base) (/ w1 2.0)) (cadr base)))
        nil)
)

;;; ---------------------------------------------------------------------------
;;; PIPE CONNECTION - drawn, not inserted
;;;
;;; A pipe entering the box, shown as its soffit and invert with a centreline
;;; between them, running 4 feet out from the face. That is what the block it
;;; replaces did: it was inserted at 4 units long by the pipe diameter tall.
;;;
;;;   face  point on the box face at the pipe centreline
;;;   dia   pipe internal diameter
;;;   ang   direction the pipe runs away from the box
;;; ---------------------------------------------------------------------------

(defun BoxCul:Pipe ( face dia ang / run half perp a b c d )
    (setq run  4.0
          half (/ dia 2.0)
          perp (+ ang (* pi 0.5))
          a (polar face perp half)
          b (polar a ang run)
          c (polar face (- perp pi) half)
          d (polar c ang run))
    (BoxCul:Concrete)
    (BoxCul:Line (list a b) nil)      ; soffit
    (BoxCul:Line (list c d) nil)      ; invert
    (BoxCul:Hidden)
    (BoxCul:Line (list face (polar face ang run)) nil)   ; centreline
    (BoxCul:Concrete)
    (princ)
)

;;; ---------------------------------------------------------------------------
;;; ONE SECTION
;;;
;;; Draws a full section through the box: outside concrete with its footing
;;; steps, inside face, wall and slab reinforcement, dowels, shear keys and the
;;; dimension string.
;;;
;;; Called twice - once with the long clear span and the side-wall dowels, once
;;; with the short span and the end-wall dowels. Everything else is identical,
;;; which is exactly why it is one function.
;;;
;;;   ox oy   bottom-left of the section's footing
;;;   clear   clear span being cut across
;;;   ofd ifd outside and inside face dowel lengths
;;; ---------------------------------------------------------------------------

(defun BoxCul:Section ( ox oy clear wt tst bst fe h ofd ifd
                        / outw x1 x2 x3 x4 x5 x6 y1 y2 y3 y4 oh
                          rx1 rx2 rx3 rx4 ry1 ry2 ry3 ry4 ry5 ry6 ry7 ry8
                          mid )

    (setq outw (+ clear (* 2.0 wt))     ; outside face to outside face
          oh   (+ h tst)                ; inside height plus the top slab

          ;; Vertical setting-out, from the underside of the footing up.
          y1 oy                          ; underside of the bottom slab
          y2 (+ y1 bst)                  ; top of the bottom slab
          y3 (+ y2 oh)                   ; top of the top slab
          y4 (- y3 tst)                  ; underside of the top slab

          ;; Horizontal, from the outside edge of the footing across.
          x1 ox                          ; outside edge of the footing
          x2 (+ x1 (* 2.0 fe) outw)      ; far outside edge of the footing
          x3 (- x2 fe)                   ; outside face of the far wall
          x4 (+ x1 fe)                   ; outside face of the near wall
          x5 (+ x4 wt)                   ; inside face of the near wall
          x6 (+ x5 clear))               ; inside face of the far wall

    ;; --- outside concrete, stepping out over the footing ---------------------
    (BoxCul:Concrete)
    (BoxCul:Line
        (list (list x1 y1) (list x2 y1) (list x2 y2) (list x3 y2)
              (list x3 y3) (list x4 y3) (list x4 y2) (list x1 y2))
        t)

    ;; --- inside face of the cell --------------------------------------------
    (BoxCul:Line
        (list (list x5 y2) (list x6 y2) (list x6 y4) (list x5 y4))
        t)

    ;; --- shear keys at the wall-to-slab construction joints ------------------
    (BoxCul:Key (list (+ x4 (/ wt 2.0)) y2) wt t)     ; near wall, on the footing
    (BoxCul:Key (list (+ x4 (/ wt 2.0)) y4) wt nil)   ; near wall, under the roof
    (BoxCul:Key (list (- x3 (/ wt 2.0)) y2) wt t)     ; far wall
    (BoxCul:Key (list (- x3 (/ wt 2.0)) y4) wt nil)

    ;; --- wall reinforcement and dowels, near wall ----------------------------
    ;; Two curtains: one near the inside face, one near the outside. Each is
    ;; lapped onto a dowel cast into the bottom slab.
    (BoxCul:Rebar)
    (setq rx1 (- x5 BoxCul:COVER)                  ; inside face steel
          rx2 (+ x4 BoxCul:COVER)                  ; outside face steel
          rx3 (- rx1 BoxCul:BARGAP)                ; inside dowel, just clear
          rx4 (+ rx2 BoxCul:BARGAP)                ; outside dowel
          ry1 (+ y1 bst ifd)                       ; top of the inside dowel
          ry2 (+ y1 bst ofd)                       ; top of the outside dowel
          ry3 (+ y1 BoxCul:DOWEND)                 ; foot of the inside dowel
          ry4 (+ y1 BoxCul:DOWEND2)                ; foot of the outside dowel
          ry5 (+ ry1 BoxCul:BEND)
          ry6 (+ ry2 BoxCul:BEND)
          ry7 (- y3 BoxCul:DOWEND)                 ; top of the inside curtain
          ry8 (- y3 BoxCul:DOWEND2))               ; top of the outside curtain

    (BoxCul:Line (list (list rx1 ry1) (list rx1 ry3)) nil)   ; inside dowel
    (BoxCul:Line (list (list rx2 ry2) (list rx2 ry4)) nil)   ; outside dowel
    (BoxCul:Line (list (list rx3 y2) (list rx3 ry1)
                       (list rx1 ry5) (list rx1 ry7)) nil)   ; inside curtain
    (BoxCul:Line (list (list rx4 y2) (list rx4 ry2)
                       (list rx2 ry6) (list rx2 ry8)) nil)   ; outside curtain

    ;; --- and mirrored onto the far wall --------------------------------------
    ;; Built by reflecting each x about the centreline rather than by running the
    ;; MIRROR command, so nothing depends on what happens to be selectable.
    (setq mid (/ (+ x5 x6) 2.0))
    (BoxCul:Line (list (list (- (* 2 mid) rx1) ry1) (list (- (* 2 mid) rx1) ry3)) nil)
    (BoxCul:Line (list (list (- (* 2 mid) rx2) ry2) (list (- (* 2 mid) rx2) ry4)) nil)
    (BoxCul:Line (list (list (- (* 2 mid) rx3) y2)  (list (- (* 2 mid) rx3) ry1)
                       (list (- (* 2 mid) rx1) ry5) (list (- (* 2 mid) rx1) ry7)) nil)
    (BoxCul:Line (list (list (- (* 2 mid) rx4) y2)  (list (- (* 2 mid) rx4) ry2)
                       (list (- (* 2 mid) rx2) ry6) (list (- (* 2 mid) rx2) ry8)) nil)

    ;; --- slab reinforcement --------------------------------------------------
    ;; Bottom slab gets a mat top and bottom; top slab likewise, both held clear
    ;; of the faces by the cover.
    (BoxCul:Line (list (list (+ x1 BoxCul:COVER) (+ y1 BoxCul:BOTCVR))
                       (list (- x2 BoxCul:COVER) (+ y1 BoxCul:BOTCVR))) nil)
    (BoxCul:Line (list (list (+ x1 BoxCul:COVER) (- y2 BoxCul:COVER))
                       (list (- x2 BoxCul:COVER) (- y2 BoxCul:COVER))) nil)
    (BoxCul:Line (list (list (+ x4 BoxCul:COVER) (+ y4 BoxCul:COVER))
                       (list (- x3 BoxCul:COVER) (+ y4 BoxCul:COVER))) nil)
    (BoxCul:Line (list (list (+ x4 BoxCul:COVER) (- y3 BoxCul:COVER))
                       (list (- x3 BoxCul:COVER) (- y3 BoxCul:COVER))) nil)

    ;; --- dimensions ----------------------------------------------------------
    (BoxCul:Dim (list x1 y1) (list x1 y2) (list (- x1 3.0) y1)
                (BoxCul:Inches bst) t)
    (BoxCul:Dim (list x1 y2) (list x1 y4) (list (- x1 4.5) y2)
                (BoxCul:Inches h) t)
    (BoxCul:Dim (list x1 y4) (list x1 y3) (list (- x1 6.0) y4)
                (BoxCul:Inches tst) t)
    (BoxCul:Dim (list rx2 y2) (list rx2 ry2) (list (- x4 1.5) y2)
                (BoxCul:Inches ofd) t)
    (BoxCul:Dim (list rx1 y2) (list rx1 ry1) (list (+ x5 1.5) y2)
                (BoxCul:Inches ifd) t)

    (BoxCul:Concrete)
    ;; Hand back the setting-out the caller needs for pipe connections.
    (list x5 x6 y2 y4)
)

;;; ---------------------------------------------------------------------------
;;; THE PLANS
;;; ---------------------------------------------------------------------------

(defun BoxCul:Plans ( ox oy l w wt fe / ol ow x1 x2 x3 x4 y1 y2 y3 y4 rx )

    (setq ol (+ l (* 2.0 wt))
          ow (+ w (* 2.0 wt))
          x1 ox           x2 (+ ox ol)
          y1 oy           y2 (+ oy ow)
          x3 (+ x1 wt)    x4 (- x2 wt)
          y3 (+ y1 wt)    y4 (- y2 wt))

    ;; --- sectional plan ------------------------------------------------------
    (BoxCul:Concrete)
    (BoxCul:Line (list (list x1 y1) (list x2 y1) (list x2 y2) (list x1 y2)) t)
    (BoxCul:Line (list (list x3 y3) (list x4 y3) (list x4 y4) (list x3 y4)) t)

    ;; Footing extension, dashed round the outside.
    (BoxCul:Footing)
    (BoxCul:Line (list (list (- x1 fe) (- y1 fe)) (list (+ x2 fe) (- y1 fe))
                       (list (+ x2 fe) (+ y2 fe)) (list (- x1 fe) (+ y2 fe))) t)

    ;; --- dimensions ----------------------------------------------------------
    (BoxCul:Dim (list x1 y1) (list x1 y2) (list (- x1 9.0) y1) (BoxCul:Inches ow) t)
    (BoxCul:Dim (list x1 y2) (list x2 y2) (list x2 (+ y2 9.0)) (BoxCul:Inches ol) nil)
    (BoxCul:Dim (list x1 y1) (list x3 y3) (list (- x1 6.0) y1) (BoxCul:Inches wt) t)
    (BoxCul:Dim (list x3 y3) (list x3 y4) (list (- x1 7.5) y3) (BoxCul:Inches w)  t)

    ;; --- roof plan, set out to the right -------------------------------------
    (setq rx (+ x2 10.0))
    (BoxCul:Concrete)
    (BoxCul:Line (list (list rx y1) (list (+ rx ol) y1)
                       (list (+ rx ol) y2) (list rx y2)) t)
    (BoxCul:Hidden)
    (BoxCul:Line (list (list (+ rx wt) y3) (list (+ rx ol (- wt)) y3)
                       (list (+ rx ol (- wt)) y4) (list (+ rx wt) y4)) t)
    (BoxCul:Concrete)

    ;; Setting-out the caller needs to place pipes on the plan.
    (list x3 x4 y3 y4)
)

;;; ---------------------------------------------------------------------------
;;; PIPE CONNECTIONS
;;;
;;; A pipe is located on the plan by its distance along the wall from a corner,
;;; and on the section by its INVERT LEVEL, which is what a drainage layout
;;; actually gives you. The section height is worked back from the invert
;;; relative to the top of the bottom slab.
;;; ---------------------------------------------------------------------------

(defun BoxCul:AskPipe ( side / yn inv dia loc )
    (initget "Yes No")
    (setq yn (getkword (strcat "\nPipe on the " side " side [Yes/No] <No>: ")))
    (if (= yn "Yes")
        (progn
            ;; No INITGET filter on the invert: a level below datum is negative
            ;; and perfectly legitimate.
            (setq inv (getdist "\n  Invert level of the pipe: "))
            (initget 6) (setq dia (getdist "\n  Internal diameter of the pipe: "))
            (initget 4) (setq loc (getdist "\n  Distance along the wall to the pipe centreline: "))
            (if (and inv dia loc) (list inv dia loc))))
)

;;; ---------------------------------------------------------------------------
;;; MAIN COMMAND
;;; ---------------------------------------------------------------------------

(defun c:BOXCULVERT ( / *error* vars vals origin
                        l w h wt tst bst fe swo swi two twi el
                        ol ow oh planOrg sec1Org sec2Org planSet s1Set s2Set
                        pipe )

    (setq vars '("CMDECHO" "OSMODE" "BLIPMODE" "CLAYER" "DIMZIN")
          vals (mapcar 'getvar vars))

    (defun BoxCul:Restore ( )
        (mapcar 'setvar vars vals)
        (while (= 8 (logand 8 (getvar 'undoctl))) (command "_.UNDO" "_End"))
        (vl-catch-all-apply '(lambda ( ) (*pop-error-mode*)) '())
        (princ)
    )

    (defun *error* ( msg )
        (BoxCul:Restore)
        (if (and msg (not (wcmatch (strcase msg t) "*break*,*cancel*,*exit*")))
            (princ (strcat "\n** BOXCULVERT error: " msg " **")))
        (princ)
    )

    (setvar "CMDECHO" 0)
    ;; AutoCAD 2015 and later refuse (command) inside an *error* handler
    ;; unless the routine says up front that it will use one. Restore does,
    ;; to close this undo group. The declaring call is absent on older
    ;; releases, so it is wrapped rather than tested for.
    (vl-catch-all-apply '(lambda ( ) (*push-error-using-command*)) '())
    (command "_.UNDO" "_Begin")

    (princ "\nAll dimensions in FEET. Dimension text is written in inches.")

    (setq l   (BoxCul:Ask "L"   "Clear span, long direction")
          w   (BoxCul:Ask "W"   "Clear span, short direction")
          h   (BoxCul:Ask "H"   "Clear height inside the box")
          wt  (BoxCul:Ask "WT"  "Wall thickness")
          tst (BoxCul:Ask "TST" "Top slab thickness")
          bst (BoxCul:Ask "BST" "Bottom slab thickness")
          fe  (BoxCul:Ask "FE"  "Footing extension beyond the wall")
          swo (BoxCul:Ask "SWTOFD" "Side wall dowel, outside face")
          swi (BoxCul:Ask "SWTIFD" "Side wall dowel, inside face")
          two (BoxCul:Ask "TWTOFD" "End wall dowel, outside face")
          twi (BoxCul:Ask "TWTIFD" "End wall dowel, inside face")
          el  (BoxCul:Ask "EL"  "Level at the top of the bottom slab"))

    (setq ol (+ l (* 2.0 wt))
          ow (+ w (* 2.0 wt))
          oh (+ h tst))

    (cond
        ((or (<= l 0.0) (<= w 0.0) (<= h 0.0) (<= wt 0.0))
         (princ "\n** Spans, height and wall thickness must all be greater than zero. **"))

        ((null (setq origin (getpoint "\nBottom-left corner for the detail: ")))
         (princ "\nCancelled."))

        (t
            (setvar "OSMODE" 0)
            (setvar "BLIPMODE" 0)

            ;; Sheet layout, all relative to the picked point: the two sections
            ;; sit along the bottom, the plans above them.
            (setq sec1Org (list (car origin) (cadr origin))
                  sec2Org (list (+ (car origin) (* 2.0 fe) ol 12.0) (cadr origin))
                  planOrg (list (car origin) (+ (cadr origin) bst oh 15.0)))

            (setq planSet (BoxCul:Plans (car planOrg) (cadr planOrg) l w wt fe))

            ;; Section 1 cuts across the SHORT span, so it shows the long walls.
            (setq s1Set (BoxCul:Section (car sec1Org) (cadr sec1Org)
                                        l wt tst bst fe h swo swi))
            ;; Section 2 cuts the other way.
            (setq s2Set (BoxCul:Section (car sec2Org) (cadr sec2Org)
                                        w wt tst bst fe h two twi))

            ;; --- pipe connections -----------------------------------------
            ;; Placed on the plan by distance along the wall, and on Section 1
            ;; by invert level worked back from the top of the bottom slab.
            (foreach side '("left" "right" "top" "bottom")
                (if (setq pipe (BoxCul:AskPipe side))
                    (progn
                        (cond
                            ((= side "left")
                             (BoxCul:Pipe (list (nth 0 planSet)
                                                (+ (nth 2 planSet) (caddr pipe)))
                                          (cadr pipe) pi))
                            ((= side "right")
                             (BoxCul:Pipe (list (nth 1 planSet)
                                                (+ (nth 2 planSet) (caddr pipe)))
                                          (cadr pipe) 0.0))
                            ((= side "top")
                             (BoxCul:Pipe (list (+ (nth 0 planSet) (caddr pipe))
                                                (nth 3 planSet))
                                          (cadr pipe) (* pi 0.5)))
                            ((= side "bottom")
                             (BoxCul:Pipe (list (+ (nth 0 planSet) (caddr pipe))
                                                (nth 2 planSet))
                                          (cadr pipe) (* pi 1.5))))

                        ;; And the matching stub on Section 1, set out from the
                        ;; invert: the pipe centreline sits half a diameter above
                        ;; its invert, and the invert is measured from the top of
                        ;; the bottom slab.
                        (BoxCul:Pipe
                            (list (if (member side '("left" "bottom"))
                                      (nth 0 s1Set) (nth 1 s1Set))
                                  (+ (nth 2 s1Set) (- (car pipe) el) (/ (cadr pipe) 2.0)))
                            (cadr pipe)
                            (if (member side '("left" "bottom")) pi 0.0))
                    )
                )
            )

            (princ (strcat "\nBox culvert drawn - " (rtos l 2 2) " x " (rtos w 2 2)
                           " clear, " (rtos h 2 2) " high, "
                           (rtos wt 2 2) " walls."))
        )
    )

    (BoxCul:Restore)
    (princ)
)

(princ)
