两个步骤:第一步,导入已有户型图,第二部:系统自动智能设计。我们刨析一下实现原理。首先我们总结一下设计规律,智能匹配户型与家具无外乎两种情况:1-同户型不同家具,2-同家具不同户型。所以我们在导入户型图自动设计的时候必须先解决这两个问题:
一:假如我有若干套相同的户型,该如何自动设计不同的设计风格和设计方案?
二:假如我已有一套设计方案(所有硬装、软装家具)该如何自动智能的布置到不同的户型里?
(文末会提供实现的算法思路和源码,完整介绍如何实现‘从导入户型图到自动设计’的整个流程及原理)
我们首先看算法运行结果,即第一个问题的实际图解:(我找了几种典型户型图作为案例)
以上是相同户型图不同的家具的设计结果,由图可看出,在设计的合理性方面是不存在很大的问题的,这里面会涉及到一些算法的影响因素:房间面积、房间形状、最小区域面积、房间朝向、房间形状重心点……
下面罗列相同家具放到不同房间里的效果,这种在算法复杂度上会有较大提升,为了实现设计的合理性,我们的考量因素会更多,我们先看图,最后剖析算法实现原理和设计规律:
其实单单从图解上已经不难看出设计规律,即每一个区域设计的合理性考量。正常大脑思维对房间的设计考量因素无非:房间面积、区域面积、房间功能分块、流通性、规整等。也可加入其它考量因素例如审美相关:留白、不对称、(颜色、风格等因素本章不做探讨)视觉偏差、视觉残像等。除此之外,甚至可以加入风水考量因素:房间正朝向、软硬装家具摆放方位、八卦方位、五行方位等。
好了,图解完成,最后由具体变抽象,将实际问题转化成数学模型,通过代码描述一下具体实现:
算法思路:
考虑到文中所讲的设计合理性考量,我们需要对整个房间提取特征数据,房间中可以提取到的特征数据为:房间各个顶点的坐标、各个顶点的坐标id、墙体id、墙体长度、房间面积、房间重心点。(风格、房间类型不必纳入特征数据中)
此时设计师大脑中幻想出了一套设计方案,想通过我们的系统进行记录,则应转化成具体数据,也就是转化成如上所提取的特征数据来存储,我们通过具体图解来理解算法原理
我们分步骤讲解这两个户型中沙发茶几组合是如何由上面的房间位置自动匹配到下面的房间位置里。
一:找到距原房间沙发茶几组合的中心点最近的墙体的定比分点比例
二:由中心点向墙体做垂线,计算出垂足点距离墙体两端的距离比例,并作记录 X :Y
三:找到沙发茶几组合所距离最近墙体的距离比例,并作记录 向量DO :向量OP
四:将两个定比分点的比例转换成代码的方式:
五:程序中动态计算并存储的算法实现:
///
/// 获得当前家具在房间里的放置位置
///
/// 单个家具所在坐标
/// 家具所在房间
/// 家具可序列化信息
///
public SetScorePoint GetPoint(Vector3 position, Floor floor)
{
//定比分点
SetScorePoint Point = new SetScorePoint();
//临时数据
List
//墙体集合
List
//当前点距离墙的距离
float tempdistance = 0;
//对应墙体的索引
int index = 0;
//家具和距离最近的那面墙的垂足点坐标
Vector3 point = new Vector3();
//距离最近的那面墙
Wall wall = new Wall();
//保存所有当前点距离墙的距离
for (int i = 0; i < floor.VHWall_List.Count; i++)
{
tempdistance = Vector3.Distance(position, mathf.PointForPointToABLine(position, floor.VHWall_List[i].Vector_Start, floor.VHWall_List[i].Vector_End));
distancelist.Add(tempdistance);
idlist.Add(new KeyValuePair
}
//对距离排序
distancelist.Sort();
//获得距离最近的墙idlist[distancelist[0]],和所在墙的距离比例
for (int i = 0; i < idlist.Count; i++)
{
if (idlist[i].Key == distancelist[0])
{
wall = idlist[i].Value;
index = i;
point = mathf.PointForPointToABLine(position, wall.Vector_Start, wall.Vector_End);
break;
}
}
//获得墙体id
Point.wallid = index;
//获得所在墙比例
Point.i = Vector3.Distance(point, wall.Vector_Start) / Vector3.Distance(wall.Vector_End, wall.Vector_Start);
//获得所在墙到对面墙比例
LayerMask mask = 1 << (LayerMask.NameToLayer("Wall"));
Ray ray = new Ray(new Vector3(position.x, 0.5f, position.z), (new Vector3(position.x, 0, position.z) - new Vector3(point.x, 0, point.z)).normalized);
RaycastHit[] hits = Physics.RaycastAll(ray, 500, mask);
if (hits.Length > 0)
{
Point.j = Vector3.Distance(position, point) / Vector3.Distance(point, hits[0].point);
}
}
return Point;
}
六:程序中具体应用设计的算法实现:
///
/// 定比分点算法获得位置
///
///
///
///
public Vector3 GetPosition(BaseData data, VertexHelperUI_Floor room)
{
Point spa = data as Score;
//最终坐标
Vector3 position = Vector3.zero;
//对应墙体
Wall wall_i = new Wall();
//定比分点在对应墙体上的坐标
Vector3 Score_i = new Vector3();
if (spa.wallid < room.VHWall_List.Count)
{
wall_i = room.VHWall_List[spa.wallid];
}
else
{
wall_i = room.VHWall_List[spa.wallid % room.VHWall_List.Count];
}
//获得定比分点墙体上坐标
Score_i = wall_i.Vector_Start + (wall_i.Vector_End - wall_i.Vector_Start) * spa.i;
LayerMask mask = 1 << (LayerMask.NameToLayer("Wall"));
//顺时针转90度方向
Vector3 forward_0 = Quaternion.AngleAxis(90, Vector3.up) * (wall_i.Vector_End - Score_i);
Ray ray_0 = new Ray(new Vector3(Score_i.x, 0.5f, Score_i.z), new Vector3(forward_0.x, 0, forward_0.z));
RaycastHit[] hits_0 = Physics.RaycastAll(ray_0, 500, mask);
if (hits_0.Length > 0)
{
for (int i = 0; i < room.VHWall_List.Count; i++)
{
if (IWallManager.Instance.GetWall(hits_0[0].transform.gameObject).id == room.VHWall_List[i].ID)
{
position = Score_i + (hits_0[0].point - Score_i) * spa.j;
return position;
}
}
}
//逆时针转90度方向
Vector3 forward_1 = Quaternion.AngleAxis(90, Vector3.down) * (wall_i.Vector_End - Score_i);
Ray ray_1 = new Ray(new Vector3(Score_i.x, 0.5f, Score_i.z), new Vector3(forward_1.x, 0, forward_1.z));
RaycastHit[] hits_1 = Physics.RaycastAll(ray_1, 500, mask);
if (hits_1.Length > 0)
{
for (int i = 0; i < room.VHWall_List.Count; i++)
{
if (IWallManager.Instance.GetWall(hits_1[0].transform.gameObject).id == room.VHWall_List[i].ID)
{
position = Score_i + (hits_1[0].point - Score_i) * spa.j;
return position;
}
}
}
return position;
}
一切就绪,大家可以尝试把算法套用到自己的工程里,实现一下。现在先实现了初级阶段,即:仅考虑墙面的定比分点比例的自动设计,后续文章会陆续介绍如何根据房间面积、设计风格、审美、风水等因素所实现的自动设计算法思路,欢迎小伙伴们继续关注、订阅支持,谢谢。