推荐:使用 NSDT场景设计器 快速搭建 3D场景。
我们最近上线的一个新项目是 Franke Water Systems 的洗脸盆产品线的 3D配置器。 在本文中,我们想解释该项目是如何开发的、使用了哪些技术以及我们如何实现 3D 配置器。
1、3D产品配置器的初始需求
多年来,Franke 一直使用基于 Visual Basic Code 的 Excel 计算工具来创建个性化洗脸盆。 这现在将被我们的3D配置器所取代,以简化工作并将工作流程数字化。
在新应用程序的帮助下,可以在 2D 和 3D 视图中配置洗脸盆。 这包括选择合适的水槽和裙边,输入与安装类型、转角设计、板尺寸和水槽定位有关的洗脸盆所需尺寸,以及凹槽和安装部件,例如配件孔和 皂液器。 此外,用户可以对整个洗脸盆和涂层的颜色进行个性化设置,并对安装配件和下水配件进行各种设置。
最后,洗脸盆完成后,客户会得到总成本概览,数据会直接传输到 SAP 系统。
- 应用程序的灵活性、设计和可扩展性
用户界面设计的要求之一是响应式设计,因为3D产品配置器将来会在平板电脑和更大的触摸屏上使用。 此外,应用程序应该是可扩展的,以解决新的扩展问题。
版本 1 的要求是提供独立于房间的配置,因此墙壁的尺寸适应洗脸盆的长度。 在版本 2 中,还可以手动输入房间和墙壁设置。 此外,在版本 2 中,配件和配件将进一步配置。 以后应该不仅可以配置洗脸盆,还可以配置一个完整的卫浴间。
- 配置器如何支持 Franke 的分发?
新建筑项目(例如建造购物中心)的规划者需要用于新卫生设施的洗脸盆。 在此过程中,Franke 的销售代表将他的平板电脑带到客户面前,并直接在他的设备上现场与他在线配置所需的洗脸盆。
- 它在 Franke 内部是如何运作的?
Franke的内饰和现场服务部门将接受新的3D配置器的培训。 每个员工都有自己的帐户,他们可以在其中为不同的项目创建配置。 他可以命名配置并定义建设项目,即项目所在的位置,在我们的例子中是购物中心。 在销售代表与客户一起完成配置后,所有内容都会再次列在概览页面上; 还将向客户提供其他产品(例如水龙头、皂液器等,具体取决于配置),然后他可以增加/减少这些产品。 配置可以保存为草稿,以便以后进行调整,或者可以在无法再编辑时完成。 Franke 然后在内部检查配置(在与客户协商后)并通过提交订单完成最后一步。
2、3D产品配置器项目启动
2.1 原型 - 定义 MVP
在与 Franke Water Systems 项目团队举行的为期五天的研讨会上,总结了产品配置器的想法和概念,并讨论了方法和长期目标。 遵循“设计思维”方法。
设计思维是斯坦福大学教授创造性解决问题的概念。 设计思维背后的基本理念是,跨学科团队尤其可以创造真正的、杰出的创新。 设计思维过程旨在汇集对一个问题的尽可能多的不同经验、观点和观点。
首先,重要的是要观察、识别和了解目标群体的需求。 设计思维有五个不同的阶段,如下所示:
- 同理心迭代:了解问题、用户及其需求
- 定义:以用户为中心,重构问题,制定精准问题
- 想法:在头脑风暴会议中产生许多想法
- 原型:从最有前途的方法开发原型/解决方案
- 测试:测试和进一步开发原型或解决方案
2.2 线框图
基于设计思维过程的结果,我们为各种设备创建了第一个线框。 可用性是一个重点,因为用户应该能够尽可能快速、轻松地操作所有内容,而不必知道后台发生了什么。 线框图阶段完成后,我们可以在下一步中开发 UI 设计。
2.3 界面设计
为了创建屏幕设计,我们使用了 Adobe InDesign 和 Figma。 由于功能和粗略的设计已经在线框图阶段完成,主要关注的是各种元素的大小、颜色和排列的设计。
与 Franke 以及我们的开发人员定期进行沟通以获得定期反馈,因为设计需要他们在功能和可能/可行等方面的意见。
3、3D产品配置器技术栈
我们使用 React 和 TypeScript 开发前端,使用 Node.js 和 CosmosDB 开发后端。
- React
3D配置器前端是使用 Facebook React 开发的 Javascript 库实现的,React库专为帮助创建快速响应的 Web 应用程序而设计。 它非常适合单页应用程序,可用于生成服务器端呈现网站、移动原生应用程序或渐进式 Web 应用程序。 在我们的案例中,我们开发了一个渐进式 Web 应用程序 (PWA)。
- TypeScript
TypeScript 是微软基于 ECMA-6 标准开发的一种面向对象的编程语言。 它支持使用基于传统面向对象编程语言的语言结构,例如类、接口、继承、模块和具有预定义类型的泛型。 TypeScript 包含一个编译器,可将代码编译为适用于现代和旧版浏览器的 JavaScript。
- Node.js 作为开发和部署工具
Node.js 是软件开发中用于运行网络应用程序的服务器端平台。 特别是网络服务器可以用它来实现。 Node.js 在最初为 Google Chrome 开发的 JavaScript 运行时环境“V8”中运行,并提供了一种资源高效的架构,可以实现特别大量的并发网络连接。
- 微软Azure
Azure是微软的云计算平台,与亚马逊的AWS一样,提供发布单页应用、数据管理、云计算、人工智能等服务。 以3D配置器为例,我们使用MS Azure编译前后端代码,在服务器上持续部署,打包到Docker容器中,然后自动部署。
- 持续部署
持续部署是近年来越来越流行的一种软件自动化发布方式,属于持续集成的一部分。 基本上,这是关于软件的持续进一步开发及其在不停机的情况下提供的。 3D 配置器的部署是通过 Microsoft Azure 的“Linux 应用服务”完成的。 我们的应用程序的持续集成通过 Git 进行,Bitbucket 作为主机。 因此,当我们通过 Bitbucket 发布新的生产就绪版本时,我们会自动刷新 Linux 构建服务器上的 Git 存储库,它会调用我们自行创建的预配置部署脚本,并在编译成功后发布应用程序。 如果过程中出现错误,将不会发布新版本。 这确保编译失败的代码不会发送给用户。
- 单元测试
单元测试用于确定应用程序各个功能的有效性。 例如,如果我们有一个用于将数字相加的函数,当被告知将 2 和 2 相加时,它应该返回 4,如果没有,则不要部署代码。 我们对测量洗脸盆组件的距离和尺寸时执行的复杂计算实施了单元测试。
4、3D产品配置器前端开发
为了获得项目概览,首先定义了 webapp 的页面和导航结构。 在这些结构的基础上定义了 React 组件,这些组件针对可重用性进行了优化以节省开发时间。
在实施的第一步中,登录和注册过程的公共页面被开发为 React 组件。 作为其中的一部分,这些过程所需的表单组件(例如输入/选择字段或验证过程)已开发为可重用组件。
在下一步中,为公共页面(例如注册或登录区域)以及应用程序的私有部分(仅在登录后可用)的页面开发组件。 为了完成注册过程,创建了各种用户管理邮件的电子邮件模板。 为了支持这一点,后端被扩展以帮助管理和向用户发送响应(例如发送电子邮件)。
3D配置过程包括 7 个主要类别,每一步有更多选择,这就是实现过滤器组件的原因。 一旦用户选择了一个选项,信息就会被传递到“Plate Builder”中,计算元素的正确位置(以毫米为单位)。 然后将结果(板生成器组件)传递到渲染场景的 Three.js 视图,如下所示:
private initThreeManager() {
if (!this.threeManager) {
this.threeManager = new ThreeManager(
this.threeManagerData,
{
width: this.dimensions.width,
height: this.dimensions.height
},
this.container
);
}
if (this.container && this.container) {
this.threeManager.update(this.threeManagerData);
}
this.setState({isInitialized: true});
}
private updateThreeManager() {
if (this.container && this.threeManagerData) {
this.threeManager.update(this.threeManagerData, this.dimensions);
}
}
private get threeManagerData() {
const {configuration, trough} = this.props;
if (configuration && configuration.result && trough && trough.result) {
const plateBuilder = new PlateBuilder(configuration.result, trough.result);
return plateBuilder.components;
} else {
return;
}
}
此外,配置还使用了不同的交互元素。 可以通过手风琴、下拉菜单、滑块或单选框和复选框来创建此自定义选项。 从以下代码片段可以看出,所有可能的选项都是在一个循环中创建的,因此将来可以轻松实现更多选项:
public render() {
return (
<div className="partial-installation">
<div className="row">
{this.defaults && this.defaults.map((item, index) => {
const key = `partial_installation_item_${index}`;
return (
<div key={key} className="col-6 col-sm-6 col-md-3">
<Button
className={`p-0 btn border-0 display-block item-${item}`}
onClick={() => this.handleClick(item)}
disabled={this.props.configuration.updating || this.selected === item}>
<span className="item">{item}</span>
<span className={`${this.selected === item ? 'box-installation active' : 'box-installation'}`} />
</Button>
</div>
);
})}
</div>
</div>
);
}
如果用户输入错误,则会出现警告/错误消息,指示输入不正确。 与整个配置过程一样,这是多语言的并且由应用程序中的全局状态控制。
public render() {
return (
<div className="configuration-3d-alert">
<Alert color="dark" isOpen={this.state.visible} toggle={this.onDismiss}>
<FormattedMessage {...MESSAGES.configuration3dAlertAttention} />
<strong>
<FormattedMessage {...MESSAGES.configuration3dAlertAttention} />
</strong>
<FormattedMessage {...MESSAGES.configuration3dAlertMessage} />
<br />
<FormattedMessage {...MESSAGES.configuration3dAlertHint} />
</Alert>
</div>
);
}
- 良好的性能而不牺牲用户体验
为了确保移动设备上的最佳用户体验,3D配置器已针对各种移动设备和平板电脑分辨率进行了优化。 关键点之一是,由于移动导航发生了变化,因此使用拇指导航可以很容易地通过智能手机使用。
- Plate-Builder
Plate-Builder的主要目标是获取所有用户输入的数据,并使用整个洗脸盆及其所有元素(例如槽/水龙头/等)在其正确位置填充数据集。 这为我们提供了一些可以在多个地方重复使用的东西(例如填充 3D/2D 场景,计算总价),可以很容易地扩展(因为它专注于应用程序的一部分),也很容易单元化 经过测试(因为我们从原始电子表格中知道每个用户配置应该产生什么结果)。 这个模块的输出我们喜欢称之为“plate builder components”。
5、3D产品配置器的场景结构
3D 场景的主要目标是获取Plate Builder组件并实时可视化。 我们的 Three.js 模块首先初始化 Three.js 库运行所需的所有内容(例如渲染器/效果/相机/等),添加一些初始基本元素(即地板和灯光),然后开始循环调用plate builder 组件。 当它找到一个模型时,会加载模型 URL,对其进行纹理处理,并将其放置在提供的位置。 当它找到一个形状时,会构建它,对其进行纹理处理并将其放置在提供的位置。 目标是使 Three.js 模块尽可能简单,以便它可以轻松维护,几乎不会出错。
- 2D场景
2D 场景的主要目标是采用Plate Builder组件并输出平面 SVG 元素。 我们的 SVG 模块首先在提供的上下文中初始化其 SVG HTML 标记,然后根据传递给它的板组件开始绘制。 最初这实际上是由 Three.js 提供的,它只是使用自定义效果来生成一种 2D 轮廓,但是我们放弃了这一点,以提供更高效和响应更快的解决方案。
最大的挑战是板的角半径。 试图改变现有的线以产生曲线是不可行的,最后我们设法想出了一个简单的解决方案,将所需的角简单地叠加为四分之一圆/线。
- Three.js
为了创建 3D 场景,我们使用了 Three.js,这是一个流行的 Javascript 库,用于在浏览器中显示 3D 场景并在用户的 GPU 上计算它们。 为此我们使用了 WebGL,这样我们就可以不用外部插件。 Three.js 还提供了可以跨平台创建 3D 场景的优势,即独立于平台。 为了在浏览器中显示场景,首先创建一个画布元素,然后在其上初始化 Three.js 渲染器。 场景图可用于确定最终在浏览器中显示的内容。 然后为场景图提供一个相机对象,该对象定义显示场景的角度和距离。
除了相机对象之外,要绘制的对象,即所谓的“网格”,也被转移到场景图中。 此外,这些对象还有更多信息,例如表面信息、方向或空间坐标。 例如,在我们的项目中,盥洗台就是这样的对象。 用于场景阴影照明的光源也被转移到场景图中。
- WebGL
Three.js 的渲染基于 WebGL,WebGL 是一种 API,可以在 Web 浏览器中实现 3D 图形的硬件加速渲染。 该 API 基于 OpenGL 2.0,允许在 HTML canvas 元素中呈现 3D 内容。
- Blender
所有 3D 对象都使用软件 Blender 建模 Blender 是一个免费软件包,可用于 3D 建模、纹理和 3D 渲染。
6、3D产品配置器后端的演变
后端是一个基于 LoopBack Framework 的 RESTful API,它已经提供了一些基本的端点,例如对用户进行身份验证。 它为每个模型提供CRUD,适用于无数的数据库类型。
在后端开发的第一步中,我们开始设置 LoopBack 并使其适应配置器的需求。 在 LoopBack CLI 的帮助下,我们创建了所有先前计划和需要的远程端点,并调整了适当的数据模型。
使用 lb 模型配置命令,LoopBack 创建一个 Json 文件作为设置,一个扩展端点的 JavaScript 文件,并将它们分配给 Global Express 对象。 以下是此类模型的示例:
Model.json
{
"name": "Model",
"base": "PersistedModel",
"idInjection": true,
"options": {
"validateUpsert": true
},
"properties": {
"name": {
"type": "string",
"required": true
}
},
"validations": [],
"relations": {},
"acls": [],
"methods": {}
}
Model.js
module.export = (Model) => {
// Here you can create logic for the model
};
将此模型添加到全局对象时,API 具有完全配置的终结点,包括所有常见的剩余 API 方法,例如 GET、POST、PUT 和 DELETE。通过添加属性、关系和验证,我们可以对配置器的端点进行必要的调整。然而,这只是我们需求的一半。进行了其他更改以提供所需的 API,我们将在以下部分中进行说明。
6.1 用户注册和身份验证
对于用户的身份验证,我们使用 LoopBack v3 提供的访问令牌方法。在这里,用户使用用户名和密码的组合进行注册。API 检查登录名,并从两个值以及魔术键和我们配置的盐创建一个 256 位哈希。此加密组合存储在身份验证数据库中,用于验证所有后续用户请求。
除了此注册过程之外,我们还合并了双重选择加入环回过程,以确保每个用户在登录时提供有效的电子邮件。
但是,若要访问受保护的远程方法,前端用户需要有效的访问令牌。这是在用户登录时创建的。
6.2 配置中的数据流和错误消息
为了在前端验证和存储用户制作的输入,我们必须为数据流提出一个复杂的架构。基于客户指定的Excel工具,各个槽与洗脸盆上的插入部件相关的相互关系的复杂性和可能性给我们带来了艰巨的任务。
同样,计算结果必须存储在可显示的记录中。因此,任务是验证用户输入的数据,保存数据并在前端显示有效的配置。不可能的产品组合应导致错误并阻止 API 保存数据,以便不会发生错误的配置,并且数据库中仅存储 100% 有效的配置。每当前端中的值发生更改并立即向客户端使用的设备发送响应时,就会发生此过程。
6.3 数据流和输入验证如何工作?
在下文中,我们将更详细地介绍这些要点。
前端将数据呈现为 3D 场景,并让用户能够根据提供的信息更改数据。这些使用PUT远程方法发送到服务器。
后端接收数据并根据生成的默认值验证每个发送的参数,并根据这些值创建错误或准备数据存储在数据库中。例如:用户想在水槽旁边钻一个孔,我们检查水槽之间是否有足够的空间,下一个碗/水槽边缘,包括最小距离,并根据结果生成错误或准备所有其他数据以保存在前面的数据库中。
3.1. 如果创建的3D配置包含错误,我们将在此时停止验证其余配置并向前端发送错误。
3.2. 创建的3D配置在第一次检查后有效,后端可以继续其工作:
3.2.1. 该配置对应于我们所需的依赖项并且没有错误。 我们可以返回有效数据并准备用于保存数据的值。
3.2.2. 基于更改后的有效数据,为3D配置器产生新的可能组合。 为了能够在前端显示这些,后端会生成动态的默认值(default values)。 我们将这些添加到配置中。
3.2.3. 要获得合规的数据集,我们需要重新检查并准备数据库的更改值。 如果一切都合适,数据将存储在数据库中。
- 后端将数据发送回客户端作为响应。
5.前端检查数据,将其与默认值进行比较,并在必要时为界面准备警告。
6.到这里数据流的循环就结束了,看后台是报错还是保存配置成功:
a) API 结果返回错误。 用户被告知他需要改进的地方。
b) 一切正常,新数据显示在界面中,用户可以继续配置。
7、我们从3D产品配置器项目中学到了什么?
总体而言,该项目要求很多:没有现有的有效产品数据,前端没有明确的错误概念,以及其他动态因素,例如欧盟国家的产品价格不同,这只是几个例子。这就是为什么从一开始就采用敏捷方法是这个项目的正确选择。
项目的最终交付时间表:
- 4周内完成概念和用户体验验证
- 在 4 周内完成桌面和移动设备的屏幕设计。
- 测试版的纯开发时间在短短 4 个月内实现了 30 多种不同的 3D 模型
7.1 JavaScript 是正确的选择吗?
技术的选择是正确的。我们设法在纯JavaScript代码的基础上开发应用程序,包括前端和后端。在我们的研究中发现的大多数3D配置器都配备了PHP后端。我们的开发人员具有 JavaScript 方面的专业技能,使他们能够轻松地处理应用程序的两个部分,并且应用程序的性能也很好。
7.2 微软Azure和微服务 - 不容小觑!
与我们的其他项目不同,其中大多数项目在AWS或其他服务器上运行,客户的要求之一是部署到Microsoft Azure服务器并使用微服务架构。
微服务是一种流行的架构蓝图,用于构建可快速发展的健壮、高度可扩展、可独立部署的应用程序。但是,成功的微服务体系结构需要不同的方法来设计和构建应用程序。
这里需要注意,因为看似简单的功能(例如生成 PDF 或数据的使用)在使用 Azure 服务器时由于技术限制和成本而有所不同!
7.3 结果超出预期
Franke和Ventzke Media团队为我们与他们密切合作创造的结果感到非常自豪。我们很高兴能够在日常使用中遵循该工具,它将在明年支持Franke的完整现场和现场服务。
原文链接:3D产品配置器开发实战 — BimAnt