mountView(host: HTMLElement): Promise<void>
挂载视图(对标旧 mountView(host),async:pixi v8 Application 必须 await init)。
v2 渲染底座由本方法统一创建并**附加**到 headless SDK:
- 构造已注入 renderHost(外部自建 pixi)或已挂载 → 仅恢复 ticker
- 否则:宿主内自建/复用 canvas → createPixiBootstrap → 把 _sceneContainer 挂到
pixi viewport 子容器 → sdk.attachRenderHost(重定向 viewportControl + 补接线)
→ 激活选择拖拽工具 + 转发画布指针事件 → 订阅旧事件桥接 → 启动渲染
editor 在 SDK 构造(new 过程)同步初始化,不依赖 pixi await;数据操作与 mountView
并发时 SDK 始终是同一实例,无「先数据后 mount 抛错」或「mount 覆盖 headless SDK」竞态。
unmountView(): void
卸载视图(对标旧 unmountView()):停 ticker + 释放渲染底座。
释放:指针代理 cleanup、pixi 底座 destroy、mountView 自建的 canvas 移出 DOM。
editor(数据层)不销毁,可继续无头数据操作;已释放渲染底座的 adapter 无法再 mountView。
init2D(options?: LegacyRenderSDK2DOptions): void
兼容旧 init2D(options) 配置装配入口(对标旧 render-2d-sdk init2D)。
v2 渲染底座由 mountView 创建、SDK 在构造/懒构建期装配,init2D 不重建底座,
仅消费 v1 传入的配置(canvasClass / toolbarClass / editor2DSettings.snapThreshold),
落到实例字段,供后续 mountView / 懒构建 SDK 时生效。
editor2DSettings.enableSnap 在 v2 刻意不消费:吸附开关是交互状态机
(SnapResetHandler 在交互 reset 时置 false,非静态配置)。
snapThreshold 映射到 v2 editorSettings.element.snapDistanceInPixel。
必须在 SDK 构建(构造注入 renderHost / mountView / 首次数据操作)前调用,
否则配置无法生效,属于非预期路径,dev 抛错暴露。
undo(): Promise<number>
redo(): Promise<number>
getFloors(): string[]
changeFloor(floorId: string, _force = false): void
deleteFloor(floorId: string): void
getRooms(_floorId?: string): Array<
updateRoomName(id: string, name: string): void
serialize(): unknown
deserialize(json: SchemaV2.ProjectData): void
clear(): void
getModel(): unknown
getBrowserSupport(): FloorplanBrowserSupport
当前浏览器对 SDK 的支持情况(透传 FloorplanSDK 静态检测)。
宿主可在 mountView / create 前调用,按 level 做降级提示或功能禁用。
getAnchorPosition(targetId: string, anchorType: AnchorType, out?: unknown)
getAnchorPositions(targetIds: string[], anchorTypes: AnchorType[], out?: unknown)
switchContext(contextId: "Layout" | "Inner" | "Device"): void
切换业务交互场景(Layout / Inner / Device)
renameRoom(params: { name: string; roomId?: string }): string
重命名房间(updateRoomName 的别名语义)
loadFromProjectData(data: unknown): void
加载项目数据(对标旧 loadFromProjectData)。
v2: deserialize + 切到第一层 + 清空撤销栈(对齐 FloorplanSDK.load)。
默认撤销栈为空(对齐 v1「无默认 interactiveType 记录」);
宿主首次 switchContext 才产生第一条可撤销记录。
已挂载时自动居中一次(对齐 v1 deserialize → view.onProjectLoaded 的 focusToCenter);
未挂载(headless 先加载后 mountView)时跳过,由 mountView 末尾的统一聚焦承担。
beginCreate(elementType: string, config?: unknown): Promise<string>
开始创建元素(对标旧 beginCreate(elementType, config))。
v2 返回新元素 id;old 返回元素实例。房间创建 v2 不支持(房间由墙体围合拓扑自动生成)。
deleteElement(id: string): Promise<void>
删除元素(对标旧 deleteElement(id))。
v2 直接走按 id 删除 API(target 处于选中态时内部 confirm 退选中,无需先程序化选中)。
placeOutdoorDeviceGroup(roomId: string, screenPos: { x: number; y: number }, _devices: unknown[], roomName: string,): string
放置室外设备组(对标旧 placeOutdoorDeviceGroup)。
v2 从 sync 缓存读设备元数据(调用前须 syncLatestDevices),入参 devices 忽略;
v2 以入参 roomId 作为新组 id,故返回值近似回传 roomId。
placeOutdoor(roomId: string, screenPos: { x: number; y: number }): string
放置室外设备组(对标旧 placeOutdoor;roomName 从 repository 设备组反查)
replaceRoom(internalRoomId: string, roomId: string, roomName: string, _isAutoLayout = true): string[]
放入/替换房间设备组(对标旧 replaceRoom(..., isAutoLayout))。
v2 仅支持自动布局路径,isAutoLayout=false 近似走同一路径。
getFlattenDeviceIds(_floorId?: string, deviceGroupId?: string): string[]
拍平已摆放设备 id(对标旧 getFlattenDeviceIds(floorId, deviceGroupId));v2 基于当前楼层
getRoomDataById(roomId: string): unknown
按 id 查房间数据(对标旧 getRoomDataById;v2 返回 RoomSummary 摘要)
updateRoomDevices(internalRoomId: string, roomId: string): void
更新房间内设备组(对标旧 updateRoomDevices;v2 走 replaceRoom 自动布局)
focusToCenterConsiderDevice(): void
聚焦到户型中心且考虑设备(对标旧 focusToCenterConsiderDevice;v2 与 focusToCenter 收敛为 fitToScene 适配全场景含设备)
getZoomPercent(): number
当前缩放百分比(对标旧 getZoomPercent;100 = 最近一次居中/fit)。
adjustZoomPercent(directionOrTarget: "in" | "out" | number, anchor?: "content" | "canvas" | "pointer", pointerScreen?: { x: number; y: number },): void
百分比缩放(对标旧 adjustZoomPercent:步进 ±10% 或绝对 1–1000)。
zoomIn(): number
放大一档(对标 v2 zoomIn;相对 scale ×1.25)
zoomOut(): number
缩小一档(对标 v2 zoomOut;相对 scale ÷1.25)
attch(objectId: string): void
聚焦到对象(对标旧 attch(objectId));v2 按对象世界包围盒适配视口
refreshTabView(_interactiveType: unknown): void
刷新 tab 渲染(对标旧 refreshTabView)。
v2 无独立 tab 刷新,用 model→scene 全量同步近似。
setInteractiveType(intactiveType: unknown): void
设置交互类型(对标旧 setInteractiveType)。
v2 以业务场景切换对应:Structure→Layout / Furniture→Inner / Device→Device。
getRoomByPosition(position: { x: number; y: number }): unknown
highlightRoom(internalRoomId: string): void
resetHighlight(): void
syncLatestDevices(data: { value: unknown[] }, _floorId?: string): void
syncLatestRooms(rooms: unknown[], _floorId?: string): void
rebuildLatestDevices(_floorId?: string): void
rebuildLatestRooms(_floorId?: string): void
getDevices(_floorId?: string): string[]
getDevicesByIds(ids: string[]): unknown[]
按 id 取设备数据(对标旧 getDevicesByIds)。从 model 读(repository.device),
对齐 v1 model.objects 语义——scene 重建后可能残留陈旧视图对象,model 才是同步源权威。
getDeviceGroups(_floorId?: string): unknown[]
focusToCenter(): void
聚焦户型中心并缩放适配(对标旧 focusToCenter)。
v2 场景即当前楼层全量(墙/房间/设备),与 focusToCenterConsiderDevice 收敛为 fitToScene。
focusToRoom(roomId: string): void
聚焦到房间,动画结束时派发 EndFocusElement 事件
getLastSelRoomInfo()
getRoomWindowAndDoorCount(roomId: string)
unembed(id: string): void
解除嵌入(对齐 v1 SDK unembed(id):删掉该嵌入设备 + 选中宿主家具)。
转调 editor.unembed 走完整链路(退选中态 → entityDeleteCommitting → intent →
model 落地 + 解开家具 embeddedItemIds → sceneChanges 回包移除视图 → 选中宿主)。
早前实现直接 `repository.updateDevice(id, { embedTargetId: null })`,有三处问题:
1. 未包 withTransaction → 不产 documentChange → 视图完全不更新(表现为"点击没反应")
2. 只单向置空设备侧字段 → 家具 embeddedItemIds 残留已失效 id
3. 语义不符 v1(v1 是删除设备,不是把设备留在原地解开引用)
legacy 接口保持同步签名,内部 promise 的异常仅记录,不外抛。
triggerDeviceStateChange(_roomId: string, _id: string, _floorId?: string): void
triggerDeviceAnimation(_idList: string[]): void
触发设备动画(对标旧 triggerDeviceAnimation):对齐 v1 空实现(2D 无动画需求)
setRenderState(_state: unknown): void
设置渲染状态(对标旧 setRenderState):对齐 v1 空实现(真逻辑在 v1 亦为伪代码)
forcePutDown(): void
createFloorFromProjectData(data: unknown, floorFrom: string, floorTo: string): void
单楼层复制(对标旧 createFloorFromProjectData(data, floorFrom, floorTo))。
转调 editor 层(命令源头 CommandFacade + 复制后切到目标楼层)。
v1 契约 data 为任意 JSON,委托前收窄到 SchemaV2.ProjectData。
setDeviceState(_idList: string[], _state: unknown): void
设备状态区分(对标旧 setDeviceState):v2 不做真实 IOT,仅渲染层无状态开关
filterDevices(_bizTypes: string[] | null): void
过滤设备(对标旧 filterDevices):v2 2D 无设备图标过滤
checkPlacementValidity(_id: string): boolean
检查房间能否放置设备图标(对标旧 checkPlacementValidity)
calculateDeviceState(_data: unknown): unknown
计算设备状态(对标旧 calculateDeviceState)
getBubbleDirections(ids: string[]): number[]
气泡方向(对标旧 getBubbleDirections):v2 无方向推断,默认全部向上(AnchorType.TOP=2)
deleteRoomDevices(internalRoomId: string, isUserManualOp = true): void
删除房间内设备(对标旧 deleteRoomDevices(internalRoomId, isUserManualOp))。
转调 editor 层(命令源头 CommandFacade;级联删除组内设备 + 清空房间名)。
clearView(): void
清空 2D 视图(对标旧 clearView):v2 视图生命周期随 editor,无独立清空
attachRandomBindRoom(_floorId: string, _fitRoomCenter = false): string | null
随机聚焦绑定房间(对标旧 attachRandomBindRoom):v2 无绑定房间概念,返回 null
getVirtualDevices(floorId: string, deviceGroupId?: string): VirtualDeviceSummary[]
虚拟设备列表(对标旧 getVirtualDevices 的联合形状)。
转调 editor 层(查询源头 FloorplanQueryService):Device 按 isVirtual 筛 +
ComposedDevice 返回 {id,deviceType,subDevices};deviceGroupId 可选过滤。
captureView(_interactiveType?: unknown): Promise<Blob>
截取当前楼层视图(对标旧 captureView(interactiveType))。
v2 截图走 app.renderer.extract(pixi renderer 为宿主所有,需经 options.app 注入
createPixiBootstrap 返回的 Application)。未注入属非预期调用路径:dev 抛错暴露,生产回退空 Blob。
interactiveType 忽略:v2 2D 场景下各交互类型实体均渲染,无需临时切换交互类型。
syncMobileOrientationInfo(info: { state?: string; left?: number; right?: number; up?: number; down?: number }): void
同步手机横竖屏信息(对标旧 syncMobileOrientationInfo)。
v1 语义:editor2D.setMobileOrientationInfo(info) + view.onWindowResize()(按新朝向重排画布)。
v2 PC 端无独立横竖屏布局:仅记录 info,并触发一次按 canvas 父容器尺寸的 resize(对齐 onWindowResize)。
headless(构造与 mountView 均无 canvas)仅记录,跳过 resize。
resetLastSelRoomForDeviceTab(roomId?: string): void
设备 tab 重置房间选中(对标旧 resetLastSelRoomForDeviceTab)。
v1 语义:view.resetLastSelRoomForDeviceTab(roomId) 清空 lastSelRoomId + 取消高亮,传 roomId 匹配才清。
v2 粘性房间选中由 roomSelectionContext(SSOT)维护,getLastSelRoomInfo 直接读它;
此处转调 editor.clearLastSelRoom,roomId 匹配守卫在其内部对 SSOT 生效。
processMultiClickIcon(event: { detail?: { object?: unknown; type?: string; id?: string } }): void
连点 RoomItem 图标(对齐旧 SDK roomItemClick 处理器,驱动 multiClickOtherTabIcon)。
v2 无 roomItemClick 事件源,selection(每次点击都派发)已在 _dispatchLegacyClick 接入
_trackMultiClick;本方法保留 v1 签名供宿主直接调用(event.detail 形状 {object,type,id})。
createFromFloorplanData(data: unknown, floorId: string, realArea?: number): void
createFromWallData(data: unknown, floorId: string, realArea?: number): void
checkFloorplanValidity(data: unknown): number
返回墙线围合出的多边形数量(对齐 v1 GeomUtil.polygonizeMultiLineString(data).array.length)
parsePoint(_pointStr: string): number[]
getWallDataFromFloorplanData(_data: unknown): unknown
将户型数据JSON转换为GeoJSON MultiLineStrings
getDoorDataFromFloorplanData(_data: unknown): unknown
getWindowDataFromFloorplanData(_data: unknown): unknown
getRoomDataFromFloorplanData(_data: unknown): unknown
FloorplanSDK(平台无关核心)
// 请按平台从子路径导入,避免交叉打包
getBrowserSupport(): FloorplanBrowserSupport
检测当前浏览器是否具备运行 SDK 的渲染能力(宿主在 `create()` 前调用,用于降级提示/功能禁用)。
判定依据:pixi v8 autoDetectRenderer 优先级 webgl → webgpu → canvas;
WebGLRenderer 内部 WebGL2 优先、失败回退 WebGL1(GlContextSystem.createContext)。
- good:WebGL2 / WebGPU 可用(推荐运行)
- minimum:仅 WebGL1 / Canvas 2D 可用(WebGL1 能跑 pixi 但缺 WebGL2 特性;Canvas 为 pixi experimental 兜底)
- unsupported:全部不可用
- webgpu 字段为同步粗判(`navigator.gpu` 存在性);pixi 实际以
`requestAdapter + requestDevice` 判定,极端差异(gpu 存在但 requestAdapter 失败)不在此覆盖
beginCreate(type: string, config?: unknown): Promise<string>;
async beginCreate(
type: string,
mode: PanelCreateMode,
worldPos:
开始创建元素,返回 promise,resolve 时给出新元素 id。
- 四参:对齐 editor / 面板拖放(mode + worldPos + props)
- 两参:兼容旧宿主调用,固定 drag + (0,0),config 作为 props
activateSelectDragTool(opts?: { replay?: boolean }): void
forwardPointerEvents(source: HTMLElement): () => void
将 DOM 指针事件转发到编辑器输入管线,返回 cleanup
deleteSelected(): Promise<void>
deleteEntity(entityId: string): Promise<void>
直接删除指定 id 的元素(无需先选中;目标处于选中态时内部退选中)
listRooms(): RoomSummary[]
getRoom(roomId: string): RoomSummary | null
getRoomAtPosition(worldPos: { x: number; y: number }): RoomSummary | null
按世界坐标命中房间(严格围合命中,室外返回 null)。
宿主拖放房间卡片时:先 screenToWorld,再调本方法;未命中则不高亮、不 replaceRoom。
getFlattenDeviceIds(deviceGroupId?: string): string[]
拍平已摆放设备 id(含 Device + ComposedDevice.subDevices)。
可指定 deviceGroupId 只查该房间设备组。
getSyncedLuaDevices(): LuaDevice[]
getSyncedDevices(deviceGroupId: string): DeviceInfoForSync[]
getUnplacedDevices(deviceGroupId: string): DeviceInfoForSync[]
设备组中尚未摆放到户型上的设备(对标老 getFlattenDeviceIds 反差集)。
选中已绑定房间时,面板应用此列表,不再展示全量设备目录。
getDeviceOwnerRoomId(deviceId: string): string | null
设备所属房间 id;室外设备返回 null。
@deprecated 使用 {@link getItemOwnerRoomId};保留旧宿主兼容。
getItemOwnerRoomId(itemId: string): string | null
设备/家居/复合设备所属房间 id;室外返回 null
getLastSelRoomInfo()
最近一次粘性选中房间信息(对标老 getLastSelRoomInfo)。
点空白清空;切 tab / 选设备不丢。
highlightRoom(roomId: string): void
高亮闪烁房间关联墙体(拖拽卡片悬停时调用,对标老 highlightRoom)。
resetHighlight(): void
清除房间墙体高亮(对标老 resetHighlight)
selectEntity(entityId: string): Promise<void>
程序化选中实体(同步工具选中态 + selection 副作用,含房间墙体高亮)。
房间卡片自动布局完成后调用,保持目标房间选中。
async:editor.selectEntity 内部先 await confirm() 落掉旧选中再重选目标;
需要「选中后立即操作」(如删除)的调用方必须 await。
syncLatestDevices(devices: LuaDevice[]): void
sync 阶段:缓存最新设备元数据(不落地)。
replaceRoom / placeOutdoor 前必须先 sync,设备组按 LuaDevice.roomId 分组。
replaceRoom(internalRoomId: string, deviceGroupId: string, roomName: string): string[]
把 deviceGroup 自动布局到指定房间(对标老 replaceRoom(..., isAutoLayout=true))。
调用前须 syncLatestDevices,且 devices[].roomId === deviceGroupId。
placeOutdoor(deviceGroupId: string, worldPos: { x: number; y: number }, roomName: string): OutdoorPlacementResult
放置室外设备组(世界坐标锚点)。
调用前须 syncLatestDevices,且 devices[].roomId === deviceGroupId。
undo(): Promise<number>
撤销,返回剩余可撤销步数(stackChange 由 editor 统一 emit,_wireEvents 订阅翻译)
redo(): Promise<number>
serialize(): unknown
load(project: SchemaV2.ProjectData)
加载户型数据。
反序列化后切到第一层(deserialize 默认停在最后一层),并清空撤销栈。
默认撤销栈为空(对齐 v1「无默认 interactiveType 记录」);
宿主首次 switchContext 才产生第一条可撤销记录。
getFloors(): string[]
列出所有楼层 id(顺序与 model.floors 一致)
getCurrentFloorId(): string
changeFloor(floorId: string): void
createFloor(floorId: string, scaleRatio = 1): void
deleteFloor(floorId: string): void
switchContext(ctx: "Layout" | "Inner" | "Device"): void
renameRoom(params: { name: string; roomId?: string }): string
getZoom(): number
setZoom(scale: number): void
moveCenter(x: number, y: number): void
screenToWorld(x: number, y: number)
屏幕坐标 → 世界坐标(相对 canvas 左上角的逻辑像素)
resize(width: number, height: number): void
通知视口尺寸变化。
渲染表面重设(renderHost.renderer 缓冲 + viewport 尺寸)已内聚到 FloorplanEditor3DHome.resize,
此处直接委托:编辑器内部会刷新 overlay/element/preview 视图并派发 viewResized,经公共 fitToScene
补发 viewportChanged → viewChange,对齐 v1 onWindowResize。window 自动跟随由编辑器 resizeOnWindow
承担,SDK 层不再自行监听。
fitToScene(): void
将视口适配到当前 scene 包围盒(带边距);空场景回退默认中心。
对齐 v1 focusToCenter 语义:auto-resize 在重设渲染表面后调用,内容跟随容器重新居中、缩放,
避免 resize 后内容锚定在旧屏幕位置(pixi-viewport.resize 不改 transform)。
调用链:SDK → editor → command。
viewChange 由 editor 补发 viewportChanged → SDK 翻译(_wireEvents),本层不再手动派发。
getEditor(): FloorplanEditor3DHome
返回内部 FloorplanEditor3DHome。
供需要 scene/model/eventOutput 的宿主高级 UI(如完整选中弹窗)使用;
常规宿主请优先用事件与公共 API。
attachRenderHost(host: FloorplanEditor3DHomeRenderHost, canvas?: HTMLCanvasElement): void
后补渲染宿主(headless 构造后,渲染底座就绪时调用)。
委托 editor.attachRenderHost 写入渲染字段 + 重定向 viewportControl,再补接线:
- 视图同步(zoomed/moved → viewChange / pinch-end → pinchEnd),headless 构造期无 viewport 未挂
- 平台输入端口(子类 override _attachViewportInput,挂真实 pixi-viewport / 窗口 resize 跟随)
destroy(): void
销毁编辑器(幂等;window resize 监听由编辑器 destroy 统一清理)