首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >具有特定规则的细分矩形

具有特定规则的细分矩形
EN

Stack Overflow用户
提问于 2019-02-12 16:26:46
回答 1查看 331关注 0票数 0

所有人。首先,我是一个业余程序员。为了学习,我尝试用C++和SFML制作一个简单的城市建设应用程序。这并不是一个真正的游戏,因为它只会建造城市街区和建筑物,并向用户展示。现在,我可以创建城市街区。我的问题是如何将这些建筑物细分为建筑物。我真的不知道该怎么做。可能的解决方案是(我没有足够的声誉发布图片,但有链接):https://i.postimg.cc/630GKGW7/bitmap.png

唯一的规则是:(1)每座建筑物必须符合已知的最小和最大尺寸;(2)每座建筑物必须至少有一张脸接触任何块边缘;(3)不应留下任何空位。

我已经胡思乱想好几天了。有人能告诉我怎么做吗?伪码也很好。

提前谢谢!

EN

回答 1

Stack Overflow用户

回答已采纳

发布于 2019-02-12 23:19:51

请注意,我将使用OOP语法来简化操作,但这不是有效的代码。让我们首先创建一个接口来定义我们想要的行为:

代码语言:javascript
复制
class CityBlock {
  Building[] buildings // should initially contain one building taking up the whole CityBlock

  double width
  double height
  double maxBuildingSize
  double minBuildingSize

  splitBuilding(horizontal/vertical, coord) // This will split a building horizontally/vertically
  createRandomBuildings() // this is what we want to create!
}

class Building {
  Point position // position of top-left corner
  Building[] subBuildings // buildings created by subdivision

  double width
  double height

  double size() { return width * height }
}

现在是有趣的部分!让我们尝试制作createRandomBuildings()方法。我将采用的方法是反复对建筑物进行细分,直到它们位于2 * minBuildingSize (小于这意味着没有任何细分可以创建两个有效的建筑物)和maxBuildingSize之间。

重要注意事项:这种方法只保证有效的建筑物如果maxBuildingSize >= 2 * minBuildingSize,即使一个有效的细分是可能的。考虑到您的用例,我认为size约束不会带来任何问题,更“随机”的解决方案更好,而不是更确定的解决方案。

我们开始吧!我们将创建一个名为subdivide的递归函数来完成繁重的工作。

代码语言:javascript
复制
Building[] subdivide(Building b, horizontal/vertical) {} // Subdivides b into a random number of other buildings

我要细分每一座建筑的方法是把它分成一个随机数量的水平/垂直段。例如。

从这里开始

到这个

注意:为了简化问题,我将把细分处理为垂直的,如上面的图像所示。对于水平细分,只需交换宽度/高度。

当然,我们不能使用任何数量的细分。太多,所有由此产生的建筑物都会太小。因此,我们应首先界定容许我们兴建有效建筑物的分区数目上限。

代码语言:javascript
复制
minSubdivisionWidth = minSize / b.height // ensures that subdivisionWidth * b.height >= minSize
maxSubdivisions = floor(b.width / minSubdivisionWidth)

subdivisions = randomInt(2, maxSubdivisions)

现在我们有一个有效的细分,我们需要随机空间,同时确保建筑物不太小。要做到这一点,让我们将可用的空间分成两部分:最小空间和空闲空间。每个细分都需要最小的空间,但也有相当于b.size() - minBuildingSize * subdivisions的空闲(或剩余)空间。这个自由空间是我们想要在我们的细分矩形中随机分布的。

蓝色是最小的空间,粉红色是自由空间。

让我们分配这个空间:

代码语言:javascript
复制
widths[] // This will be the widths of our subdivided buildings
freeWidth = b.width - minSubdivisionWidth * subdivisions
weights[] // randomly assigned weight for free space
sumWeight

for i = 1 to subdivisions {
  randWeight = random()
  weights[i] = randWeight
  sumWeight += randWeight
}

for i = 1 to subdivisions {
  widths[i] = minSubdivisionWidth + (weights[i] / sumWeight) * freeWidth
}

现在我们可以进行实际的细分:

代码语言:javascript
复制
// transform individual widths into coordinates for building split
cumulativeWidth = 0

for i = 1 to (subdivisions - 1) {
  cumulativeWidth += widths[i]
  splitBuilding(vertical, cumulativeWidth)
}

我们快到了!现在,我们只需要一个片段,以随机而不是细分,如果建筑物是低于最大值:

代码语言:javascript
复制
probToNotSubdivide = .3 // obviously change this to whatever
if b.size() < maxBuildingSize and randomDouble(0, 1) <= probToNotSubdivide { return }

如果建筑物太小,就不要再细分:

代码语言:javascript
复制
if b.size() < minBuildingSize * 2 { return }

如果要把建筑物从砖块的边缘切下来,就不要再分了:

代码语言:javascript
复制
/* 
If the building is touching a horizontal edge, vertical subdivisions 
will not cut anything off. If the building is touching both 
vertical edges, one subdivision can be made.
*/
if not (b.position.y == 0 or (b.position.y + b.height) == cityBlock.height) {
  if b.width == cityBlock.width {
    // do one subdivision and recurse
    splitBuilding(vertical, randomDouble(minSubdivisionWidth, cityBlock.width - minSubdivisionWidth)
    for subBuilding in b.subBuildings {
      subdivide(horizontal, subBuilding)
    }
    return
  } else { return }
}

在结尾加上一些递归,然后.

代码语言:javascript
复制
Building[] subdivide(Building b, horizontal/vertical) {
  // exit conditions
  if b.size() < maxBuildingSize and randomDouble(0, 1) <= probToNotSubdivide { return }
  if b.size() < minBuildingSize * 2 { return }

  /* 
  If the building is touching a horizontal edge, vertical subdivisions 
  will not cut anything off. If the building is touching both 
  vertical edges, one subdivision can be made.
  */
  if not (b.position.y == 0 or (b.position.y + b.height) == cityBlock.height) {
    if b.width == cityBlock.width {
      // do one subdivision and recurse
      splitBuilding(vertical, randomDouble(minSubdivisionWidth, cityBlock.width - minSubdivisionWidth)
      for subBuilding in b.subBuildings {
        subdivide(horizontal, subBuilding)
      }
      return
    } else { return }
  }

  // get # subdivisions
  minSubdivisionWidth = minSize / b.height // ensures that subdivisionWidth * b.height <= minSize
  maxSubdivisions = floor(b.width / minSubdivisionWidth)
  subdivisions = randomInt(2, maxSubdivisions)

  // get subdivision widths
  widths[] // This will be the widths of our subdivided buildings
  freeWidth = b.width - minSubdivisionWidth * subdivisions
  weights[] // randomly assigned weight for free space
  sumWeight

  for i = 1 to subdivisions {
    randWeight = random()
    weights[i] = randWeight
    sumWeight += randWeight
  }

  for i = 1 to subdivisions {
    widths[i] = minSubdivisionWidth + (weights[i] / sumWeight) * freeWidth
  }

  // transform individual widths into coordinates for building split
  cumulativeWidth = 0

  for i = 1 to (subdivisions - 1) {
    cumulativeWidth += widths[i]
    splitBuilding(vertical, cumulativeWidth)
  }

  // recurse
  for subBuilding in b.subBuildings {
    subdivide(horizontal, subBuilding)
  }
}

就这样!现在我们有了createRandomBuildings() { subdivide(vertical, initialBuilding) },我们已经细分了我们的城市街区。

P.S.,这段代码不是有效的,这也是一篇很长的文章。如果这里有什么东西不能正常工作,请编辑/评论这个答案。我希望这能给你一些关于你可以采取的方法的一些见解。

编辑:为了澄清,您应该在每个递归级别上的水平和垂直细分之间切换。

票数 1
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/54654562

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档