Javier Sandoval
Javier Sandoval

Reputation: 507

Randomly divide a square into smaller squares in Netlogo

Is there a way to divide a rectangle into smaller, random size rectangles in Netlogo? Like the following image with 6 rectangles, one which has been subdivided, and the subdivision painted black:

Subdivided farmland

This question is because I have a shp file of farmland loaded in Netlogo composed of regular rectangles, and want to simulate how they are subdivided and occupied by urban development. Size of subdivisions can be a range between 1/3 to 1/8 of total square. Thanks.

Upvotes: 0

Views: 204

Answers (1)

Luke C
Luke C

Reputation: 10301

I think that's pretty tricky, especially if you want specific minimum dimensions. Maybe play around with the code below to see if it gets you started? It basically takes a rectangle input (a farm plot, potentially) and generates a whole bunch of overlapping rectangles. The problem with the random bit is that you can (as is) get sections that are narrower or taller than your 1/8 since you get overlap. Play around with the counter to increase or reduce the number of rectangles it makes- fewer means that the entire patch may not be subdivided, more means longer run time and potentially more small subdivisions.

patches-own [ id ]

to setup
  ca
  resize-world -50 50 -50 50
  set-patch-size 4
  ask patches [ set id -1 ]
  ask rectangle -20 20 -40 40 [
    set pcolor red
  ]
  reset-ticks
end

to go 
  rectangle-sub -20 20 40 -40
  tick
end

to rectangle-sub [ x0 x1 y0 y1 ]

  ; Make sure the x0 x1 / y0 y1 order is correct
  let xp0 min (list x0 x1)
  let xp1 max (list x0 x1)
  let yp0 min (list y0 y1)
  let yp1 max (list y0 y1)

  ; Define the rectangle to subdivide
  let main_rect rectangle xp0 xp1 yp0 yp1

  ; Define width and height
  let width xp1 - xp0
  let height yp1 - yp0
  let w3 round ( width / 3 )
  let w8 round ( width / 8 )
  let h3 round ( height / 3 )
  let h8 round ( height / 8 )

  ; Set a while loop to make mini rectangles, with
  ; a counter to stop the loop after too many tries
  let counter 0
  while [ counter < 100 and any? main_rect with [id = -1 ] ] [
    let newx0 xp0 + ( random width )
    let newx1 newx0 + ( ceiling width / ( random  6 + 3 ) )
    let newy0 yp0 + ( random height )
    let newy1 newy0 + ( ceiling height / ( random 6 + 3 ) )

    ; define a sub rectangle
    let newrect rectangle newx0 newx1 newy0 newy1

    ; remove any patches from the sub rectangle that are not
    ; also part of the main rectangle
    set newrect newrect with [ member? self main_rect ]
    if any? newrect [

      ; Make sure the dimensions aren't too small or big
      let nwidth ( max [pxcor] of newrect - min [pxcor] of newrect )
      let nheight ( max [pycor] of newrect - min [pycor] of newrect )
      if  nwidth < w3 and nwidth > w8 and  nheight < h3 and nheight > h8 [

        ; Choose a random patch and assign its id to all others 
        ; in the same newrect patch-set
        let groupid random 10000
        ask newrect [ 
          set id groupid
          set pcolor id / 100
        ]
      ]
    ]
    set counter counter + 1
  ]
  print counter

end

to-report rectangle [ x0 x1 y0 y1 ]
  ; reports a patch-set bounded by 
  ; the coordinate arguments passed
  report patches with [ 
    pxcor > x0 and pxcor < x1 and
    pycor > y0 and pycor < y1
  ]
end

EDITS:

Ok- I may have gone a bit overboard here but I like this problem. Here is an alternative solution that has turtles draw the rectangles- that way there is no overlap. Additionally, rectangles have to build off the outside or previously id-d rectangles, so you don't get random disconnected rectangles. However, it can leave one-patch squares with no id within the rectangle, so you'll have to sort those out as you like.

To see what how it works, put the display to continuous and slow it way down. In essence, a turtle sprouts on a patch without an id, hatches a partner who travels away some distance before they both turn the same direction and move forward and move some distance. The x and y coordinates of each corner patch are stored in a list then used to assign an id to the rectangle they define.

patches-own [ id ]

to setup
  ca
  resize-world -50 50 -50 50
  set-patch-size 5
  ask patches [ set id -1 ]
  ask rectangle -20 20 -40 40 [
    set pcolor red
  ]
  reset-ticks
end

to go
  turtle-define-rect -20 20 40 -40
  tick
end


to turtle-define-rect [ x0 x1 y0 y1 ]

  ; Make sure the x0 x1 / y0 y1 order is correct
  let xp0 min (list x0 x1)
  let xp1 max (list x0 x1)
  let yp0 min (list y0 y1)
  let yp1 max (list y0 y1)

  ; Define the rectangle to subdivide
  let main_rect rectangle xp0 xp1 yp0 yp1

  let xlist []
  let ylist []
  let possible_area one-of main_rect with [ 
    id = -1 and 
    any? neighbors4 with [id = -1] and
    any? neighbors4 with [not member? self main_rect or id != -1]
  ]
  if possible_area != nobody [
    ask possible_area [
      let w random 5 + 5
      let h random 10 + 10
      sprout 1 [
        set color blue
        set size 3
        let start-patch patch-here
        let id_temp [id] of patch-here
        face one-of neighbors4 with [not member? self main_rect or id != -1]
        rt 180
        hatch 1 [
          create-link-with one-of other turtles-here
          repeat w [
            if ( [id] of patch-ahead 1 = -1 ) and ( [ member? self main_rect] of patch-ahead 1 ) [
              fd 1
            ]
          ]
        ] 
        set xlist lput xcor xlist
        set ylist lput ycor ylist
        ask link-neighbors [
          set xlist lput xcor xlist
          set ylist lput ycor ylist
        ]
        let turn one-of [ 90 -90 ]
        rt turn
        ask link-neighbors [
          rt turn
        ]
        ask my-links [ 
          tie 
        ]
        repeat h [
          if ( 
            ( [id] of patch-ahead 1 = -1 ) and 
            ( [ member? self main_rect] of patch-ahead 1 ) and
            ( [ [id] of patch-ahead 1 = -1 ] of link-neighbors = [true] ) and
            ( [ [ member? self main_rect] of patch-ahead 1  ] of link-neighbors = [true] )
          )
          [
            fd 1
          ]
        ]
        set xlist lput xcor xlist
        set ylist lput ycor ylist
        ask link-neighbors [
          set xlist lput xcor xlist
          set ylist lput ycor ylist
        ]
        ask link-neighbors [ die ]
        die
      ]
    ]
    let xt0 min xlist
    let xt1 max xlist
    let yt0 min ylist
    let yt1 max ylist 
    let new_id random 10000

    ask rectangle xt0 xt1 yt0 yt1 [
      set id new_id
    ]    
  ] 

  ask main_rect with [ id != -1 ] [
    set pcolor id / 100
  ]
end

to-report rectangle [ x0 x1 y0 y1 ]
  ; reports a patch-set bounded by
  ; the coordinate arguments passed
  report patches with [
    pxcor >= x0 and pxcor <= x1 and
    pycor >= y0 and pycor <= y1
  ]
end

Output looks something like:

enter image description here

Where colors other than red indicate rectangles with different ids.

Upvotes: 1

Related Questions