惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

S
Securelist
有赞技术团队
有赞技术团队
WordPress大学
WordPress大学
V
V2EX
Google DeepMind News
Google DeepMind News
B
Blog RSS Feed
The Register - Security
The Register - Security
Recorded Future
Recorded Future
Y
Y Combinator Blog
小众软件
小众软件
Jina AI
Jina AI
V2EX - 技术
V2EX - 技术
钛媒体:引领未来商业与生活新知
钛媒体:引领未来商业与生活新知
P
Proofpoint News Feed
Engineering at Meta
Engineering at Meta
宝玉的分享
宝玉的分享
The Hacker News
The Hacker News
C
Cybersecurity and Infrastructure Security Agency CISA
K
Kaspersky official blog
博客园 - 三生石上(FineUI控件)
T
Threatpost
博客园 - 聂微东
Scott Helme
Scott Helme
IT之家
IT之家
N
Netflix TechBlog - Medium
T
The Exploit Database - CXSecurity.com
S
Schneier on Security
MongoDB | Blog
MongoDB | Blog
T
Tor Project blog
C
CXSECURITY Database RSS Feed - CXSecurity.com
A
About on SuperTechFans
酷 壳 – CoolShell
酷 壳 – CoolShell
C
CERT Recently Published Vulnerability Notes
P
Palo Alto Networks Blog
Spread Privacy
Spread Privacy
C
Check Point Blog
L
LINUX DO - 最新话题
D
Darknet – Hacking Tools, Hacker News & Cyber Security
Last Week in AI
Last Week in AI
Attack and Defense Labs
Attack and Defense Labs
T
Tailwind CSS Blog
罗磊的独立博客
让小产品的独立变现更简单 - ezindie.com
让小产品的独立变现更简单 - ezindie.com
Webroot Blog
Webroot Blog
Help Net Security
Help Net Security
cs.AI updates on arXiv.org
cs.AI updates on arXiv.org
OSCHINA 社区最新新闻
OSCHINA 社区最新新闻
爱范儿
爱范儿
PCI Perspectives
PCI Perspectives
Security Latest
Security Latest

博客园 - 我才是银古

第16章:常见问题、排错与最佳实践 第15章:扩展生态、MCAD 与外部集成 第12章:实战案例:机械结构与 3D 打印零件 第14章:构建、测试、调试与贡献流程 第13章:OpenSCAD 源码架构与核心执行流程 第11章:预览、渲染、网格精度与性能优化 第09章:列表推导、递归与算法建模 第08章:参数化零件库与复用设计 第10章:导入导出、命令行与自动化 第06章:CSG 布尔建模方法 第07章:二维图形、拉伸、旋转与投影 第05章:基础几何、坐标系与变换 第04章:参数、变量、函数、模块与作用域 OpenSCAD 教程目录 第03章:OpenSCAD 语言基础 第02章:安装、环境配置与开发工作流 第01章:OpenSCAD 项目全景与学习路线 第02章:源码获取、编译与开发环境配置 第01章:OCCT项目全景与学习路线 第18章:二次开发实战与综合案例 第18章:综合实战案例 第17章:数据交换与协同 第16章:源码架构与二次开发 第15章:插件与自定义工作台开发 第14章:Python脚本宏与自动化 第13章:FEM仿真分析 第12章:CAM数控加工 第11章:SurfaceMesh与逆向工程 第10章:Draft二维绘图与BIM建筑 第09章:工程图TechDraw 第07章:参数化表达式与Spreadsheet 第08章:装配设计Assembly 第06章:Part工作台与几何内核 第05章:PartDesign实体特征建模 第04章:草图Sketcher约束建模 第02章:安装版本与工作环境配置 第03章:界面工作台与基础操作 第01章:项目全景与学习路线 第十二章:插件开发、研究功能与最佳实践 第十章:定时任务与自动化(Cron) 第七章:技能、记忆与自学习闭环 第八章:MCP 集成与上下文文件 第六章:工具系统与终端后端 第五章:模型供应商与配置体系 Hermes Agent 教程目录 第十一章:语音、视觉、浏览器与子代理协作 第四章:CLI/TUI 与会话管理 第十二章:学习路线、实战方案与最佳实践 第十一章:源码结构、开发调试与插件开发 第十章:自动化、远程访问、日志与排障 第九章:Control UI、节点、Canvas 与语音能力 第七章:工具、技能、插件与能力扩展 第八章:安全模型、访问控制与沙箱实践 第六章:Agent 工作区、会话与多智能体路由 第五章:多通道消息接入与聊天平台配置 第四章:配置体系、模型接入与认证管理 第三章:Gateway 架构、协议与运行机制 第二章:安装、环境准备与快速上手 第一章:OpenClaw 项目概览与核心定位 oh-my-openagent 教程目录 09-命令模型回退与配置参考 10-实战案例最佳实践与故障排除 05-工作模式-Ultrawork-Prometheus-Atlas 08-Hooks与MCP系统 06-Category与Skill系统 07-核心工具链 04-智能体全景详解 03-安装与环境配置 02-整体架构与多模型编排机制 01-项目简介与核心理念 01-项目概览与学习路线 02-安装部署与工具适配 03-Skill机制与using-superpowers 05-TDD系统化调试与完成前验证 04-需求澄清方案设计与计划编写 07-并行智能体子智能体与Git-Worktree 第六章:代码审查、反馈处理与分支收尾 08-中国特色Skills与本土团队落地 09-MCP构建工作流执行与自定义Skill 第23章:FreeCAD-Python-API Clipper2 C# 源码解读教程 第19章:PolyTree 多边形树结构 第20章:实际应用与最佳实践 第18章:Minkowski 和与差 第17章:RectClip 矩形裁剪优化 第16章:ClipperOffset 偏移类详解 第15章:填充规则详解 第14章:布尔运算执行流程 第13章:ClipperD 浮点裁剪类 第11章:OutRec 与 OutPt 输出结构 第9章:Active 活动边结构 第10章:Vertex 顶点与 LocalMinima 局部极小值 第12章:Clipper64 裁剪类详解 第7章:高精度运算与128位整数 第8章:ClipperBase 基类详解 第5章:枚举类型与常量定义 第6章:InternalClipper 内部工具类 第2章:核心数据结构 - Point64、PointD 第3章:路径与多边形表示 - Path64、PathD、Paths64、PathsD 第4章:矩形边界 - Rect64、RectD
板房系统开发详解
我才是银古 · 2026-06-19 · via 博客园 - 我才是银古

第七章:板房系统开发详解

7.1 板房系统概述

7.1.1 功能介绍

板房系统是FY_Layout中最复杂的功能模块,用于在施工场地布置临时板房建筑。主要功能包括:

  • 板房楼栋的创建和布置
  • 单间板房的配置
  • 楼层管理
  • 房间功能设置(宿舍、办公室、会议室等)
  • 人员配置
  • 三维模型生成

7.1.2 核心类结构

PlateHouse/
├── PlateBuilding.cs          # 单间板房类
├── PlateBuildGroup.cs        # 板房楼栋类
├── PlateBuildingFloor.cs     # 楼层类
├── PlateRoom.cs              # 房间类
├── PlateK.cs                 # 板房单元格
├── PlatePassageway.cs        # 走廊类
├── StairsCell.cs             # 楼梯单元
├── GridCell.cs               # 网格单元
├── PlateBuildingAction.cs    # 板房操作类
├── PlateBuildGroupAction.cs  # 楼栋操作类
├── PlateBuild3dAction.cs     # 三维渲染类
├── PlateSet.cs               # 板房设置窗口
├── PlateGroupSet.cs          # 楼栋设置窗口
├── PlateRoomConfigManager.cs # 房间配置管理器
└── RoomConfigControl.cs      # 房间配置控件

7.2 板房数据模型

7.2.1 PlateBuilding - 单间板房

public class PlateBuilding : DirectComponent
{
    /// <summary>
    /// 板房位置
    /// </summary>
    public Vector2 Position { get; set; }
    
    /// <summary>
    /// 板房宽度
    /// </summary>
    public double Width
    {
        get => Properties.GetValue<double>("Width");
        set => SetProps((GetPropId(nameof(Width)), value));
    }
    
    /// <summary>
    /// 板房深度(长度)
    /// </summary>
    public double Depth
    {
        get => Properties.GetValue<double>("Depth");
        set => SetProps((GetPropId(nameof(Depth)), value));
    }
    
    /// <summary>
    /// 板房高度
    /// </summary>
    public double Height
    {
        get => Properties.GetValue<double>("Height");
        set => SetProps((GetPropId(nameof(Height)), value));
    }
    
    /// <summary>
    /// 旋转角度
    /// </summary>
    public double Rotation
    {
        get => Properties.GetValue<double>("Rotation");
        set => SetProps((GetPropId(nameof(Rotation)), value));
    }
    
    /// <summary>
    /// 所属楼栋
    /// </summary>
    public PlateBuildGroup ParentGroup { get; set; }
    
    /// <summary>
    /// 所在楼层
    /// </summary>
    public int FloorIndex { get; set; }
    
    /// <summary>
    /// 房间类型
    /// </summary>
    public RoomType RoomType { get; set; }
    
    public PlateBuilding()
    {
        Type = LayoutElementType.PlateBuilding;
        Width = 6000;   // 默认6米
        Depth = 3000;   // 默认3米
        Height = 2800;  // 默认2.8米
    }
    
    /// <summary>
    /// 获取板房轮廓
    /// </summary>
    public override Curve2dGroupCollection GetShapes()
    {
        var shapes = new Curve2dGroupCollection();
        var group = new Curve2dGroup();
        
        // 计算四个角点
        var halfW = Width / 2;
        var halfD = Depth / 2;
        
        var p1 = new Vector2(-halfW, -halfD);
        var p2 = new Vector2(halfW, -halfD);
        var p3 = new Vector2(halfW, halfD);
        var p4 = new Vector2(-halfW, halfD);
        
        // 应用旋转
        var rotMatrix = new Matrix3().MakeRotation(Rotation);
        p1.ApplyMatrix3(rotMatrix);
        p2.ApplyMatrix3(rotMatrix);
        p3.ApplyMatrix3(rotMatrix);
        p4.ApplyMatrix3(rotMatrix);
        
        // 平移到位置
        p1.Add(Position);
        p2.Add(Position);
        p3.Add(Position);
        p4.Add(Position);
        
        // 创建轮廓
        group.Curve2ds.Add(new Line2d(p1, p2));
        group.Curve2ds.Add(new Line2d(p2, p3));
        group.Curve2ds.Add(new Line2d(p3, p4));
        group.Curve2ds.Add(new Line2d(p4, p1));
        
        shapes.Add(group);
        return shapes;
    }
}

public enum RoomType
{
    Dormitory,      // 宿舍
    Office,         // 办公室
    MeetingRoom,    // 会议室
    Canteen,        // 食堂
    Bathroom,       // 卫生间
    Storage,        // 仓库
    Guard,          // 门卫室
    Other           // 其他
}

7.2.2 PlateBuildGroup - 板房楼栋

public class PlateBuildGroup : DirectComponent
{
    /// <summary>
    /// 楼栋位置
    /// </summary>
    public Vector2 Position { get; set; }
    
    /// <summary>
    /// 旋转角度
    /// </summary>
    public double Rotation { get; set; }
    
    /// <summary>
    /// 楼层数
    /// </summary>
    public int FloorCount
    {
        get => Properties.GetValue<int>("FloorCount");
        set => SetProps((GetPropId(nameof(FloorCount)), value));
    }
    
    /// <summary>
    /// 每层房间数(X方向)
    /// </summary>
    public int RoomsPerFloorX
    {
        get => Properties.GetValue<int>("RoomsPerFloorX");
        set => SetProps((GetPropId(nameof(RoomsPerFloorX)), value));
    }
    
    /// <summary>
    /// 每层房间数(Y方向)
    /// </summary>
    public int RoomsPerFloorY
    {
        get => Properties.GetValue<int>("RoomsPerFloorY");
        set => SetProps((GetPropId(nameof(RoomsPerFloorY)), value));
    }
    
    /// <summary>
    /// 单间宽度
    /// </summary>
    public double RoomWidth
    {
        get => Properties.GetValue<double>("RoomWidth");
        set => SetProps((GetPropId(nameof(RoomWidth)), value));
    }
    
    /// <summary>
    /// 单间深度
    /// </summary>
    public double RoomDepth
    {
        get => Properties.GetValue<double>("RoomDepth");
        set => SetProps((GetPropId(nameof(RoomDepth)), value));
    }
    
    /// <summary>
    /// 层高
    /// </summary>
    public double FloorHeight
    {
        get => Properties.GetValue<double>("FloorHeight");
        set => SetProps((GetPropId(nameof(FloorHeight)), value));
    }
    
    /// <summary>
    /// 走廊宽度
    /// </summary>
    public double CorridorWidth
    {
        get => Properties.GetValue<double>("CorridorWidth");
        set => SetProps((GetPropId(nameof(CorridorWidth)), value));
    }
    
    /// <summary>
    /// 楼层列表
    /// </summary>
    public List<PlateBuildingFloor> Floors { get; set; } = new List<PlateBuildingFloor>();
    
    /// <summary>
    /// 楼栋名称
    /// </summary>
    public string BuildingName { get; set; }
    
    public PlateBuildGroup()
    {
        Type = LayoutElementType.PlateBuildGroup;
        FloorCount = 2;
        RoomsPerFloorX = 5;
        RoomsPerFloorY = 2;
        RoomWidth = 6000;
        RoomDepth = 3000;
        FloorHeight = 2800;
        CorridorWidth = 1500;
    }
    
    /// <summary>
    /// 生成楼层和房间
    /// </summary>
    public void GenerateFloors()
    {
        Floors.Clear();
        
        for (int f = 0; f < FloorCount; f++)
        {
            var floor = new PlateBuildingFloor
            {
                FloorIndex = f,
                Elevation = f * FloorHeight,
                Rooms = new List<PlateRoom>()
            };
            
            // 生成房间
            for (int x = 0; x < RoomsPerFloorX; x++)
            {
                for (int y = 0; y < RoomsPerFloorY; y++)
                {
                    var room = new PlateRoom
                    {
                        GridX = x,
                        GridY = y,
                        Width = RoomWidth,
                        Depth = RoomDepth,
                        RoomType = RoomType.Dormitory
                    };
                    floor.Rooms.Add(room);
                }
            }
            
            Floors.Add(floor);
        }
    }
    
    /// <summary>
    /// 计算楼栋总尺寸
    /// </summary>
    public (double width, double depth, double height) GetTotalSize()
    {
        double width = RoomsPerFloorX * RoomWidth;
        double depth = RoomsPerFloorY * RoomDepth + CorridorWidth;
        double height = FloorCount * FloorHeight;
        return (width, depth, height);
    }
}

7.2.3 PlateBuildingFloor - 楼层

public class PlateBuildingFloor
{
    /// <summary>
    /// 楼层索引(从0开始)
    /// </summary>
    public int FloorIndex { get; set; }
    
    /// <summary>
    /// 楼层标高
    /// </summary>
    public double Elevation { get; set; }
    
    /// <summary>
    /// 房间列表
    /// </summary>
    public List<PlateRoom> Rooms { get; set; } = new List<PlateRoom>();
    
    /// <summary>
    /// 楼梯列表
    /// </summary>
    public List<StairsCell> Stairs { get; set; } = new List<StairsCell>();
    
    /// <summary>
    /// 走廊
    /// </summary>
    public PlatePassageway Corridor { get; set; }
    
    /// <summary>
    /// 获取楼层名称
    /// </summary>
    public string FloorName => FloorIndex == 0 ? "首层" : $"{FloorIndex + 1}层";
    
    /// <summary>
    /// 获取指定位置的房间
    /// </summary>
    public PlateRoom GetRoom(int gridX, int gridY)
    {
        return Rooms.FirstOrDefault(r => r.GridX == gridX && r.GridY == gridY);
    }
}

7.2.4 PlateRoom - 房间

public class PlateRoom
{
    /// <summary>
    /// 网格X坐标
    /// </summary>
    public int GridX { get; set; }
    
    /// <summary>
    /// 网格Y坐标
    /// </summary>
    public int GridY { get; set; }
    
    /// <summary>
    /// 房间宽度
    /// </summary>
    public double Width { get; set; }
    
    /// <summary>
    /// 房间深度
    /// </summary>
    public double Depth { get; set; }
    
    /// <summary>
    /// 房间类型
    /// </summary>
    public RoomType RoomType { get; set; }
    
    /// <summary>
    /// 房间名称
    /// </summary>
    public string RoomName { get; set; }
    
    /// <summary>
    /// 容纳人数
    /// </summary>
    public int Capacity { get; set; }
    
    /// <summary>
    /// 已分配人数
    /// </summary>
    public int AssignedCount { get; set; }
    
    /// <summary>
    /// 门的位置
    /// </summary>
    public DoorPosition DoorPosition { get; set; }
    
    /// <summary>
    /// 窗的位置列表
    /// </summary>
    public List<WindowPosition> Windows { get; set; } = new List<WindowPosition>();
    
    public PlateRoom()
    {
        Width = 6000;
        Depth = 3000;
        RoomType = RoomType.Dormitory;
        Capacity = 4;
    }
    
    /// <summary>
    /// 获取房间面积
    /// </summary>
    public double GetArea()
    {
        return Width * Depth / 1000000; // 转换为平方米
    }
}

public class DoorPosition
{
    public Side Side { get; set; }      // 门所在的边
    public double Offset { get; set; }   // 沿边的偏移
    public double Width { get; set; }    // 门宽
    public double Height { get; set; }   // 门高
}

public class WindowPosition
{
    public Side Side { get; set; }
    public double Offset { get; set; }
    public double Width { get; set; }
    public double Height { get; set; }
    public double Sill { get; set; }     // 窗台高度
}

public enum Side
{
    Front, Back, Left, Right
}

7.3 板房操作类

7.3.1 PlateBuildingAction

public class PlateBuildingAction : DirectComponentAction
{
    public PlateBuildingAction() { }
    
    public PlateBuildingAction(IDocumentEditor docEditor) : base(docEditor)
    {
        commandCtrl.WriteInfo("命令:PlateBuilding");
    }
    
    /// <summary>
    /// 执行创建
    /// </summary>
    public async void ExecCreate(string[] args = null)
    {
        var pointInputer = new PointInputer(docEditor);
        
        // 获取放置位置
        commandCtrl.WriteInfo("指定板房放置位置:");
        var result = await pointInputer.Execute();
        
        if (result.Status != InputStatus.OK)
        {
            commandCtrl.WriteInfo("操作已取消");
            return;
        }
        
        // 显示设置对话框
        var settingForm = new PlateSet();
        if (settingForm.ShowDialog() == DialogResult.OK)
        {
            CreatePlateBuilding(result.Point, settingForm.Settings);
        }
    }
    
    private void CreatePlateBuilding(Vector2 position, PlateBuildingSettings settings)
    {
        var doc = docRt.Document;
        
        var plate = new PlateBuilding
        {
            Position = position,
            Width = settings.Width,
            Depth = settings.Depth,
            Height = settings.Height,
            RoomType = settings.RoomType
        };
        
        plate.Initilize(doc);
        plate.ResetBoundingBox();
        plate.Layer = GetLayer().Name;
        
        vportRt.ActiveElementSet.InsertElement(plate);
        docRt.Action.ClearSelects();
    }
    
    /// <summary>
    /// 绘制板房
    /// </summary>
    public override void Draw(LcCanvas2d canvas, LcElement element, Matrix3 matrix)
    {
        var plate = element as PlateBuilding;
        var pen = GetDrawPen(plate);
        
        // 绘制轮廓
        foreach (var curve in plate.GetShapes()[0].Curve2ds)
        {
            canvas.DrawCurve(pen, curve, matrix);
        }
        
        // 绘制房间类型标注
        var textPaint = new LcTextPaint
        {
            Color = pen.Color,
            FontName = "仿宋",
            Size = 500,
            Position = plate.Position
        };
        canvas.DrawText(textPaint, GetRoomTypeText(plate.RoomType), matrix, out _);
    }
    
    private string GetRoomTypeText(RoomType type)
    {
        return type switch
        {
            RoomType.Dormitory => "宿舍",
            RoomType.Office => "办公室",
            RoomType.MeetingRoom => "会议室",
            RoomType.Canteen => "食堂",
            RoomType.Bathroom => "卫生间",
            RoomType.Storage => "仓库",
            RoomType.Guard => "门卫",
            _ => "其他"
        };
    }
}

7.3.2 PlateBuildGroupAction

public class PlateBuildGroupAction : DirectComponentAction
{
    public PlateBuildGroupAction() { }
    
    public PlateBuildGroupAction(IDocumentEditor docEditor) : base(docEditor)
    {
        commandCtrl.WriteInfo("命令:PlateBuildGroup");
    }
    
    /// <summary>
    /// 创建板房楼栋
    /// </summary>
    public async void ExecCreate(string[] args = null)
    {
        var pointInputer = new PointInputer(docEditor);
        
        // 获取放置位置
        commandCtrl.WriteInfo("指定楼栋放置位置:");
        var result = await pointInputer.Execute();
        
        if (result.Status != InputStatus.OK)
        {
            Cancel();
            return;
        }
        
        // 显示楼栋设置对话框
        var settingForm = new PlateGroupSet();
        if (settingForm.ShowDialog() == DialogResult.OK)
        {
            CreateBuildGroup(result.Point, settingForm.Settings);
        }
    }
    
    private void CreateBuildGroup(Vector2 position, PlateBuildGroupSettings settings)
    {
        var doc = docRt.Document;
        
        var group = new PlateBuildGroup
        {
            Position = position,
            BuildingName = settings.BuildingName,
            FloorCount = settings.FloorCount,
            RoomsPerFloorX = settings.RoomsPerFloorX,
            RoomsPerFloorY = settings.RoomsPerFloorY,
            RoomWidth = settings.RoomWidth,
            RoomDepth = settings.RoomDepth,
            FloorHeight = settings.FloorHeight,
            CorridorWidth = settings.CorridorWidth
        };
        
        group.GenerateFloors();
        group.Initilize(doc);
        group.ResetBoundingBox();
        group.Layer = GetLayer().Name;
        
        vportRt.ActiveElementSet.InsertElement(group);
        docRt.Action.ClearSelects();
        
        commandCtrl.WriteInfo($"已创建楼栋:{settings.BuildingName},共{settings.FloorCount}层,{settings.RoomsPerFloorX * settings.RoomsPerFloorY * settings.FloorCount}个房间");
    }
    
    /// <summary>
    /// 设置楼栋属性
    /// </summary>
    public void SetGroup(string[] args = null)
    {
        var selectedElements = docRt.GetSelectedElements();
        var groups = selectedElements.OfType<PlateBuildGroup>().ToList();
        
        if (groups.Count == 0)
        {
            commandCtrl.WriteError("请先选择板房楼栋");
            return;
        }
        
        var settingForm = new PlateGroupSet(groups[0]);
        if (settingForm.ShowDialog() == DialogResult.OK)
        {
            foreach (var group in groups)
            {
                UpdateGroupSettings(group, settingForm.Settings);
            }
        }
    }
    
    private void UpdateGroupSettings(PlateBuildGroup group, PlateBuildGroupSettings settings)
    {
        group.BuildingName = settings.BuildingName;
        group.FloorCount = settings.FloorCount;
        group.RoomsPerFloorX = settings.RoomsPerFloorX;
        group.RoomsPerFloorY = settings.RoomsPerFloorY;
        group.RoomWidth = settings.RoomWidth;
        group.RoomDepth = settings.RoomDepth;
        group.FloorHeight = settings.FloorHeight;
        group.CorridorWidth = settings.CorridorWidth;
        
        group.GenerateFloors();
        group.ResetCache();
    }
    
    /// <summary>
    /// 绘制楼栋
    /// </summary>
    public override void Draw(LcCanvas2d canvas, LcElement element, Matrix3 matrix)
    {
        var group = element as PlateBuildGroup;
        var pen = GetDrawPen(group);
        
        // 计算楼栋边界
        var (width, depth, _) = group.GetTotalSize();
        var halfW = width / 2;
        var halfD = depth / 2;
        
        // 绘制外边框
        var p1 = group.Position + new Vector2(-halfW, -halfD);
        var p2 = group.Position + new Vector2(halfW, -halfD);
        var p3 = group.Position + new Vector2(halfW, halfD);
        var p4 = group.Position + new Vector2(-halfW, halfD);
        
        canvas.DrawLine(pen, p1.ApplyMatrix3(matrix), p2.ApplyMatrix3(matrix));
        canvas.DrawLine(pen, p2.ApplyMatrix3(matrix), p3.ApplyMatrix3(matrix));
        canvas.DrawLine(pen, p3.ApplyMatrix3(matrix), p4.ApplyMatrix3(matrix));
        canvas.DrawLine(pen, p4.ApplyMatrix3(matrix), p1.ApplyMatrix3(matrix));
        
        // 绘制内部网格(房间分隔)
        DrawRoomGrid(canvas, group, matrix, pen);
        
        // 绘制楼栋名称
        var textPaint = new LcTextPaint
        {
            Color = pen.Color,
            FontName = "仿宋",
            Size = 1000,
            Position = group.Position
        };
        canvas.DrawText(textPaint, group.BuildingName ?? "板房楼栋", matrix, out _);
    }
    
    private void DrawRoomGrid(LcCanvas2d canvas, PlateBuildGroup group, Matrix3 matrix, LcPaint pen)
    {
        var (width, depth, _) = group.GetTotalSize();
        var startX = group.Position.X - width / 2;
        var startY = group.Position.Y - depth / 2;
        
        // 绘制垂直线(X方向分隔)
        for (int x = 1; x < group.RoomsPerFloorX; x++)
        {
            var lineX = startX + x * group.RoomWidth;
            var p1 = new Vector2(lineX, startY);
            var p2 = new Vector2(lineX, startY + depth);
            canvas.DrawLine(pen, p1.ApplyMatrix3(matrix), p2.ApplyMatrix3(matrix));
        }
        
        // 绘制水平线(Y方向分隔)
        for (int y = 1; y <= group.RoomsPerFloorY; y++)
        {
            var lineY = startY + y * group.RoomDepth;
            if (y == group.RoomsPerFloorY)
            {
                // 走廊分隔线
                lineY = startY + group.RoomsPerFloorY * group.RoomDepth;
            }
            var p1 = new Vector2(startX, lineY);
            var p2 = new Vector2(startX + width, lineY);
            canvas.DrawLine(pen, p1.ApplyMatrix3(matrix), p2.ApplyMatrix3(matrix));
        }
    }
}

7.4 房间配置管理

7.4.1 PlateRoomConfigManager

public static class PlateRoomConfigManager
{
    private static Dictionary<RoomType, RoomConfig> Configs = new Dictionary<RoomType, RoomConfig>();
    
    public static void Init()
    {
        // 初始化默认配置
        Configs[RoomType.Dormitory] = new RoomConfig
        {
            Type = RoomType.Dormitory,
            Name = "宿舍",
            DefaultCapacity = 4,
            MinArea = 12,
            RequiredFurniture = new[] { "床", "衣柜", "桌子" }
        };
        
        Configs[RoomType.Office] = new RoomConfig
        {
            Type = RoomType.Office,
            Name = "办公室",
            DefaultCapacity = 2,
            MinArea = 15,
            RequiredFurniture = new[] { "办公桌", "椅子", "文件柜" }
        };
        
        Configs[RoomType.MeetingRoom] = new RoomConfig
        {
            Type = RoomType.MeetingRoom,
            Name = "会议室",
            DefaultCapacity = 10,
            MinArea = 30,
            RequiredFurniture = new[] { "会议桌", "椅子", "投影仪" }
        };
        
        Configs[RoomType.Canteen] = new RoomConfig
        {
            Type = RoomType.Canteen,
            Name = "食堂",
            DefaultCapacity = 50,
            MinArea = 100,
            RequiredFurniture = new[] { "餐桌", "椅子", "厨具" }
        };
        
        Configs[RoomType.Bathroom] = new RoomConfig
        {
            Type = RoomType.Bathroom,
            Name = "卫生间",
            DefaultCapacity = 5,
            MinArea = 10,
            RequiredFurniture = new[] { "便器", "洗手台", "镜子" }
        };
        
        // 加载自定义配置
        LoadCustomConfigs();
    }
    
    public static RoomConfig GetConfig(RoomType type)
    {
        return Configs.TryGetValue(type, out var config) ? config : null;
    }
    
    public static void SetConfig(RoomType type, RoomConfig config)
    {
        Configs[type] = config;
        SaveCustomConfigs();
    }
    
    private static void LoadCustomConfigs()
    {
        var path = GetConfigFilePath();
        if (File.Exists(path))
        {
            var json = File.ReadAllText(path);
            var customConfigs = JsonConvert.DeserializeObject<Dictionary<RoomType, RoomConfig>>(json);
            foreach (var kvp in customConfigs)
            {
                Configs[kvp.Key] = kvp.Value;
            }
        }
    }
    
    private static void SaveCustomConfigs()
    {
        var path = GetConfigFilePath();
        var json = JsonConvert.SerializeObject(Configs, Formatting.Indented);
        File.WriteAllText(path, json);
    }
    
    private static string GetConfigFilePath()
    {
        return Path.Combine(
            Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData),
            "LightCAD", "QdLayout", "room_configs.json"
        );
    }
}

public class RoomConfig
{
    public RoomType Type { get; set; }
    public string Name { get; set; }
    public int DefaultCapacity { get; set; }
    public double MinArea { get; set; }
    public string[] RequiredFurniture { get; set; }
}

7.5 三维模型生成

7.5.1 PlateBuild3dAction

public class PlateBuild3dAction : IElement3dAction
{
    /// <summary>
    /// 生成板房楼栋的三维模型
    /// </summary>
    public Object3D Get3dObject(LcElement element, DocumentRuntime docRt)
    {
        var group = element as PlateBuildGroup;
        var root = new Object3D();
        
        // 获取楼栋尺寸
        var (width, depth, height) = group.GetTotalSize();
        
        // 生成每层
        for (int f = 0; f < group.FloorCount; f++)
        {
            var floorObj = CreateFloor3D(group, f);
            floorObj.Position.Y = f * group.FloorHeight;
            root.Add(floorObj);
        }
        
        // 生成屋顶
        var roofObj = CreateRoof3D(group);
        roofObj.Position.Y = height;
        root.Add(roofObj);
        
        // 生成楼梯
        if (group.FloorCount > 1)
        {
            var stairsObj = CreateStairs3D(group);
            root.Add(stairsObj);
        }
        
        // 设置位置和旋转
        root.Position.X = group.Position.X;
        root.Position.Z = group.Position.Y;
        root.Rotation.Y = group.Rotation;
        
        return root;
    }
    
    private Object3D CreateFloor3D(PlateBuildGroup group, int floorIndex)
    {
        var floor = new Object3D();
        var (width, depth, _) = group.GetTotalSize();
        
        // 创建楼板
        var slabGeometry = new BoxGeometry(width, 100, depth);
        var slabMaterial = new MeshLambertMaterial
        {
            Color = new ThreeJs4Net.Color(0xCCCCCC)
        };
        var slab = new Mesh(slabGeometry, slabMaterial);
        floor.Add(slab);
        
        // 创建房间
        var startX = -width / 2 + group.RoomWidth / 2;
        var startZ = -depth / 2 + group.RoomDepth / 2;
        
        for (int x = 0; x < group.RoomsPerFloorX; x++)
        {
            for (int y = 0; y < group.RoomsPerFloorY; y++)
            {
                var room = CreateRoom3D(group, floorIndex, x, y);
                room.Position.X = startX + x * group.RoomWidth;
                room.Position.Z = startZ + y * group.RoomDepth;
                floor.Add(room);
            }
        }
        
        // 创建走廊
        var corridorObj = CreateCorridor3D(group);
        corridorObj.Position.Z = -depth / 2 + group.RoomsPerFloorY * group.RoomDepth + group.CorridorWidth / 2;
        floor.Add(corridorObj);
        
        return floor;
    }
    
    private Object3D CreateRoom3D(PlateBuildGroup group, int floorIndex, int gridX, int gridY)
    {
        var room = new Object3D();
        var floor = group.Floors[floorIndex];
        var roomData = floor.GetRoom(gridX, gridY);
        
        // 墙体高度
        var wallHeight = group.FloorHeight - 100;  // 减去楼板厚度
        var wallThickness = 100;
        
        // 创建四面墙
        // 前墙
        var frontWall = CreateWall(group.RoomWidth, wallHeight, wallThickness);
        frontWall.Position.Z = group.RoomDepth / 2;
        room.Add(frontWall);
        
        // 后墙
        var backWall = CreateWall(group.RoomWidth, wallHeight, wallThickness);
        backWall.Position.Z = -group.RoomDepth / 2;
        room.Add(backWall);
        
        // 左墙
        var leftWall = CreateWall(wallThickness, wallHeight, group.RoomDepth);
        leftWall.Position.X = -group.RoomWidth / 2;
        room.Add(leftWall);
        
        // 右墙
        var rightWall = CreateWall(wallThickness, wallHeight, group.RoomDepth);
        rightWall.Position.X = group.RoomWidth / 2;
        room.Add(rightWall);
        
        // 添加门洞
        if (roomData?.DoorPosition != null)
        {
            // 在相应的墙上创建门洞
            // 简化实现:使用布尔运算或多个几何体组合
        }
        
        return room;
    }
    
    private Mesh CreateWall(double width, double height, double depth)
    {
        var geometry = new BoxGeometry(width, height, depth);
        var material = new MeshLambertMaterial
        {
            Color = new ThreeJs4Net.Color(0xFFFFFF)
        };
        var mesh = new Mesh(geometry, material);
        mesh.Position.Y = height / 2;
        return mesh;
    }
    
    private Object3D CreateCorridor3D(PlateBuildGroup group)
    {
        var corridor = new Object3D();
        var (width, _, _) = group.GetTotalSize();
        
        // 走廊地面
        var floorGeometry = new BoxGeometry(width, 50, group.CorridorWidth);
        var floorMaterial = new MeshLambertMaterial
        {
            Color = new ThreeJs4Net.Color(0x808080)
        };
        var floorMesh = new Mesh(floorGeometry, floorMaterial);
        corridor.Add(floorMesh);
        
        // 走廊护栏
        var railHeight = 1000;
        var railGeometry = new BoxGeometry(width, railHeight, 50);
        var railMaterial = new MeshLambertMaterial
        {
            Color = new ThreeJs4Net.Color(0x404040)
        };
        var railMesh = new Mesh(railGeometry, railMaterial);
        railMesh.Position.Y = railHeight / 2;
        railMesh.Position.Z = group.CorridorWidth / 2;
        corridor.Add(railMesh);
        
        return corridor;
    }
    
    private Object3D CreateRoof3D(PlateBuildGroup group)
    {
        var roof = new Object3D();
        var (width, depth, _) = group.GetTotalSize();
        
        var roofGeometry = new BoxGeometry(width + 200, 100, depth + 200);
        var roofMaterial = new MeshLambertMaterial
        {
            Color = new ThreeJs4Net.Color(0x404040)
        };
        var roofMesh = new Mesh(roofGeometry, roofMaterial);
        roof.Add(roofMesh);
        
        return roof;
    }
    
    private Object3D CreateStairs3D(PlateBuildGroup group)
    {
        var stairs = new Object3D();
        
        // 简化的楼梯实现
        var stairWidth = 1200;
        var stairDepth = 2400;
        
        for (int f = 0; f < group.FloorCount - 1; f++)
        {
            var stairRun = CreateStairRun(stairWidth, group.FloorHeight, stairDepth);
            stairRun.Position.Y = f * group.FloorHeight;
            stairs.Add(stairRun);
        }
        
        return stairs;
    }
    
    private Object3D CreateStairRun(double width, double height, double depth)
    {
        var stairRun = new Object3D();
        
        int stepCount = 16;
        var stepHeight = height / stepCount;
        var stepDepth = depth / stepCount;
        
        for (int i = 0; i < stepCount; i++)
        {
            var stepGeometry = new BoxGeometry(width, stepHeight, stepDepth);
            var stepMaterial = new MeshLambertMaterial
            {
                Color = new ThreeJs4Net.Color(0x808080)
            };
            var stepMesh = new Mesh(stepGeometry, stepMaterial);
            stepMesh.Position.Y = i * stepHeight + stepHeight / 2;
            stepMesh.Position.Z = i * stepDepth + stepDepth / 2;
            stairRun.Add(stepMesh);
        }
        
        return stairRun;
    }
}

7.6 设置界面

7.6.1 PlateGroupSet窗口

设置楼栋参数的WinForms对话框:

public partial class PlateGroupSet : Form
{
    public PlateBuildGroupSettings Settings { get; private set; }
    
    public PlateGroupSet()
    {
        InitializeComponent();
        Settings = new PlateBuildGroupSettings();
        LoadDefaults();
    }
    
    public PlateGroupSet(PlateBuildGroup existingGroup) : this()
    {
        // 从现有楼栋加载设置
        txtBuildingName.Text = existingGroup.BuildingName;
        numFloorCount.Value = existingGroup.FloorCount;
        numRoomsX.Value = existingGroup.RoomsPerFloorX;
        numRoomsY.Value = existingGroup.RoomsPerFloorY;
        numRoomWidth.Value = (decimal)(existingGroup.RoomWidth / 1000);
        numRoomDepth.Value = (decimal)(existingGroup.RoomDepth / 1000);
        numFloorHeight.Value = (decimal)(existingGroup.FloorHeight / 1000);
        numCorridorWidth.Value = (decimal)(existingGroup.CorridorWidth / 1000);
    }
    
    private void LoadDefaults()
    {
        txtBuildingName.Text = "板房楼栋";
        numFloorCount.Value = 2;
        numRoomsX.Value = 5;
        numRoomsY.Value = 2;
        numRoomWidth.Value = 6;
        numRoomDepth.Value = 3;
        numFloorHeight.Value = 2.8m;
        numCorridorWidth.Value = 1.5m;
    }
    
    private void btnOK_Click(object sender, EventArgs e)
    {
        Settings = new PlateBuildGroupSettings
        {
            BuildingName = txtBuildingName.Text,
            FloorCount = (int)numFloorCount.Value,
            RoomsPerFloorX = (int)numRoomsX.Value,
            RoomsPerFloorY = (int)numRoomsY.Value,
            RoomWidth = (double)numRoomWidth.Value * 1000,
            RoomDepth = (double)numRoomDepth.Value * 1000,
            FloorHeight = (double)numFloorHeight.Value * 1000,
            CorridorWidth = (double)numCorridorWidth.Value * 1000
        };
        
        DialogResult = DialogResult.OK;
        Close();
    }
}

public class PlateBuildGroupSettings
{
    public string BuildingName { get; set; }
    public int FloorCount { get; set; }
    public int RoomsPerFloorX { get; set; }
    public int RoomsPerFloorY { get; set; }
    public double RoomWidth { get; set; }
    public double RoomDepth { get; set; }
    public double FloorHeight { get; set; }
    public double CorridorWidth { get; set; }
}

7.7 本章小结

本章详细介绍了FY_Layout中板房系统的开发:

  1. 数据模型:PlateBuilding、PlateBuildGroup、PlateBuildingFloor、PlateRoom的设计
  2. 操作类:PlateBuildingAction和PlateBuildGroupAction的实现
  3. 房间配置:PlateRoomConfigManager的配置管理
  4. 三维模型:PlateBuild3dAction的三维生成逻辑
  5. 设置界面:PlateGroupSet对话框的实现

板房系统是FY_Layout中最复杂的模块,涉及多层数据结构、参数化设计和三维模型生成。掌握这个模块的开发方法,对于开发其他复杂的BIM组件有很大帮助。

下一章我们将学习二维绘图与交互操作的详细内容。