Hi all.
I'm trying to create a rule that places houses of a specified depth and width around the perimeter of a Lot.
The lots are usually not rectangular, and I'm trying to create clusters of houses to maximise efficiency.
What I've tried so far is using the splitAndSetback operation, which carves the boundary into segments. Good so far. Other than houses, some of the buildings need to be perimeter blocks which wrap around the corners, so this operation is very useful.
I'm then trying to use the innerRectangle function to create a rectangle, that I can then split into individual shapes that will become the houses.
Unfortunately, sometimes the innerRectangle is narrower than the depth I need for the houses - see shape on the left.
Can anyone think of a way to alternate way to fit a rectangle with a fixed depth into these shapes, as pictured below in green?
Any help much appreciated.
For reference, the end goal is something similar to this.
Hi,
I had a similar challenge in a project so I ended up placing the buildings as fixed objects with the correct sizes using the 'i' operation. Thus, regardless of the shape of the InnerRectangle, the building kept its size.
I had 3 typologies, for the left, right and middle position. I also added scope controls for tunning the position and rotation.
Here's the CGA code:
/**
* File: Townhouses.cga
* Created: 24 May 2023 13:17:26 GMT
* Author: PFontes
*/
version "2022.1"
@Enum("Lawn 1", "Lawn 2") @Order(3)
attr grassType = "Lawn 1"
@Order(4)
attr xTextureSize = 5
@Order(5)
attr zTextureSize = 5
@Range(min=-180, max=180, restricted=true)
attr rotateScope = 90
@Range(min=-10, max=10, restricted=false)
attr scopeTranslateX = 0
@Range(min=-10, max=10, restricted=false)
attr scopeTranslateY = 0
@Range(min=-10, max=10, restricted=false)
attr scopeTranslateZ = 0
@Enum("Left", "Right", "Centre")
attr villaType = "Centre"
@Enum("uvtest", "white")
attr textureType = "uvtest"
Lot -->
innerRectangle(scope) { shape: GrassTexture Insert | remainder: GrassTexture}
InsertVilla -->
case villaType == "Left":
i("Townhouses/Townhouse Semi Left.dae", yUp, keepSizeAndPosition)
center(xz)
projectUV(0)
softenNormals(0)
comp(f){horizontal: GenericTexture | all: GenericTexture}
case villaType == "Right":
i("Townhouses/Townhouse Semi Right.dae", yUp, keepSizeAndPosition)
center(xz) projectUV(0)
softenNormals(0)
comp(f){horizontal: GenericTexture | all: GenericTexture}
else:
i("Townhouses/Townhouse.dae", yUp, keepSizeAndPosition)
center(xz)
softenNormals(0)
comp(f){horizontal: GenericTexture | all: GenericTexture}
Insert -->
rotateScope(0, rotateScope, 0)
t(scopeTranslateX, scopeTranslateY, scopeTranslateZ)
alignScopeToAxes(y)
InsertVilla
GenericTexture -->
case textureType == "uvtest":
texture("/ESRI.lib/assets/General/uvtest.png")
setupProjection(0, scope.xy, 1, 1, 0, 0, 1)
projectUV(0)
else: White.
GrassTexture -->
case grassType== "Lawn 1":
setupProjection(0, scope.xz, xTextureSize, zTextureSize)
texture("assets/Lawn 1.jpg")
projectUV(0)
else:
setupProjection(0, scope.xz, xTextureSize, zTextureSize)
texture("assets/Lawn 2.jpg")
projectUV(0)
Hope this helps.
Thanks @plfontes - the way you've centred the assets is useful. How are you carving up the original Lot into the individual houses (I can't see this in the CGA) - is that with the in-built subdivision types for the Block?
Hi,
The plot shapes were imported from GIS. Not done in CE.
But this rule would have worked the same way if the subdivision was done in CE, although probably less precise.
Thank you - sounds like we have a slightly different situation in that I need to generate the plot shapes inside of CE.
To simplify the original question - I need to fit a rectangle / box of a given depth into another shape - the width should be as long as can fit in that shape.
Any ideas?
One way I can think is to use the offset subdivision to have this level of control over the shape of the plots. But from looking at your images, seems that you have already used it. Another option would be not to use shape subdivision but use the split() operation and fix the size of the split on X and Z axis.
For example, this test rule I created while ago for filling a grid of 9x9m cell size within any given plot using split() instead of subdividing the initial block.
/**
* File: grid_test.cga
* Created: 25 Sep 2022 07:40:38 GMT
* Author: PFontes
*/
version "2019.1"
attr gridCellSize = 9
attr setbackAll = 0
@Enum("Global", "Local")
attr alignment = "Global"
@StartRule
Lot -->
setback(setbackAll){all: NIL | remainder: Parcel}
Parcel -->
case alignment == "Global" :
alignScopeToAxes(world.xyz)
split(x){gridCellSize: mirrorScope(false,true,false) set(scope.tz, 5) split(z){gridCellSize: A}*}*
else :
alignScopeToGeometry(yUp, any, longest)
split(x){gridCellSize: mirrorScope(true,false,true) set(scope.tz, -setbackAll) split(z){gridCellSize: A}*}*
A -->
innerRectangle(scope){shape: label("Bldg") B | remainder: NIL}
B -->
case geometry.area <= 60 : NIL
else: extrude(rand(3,6))
Check if any of this can help you.
Glad to be of help