Random Lines / Region Area Calculation / Loops

Random Lines / Region Area Calculation / Loops

surfer96
Advocate Advocate
1,876 Views
19 Replies
Message 1 of 20

Random Lines / Region Area Calculation / Loops

surfer96
Advocate
Advocate

The picture shows a 100x100 box with 10 random lines in both horizontal and vertical directions.

All lines were broken with code from the breakall.lsp written by Charles Alan Butler and then transformed into regions with AutoCAD's "_region" command.

01.JPG

The area for any of the roughly 100 resulting regions must be within a range between 50 and 200 mm².

I need a function calculating all regions' areas, appending them to a list and first erasing the surrounding "100x100-box-region" always greater than 200 mm². The area list may be sorted, the min list area checked with >50 and the max list area with <200. If both conditions are met all random lines will be drawn, if not some kind of "while loop" will have to start again with a new set of random lines and finally print the number of loop calculations to the command line. Regions might be changed into polylines with e. g. "Region2Polyline" if that makes the area extraction easier.

Does anyone have an idea...

(defun LM:rand ( / a c m )
    (setq m   4294967296.0
          a   1664525.0
          c   1013904223.0
          $xn (rem (+ c (* a (cond ($xn) ((getvar 'date))))) m)
    )
    (/ $xn m)
); random function

(command "_pline" '(0 0) '(100 0) '(100 100) '(0 100) "_c")
(command "_explode" "l"); surrounding box

(setq bw 100); box width
(setq NoL 9); number of horizontal / vertical lines

(repeat NoL
   (setq pt1 (list 0.0 (* (LM:rand) bw)))
   (setq pt2 (list bw (* (LM:rand) bw)))
   (command "_line" "_none" pt1 "_none"  pt2 "")
); horizontal lines

(repeat NoL
   (setq pt1 (list (* (LM:rand) bw) 0.0))
   (setq pt2 (list (* (LM:rand) bw) bw))
   (command "_line" "_none" pt1 "_none"  pt2 "")
); vertical lines

;;+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
;;               M A I N   S U B R O U T I N E                   
;;+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

(defun break_with (ss2brk ss2brkwith self / cmd intpts lst masterlist ss ssobjs
                   onlockedlayer ssget->vla-list list->3pair
                   get_interpts break_obj
                  )
  ;; ss2brk     selection set to break
  ;; ss2brkwith selection set to use as break points
  ;; self       when true will allow an object to break itself
  ;;            note that plined will break at each vertex
  (vl-load-com)


;;+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
;;                S U B   F U N C T I O N S                      
;;+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

  (defun onlockedlayer (ename / entlst)
    (setq entlst (tblsearch "LAYER" (cdr (assoc 8 (entget ename)))))
    (= 4 (logand 4 (cdr (assoc 70 entlst))))
  )
  
  (defun ssget->vla-list (ss / i ename lst)
    (setq i -1)
    (while (setq ename (ssname ss (setq i (1+ i))))
      (setq lst (cons (vlax-ename->vla-object ename) lst))
    )
    lst
  )

  (defun list->3pair (old / new)
    (while (setq new (cons (list (car old) (cadr old) (caddr old)) new)
                 old (cdddr old))
    )
    (reverse new)
  )
  
;;==============================================================
;;  return a list of intersect points
;;==============================================================
(defun get_interpts (obj1 obj2 / iplist)
  (if (not (vl-catch-all-error-p
             (setq iplist (vl-catch-all-apply
                            'vlax-safearray->list
                            (list
                              (vlax-variant-value
                                (vla-intersectwith obj1 obj2 acextendnone)
                              ))))))
    iplist
  )
)

;;==============================================================
;;  Break entity at break points in list
;;==============================================================
(defun break_obj (ent brkptlst / brkobjlst en enttype maxparam closedobj
                  minparam obj obj2break p1param p2 p2param
                 )

  (setq obj2break ent
        brkobjlst (list ent)
        enttype   (cdr (assoc 0 (entget ent)))
  )

  (foreach brkpt brkptlst
    ;;  get last entity created via break in case multiple breaks
    (if brkobjlst
      (progn
        ;;  if pt not on object x, switch objects
        (if (not (numberp (vl-catch-all-apply 'vlax-curve-getdistatpoint (list obj2break brkpt)))
            )
          (foreach obj brkobjlst ; find the one that pt is on
            (if (numberp (vl-catch-all-apply 'vlax-curve-getdistatpoint (list obj brkpt)))
              (setq obj2break obj) ; switch objects
            )
          )
        )
      )
    )

    ;;  Handle any objects that can not be used with the Break Command
    ;;  using one point, gap of 0.000001 is used
    (cond
      ((and (= "SPLINE" enttype) ; only closed splines
            (vlax-curve-isclosed obj2break))
       (setq p1param (vlax-curve-getparamatpoint obj2break brkpt)
             p2      (vlax-curve-getpointatparam obj2break (+ p1param 0.000001))
       )
       (command "._break" obj2break "_non" (trans brkpt 0 1) "_non" (trans p2 0 1))
      )
      ((= "CIRCLE" enttype) ; break the circle
       (setq p1param (vlax-curve-getparamatpoint obj2break brkpt)
             p2      (vlax-curve-getpointatparam obj2break (+ p1param 0.000001))
       )
       (command "._break" obj2break "_non" (trans brkpt 0 1) "_non" (trans p2 0 1))
       (setq enttype "ARC")
      )
      ((and (= "ELLIPSE" enttype) ; only closed ellipse
            (vlax-curve-isclosed obj2break))
       ;;  Break the ellipse, code borrowed from Joe Burke  6/6/2005
       (setq p1param  (vlax-curve-getparamatpoint obj2break brkpt)
             p2param  (+ p1param 0.000001)
             minparam (min p1param p2param)
             maxparam (max p1param p2param)
             obj      (vlax-ename->vla-object obj2break)
       )
       (vlax-put obj 'startparameter maxparam)
       (vlax-put obj 'endparameter (+ minparam (* pi 2)))
      )
      
      ;;==================================
      (t  ;   Objects that can be broken     
       (setq closedobj (vlax-curve-isclosed obj2break))
       (command "._break" obj2break "_non" (trans brkpt 0 1) "_non" (trans brkpt 0 1))
       (if (not closedobj) ; new object was created
           (setq brkobjlst (cons (entlast) brkobjlst))
       )
      )
    )
  )
)

;;+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
;;                   S T A R T   H E R E                         
;;+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
    (if (and ss2brk ss2brkwith)
    (progn
      ;;  CREATE a list of entity & it's break points
      (foreach obj (ssget->vla-list ss2brk) ; check each object in ss2brk
        (if (not (onlockedlayer (vlax-vla-object->ename obj)))
          (progn
            (setq lst nil)
            ;; check for break pts with other objects in ss2brkwith
            (foreach intobj (ssget->vla-list ss2brkwith) 
              (if (and (or self (not (equal obj intobj)))
                       (setq intpts (get_interpts obj intobj))
                  )
                (setq lst (append (list->3pair intpts) lst)) ; entity w/ break points
              )
            )
            (if lst
              (setq masterlist (cons (cons (vlax-vla-object->ename obj) lst) masterlist))
            )
          )
        )
      )
      ;;  masterlist = ((ent brkpts)(ent brkpts)...)
      (if masterlist
        (foreach obj2brk masterlist
          (break_obj (car obj2brk) (cdr obj2brk))
        )
      )
      )
  )
)

;;==========================================
;;        Break all objects selected        
;;==========================================

(command "._undo" "_begin")
(setq cmd (getvar "CMDECHO"))
(setvar "CMDECHO" 0)

  ;;  get objects to break
  (if (setq ss (ssget "A" '((0 . "LINE,ARC,SPLINE,LWPOLYLINE,POLYLINE,CIRCLE,ELLIPSE"))))
      (Break_with ss ss nil) ; ss2break ss2breakwith (flag nil = not to break with self)
  )

(setvar "CMDECHO" cmd)
(command "._undo" "_end")

;;==========================================
;;        Create Regions
;;==========================================

(command "_region" "_all" ""); create regions from lines
0 Likes
Accepted solutions (1)
1,877 Views
19 Replies
Replies (19)
Message 2 of 20

dlanorh
Advisor
Advisor

Regions have an area property.

This lisp will get all regions in the drawing, delete those not meeting your criteria and return a list (r_lst) of those that do.

 

(defun c:del_regions ( / c_doc ss r_lst)
  (setq c_doc (vla-get-activedocument (vlax-get-acad-object))
        ss (ssget "_X" '((0 . "REGION")))
  )
  (if ss
    (vlax-for obj (vla-get-activeselectionset c_doc)
      (if (< 50.0 (vlax-get-property obj 'area) 200.0)
        (setq r_lst (cons obj r_lst))
        (vla-delete obj)
      );end_if  
    );end_for
  );end_if
);end_defun  

I am not one of the robots you're looking for

Message 3 of 20

surfer96
Advocate
Advocate

Your routine works but it deletes the regions with areas outside the 50-200 area range.

01.JPG

Instead no region should be deleted. In case at  least one region in the selection set would for being <50 or > 200, all the region points should be randomly redrawn and the area-checking-loop restarted.

From the all-regions selection set we might need a list of all region-areas instead of a list of all regions. Then sort the areas-list and first delete its max value which will be the rose "box region". The min value will then have to be >50 and the max value of the remainig list (i.e. the second highest value of the initial list) <200.

If both conditions are met the loop has to be left, if not restart the loop to redraw all regions.

 

Here's some sample code for two random regions in a 100 x 100 box:

(defun c:3regions ()

(defun LM:rand ( / a c m )
    (setq m   4294967296.0
          a   1664525.0
          c   1013904223.0
          $xn (rem (+ c (* a (cond ($xn) ((getvar 'date))))) m)
    )
    (/ $xn m)
)

(command "_pline" '(0 0) '(100 0) '(100 100) '(0 100) "_c"); box

(setq pt10 (list 0.0 0.0))
(setq pt11 (list (* (LM:rand) 100.0) (* (LM:rand) 100.0)))
(setq pt12 (list (* (LM:rand) 100.0) (* (LM:rand) 100.0)))
(command "_pline" "_none" pt10 "_none"  pt11 "_none"  pt12 "_close"); pline no1

(setq pt20 (list 100.0 100.0))
(setq pt21 (list (* (LM:rand) 100.0) (* (LM:rand) 100.0)))
(setq pt22 (list (* (LM:rand) 100.0) (* (LM:rand) 100.0)))
(command "_pline" "_none" pt20 "_none"  pt21 "_none"  pt22 "_close"); pline no2

(command "_region" "_all" "")

(princ)
)
0 Likes
Message 4 of 20

surfer96
Advocate
Advocate

To start it would already help me to know how to generate a list of the regions' areas.

And how to print this area list to the command line for verification puposes.

0 Likes
Message 5 of 20

dbhunia
Advisor
Advisor

For your attached code... try like this.....

 

(defun c:3regions ()
(vl-load-com)
(defun LM:rand ( / a c m )
    (setq m   4294967296.0
          a   1664525.0
          c   1013904223.0
          $xn (rem (+ c (* a (cond ($xn) ((getvar 'date))))) m)
    )
    (/ $xn m)
)

(command "_pline" '(0 0) '(100 0) '(100 100) '(0 100) "_c"); box

(setq pt10 (list 0.0 0.0))
(setq pt11 (list (* (LM:rand) 100.0) (* (LM:rand) 100.0)))
(setq pt12 (list (* (LM:rand) 100.0) (* (LM:rand) 100.0)))
(command "_pline" "_none" pt10 "_none"  pt11 "_none"  pt12 "_close"); pline no1
(setq PNO1_Area (vlax-get-property (vlax-ename->vla-object (entlast)) 'area))

(setq pt20 (list 100.0 100.0))
(setq pt21 (list (* (LM:rand) 100.0) (* (LM:rand) 100.0)))
(setq pt22 (list (* (LM:rand) 100.0) (* (LM:rand) 100.0)))
(command "_pline" "_none" pt20 "_none"  pt21 "_none"  pt22 "_close"); pline no2
(setq PNO2_Area (vlax-get-property (vlax-ename->vla-object (entlast)) 'area))

(command "_region" "_all" "")

(princ (strcat "\nArea Pline No1: " (rtos PNO1_Area 2 4)))
(princ (strcat "\nArea Pline No2: " (rtos PNO2_Area 2 4)))
(princ)
)

 


Debashis Bhunia
Co-Founder of Geometrifying Trigonometry(C)
________________________________________________
Walking is the First step of Running, Technique comes Next....
Message 6 of 20

dlanorh
Advisor
Advisor
Accepted solution

I misinterpreted your "delete Rose colored region" to mean deleting everything in that region.  

 

(defun c:del_regions ( / c_doc ss r_lst)
  (setq c_doc (vla-get-activedocument (vlax-get-acad-object));;<==This gets the active document (the drawing)
        ss (ssget "_X" '((0 . "REGION")));;<==This gets a selectionset of all the regions
  )
  (if ss;;<==If there is anything in the selection set
    (vlax-for obj (vla-get-activeselectionset c_doc);;<==Get the selection set as a list of objects
      (setq r_area (vlax-get-property obj 'area));;<==gets the area property value and assigns it to variable r_area
      (if (>7500.0 r_area);;<==If area > 7500.0 (large rose region)
         (vla-delete obj);;Delete it
         (setq r_lst (cons r_area r_lst));;<==If area <7500 add area to list r_lst
      );end_if  
    );end_for
  );end_if
  (setq temp (vl-princ-to-string r_lst));;<==converts list to a string
(princ temp);;<==prints the string to the command line
);end_defun

Attached is rdlrep.lisp to generate random lines and then regions. It uses a different psuedo random number generator. You are asked to specify the number of lines to generate. If you enter 5, it will then produce 5 lines left to right and 5 bottom to top. You can also adjust the grid size and minimum length of line. These are documented and can be found in the (setq) statement under the local error defun.

I am not one of the robots you're looking for

Message 7 of 20

surfer96
Advocate
Advocate

Your code looks good, the routine loaded correctly, but did not work for a sample drawing with 3 regions.

 

AutoCAD said:

command: DEL_REGIONS

; error: no function definition: >7500

 

Is there a real/integer problem with

"(if (>7500.0 r_area);;<==If area > 7500.0 (large rose region)"

?

 

0 Likes
Message 8 of 20

CodeDing
Advisor
Advisor

@surfer96,

 

To address this one concern...


; error: no function definition: >7500

 


It is just a simple typo.. It is saying that ">7500" is not a function. You need to add a space between the ">" and "7500.0".

(if (> 7500.0 r_area);;<==If area > 7500.0 (large rose region)

Best,

~DD

 

 

0 Likes
Message 9 of 20

dlanorh
Advisor
Advisor

It's still wrong, as it should be Robot Sad

 

(if (> r_area 7500.0)

I am not one of the robots you're looking for

Message 10 of 20

surfer96
Advocate
Advocate

Tried rdlrep2.lsp which worked. If I got it, then "r_lst" is the list of region-areas, is that correct?

A minimum line length of e. g. 101 is not required , instead all line lenghts may be free, provided the lines span from box edge to box edge. However all randomly generated region-areas need to be within a RANGE from 50 to 200 mm² for ten lines in each direction after looping.

 

What about the following idea:

 

l_min 101.0 ; a minimum length of line is not required 

 

;; set an initial region-area list
(setq r_list '(0 (* grid grid))

 

(while (< (car r_lst) 50 200 (last r_lst))

 

;whole random calculation of lines and regions

 

(setq r_lst (vl-sort r_lst '<)); sort region-area list from smallest (car r_lst) to largest (last r_lst)

); end while

 

Could that work?

0 Likes
Message 11 of 20

dlanorh
Advisor
Advisor

@surfer96 wrote:

Tried rdlrep2.lsp which worked. If I got it, then "r_lst" is the list of region-areas, is that correct?

A minimum line length of e. g. 101 is not required , instead all line lenghts may be free, provided the lines span from box edge to box edge. However all randomly generated region-areas need to be within a RANGE from 50 to 200 mm² for ten lines in each direction after looping.

 

What about the following idea:

 

l_min 101.0 ; a minimum length of line is not required 

 

;; set an initial region-area list
(setq r_list '(0 (* grid grid))

 

(while (< (car r_lst) 50 200 (last r_lst))

 

;whole random calculation of lines and regions

 

(setq r_lst (vl-sort r_lst '<)); sort region-area list from smallest (car r_lst) to largest (last r_lst)

); end while

 

Could that work?


Sorry, I missed your reply.

 

Yes r_lst is the list of region areas

 

I included l_min in case it was required at some point. The default min length is 100.0 so (setq l_min 100.0)

 

It is pointless putting the min and max areas into the list as they will always be the min and max, plus an area of 0.0 is the inverse of infinity and an impossibility for a region

 

(while (< (car r_lst) 50 200 (last r_lst)) ?? Not sure what you are trying to do? What are you testing? As written and coupled with the min and max above this will always be true, thus an infinite loop.

 

If you are trying to remove areas < 50 or >200 then it should go outside any loop

 

(setq r_lst (vl-remove-if-not '(lambda (x) (< 50.0 x 200.0)) r_lst))

If you ony want to save the area of regions where the area is >50 and <200 then

 

(if (< 50.0 r_area 200.0).........)

(setq r_lst (vl-sort r_lst '<)) Correct to sort the list min to max, but don't put sort routines inside loops, they only need to run once.

 

 

I am not one of the robots you're looking for

0 Likes
Message 12 of 20

surfer96
Advocate
Advocate

Actually I don't want to delete any regions. Instead all regions should be recalculated in case the smallest region is smaller than 50 and the largest ist larger than 200.

What I meant was the whole random calculation of lines and regions could be "wrapped" in some kind of "while loop envelope" forcing all outcoming regions' areas to be within (50 - 200):

 

(while (or (< car r_lst 50) (> last r_lst 200))

 

"whole random calculation of lines and regions"

 

); end while

 

0 Likes
Message 13 of 20

dlanorh
Advisor
Advisor

Look at the image in your initial post. The probability of generating a region with an "out of scope" area (< x 50) (> x 200) using constricted random straight lines is infinitesimally large. The less lines you use the greater the chance a region will be too large. The more lines you use the chance of creating a region that is too small by intersection increases.

 

You would be better served generating random points, and attempting to contruct a triangular irregular network.

I am not one of the robots you're looking for

0 Likes
Message 14 of 20

surfer96
Advocate
Advocate

Delaunay-, Voronoi- or similarly generated triangles from random points will not be "in line", but I'm definitely looking for a "linear" solution.

I tried to wrap your code with another while loop.

To simplify calculation the number of "inner random lines" in the "100-100-box" is set two one, ideally dividing it into 4 equal parts of 2500 mm² each. No region should be outside a 500-5000 mm² range.

 

Variables:

(setq bot_rg 500) ; bottom range
(setq top_rg 5000) ; top range
(setq r_lst '(0 (expt grid 2))) ; start region area list
(setq min_r_lst (car r_lst)) ; smallest in region area list
(setq max_r_lst (last r_lst)) ; largest in region area list

 

Loop:

(while (or (< min_r_lst bot_rg) (> max_r_lst top_rg))

(setq r_lst nil)

(while (not r_lst)
(while (> lcnt 0)

...

)

It works from time to time but prompts an error message in most cases.

Error : bad argument: AcDbCurve 97 occured

 

What's wrong with the loop?

Unbenannt.JPG

0 Likes
Message 15 of 20

dlanorh
Advisor
Advisor

Unfortunatly the code was not designed to be looped. It starts with a polyline, adds random polylines calculating distances along and intersections with the boundary which are then sorted and fed back into the boundary. The same is done testing each random polyline against all the others. Once this is completed all the polylines are exploded to lines and the regions formed. All lines are deleted as part of the region command leaving you with just regions. The errors are generated because the second tiime through the loop there is no pristine boundary polyline for the vlax-curve functions to work with, and after that the new random lines won't interact with the existing regions because they aren't polylines. You can however explode them and reconstitute them as polylines .

I am not one of the robots you're looking for

0 Likes
Message 16 of 20

surfer96
Advocate
Advocate

If the pristine rectangle boundary is redrawn and any object in model space deleted before every new round through the loop it could work. I' ll give that a try.

By the way instead of working with an area-range as condition, one could say that the distance between any break point and its closest and second closest break point neighbours must be within a distance-range. This would lead to minimal and maximal equilateral triangles, thus indirectly to an area-range.

Maybe this would speed up the loop...

0 Likes
Message 17 of 20

surfer96
Advocate
Advocate

Tried to wrap your code with a second while loop (attached file) but it's still not working...

I thought if I just erased all objects from model space and then restarted your code including the pristine boundary polyline, it should, but it doesn't. Why is that? Do further lists, safearrays or variables need to be cleared before each new round through the loop? Do you think it can be fixed to match a region-area-range?

  (setq c_doc (vla-get-activedocument (vlax-get-acad-object))
        ms (vla-get-modelspace c_doc)  
        pl_pts (vlax-make-safearray vlax-vbDouble '(0 . 3)) 
        grid 100.0    ;;change grid size here
        l_min 101.0   ;;change min length of line here
  );end_setq

  (setq bot_rg 1500			; bottom range
	top_rg 3500			; top range
	r_lst '(0 (expt grid 2))	; start region area list 
	min_r_lst (car r_lst)		; smallest in region area list
	max_r_lst (last r_lst)	; largest in region area list
  )

  (initget (+ 1 2 4))
  (setq cnt (getint "\nEnter No of Lines to Produce : ")
        lcnt cnt
  );end_setq
= = = = =
;; WRAPPPING WHILE LOOP REGION AREAS
  (while (or (< min_r_lst bot_rg) (> max_r_lst top_rg))
    
    (setq ss (ssget "_x" '((410 . "Model"))))	
    (vl-cmdf "_erase" ss "")			; delete all ojects in model space
    	(setq r_lst nil				; set lists to nil
	      p_lst nil
	      l_lst nil)

  (vl-cmdf "_pline" '(0 0) '(100 0) '(100 100) '(0 100) "_c")
= = = = =
  ); end while r_list creation

    (setq r_lst (vl-sort r_lst '<)
	  min_r_lst (car r_lst)
	  max_r_lst (last r_lst))

  ); END WRAPPPING WHILE LOOP REGION AREAS

 

0 Likes
Message 18 of 20

dlanorh
Advisor
Advisor

I'm unable to test code as I am away for the weekend. What errors are you getting?

 

 

I am not one of the robots you're looking for

0 Likes
Message 19 of 20

surfer96
Advocate
Advocate

AutoCAD says "error: bad argument type: AcDbCurve 17676 occured".

It seems as if the loop starts correctly but then stops somewhere in between returning incomplete results with e.g. 5 lines and 1 polyline like this:Unbenannt 02.JPG

 

 

 

0 Likes
Message 20 of 20

surfer96
Advocate
Advocate

Look at this one, mainly based on  Charles Alan Butler's "BreakObjects.lsp".

The lower boundary for the region area range is set to the very small value of 0.01, just to get some result in acceptable time.

 

It would be interesting to know if it's slower or faster compared to your own aproach?

0 Likes