Compare commits

...

127 Commits

Author SHA1 Message Date
wuyize a84c8cb752 Fix: StrokeEndType::kClosed渲染不正确 2023-03-20 23:25:38 +08:00
ArgonarioD e39eb15f03 Merge branch 'main' of http://101.34.228.45:3000/BigC/ArchitectureColoredPainting 2023-03-20 22:49:00 +08:00
ArgonarioD cee26188e1 [editor] 增加了“图层信息”的.ui实现 | #10 2023-03-20 22:48:41 +08:00
karlis 826a8d3e2e Merge branch 'main' of http://101.34.228.45:3000/BigC/ArchitectureColoredPainting 2023-03-20 22:27:04 +08:00
karlis 769effe49c FIX: 修正文件路径逻辑 2023-03-20 22:26:38 +08:00
wuyize a07bf3bced 优化包围盒计算 2023-03-20 21:51:04 +08:00
karlis b944572204 添加项目信息管理器 2023-03-20 21:12:23 +08:00
ArgonarioD d55b49e119 [editor/style] fix: stroke对于空描边的错误显示 2023-03-20 21:10:01 +08:00
karlis 0543a02dfb Merge branch 'main' of http://101.34.228.45:3000/BigC/ArchitectureColoredPainting 2023-03-20 20:16:30 +08:00
karlis e6571faf8c FIX | #20 2023-03-20 20:16:15 +08:00
ArgonarioD bf71d17b5d [editor/style] 适配了新的StrokeEndType | #18
* [stroke] 适配了新添加的StrokeEndType::kClosed
 * [stroke] 对接了Renderer方面提供的新接口
2023-03-20 19:58:02 +08:00
ArgonarioD 4b2776d429 [editor/style] 优化了Stroke的添加交互 | #17 2023-03-20 19:03:15 +08:00
karlis 831541ff36 Merge branch 'main' of http://101.34.228.45:3000/BigC/ArchitectureColoredPainting 2023-03-20 18:04:01 +08:00
karlis 6a1c64a426 添加Ctrl+左键快捷缩放,C+S+左键等比缩放 2023-03-20 18:03:54 +08:00
wuyize ae641bb87c 增加了StrokeEndType::kClosed 2023-03-20 17:52:57 +08:00
karlis f25f2d2a81 实现高亮选中Layer 2023-03-20 17:35:33 +08:00
karlis 8a34f57251 实现显示/隐藏图层功能 | #16 2023-03-20 15:52:27 +08:00
karlis f06fbbaef6 FIX: 暂时修复修改图层属性导致的crash 2023-03-20 14:44:36 +08:00
ArgonarioD 855dd2e075 [editor] 修复了一些会导致warning的问题 | #15
* 添加了ColorHelper
 * LayerStyleDialog重复setLayer导致的warning
 * QColorDialog::getColor时由于Qt的bug未正确设置大小导致的warning
2023-03-20 02:48:52 +08:00
wuyize b6e79ee6de 实现通过json文件构造Painting 2023-03-20 00:26:50 +08:00
karlis b3bbf6c1be 重写刷新信号及逻辑 2023-03-19 16:07:35 +08:00
karlis 8bf6835c23 添加LayerWrapper接口 2023-03-19 15:48:42 +08:00
karlis 3904ff0b61 添加删除逻辑检测 | #9 2023-03-19 15:40:50 +08:00
ArgonarioD 587c09115a [editor/style] 初始化区分封闭与非封闭图元 | #12
* LayerStyleContainer的构造函数添加了isClosedElement参数
2023-03-19 14:43:25 +08:00
ArgonarioD 66cde802ec 为GraphicElement添加了isClosed接口 | #11 2023-03-19 14:12:44 +08:00
ArgonarioD 4c95d6e362 微调了筛选器 2023-03-19 13:29:52 +08:00
wuyize 12b62bf039 Fix: element.comp填充样式渲染错误 |#13 2023-03-19 12:19:10 +08:00
ArgonarioD 0e62672b58 [editor/style] 实现了FillElementLayerStyle | #2 2023-03-19 01:27:42 +08:00
karlis 69422ba96a Merge branch 'main' of http://101.34.228.45:3000/BigC/ArchitectureColoredPainting 2023-03-18 23:10:34 +08:00
karlis 3d8420f617 优化图层列表显示效果 | #7 2023-03-18 23:10:16 +08:00
yang.yongquan e48954175d Merge branch 'main' of http://101.34.228.45:3000/BigC/ArchitectureColoredPainting 2023-03-18 20:30:31 +08:00
yang.yongquan 3c2eada43f 添加了获得线宽的函数 2023-03-18 20:26:07 +08:00
karlis 6494c2b9af Merge branch 'main' of http://101.34.228.45:3000/BigC/ArchitectureColoredPainting 2023-03-18 20:20:25 +08:00
wuyize fd1baf42f6 Renderer图元变换改用矩阵 2023-03-18 20:17:12 +08:00
karlis aeead9d22e 添加创建时递归约束 2023-03-18 20:12:43 +08:00
karlis 84bddf4447 Merge branch 'main' of http://101.34.228.45:3000/BigC/ArchitectureColoredPainting 2023-03-18 16:26:51 +08:00
wuyize 31f2c1be8f 打开模型时卸载现有模型 2023-03-18 12:18:38 +08:00
ArgonarioD c15e8c3a5b [style] 重新设计了LayerStyle在Layer中的存储形式
新增了LayerStyleContainer类,修改了相关代码
修改了部分bad smells
2023-03-18 03:51:47 +08:00
karlis 24f8daf1fd 菜单优化 2023-03-17 17:14:09 +08:00
karlis 9e4cdb3a63 添加项目名称和背景颜色字段 2023-03-17 14:14:07 +08:00
wuyize 6a09bdd32c FIX: GraphicElement中pixelRatio计算错误 2023-03-17 12:29:31 +08:00
wuyize 2569b07233 Renderer部分Painting支持设置背景颜色 2023-03-17 11:54:43 +08:00
ArgonarioD 81bb1b4b86 [editor/style] 完成StrokeStyle的序列化和反序列化
[stroke] 取消创建StrokeStyle时的初值
2023-03-16 20:22:03 +08:00
wuyize 4ff5406acc FIX: qt-material-widgets资源未加载 2023-03-16 19:29:38 +08:00
karlis 6d554ab265 Merge branch 'main' of http://101.34.228.45:3000/BigC/ArchitectureColoredPainting 2023-03-16 14:51:43 +08:00
karlis a9fb9197e6 删除冗余计算,重写预览逻辑,修复BoundRect,添加预览刷新触发 2023-03-16 14:51:28 +08:00
wuyize 15a41d61a5 实现MaterialStyle的decoded方法 2023-03-16 14:48:28 +08:00
ArgonarioD 03a06ce426 [editor/style] 进一步完善StrokeStyle相关
[stroke] 初步完成StrokeStyle序列化
[editor/util] 新增JsonUtil
[stroke] 完成添加单层描边功能
[stroke] 完成设置控件
[stroke] 将StrokeStyleListView修改为了StrokeStyleWidget
2023-03-16 02:13:25 +08:00
wuyize 78c24ad373 FIX: painting渲染bug 2023-03-15 22:43:51 +08:00
karlis 627b947738 Merge branch 'main' of http://101.34.228.45:3000/BigC/ArchitectureColoredPainting 2023-03-15 21:10:19 +08:00
karlis 8903419799 修复了zt黄色的问题 2023-03-15 21:10:05 +08:00
karlis 70d5973a90 修复了yyq黄色的问题 2023-03-15 21:04:21 +08:00
karlis 6c86403c4a Merge branch 'main' of http://101.34.228.45:3000/BigC/ArchitectureColoredPainting 2023-03-15 20:22:26 +08:00
karlis b9c8624dae Merge branch 'main' of http://101.34.228.45:3000/BigC/ArchitectureColoredPainting 2023-03-15 20:22:17 +08:00
karlis 153f6bdd8e Merge branch 'main' of http://101.34.228.45:3000/BigC/ArchitectureColoredPainting 2023-03-15 20:10:18 +08:00
karlis c234c0e9b3 实现element导入,添加滚动条,修复空白打开 2023-03-15 20:10:14 +08:00
wuyize 3abc0d9bcd 初步完成json到Painting的转换 2023-03-15 19:24:33 +08:00
yang.yongquan 5d88ddf0ca 增加图片刷新的槽函数 2023-03-15 17:37:12 +08:00
karlis fa91d80b70 完善Element序列化 2023-03-15 16:36:46 +08:00
karlis 3b87644e21 修改了PixelPath 2023-03-15 16:16:59 +08:00
ArgonarioD 073f68e360 初步实现了编辑器中的StrokeStyle
[style/stroke] 实现了行删除
[style/stroke] 基本稳定了strokeStyle的修改
[paint] 令drawElement时会应用style
[style] 修改了LayerStyle
[editor] 新增了ColorPicker类和StrokeStyleListView类
2023-03-15 11:43:16 +08:00
yang.yongquan 73e63f3817 merge 2023-03-15 11:25:14 +08:00
yang.yongquan d47d94d044 增加了对于Painting的转换,没有考虑线宽的情况 2023-03-15 11:23:26 +08:00
wuyize 57d6de47f8 Fix: painting.comp bug 2023-03-15 11:23:21 +08:00
wuyize c6a4d79888 补充了StrokeEndType 2023-03-15 00:09:30 +08:00
wuyize 4ec58dbb8b FIX: 多样式不生效 2023-03-14 23:39:56 +08:00
wuyize cf3dd43a4a Merge branch 'main' of http://101.34.228.45:3000/BigC/ArchitectureColoredPainting 2023-03-14 22:44:43 +08:00
wuyize 62b9c2b026 FIX: style的bug 2023-03-14 22:44:23 +08:00
karlis 97097fcc3a 修改旋转判定,添加style渲染接口 2023-03-14 15:36:19 +08:00
karlis f2ceca724a Merge branch 'main' of http://101.34.228.45:3000/BigC/ArchitectureColoredPainting 2023-03-14 15:14:28 +08:00
karlis eab8d7aeec Merge branch 'main' of http://101.34.228.45:3000/BigC/ArchitectureColoredPainting 2023-03-14 15:14:25 +08:00
karlis dc7a793d3a Merge branch 'main' of http://101.34.228.45:3000/BigC/ArchitectureColoredPainting 2023-03-14 15:03:03 +08:00
karlis 684c28dafd bug fix 2023-03-14 15:02:49 +08:00
karlis 72f0f78e64 bug fix 2023-03-14 14:53:36 +08:00
karlis 3f1421a1bd bug:黑屏 2023-03-14 00:20:31 +08:00
karlis 4c9fe168a9 bug: pixmap溢出 2023-03-13 23:36:31 +08:00
karlis de9d7143b6 添加【可能存在问题的】拖拽和旋转 2023-03-13 21:06:05 +08:00
karlis ed4c3c0064 实现【位置错误的】LayerBounding虚线框 2023-03-13 20:47:35 +08:00
wuyize 0d42af9200 实现彩绘纹理部分替换 2023-03-13 13:48:20 +08:00
ArgonarioD acd25b4964 temp fix: 编译不通过的问题
为PaintingUtil中的transformToPainting函数临时添加了返回值,需要在后续修改
2023-03-11 23:34:13 +08:00
karlis e3217aa134 Merge branch 'main' of http://101.34.228.45:3000/BigC/ArchitectureColoredPainting 2023-03-11 22:58:31 +08:00
karlis 2cb25b8607 添加Layer的组合元素创建 2023-03-11 22:57:23 +08:00
yang.yongquan 631f0f8af7 Merge branch 'main' of http://101.34.228.45:3000/BigC/ArchitectureColoredPainting 2023-03-11 19:52:39 +08:00
yang.yongquan 50acb3b625 转换为Painting类,(z轴、线宽、测试) 2023-03-11 19:52:36 +08:00
karlis c92a3553ce 修改getReferencedBy为动态 2023-03-11 18:33:56 +08:00
wuyize 4bfdb0d9f6 Fix: 模型包围盒计算错误 2023-03-10 23:24:19 +08:00
karlis ca8d68fe93 Merge branch 'main' of http://101.34.228.45:3000/BigC/ArchitectureColoredPainting 2023-03-10 19:44:34 +08:00
karlis 3dd7fd7c0f bug fix 2023-03-10 19:44:18 +08:00
wuyize 6508f54333 MaterialStyle加入深拷贝方法clone 2023-03-10 19:41:06 +08:00
ArgonarioD b981056674 将LayerStyleDialog改为了修改拷贝对象
MaterialStyle的拷贝有问题,导致编译报错
2023-03-10 17:00:52 +08:00
ArgonarioD 293c1f3096 Merge branch 'main' of http://101.34.228.45:3000/BigC/ArchitectureColoredPainting 2023-03-10 16:07:54 +08:00
ArgonarioD 65e03ed3ba 修改了MaterialStyle的属性访问权限 2023-03-10 16:07:21 +08:00
ArgonarioD 94b16ac536 修改了LayerStyle的materialStyles的类型,添加了深拷贝构造函数 2023-03-10 16:07:07 +08:00
ArgonarioD 7c7cd02d71 移动了LayerStyleDialog所在的文件夹 2023-03-10 16:06:33 +08:00
wuyize 520597e288 添加MaterialStyleStroke的拷贝构造 2023-03-10 16:04:29 +08:00
ArgonarioD 15e2522591 让图层功能暂时能用了 2023-03-10 14:08:40 +08:00
ArgonarioD 14a1502a22 Merge branch 'main' of http://101.34.228.45:3000/BigC/ArchitectureColoredPainting 2023-03-10 13:58:11 +08:00
ArgonarioD 846f334d54 完成style的基本构架 2023-03-10 13:57:57 +08:00
karlis fedb147dd3 FIX: Crash U 2023-03-09 23:31:55 +08:00
wuyize d0cbdd65ec 解决ElementRenderer线程无法退出的问题 2023-03-09 22:54:33 +08:00
wuyize f504480050 解决线程无法析构的问题 2023-03-09 21:39:32 +08:00
ArgonarioD 52734adda8 修正了merge 2023-03-09 20:04:01 +08:00
ArgonarioD e7bce3f93e Merge 2023-03-09 19:54:07 +08:00
karlis 25eec60e5c 初步完成Layer新建 2023-03-09 19:04:33 +08:00
ArgonarioD 652fa483ba 完成了LayerStyle的基本构架 2023-03-09 16:35:04 +08:00
karlis 6f8bfa1558 Merge branch 'main' of http://101.34.228.45:3000/BigC/ArchitectureColoredPainting 2023-03-09 16:32:17 +08:00
karlis ef990cd4b4 重置了LayerTree的右键菜单 2023-03-09 16:32:00 +08:00
wuyize c2dd8271e3 ElementRender改为单例 2023-03-09 16:21:55 +08:00
wuyize 6412d9c865 模型添加彩绘配置文件 2023-03-09 15:44:14 +08:00
yang.yongquan 11c9ccc3a9 修改了ElementPool的样式 2023-03-08 19:15:42 +08:00
wuyize 08e54cfc30 ElementRenderer初始化移至构造函数 2023-03-08 16:40:04 +08:00
wuyize f4f1547d21 ElementRenderer改用独立Context 2023-03-08 12:43:19 +08:00
yang.yongquan 7d4a191f02 Merge branch 'main' of http://101.34.228.45:3000/BigC/ArchitectureColoredPainting 2023-03-08 12:40:09 +08:00
yang.yongquan 1118dc819a 修改了ElementPool 2023-03-08 12:40:05 +08:00
karlis 477517d919 优化ui,剔除不必要部分 2023-03-08 12:16:15 +08:00
karlis 368eaa6c89 添加了ElementPoolWidget 2023-03-07 22:36:39 +08:00
karlis edb7e37b91 更改了按钮位置 2023-03-07 21:12:48 +08:00
karlis 06c96144e4 Merge 2023-03-07 20:55:29 +08:00
wuyize 526f33ceba 主导航改用StackedWidget 2023-03-07 20:27:58 +08:00
karlis 1031ba5808 实现另存为和打开的路径选取 2023-03-07 19:36:07 +08:00
karlis 33475f6f57 实现保存流程 2023-03-07 17:56:18 +08:00
karlis 8ca036fafb 一系列修改 2023-03-07 17:06:26 +08:00
yang.yongquan 6a4a724f00 更新单元测试 2023-03-07 16:54:19 +08:00
yang.yongquan 53025b93e4 完成了元素池的窗口 2023-03-07 16:53:03 +08:00
yang.yongquan a28e484cb2 Merge branch 'main' of http://101.34.228.45:3000/BigC/ArchitectureColoredPainting 2023-03-07 16:24:50 +08:00
yang.yongquan f578fe6184 完成元素池,待测试 2023-03-07 16:24:38 +08:00
karlis c1ad6a61f0 添加了序列化的接口和基本实现 2023-03-06 19:43:01 +08:00
113 changed files with 7149 additions and 2670 deletions

424
4_L0.json Normal file
View File

@ -0,0 +1,424 @@
{
"background-color": "#b7a386",
"elements": [
{
"data": {
"include": "../svg/2.svg"
},
"name": "ababa",
"type": "svg-file"
},
{
"data": {
"reference-layer": "0.0"
},
"name": "ababa-group",
"type": "group"
},
{
"data": {
"include": "../svg/0.svg"
},
"name": "ababa2",
"type": "svg-file"
},
{
"data": {
"include": "D:/BigC2022/temp/ArchitectureColoredPainting/svg/4_L0.svg"
},
"name": "4_L0-fill.svg",
"type": "svg-file"
}
],
"height": 1080,
"project-name": "样例1",
"root-layer": {
"children": [
{
"children": [
{
"element": 3,
"is-folder": false,
"name": "4_L0",
"styles": [
{
"material": "AH8A/2pkiv8=",
"type": "fill"
},
{
"enableEachSideIndependent": true,
"left": "AADAQAEAJJwAf///9dKG/w==",
"right": "AADgQAAACpw=",
"type": "stroke"
}
],
"transform": {
"offset": {
"x": -230,
"y": -533
},
"rotation": 0,
"scale": {
"x": 0.32341007644113307,
"y": 0.32341007644113307
}
}
}
],
"is-folder": true,
"name": "GroupFolderExample",
"referenced-by": 1,
"transform": {
"offset": {
"x": 50,
"y": 50
},
"rotation": 0,
"scale": {
"x": 1,
"y": 1
}
}
},
{
"element": 1,
"is-folder": false,
"name": "aaaa2",
"styles": [
],
"transform": {
"offset": {
"x": 127,
"y": -109
},
"rotation": 90,
"scale": {
"x": 1,
"y": 1
}
}
},
{
"element": 1,
"is-folder": false,
"name": "aaaa3",
"styles": [
],
"transform": {
"offset": {
"x": 232,
"y": 17
},
"rotation": 180,
"scale": {
"x": 1,
"y": 1
}
}
},
{
"element": 1,
"is-folder": false,
"name": "aaaa4",
"styles": [
],
"transform": {
"offset": {
"x": 103,
"y": 130
},
"rotation": 270,
"scale": {
"x": 1,
"y": 1
}
}
},
{
"element": 1,
"is-folder": false,
"name": "bbbb1",
"styles": [
],
"transform": {
"offset": {
"x": 197,
"y": 265
},
"rotation": 0,
"scale": {
"x": 1,
"y": 1
}
}
},
{
"element": 1,
"is-folder": false,
"name": "bbbb2",
"styles": [
],
"transform": {
"offset": {
"x": 323,
"y": 156
},
"rotation": 90,
"scale": {
"x": 1,
"y": 1
}
}
},
{
"element": 1,
"is-folder": false,
"name": "bbbb3",
"styles": [
],
"transform": {
"offset": {
"x": 432,
"y": 285
},
"rotation": 180,
"scale": {
"x": 1,
"y": 1
}
}
},
{
"element": 1,
"is-folder": false,
"name": "bbbb4",
"styles": [
],
"transform": {
"offset": {
"x": 302,
"y": 399
},
"rotation": 270,
"scale": {
"x": 1,
"y": 1
}
}
},
{
"element": 1,
"is-folder": false,
"name": "cccc1",
"styles": [
],
"transform": {
"offset": {
"x": 467,
"y": 71
},
"rotation": 0,
"scale": {
"x": 1,
"y": 1
}
}
},
{
"element": 1,
"is-folder": false,
"name": "cccc2",
"styles": [
],
"transform": {
"offset": {
"x": 361,
"y": -70
},
"rotation": 90,
"scale": {
"x": 1,
"y": 1
}
}
},
{
"element": 1,
"is-folder": false,
"name": "cccc3",
"styles": [
],
"transform": {
"offset": {
"x": 273,
"y": -211
},
"rotation": 180,
"scale": {
"x": 1,
"y": 1
}
}
},
{
"element": 1,
"is-folder": false,
"name": "cccc4",
"styles": [
],
"transform": {
"offset": {
"x": 574,
"y": 198
},
"rotation": 270,
"scale": {
"x": 1,
"y": 1
}
}
},
{
"element": 1,
"is-folder": false,
"name": "dddd1",
"styles": [
],
"transform": {
"offset": {
"x": 33,
"y": -231
},
"rotation": 0,
"scale": {
"x": 1,
"y": 1
}
}
},
{
"element": 1,
"is-folder": false,
"name": "dddd2",
"styles": [
],
"transform": {
"offset": {
"x": 55,
"y": 352
},
"rotation": 90,
"scale": {
"x": 1,
"y": 1
}
}
},
{
"element": 1,
"is-folder": false,
"name": "dddd3",
"styles": [
],
"transform": {
"offset": {
"x": -40,
"y": 220
},
"rotation": 180,
"scale": {
"x": 1,
"y": 1
}
}
},
{
"element": 1,
"is-folder": false,
"name": "dddd4",
"styles": [
],
"transform": {
"offset": {
"x": 0,
"y": 0
},
"rotation": 0,
"scale": {
"x": 1,
"y": 1
}
}
},
{
"element": 1,
"is-folder": false,
"name": "eeee1",
"styles": [
],
"transform": {
"offset": {
"x": 0,
"y": 0
},
"rotation": 0,
"scale": {
"x": 1,
"y": 1
}
}
},
{
"element": 1,
"is-folder": false,
"name": "eeee2",
"styles": [
],
"transform": {
"offset": {
"x": -149,
"y": 85
},
"rotation": 90,
"scale": {
"x": 1,
"y": 1
}
}
},
{
"element": 1,
"is-folder": false,
"name": "eeee3",
"styles": [
],
"transform": {
"offset": {
"x": 166,
"y": 482
},
"rotation": 180,
"scale": {
"x": 1,
"y": 1
}
}
}
],
"is-folder": true,
"name": "root",
"referenced-by": null,
"transform": {
"offset": {
"x": 8,
"y": 20
},
"rotation": 0,
"scale": {
"x": 1.7159367435419115,
"y": 1.7159367435419115
}
}
},
"width": 1080
}

View File

@ -104,9 +104,16 @@
</Link>
</ItemDefinitionGroup>
<ItemGroup>
<ClCompile Include="src\CaptionButton.cpp" />
<ClCompile Include="src\Editor\DataManager\ProjectDataManager.cpp" />
<ClCompile Include="src\Editor\EditorWidgetComponent\FillStyleWidget.cpp" />
<ClCompile Include="src\Editor\RightBar\EditorSettingWidget.cpp" />
<ClCompile Include="src\Editor\EditorWidgetComponent\ColorPicker.cpp" />
<ClCompile Include="src\Editor\EditorWidgetComponent\LayerCreateWidget.cpp" />
<ClCompile Include="src\Editor\EditorWidgetComponent\LayerStyleDialog.cpp" />
<ClCompile Include="src\Editor\EditorWidget.cpp" />
<ClCompile Include="src\Editor\EditorWidgetItem.cpp" />
<ClCompile Include="src\Editor\ElementManager.cpp" />
<ClCompile Include="src\Editor\ElementPoolWidget.cpp" />
<ClCompile Include="src\Editor\GraphicElement.cpp" />
<ClCompile Include="src\Editor\LayerManager.cpp" />
<ClCompile Include="src\Editor\LayerStyle.cpp" />
@ -118,9 +125,10 @@
<ClCompile Include="src\Editor\ThirdPartyLib\qquick\qquicksvgparser.cpp" />
<ClCompile Include="src\Editor\ThirdPartyLib\SvgHelper.cpp" />
<ClCompile Include="src\Editor\util\PainterPathUtil.cpp" />
<ClCompile Include="src\Editor\util\PaintingUtil.cpp" />
<ClCompile Include="src\Editor\util\SvgFileLoader.cpp" />
<ClCompile Include="src\FluentMenu.cpp" />
<ClCompile Include="src\gl.c" />
<ClCompile Include="src\IconWidget.cpp" />
<ClCompile Include="src\main.cpp" />
<ClCompile Include="src\MainWindow.cpp" />
<ClCompile Include="src\NavigationBarWidget.cpp" />
@ -148,10 +156,14 @@
<ClCompile Include="src\Renderer\Painting\StraightLine.cpp" />
<ClCompile Include="src\Renderer\VirtualTextureManager.cpp" />
<ClCompile Include="src\SvgParser.cpp" />
<ClCompile Include="src\TitleWidget.cpp" />
<ClCompile Include="src\Editor\EditorWidgetComponent\StrokeStyleWidget.cpp" />
<QtUic Include="EditorLayerInfoWidget.ui" />
<QtUic Include="EditorSettingWidget.ui" />
<QtUic Include="EditorWidget.ui" />
<QtUic Include="EditorWidgetItem.ui" />
<QtUic Include="MainWindow.ui" />
<QtUic Include="RendererWidget.ui" />
<QtUic Include="src\Editor\EditorWidgetComponent\LayerCreateWidget.ui" />
</ItemGroup>
<ItemGroup>
<None Include="..\data.json" />
@ -182,11 +194,23 @@
<None Include="res\Shaders\ssgi.comp" />
</ItemGroup>
<ItemGroup>
<QtMoc Include="src\Editor\EditorWidgetComponent\StrokeStyleWidget.h" />
<QtMoc Include="src\Editor\RightBar\LayerTreeWidget.h" />
<QtMoc Include="src\Editor\RightBar\InfoDisplayWidget.h" />
<QtMoc Include="src\MainWindow.h" />
<ClInclude Include="resource.h" />
<QtMoc Include="src\Editor\EditorWidget.h" />
<QtMoc Include="src\Editor\EditorWidgetComponent\LayerCreateWidget.h" />
<QtMoc Include="src\Editor\EditorWidgetComponent\LayerStyleDialog.h" />
<QtMoc Include="src\Editor\EditorWidgetComponent\ColorPicker.h" />
<QtMoc Include="src\Editor\RightBar\EditorSettingWidget.h" />
<QtMoc Include="src\Editor\EditorWidgetComponent\FillStyleWidget.h" />
<ClInclude Include="src\Editor\DataManager\ProjectDataManager.h" />
<ClInclude Include="src\ColorHelper.hpp" />
<ClInclude Include="src\Editor\LayerWrapper.h" />
<ClInclude Include="src\Editor\util\EncodeUtil.hpp" />
<ClInclude Include="src\Editor\util\JsonUtil.hpp" />
<ClInclude Include="src\Editor\ElementManager.h" />
<QtMoc Include="src\Editor\ElementPoolWidget.h" />
<ClInclude Include="src\Editor\GraphicElement.h" />
<ClInclude Include="src\Editor\LayerManager.h" />
<ClInclude Include="src\Editor\LayerStyle.h" />
@ -197,7 +221,9 @@
<ClInclude Include="src\Editor\ThirdPartyLib\qquick\qtquickglobal_p.h" />
<ClInclude Include="src\Editor\ThirdPartyLib\SvgHelper.h" />
<ClInclude Include="src\Editor\util\PainterPathUtil.h" />
<ClInclude Include="src\Editor\util\PaintingUtil.h" />
<ClInclude Include="src\Editor\util\SvgFileLoader.h" />
<QtMoc Include="src\FluentMenu.h" />
<ClInclude Include="src\Renderer\IblUtils.h" />
<ClInclude Include="src\Renderer\Painting\BaseStyle.h" />
<ClInclude Include="src\Renderer\Painting\CubicBezierSignedDistance.h" />
@ -210,12 +236,9 @@
<ClInclude Include="src\Renderer\Preview\ElementRenderer.h" />
<ClInclude Include="src\Renderer\VirtualTextureManager.h" />
<ClInclude Include="src\SvgParser.h" />
<QtMoc Include="src\TitleWidget.h" />
<QtMoc Include="src\IconWidget.h" />
<QtMoc Include="src\CaptionButton.h" />
<QtMoc Include="src\NavigationBarWidget.h" />
<QtMoc Include="src\Renderer\RendererWidget.h" />
<QtMoc Include="src\Editor\EditorWidget.h" />
<QtMoc Include="src\Editor\EditorWidgetItem.h" />
<ClInclude Include="src\Renderer\Painting\BvhTree.h" />
<ClInclude Include="src\Renderer\Camera.h" />
<ClInclude Include="src\Renderer\Painting\CubicBezier.h" />
@ -240,9 +263,6 @@
<Project>{8f360763-a045-3808-87a9-3fae38e69819}</Project>
</ProjectReference>
</ItemGroup>
<ItemGroup>
<ResourceCompile Include="ArchitectureColoredPainting.rc" />
</ItemGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
<ImportGroup Condition="Exists('$(QtMsBuild)\qt.targets')">
<Import Project="$(QtMsBuild)\qt.targets" />

View File

@ -65,14 +65,44 @@
<Filter Include="Header Files\Renderer\Preview">
<UniqueIdentifier>{7ead1a66-586a-4584-ae80-9e7a4e667364}</UniqueIdentifier>
</Filter>
<Filter Include="Header Files\Editor\Style">
<UniqueIdentifier>{be3f4585-c8ba-410f-8619-2adcd4349f02}</UniqueIdentifier>
</Filter>
<Filter Include="Source Files\Editor\Style">
<UniqueIdentifier>{b9732a33-aa2e-4f8d-886f-1b1730c66519}</UniqueIdentifier>
</Filter>
<Filter Include="Header Files\Editor\Element">
<UniqueIdentifier>{8d846557-8fd5-47d5-8edf-eb3eb77c226b}</UniqueIdentifier>
</Filter>
<Filter Include="Source Files\Editor\Element">
<UniqueIdentifier>{22d7f3ef-8185-476e-8fe1-aea24c4faacc}</UniqueIdentifier>
</Filter>
<Filter Include="Header Files\Editor\Layer">
<UniqueIdentifier>{6fc32493-d5a2-44c3-a283-d2d3181330fb}</UniqueIdentifier>
</Filter>
<Filter Include="Source Files\Editor\Layer">
<UniqueIdentifier>{e6de889e-8313-4846-8bdf-125b766eef59}</UniqueIdentifier>
</Filter>
</ItemGroup>
<ItemGroup>
<QtUic Include="MainWindow.ui">
<Filter>Form Files</Filter>
</QtUic>
<QtUic Include="EditorWidget.ui">
<Filter>Form Files</Filter>
</QtUic>
<QtUic Include="src\Editor\EditorWidgetComponent\LayerCreateWidget.ui">
<Filter>Form Files</Filter>
</QtUic>
<QtUic Include="EditorSettingWidget.ui">
<Filter>Form Files</Filter>
</QtUic>
<QtUic Include="EditorLayerInfoWidget.ui">
<Filter>Form Files</Filter>
</QtUic>
</ItemGroup>
<ItemGroup>
<ClCompile Include="src\Editor\EditorWidget.cpp">
<ClCompile Include="src\Editor\EditorWidgetItem.cpp">
<Filter>Source Files\Editor</Filter>
</ClCompile>
<ClCompile Include="src\Renderer\Camera.cpp">
@ -105,18 +135,6 @@
<ClCompile Include="src\Renderer\RendererWidget.cpp">
<Filter>Source Files\Renderer</Filter>
</ClCompile>
<ClCompile Include="src\NavigationBarWidget.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="src\CaptionButton.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="src\IconWidget.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="src\TitleWidget.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="src\Renderer\Painting\BvhTree.cpp">
<Filter>Source Files\Renderer\Painting</Filter>
</ClCompile>
@ -138,18 +156,6 @@
<ClCompile Include="src\Renderer\Painting\Element.cpp">
<Filter>Source Files\Renderer\Painting</Filter>
</ClCompile>
<ClCompile Include="src\Editor\GraphicElement.cpp">
<Filter>Source Files\Editor</Filter>
</ClCompile>
<ClCompile Include="src\Editor\ElementManager.cpp">
<Filter>Source Files\Editor</Filter>
</ClCompile>
<ClCompile Include="src\Editor\LayerManager.cpp">
<Filter>Source Files\Editor</Filter>
</ClCompile>
<ClCompile Include="src\Editor\LayerWrapper.cpp">
<Filter>Source Files\Editor</Filter>
</ClCompile>
<ClCompile Include="src\Editor\PreviewWindow.cpp">
<Filter>Source Files\Editor</Filter>
</ClCompile>
@ -159,12 +165,6 @@
<ClCompile Include="src\Renderer\Painting\CubicBezierSignedDistance.cpp">
<Filter>Source Files\Renderer\Painting</Filter>
</ClCompile>
<ClCompile Include="src\Editor\RightBar\LayerTreeWidget.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="src\Editor\RightBar\InfoDisplayWidget.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="src\Renderer\Painting\ElementStyle.cpp">
<Filter>Source Files\Renderer\Painting</Filter>
</ClCompile>
@ -192,9 +192,6 @@
<ClCompile Include="src\Renderer\Preview\ElementRenderer.cpp">
<Filter>Source Files\Renderer\Preview</Filter>
</ClCompile>
<ClCompile Include="src\Editor\LayerStyle.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="src\Renderer\IblUtils.cpp">
<Filter>Source Files\Renderer</Filter>
</ClCompile>
@ -207,39 +204,120 @@
<ClCompile Include="src\Editor\PixelPath.cpp">
<Filter>Source Files\Editor</Filter>
</ClCompile>
<ClCompile Include="src\FluentMenu.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="src\Editor\util\PaintingUtil.cpp">
<Filter>Source Files\Editor\util</Filter>
</ClCompile>
<ClCompile Include="src\Editor\LayerStyle.cpp">
<Filter>Source Files\Editor\Style</Filter>
</ClCompile>
<ClCompile Include="src\Editor\EditorWidgetComponent\LayerStyleDialog.cpp">
<Filter>Source Files\Editor\Style</Filter>
</ClCompile>
<ClCompile Include="src\Editor\EditorWidgetComponent\StrokeStyleWidget.cpp">
<Filter>Source Files\Editor\Style</Filter>
</ClCompile>
<ClCompile Include="src\Editor\RightBar\InfoDisplayWidget.cpp">
<Filter>Source Files\Editor</Filter>
</ClCompile>
<ClCompile Include="src\NavigationBarWidget.cpp">
<Filter>Source Files\Editor</Filter>
</ClCompile>
<ClCompile Include="src\Editor\EditorWidget.cpp">
<Filter>Source Files\Editor</Filter>
</ClCompile>
<ClCompile Include="src\Editor\RightBar\EditorSettingWidget.cpp">
<Filter>Source Files\Editor</Filter>
</ClCompile>
<ClCompile Include="src\Editor\EditorWidgetComponent\FillStyleWidget.cpp">
<Filter>Source Files\Editor\Style</Filter>
</ClCompile>
<ClCompile Include="src\Editor\ElementManager.cpp">
<Filter>Source Files\Editor\Element</Filter>
</ClCompile>
<ClCompile Include="src\Editor\ElementPoolWidget.cpp">
<Filter>Source Files\Editor\Element</Filter>
</ClCompile>
<ClCompile Include="src\Editor\GraphicElement.cpp">
<Filter>Source Files\Editor\Element</Filter>
</ClCompile>
<ClCompile Include="src\Editor\LayerManager.cpp">
<Filter>Source Files\Editor\Layer</Filter>
</ClCompile>
<ClCompile Include="src\Editor\RightBar\LayerTreeWidget.cpp">
<Filter>Source Files\Editor\Layer</Filter>
</ClCompile>
<ClCompile Include="src\Editor\EditorWidgetComponent\LayerCreateWidget.cpp">
<Filter>Source Files\Editor\Layer</Filter>
</ClCompile>
<ClCompile Include="src\Editor\LayerWrapper.cpp">
<Filter>Source Files\Editor\Layer</Filter>
</ClCompile>
<ClCompile Include="src\Editor\EditorWidgetComponent\FillStyleWidget.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="src\Editor\DataManager\ProjectDataManager.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="src\Editor\EditorWidgetComponent\ColorPicker.cpp">
<Filter>Source Files\Editor</Filter>
</ClCompile>
</ItemGroup>
<ItemGroup>
<QtMoc Include="src\Renderer\RendererGLWidget.h">
<Filter>Header Files\Renderer</Filter>
</QtMoc>
<QtMoc Include="src\Editor\EditorWidget.h">
<QtMoc Include="src\Editor\EditorWidgetItem.h">
<Filter>Header Files\Editor</Filter>
</QtMoc>
<QtMoc Include="src\Renderer\RendererWidget.h">
<Filter>Header Files\Renderer</Filter>
</QtMoc>
<QtMoc Include="src\NavigationBarWidget.h">
<Filter>Header Files</Filter>
</QtMoc>
<QtMoc Include="src\CaptionButton.h">
<Filter>Header Files</Filter>
</QtMoc>
<QtMoc Include="src\IconWidget.h">
<Filter>Header Files</Filter>
</QtMoc>
<QtMoc Include="src\TitleWidget.h">
<Filter>Header Files</Filter>
</QtMoc>
<QtMoc Include="src\MainWindow.h">
<Filter>Header Files</Filter>
</QtMoc>
<QtMoc Include="src\Editor\PreviewWindow.h">
<Filter>Header Files\Editor</Filter>
</QtMoc>
<QtMoc Include="src\Editor\RightBar\LayerTreeWidget.h">
<QtMoc Include="src\FluentMenu.h">
<Filter>Header Files</Filter>
</QtMoc>
<QtMoc Include="src\Editor\EditorWidgetComponent\StrokeStyleWidget.h">
<Filter>Header Files\Editor\Style</Filter>
</QtMoc>
<QtMoc Include="src\Editor\EditorWidgetComponent\LayerStyleDialog.h">
<Filter>Header Files\Editor\Style</Filter>
</QtMoc>
<QtMoc Include="src\Editor\RightBar\InfoDisplayWidget.h">
<Filter>Header Files\Editor</Filter>
</QtMoc>
<QtMoc Include="src\Editor\EditorWidget.h">
<Filter>Header Files\Editor</Filter>
</QtMoc>
<QtMoc Include="src\MainWindow.h">
<Filter>Header Files\Editor</Filter>
</QtMoc>
<QtMoc Include="src\NavigationBarWidget.h">
<Filter>Header Files\Editor</Filter>
</QtMoc>
<QtMoc Include="src\Editor\EditorWidgetComponent\ColorPicker.h">
<Filter>Header Files\Editor</Filter>
</QtMoc>
<QtMoc Include="src\Editor\RightBar\EditorSettingWidget.h">
<Filter>Header Files\Editor</Filter>
</QtMoc>
<QtMoc Include="src\Editor\EditorWidgetComponent\FillStyleWidget.h">
<Filter>Header Files\Editor\Style</Filter>
</QtMoc>
<QtMoc Include="src\Editor\ElementPoolWidget.h">
<Filter>Header Files\Editor\Element</Filter>
</QtMoc>
<QtMoc Include="src\Editor\EditorWidgetComponent\LayerCreateWidget.h">
<Filter>Header Files\Editor\Layer</Filter>
</QtMoc>
<QtMoc Include="src\Editor\RightBar\LayerTreeWidget.h">
<Filter>Header Files\Editor\Layer</Filter>
</QtMoc>
<QtMoc Include="src\Editor\EditorWidgetComponent\FillStyleWidget.h">
<Filter>Header Files</Filter>
</QtMoc>
</ItemGroup>
@ -322,7 +400,7 @@
</None>
</ItemGroup>
<ItemGroup>
<QtUic Include="EditorWidget.ui">
<QtUic Include="EditorWidgetItem.ui">
<Filter>Form Files</Filter>
</QtUic>
<QtUic Include="RendererWidget.ui">
@ -372,18 +450,6 @@
<ClInclude Include="src\Renderer\Painting\Element.h">
<Filter>Header Files\Renderer\Painting</Filter>
</ClInclude>
<ClInclude Include="src\Editor\GraphicElement.h">
<Filter>Header Files\Editor</Filter>
</ClInclude>
<ClInclude Include="src\Editor\LayerStyle.h">
<Filter>Header Files\Editor</Filter>
</ClInclude>
<ClInclude Include="src\Editor\LayerManager.h">
<Filter>Header Files\Editor</Filter>
</ClInclude>
<ClInclude Include="src\Editor\ElementManager.h">
<Filter>Header Files\Editor</Filter>
</ClInclude>
<ClInclude Include="src\Renderer\Painting\LineTree.h">
<Filter>Header Files\Renderer\Painting</Filter>
</ClInclude>
@ -423,9 +489,6 @@
<ClInclude Include="src\Renderer\Preview\ElementRenderer.h">
<Filter>Header Files\Renderer\Preview</Filter>
</ClInclude>
<ClInclude Include="src\Editor\PixelPath.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="src\Renderer\IblUtils.h">
<Filter>Header Files\Renderer</Filter>
</ClInclude>
@ -435,7 +498,37 @@
<ClInclude Include="src\Renderer\VirtualTextureManager.h">
<Filter>Header Files\Renderer</Filter>
</ClInclude>
<ClInclude Include="resource.h">
<ClInclude Include="src\Editor\util\PaintingUtil.h">
<Filter>Header Files\Editor\util</Filter>
</ClInclude>
<ClInclude Include="src\Editor\LayerStyle.h">
<Filter>Header Files\Editor\Style</Filter>
</ClInclude>
<ClInclude Include="src\Editor\util\JsonUtil.hpp">
<Filter>Header Files\Editor\util</Filter>
</ClInclude>
<ClInclude Include="src\Editor\util\EncodeUtil.hpp">
<Filter>Header Files\Editor\util</Filter>
</ClInclude>
<ClInclude Include="src\Editor\ElementManager.h">
<Filter>Header Files\Editor\Element</Filter>
</ClInclude>
<ClInclude Include="src\Editor\GraphicElement.h">
<Filter>Header Files\Editor\Element</Filter>
</ClInclude>
<ClInclude Include="src\Editor\LayerManager.h">
<Filter>Header Files\Editor\Layer</Filter>
</ClInclude>
<ClInclude Include="src\Editor\PixelPath.h">
<Filter>Header Files\Editor</Filter>
</ClInclude>
<ClInclude Include="src\Editor\LayerWrapper.h">
<Filter>Header Files\Editor\Layer</Filter>
</ClInclude>
<ClInclude Include="src\ColorHelper.hpp">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="src\Editor\DataManager\ProjectDataManager.h">
<Filter>Header Files</Filter>
</ClInclude>
</ItemGroup>
@ -444,7 +537,4 @@
<Filter>Resource Files</Filter>
</QtRcc>
</ItemGroup>
<ItemGroup>
<ResourceCompile Include="ArchitectureColoredPainting.rc" />
</ItemGroup>
</Project>

View File

@ -0,0 +1,92 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>EditorLayerInfoWidget</class>
<widget class="QWidget" name="EditorLayerInfoWidget">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>333</width>
<height>321</height>
</rect>
</property>
<property name="windowTitle">
<string/>
</property>
<layout class="QGridLayout" name="widgetLayout">
<item row="0" column="0" colspan="2">
<widget class="QtMaterialTextField" name="name">
<property name="readOnly">
<bool>true</bool>
</property>
</widget>
</item>
<item row="1" column="0" colspan="2">
<widget class="QtMaterialTextField" name="rotation">
<property name="readOnly">
<bool>false</bool>
</property>
</widget>
</item>
<item row="2" column="0">
<widget class="QtMaterialTextField" name="offsetX">
<property name="readOnly">
<bool>false</bool>
</property>
</widget>
</item>
<item row="2" column="1">
<widget class="QtMaterialTextField" name="offsetY">
<property name="readOnly">
<bool>false</bool>
</property>
</widget>
</item>
<item row="3" column="0">
<widget class="QtMaterialTextField" name="scaleX">
<property name="readOnly">
<bool>false</bool>
</property>
</widget>
</item>
<item row="3" column="1">
<widget class="QtMaterialTextField" name="scaleY">
<property name="readOnly">
<bool>false</bool>
</property>
</widget>
</item>
<item row="4" column="0">
<widget class="QtMaterialCheckBox" name="flipX">
<property name="text">
<string>水平翻转</string>
</property>
</widget>
</item>
<item row="4" column="1">
<widget class="QtMaterialCheckBox" name="flipY">
<property name="text">
<string>竖直翻转</string>
</property>
</widget>
</item>
<item row="5" column="0" colspan="2">
<widget class="QListWidget" name="styleList"/>
</item>
</layout>
</widget>
<customwidgets>
<customwidget>
<class>QtMaterialTextField</class>
<extends>QLineEdit</extends>
<header location="global">qtmaterialtextfield.h</header>
</customwidget>
<customwidget>
<class>QtMaterialCheckBox</class>
<extends>QCheckBox</extends>
<header location="global">qtmaterialcheckbox.h</header>
</customwidget>
</customwidgets>
<resources/>
<connections/>
</ui>

View File

@ -0,0 +1,32 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>EditorSettingWidget</class>
<widget class="QWidget" name="EditorSettingWidget">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>400</width>
<height>300</height>
</rect>
</property>
<property name="windowTitle">
<string>Form</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout_2">
<item>
<layout class="QVBoxLayout" name="verticalLayout">
<item>
<widget class="QPushButton" name="backgroundColorButton">
<property name="text">
<string>设置背景颜色</string>
</property>
</widget>
</item>
</layout>
</item>
</layout>
</widget>
<resources/>
<connections/>
</ui>

View File

@ -0,0 +1,63 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>EditorSettingWidget</class>
<widget class="QWidget" name="EditorSettingWidget">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>400</width>
<height>300</height>
</rect>
</property>
<property name="windowTitle">
<string>Form</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout_2">
<item>
<layout class="QVBoxLayout" name="verticalLayout">
<item alignment="Qt::AlignHCenter">
<widget class="QPushButton" name="backgroundColorButton">
<property name="sizePolicy">
<sizepolicy hsizetype="Fixed" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="maximumSize">
<size>
<width>120</width>
<height>16777215</height>
</size>
</property>
<property name="text">
<string>设置背景颜色</string>
</property>
</widget>
</item>
<item alignment="Qt::AlignHCenter">
<widget class="QPushButton" name="renameButton">
<property name="sizePolicy">
<sizepolicy hsizetype="Minimum" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="maximumSize">
<size>
<width>120</width>
<height>16777215</height>
</size>
</property>
<property name="text">
<string>设置项目名称</string>
</property>
</widget>
</item>
</layout>
</item>
</layout>
</widget>
<resources/>
<connections/>
</ui>

View File

@ -1,19 +1,33 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>EditorWidgetClass</class>
<widget class="QWidget" name="EditorWidgetClass">
<class>EditorWidget</class>
<widget class="QWidget" name="EditorWidget">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>1124</width>
<height>695</height>
<width>1139</width>
<height>862</height>
</rect>
</property>
<property name="windowTitle">
<string>RendererWidget</string>
<string>Form</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout">
<layout class="QVBoxLayout" name="verticalLayout_2">
<property name="topMargin">
<number>0</number>
</property>
<item>
<layout class="QVBoxLayout" name="verticalLayout" stretch="1,20">
<property name="topMargin">
<number>0</number>
</property>
<item>
<widget class="QWidget" name="widget" native="true">
<property name="autoFillBackground">
<bool>false</bool>
</property>
<layout class="QHBoxLayout" name="horizontalLayout_3">
<property name="leftMargin">
<number>0</number>
</property>
@ -27,87 +41,159 @@
<number>0</number>
</property>
<item>
<widget class="QWidget" name="MainWindow" native="true">
<layout class="QVBoxLayout" name="verticalLayout_2" stretch="1,30">
<item>
<widget class="QLabel" name="Title">
<property name="text">
<string/>
<layout class="QHBoxLayout" name="horizontalLayout_2" stretch="1,1,1,1,1,0">
<property name="spacing">
<number>0</number>
</property>
<property name="alignment">
<set>Qt::AlignCenter</set>
<property name="topMargin">
<number>0</number>
</property>
<item>
<widget class="QPushButton" name="createButton">
<property name="minimumSize">
<size>
<width>80</width>
<height>40</height>
</size>
</property>
<property name="maximumSize">
<size>
<width>80</width>
<height>16777215</height>
</size>
</property>
<property name="text">
<string>新建</string>
</property>
</widget>
</item>
<item>
<layout class="QHBoxLayout" name="horizontalLayout" stretch="1,18,5">
<item>
<widget class="QWidget" name="LeftBar" native="true"/>
<widget class="QPushButton" name="openButton">
<property name="minimumSize">
<size>
<width>80</width>
<height>40</height>
</size>
</property>
<property name="maximumSize">
<size>
<width>80</width>
<height>16777215</height>
</size>
</property>
<property name="text">
<string>打开</string>
</property>
</widget>
</item>
<item>
<widget class="PreviewWindow" name="Preview"/>
<widget class="QPushButton" name="saveButton">
<property name="minimumSize">
<size>
<width>80</width>
<height>40</height>
</size>
</property>
<property name="maximumSize">
<size>
<width>80</width>
<height>16777215</height>
</size>
</property>
<property name="text">
<string>保存</string>
</property>
</widget>
</item>
<item>
<widget class="QWidget" name="RightBar" native="true">
<layout class="QVBoxLayout" name="verticalLayout_4" stretch="1,2">
<widget class="QPushButton" name="saveAsButton">
<property name="minimumSize">
<size>
<width>80</width>
<height>40</height>
</size>
</property>
<property name="maximumSize">
<size>
<width>80</width>
<height>16777215</height>
</size>
</property>
<property name="text">
<string>另存为</string>
</property>
</widget>
</item>
<item>
<widget class="QTabWidget" name="DisplayTab">
<widget class="QPushButton" name="closeButton">
<property name="sizePolicy">
<sizepolicy hsizetype="Fixed" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="minimumSize">
<size>
<width>80</width>
<height>40</height>
</size>
</property>
<property name="maximumSize">
<size>
<width>80</width>
<height>16777215</height>
</size>
</property>
<property name="text">
<string>关闭</string>
</property>
</widget>
</item>
<item>
<spacer name="horizontalSpacer_2">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>40</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
</layout>
</item>
</layout>
</widget>
</item>
<item>
<widget class="QTabWidget" name="tabWidget">
<property name="minimumSize">
<size>
<width>0</width>
<height>800</height>
</size>
</property>
<property name="currentIndex">
<number>0</number>
</property>
<widget class="InfoDisplayWidget" name="LayerDisplay">
<widget class="QWidget" name="tab">
<attribute name="title">
<string>Layer</string>
<string>Tab 1</string>
</attribute>
</widget>
<widget class="QWidget" name="ElementDisplay">
<widget class="QWidget" name="tab_2">
<attribute name="title">
<string>Element</string>
<string>Tab 2</string>
</attribute>
</widget>
</widget>
</item>
<item>
<widget class="LayerTreeWidget" name="LayerTree">
<property name="contextMenuPolicy">
<enum>Qt::CustomContextMenu</enum>
</property>
<column>
<property name="text">
<string notr="true">1</string>
</property>
</column>
</widget>
</item>
</layout>
</widget>
</item>
</layout>
</item>
</layout>
</widget>
</item>
</layout>
</widget>
<layoutdefault spacing="6" margin="11"/>
<customwidgets>
<customwidget>
<class>PreviewWindow</class>
<extends>QOpenGLWidget</extends>
<header>PreviewWindow.h</header>
</customwidget>
<customwidget>
<class>LayerTreeWidget</class>
<extends>QTreeWidget</extends>
<header location="global">LayerTreeWidget.h</header>
</customwidget>
<customwidget>
<class>InfoDisplayWidget</class>
<extends>QWidget</extends>
<header location="global">InfoDisplayWidget.h</header>
<container>1</container>
</customwidget>
</customwidgets>
<resources/>
<connections/>
</ui>

View File

@ -0,0 +1,262 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>EditorWidgetItem</class>
<widget class="QWidget" name="EditorWidgetItem">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>1473</width>
<height>1103</height>
</rect>
</property>
<property name="minimumSize">
<size>
<width>0</width>
<height>0</height>
</size>
</property>
<property name="windowTitle">
<string>RendererWidget</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout">
<property name="spacing">
<number>0</number>
</property>
<property name="leftMargin">
<number>0</number>
</property>
<property name="topMargin">
<number>0</number>
</property>
<property name="rightMargin">
<number>0</number>
</property>
<property name="bottomMargin">
<number>0</number>
</property>
<item>
<widget class="QWidget" name="MainWindow" native="true">
<layout class="QVBoxLayout" name="verticalLayout_2" stretch="20,1">
<item>
<layout class="QHBoxLayout" name="horizontalLayout" stretch="0,12,5">
<item>
<widget class="QWidget" name="LeftBar" native="true"/>
</item>
<item>
<widget class="QScrollArea" name="scrollArea">
<property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="Preferred">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="minimumSize">
<size>
<width>0</width>
<height>0</height>
</size>
</property>
<property name="maximumSize">
<size>
<width>10801080</width>
<height>10801080</height>
</size>
</property>
<property name="widgetResizable">
<bool>true</bool>
</property>
<widget class="QWidget" name="scrollAreaWidgetContents">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>1024</width>
<height>1024</height>
</rect>
</property>
<property name="sizePolicy">
<sizepolicy hsizetype="Fixed" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="minimumSize">
<size>
<width>1024</width>
<height>1024</height>
</size>
</property>
<property name="maximumSize">
<size>
<width>10241024</width>
<height>10241024</height>
</size>
</property>
<layout class="QHBoxLayout" name="horizontalLayout_2">
<item>
<widget class="PreviewWindow" name="Preview">
<property name="sizePolicy">
<sizepolicy hsizetype="Fixed" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="minimumSize">
<size>
<width>1024</width>
<height>1024</height>
</size>
</property>
<property name="maximumSize">
<size>
<width>1024</width>
<height>1024</height>
</size>
</property>
</widget>
</item>
</layout>
</widget>
</widget>
</item>
<item>
<widget class="QWidget" name="RightBar" native="true">
<layout class="QVBoxLayout" name="verticalLayout_4" stretch="3,2">
<property name="spacing">
<number>20</number>
</property>
<property name="leftMargin">
<number>11</number>
</property>
<property name="topMargin">
<number>11</number>
</property>
<property name="rightMargin">
<number>0</number>
</property>
<property name="bottomMargin">
<number>11</number>
</property>
<item>
<widget class="QTabWidget" name="DisplayTab">
<property name="minimumSize">
<size>
<width>0</width>
<height>0</height>
</size>
</property>
<property name="currentIndex">
<number>2</number>
</property>
<widget class="InfoDisplayWidget" name="LayerDisplay">
<attribute name="title">
<string>图层信息</string>
</attribute>
</widget>
<widget class="ElementPoolWidget" name="ElementDisplay">
<attribute name="title">
<string>图元池</string>
</attribute>
</widget>
<widget class="EditorSettingWidget" name="EditorSetting">
<attribute name="title">
<string>设置</string>
</attribute>
</widget>
</widget>
</item>
<item>
<widget class="LayerTreeWidget" name="LayerTree">
<property name="minimumSize">
<size>
<width>0</width>
<height>0</height>
</size>
</property>
<property name="contextMenuPolicy">
<enum>Qt::CustomContextMenu</enum>
</property>
<property name="accessibleName">
<string/>
</property>
<property name="iconSize">
<size>
<width>10</width>
<height>10</height>
</size>
</property>
<column>
<property name="text">
<string>图层名</string>
</property>
</column>
<column>
<property name="text">
<string>关联图元</string>
</property>
</column>
<column>
<property name="text">
<string>可见</string>
</property>
</column>
</widget>
</item>
</layout>
</widget>
</item>
</layout>
</item>
<item>
<spacer name="horizontalSpacer">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>40</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
</layout>
</widget>
</item>
</layout>
</widget>
<layoutdefault spacing="6" margin="11"/>
<customwidgets>
<customwidget>
<class>PreviewWindow</class>
<extends>QOpenGLWidget</extends>
<header>PreviewWindow.h</header>
</customwidget>
<customwidget>
<class>LayerTreeWidget</class>
<extends>QTreeWidget</extends>
<header location="global">LayerTreeWidget.h</header>
</customwidget>
<customwidget>
<class>InfoDisplayWidget</class>
<extends>QWidget</extends>
<header location="global">InfoDisplayWidget.h</header>
<container>1</container>
</customwidget>
<customwidget>
<class>ElementPoolWidget</class>
<extends>QWidget</extends>
<header>ElementPoolWidget.h</header>
<container>1</container>
</customwidget>
<customwidget>
<class>EditorSettingWidget</class>
<extends>QWidget</extends>
<header location="global">EditorSettingWidget.h</header>
<container>1</container>
</customwidget>
</customwidgets>
<resources/>
<connections/>
</ui>

View File

@ -1,7 +1,7 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>MainWindowClass</class>
<widget class="QMainWindow" name="MainWindowClass">
<widget class="QWidget" name="MainWindowClass">
<property name="geometry">
<rect>
<x>0</x>
@ -10,19 +10,9 @@
<height>722</height>
</rect>
</property>
<property name="windowTitle">
<string>MainWindow</string>
</property>
<property name="styleSheet">
<string notr="true">font: 10pt &quot;Segoe UI, Microsoft YaHei UI&quot;;</string>
</property>
<widget class="QWidget" name="centralWidget">
<property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="Preferred">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<layout class="QVBoxLayout" name="verticalLayout">
<property name="spacing">
<number>0</number>
@ -40,73 +30,26 @@
<number>0</number>
</property>
<item>
<layout class="QVBoxLayout" name="verticalLayout_4" stretch="1">
<property name="spacing">
<number>0</number>
</property>
<layout class="QGridLayout" name="gridLayout">
<property name="sizeConstraint">
<enum>QLayout::SetDefaultConstraint</enum>
</property>
<item>
<widget class="QTabWidget" name="tabWidget">
<property name="font">
<font>
<family>Segoe UI, Microsoft YaHei UI</family>
<pointsize>10</pointsize>
<weight>50</weight>
<italic>false</italic>
<bold>false</bold>
</font>
</property>
<property name="styleSheet">
<string notr="true">
QTabBar::tab {
height: 0px;
margin-top:0px;
}
QTabWidget::tab-bar
{
height: 0px;
top:0px;
}
QTabWidget::pane {
border: 0px;
background-color: transparent;
}
</string>
</property>
<property name="tabPosition">
<enum>QTabWidget::North</enum>
</property>
<property name="tabShape">
<enum>QTabWidget::Rounded</enum>
</property>
<property name="currentIndex">
<property name="topMargin">
<number>0</number>
</property>
<property name="elideMode">
<enum>Qt::ElideNone</enum>
<property name="spacing">
<number>0</number>
</property>
<property name="tabsClosable">
<bool>false</bool>
</property>
<widget class="EditorWidget" name="editorWidget">
<attribute name="title">
<string>纹理编辑</string>
</attribute>
</widget>
<widget class="Renderer::RendererWidget" name="rendererWidget">
<attribute name="title">
<string>场景渲染</string>
</attribute>
</widget>
<item row="0" column="0">
<widget class="QStackedWidget" name="stackedWidget">
<widget class="EditorWidget" name="editorWidget"/>
<widget class="Renderer::RendererWidget" name="rendererWidget"/>
</widget>
</item>
</layout>
</item>
</layout>
</widget>
</widget>
<layoutdefault spacing="6" margin="11"/>
<customwidgets>
<customwidget>

View File

@ -0,0 +1,2 @@
17876391417123941155.jpg 0.json 0 0 1 1
11474523244911310074.jpg 1.json 0 0 1 1

View File

@ -13,7 +13,7 @@
<property name="windowTitle">
<string>RendererWidget</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout" stretch="0,0,1">
<layout class="QVBoxLayout" name="verticalLayout" stretch="0,1">
<property name="spacing">
<number>0</number>
</property>
@ -31,8 +31,14 @@
</property>
<item>
<layout class="QHBoxLayout" name="horizontalLayout_3">
<property name="sizeConstraint">
<enum>QLayout::SetDefaultConstraint</enum>
<property name="leftMargin">
<number>6</number>
</property>
<property name="topMargin">
<number>5</number>
</property>
<property name="bottomMargin">
<number>5</number>
</property>
<item>
<widget class="QtMaterialFlatButton" name="openButton">
@ -44,12 +50,18 @@
</property>
<property name="minimumSize">
<size>
<width>80</width>
<height>40</height>
<width>50</width>
<height>30</height>
</size>
</property>
<property name="maximumSize">
<size>
<width>50</width>
<height>30</height>
</size>
</property>
<property name="text">
<string>打开</string>
<string>文件</string>
</property>
<property name="flat">
<bool>false</bool>
@ -72,38 +84,26 @@
</layout>
</item>
<item>
<widget class="QMenuBar" name="menuBar">
<layout class="QHBoxLayout" name="horizontalLayout" stretch="0,1,0">
<property name="sizeConstraint">
<enum>QLayout::SetDefaultConstraint</enum>
</property>
<item>
<widget class="QListWidget" name="textureListWidget">
<property name="sizePolicy">
<sizepolicy hsizetype="MinimumExpanding" vsizetype="Ignored">
<sizepolicy hsizetype="Fixed" vsizetype="Expanding">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="minimumSize">
<property name="maximumSize">
<size>
<width>0</width>
<height>0</height>
<height>16777215</height>
</size>
</property>
<widget class="QMenu" name="menu">
<property name="minimumSize">
<size>
<width>0</width>
<height>40</height>
</size>
</property>
<property name="title">
<string>打开</string>
</property>
</widget>
<addaction name="menu"/>
</widget>
</item>
<item>
<layout class="QHBoxLayout" name="horizontalLayout" stretch="1,0">
<property name="sizeConstraint">
<enum>QLayout::SetDefaultConstraint</enum>
</property>
<item>
<widget class="Renderer::RendererGLWidget" name="openGLWidget">
<property name="sizePolicy">

View File

@ -893,7 +893,7 @@ bool angleLargeThanPi(vec2 a, vec2 b)
/************************************************************************************/
void drawLine(in float d, inout uint styleIndex, out vec4 elementColor, out vec2 metallicRoughness)
void drawLine(in float d, uint styleIndex, out vec4 elementColor, out vec2 metallicRoughness)
{
elementColor = vec4(1);
metallicRoughness = vec2(0.8);
@ -969,14 +969,38 @@ void drawLine(in float d, inout uint styleIndex, out vec4 elementColor, out vec2
}
}
bool shouldFillBeginCap(vec2 localUV, bool onVeryBegin, int endType, vec2 p3, vec2 tangentBegin, vec2 tangentEndLast)
void nextStyleIndex(inout uint styleIndex)
{
uint headUint = floatBitsToUint(style[styleIndex + 1]);
vec4 head = unpackUnorm4x8(headUint);
switch (int(head.a * 100) % 10)
{
/// Plain
case 0: {
styleIndex += 3;
break;
}
/// RadialGradient
case 1: {
uint size = headUint % (1 << 15);
styleIndex += 2 + size * 2;
break;
}
case 2: {
break;
}
}
}
bool shouldFillBeginCap(vec2 localUV, bool onVeryBegin, int endType, vec2 p0, vec2 tangentBegin, vec2 tangentEndLast)
{
vec2 normal;
if (onVeryBegin)
{
if (endType == 0)
if (endType%2 == 0)
return true;
else if (endType == 1)
else if (endType%2 == 1)
normal = normalize(mat2(0, 1, -1, 0) * (-tangentBegin));
}
else
@ -985,17 +1009,26 @@ bool shouldFillBeginCap(vec2 localUV, bool onVeryBegin, int endType, vec2 p3, ve
vec2 normalNow = normalize(mat2(0, 1, -1, 0) * (-tangentBegin));
normal = normalLast + normalNow;
}
return angleLargeThanPi(normal, localUV - p3);
return angleLargeThanPi(normal, localUV - p0);
}
bool shouldFillEndCap(vec2 localUV, int endType, vec2 p0, vec2 tangentEnd)
bool shouldFillEndCap(vec2 localUV, bool onVeryEnd, int endType, vec2 p3, vec2 tangentEnd, vec2 tangentBeginNext)
{
vec2 normal;
if (endType == 0)
if (onVeryEnd)
{
if ((endType/2)%2 == 0)
return true;
else if (endType == 1)
else if ((endType/2)%2 == 1)
normal = normalize(mat2(0, 1, -1, 0) * tangentEnd);
return angleLargeThanPi(localUV - p0, normal);
}
else
{
vec2 normalLast = normalize(mat2(0, 1, -1, 0) * tangentEnd);
vec2 normalNow = normalize(mat2(0, 1, -1, 0) * (-tangentBeginNext));
normal = normalLast + normalNow;
}
return angleLargeThanPi(localUV - p3, normal);
}
void main()
@ -1006,9 +1039,8 @@ void main()
vec4 color = vec4(0);
// if(isinf(path[0].x)) imageStore(gBaseColor, ivec2(pixelLocation),
// vec4(vec2(pixelLocation)/vec2(imageSize(gBaseColor)), 1,1)); return;
for (uint styleIndex = 0; styleIndex < styleSize; styleIndex++)
for (uint styleIndex = 0; styleIndex < styleSize;)
{
styleIndex += 6;
vec2 localUV = vec2(pixelLocation) / pixelRatio + leftTop;
@ -1039,14 +1071,15 @@ void main()
if (num_its % 2 == 1)
{
hitElement = true;
elementColor = vec4(1, 1, 0, 0);
vec4 head = unpackUnorm4x8(floatBitsToUint(style[styleIndex]));
if (head.z == 0)
{
elementColor = vec4(unpackUnorm4x8(floatBitsToUint(style[++styleIndex])).rgb, 1);
elementColor = vec4(unpackUnorm4x8(floatBitsToUint(style[styleIndex+1])).rgb, 1);
}
}
styleIndex += 2;
}
else // Stroke
{
@ -1058,31 +1091,76 @@ void main()
// endType = 1;
int debugBegin = 0;
bool onVeryBegin = false;
bool onVeryEnd = false;
vec2 tangentEndLast;
vec2 tangentFirstBegin;
uint lastHitIndex = 0;
bool lastHitElement = false;
hitElement = false;
for (uint pathIndex = 0; pathIndex < pathSize; pathIndex++)
// for (uint pathIndex = 0; pathIndex < 4; pathIndex++)
//for (uint pathIndex = 0; pathIndex < 46; pathIndex++)
{
vec2 pTemp = path[pathIndex];
if (isinf(pTemp.x))
{
// TODO: ¼ì²âÊÇ·ñ·â±Õ²¢´¦Àí
if (hitElement && distance(localUV, p3Last) <= strokeWidth)
{
hitElement = shouldFillEndCap(localUV, endType, p3Last, tangentEndLast);
}
pBegin = path[++pathIndex];
p3Last = pBegin;
p2Last = pBegin;
if(endType == 4)
{
//onVeryBegin = false;
vec2 lastP1 = path[pathSize-3];
vec2 lastP2 = path[pathSize-2];
vec2 lastP3 = path[pathSize-1];
if (lastP3 != lastP2)
tangentEndLast = normalize(lastP3 - lastP2);
else
tangentEndLast = normalize(lastP3 - lastP1);
}
else
onVeryBegin = true;
continue;
}
mat4x2 p = mat4x2(p3Last, pTemp, path[++pathIndex], path[++pathIndex]);
vec2 tangentBeginNext;
if (pathIndex + 1 < pathSize)
{
vec2 pTemp = path[pathIndex + 1];
if (isinf(pTemp.x))
{
onVeryEnd = true;
}
else
{
onVeryEnd = false;
vec2 pNext[3] = {p[3], pTemp, path[pathIndex + 2]};
if (pNext[0] != pNext[1])
tangentBeginNext = normalize(pNext[0] - pNext[1]);
else
tangentBeginNext = normalize(pNext[0] - pNext[2]);
}
}
else
{
if(endType == 4)
{
//onVeryEnd = false;
tangentBeginNext = tangentFirstBegin;
}
else
onVeryEnd = true;
}
float d = cubic_bezier_dis(localUV, p[0], p[1], p[2], p[3], true);
if (d <= strokeWidth)
{
bool onBegin = distance(localUV, p[0]) <= strokeWidth && p3Last == p[0];
bool onBegin = distance(localUV, p[0]) <= strokeWidth;
bool onEnd = distance(localUV, p[3]) <= strokeWidth;
vec2 tangentBegin;
vec2 tangentEnd;
if (p[0] != p[1])
@ -1094,11 +1172,14 @@ void main()
else
tangentEnd = normalize(p[3] - p[1]);
if (onBegin ? shouldFillBeginCap(localUV, onVeryBegin, endType, p[0], tangentBegin, p3Last - p2Last)
: d < minDistance)
bool hit = d < minDistance;
if (onBegin)
hit = hit &&
shouldFillBeginCap(localUV, onVeryBegin, endType, p[0], tangentBegin, tangentEndLast);
if (onEnd)
hit = hit && shouldFillEndCap(localUV, onVeryEnd, endType, p[3], tangentEnd, tangentBeginNext);
if (hit)
{
minDistance = min(minDistance, d);
bool reverse = p[3].y - p[0].y < 0.;
if (tangentBegin.y == 0.)
@ -1111,28 +1192,28 @@ void main()
if (lineType == 2 || (intTest % 2 == int(lineType)))
{
minDistance = min(minDistance, d);
lastHitElement = hitElement;
lastHitIndex = pathIndex;
hitElement = true;
// elementColor = vec4(1, 1, 0, 1);
vec2 metallicRoughness;
drawLine(minDistance / strokeWidth, styleIndex, elementColor, metallicRoughness);
}
else if (p3Last == p[0])
hitElement = false;
}
tangentEndLast = tangentEnd;
if(pathIndex == 4)
tangentFirstBegin = tangentBegin;
}
p3Last = p[3];
p2Last = p[2];
onVeryBegin = false;
}
if (hitElement && distance(localUV, p3Last) <= strokeWidth)
{
hitElement = shouldFillEndCap(localUV, endType, p3Last, tangentEndLast);
}
nextStyleIndex(styleIndex);
}
if (hitElement)
color = elementColor;
styleIndex += 100;
}
if (color.a != 0)
imageStore(gBaseColor, ivec2(pixelLocation), color);

View File

@ -4,6 +4,11 @@ layout (local_size_x = 8, local_size_y = 8) in;
layout(location = 0) uniform ivec2 pixelOffset;
layout(std140, binding = 1) uniform ubo
{
vec3 backgroundColor;
};
layout(rgba8, binding = 0) uniform image2D gBaseColor;
layout(rg8, binding = 1) uniform image2D gMetallicRoughness;
@ -15,29 +20,31 @@ layout(std430, binding = 1) buffer bvhBoundBuffer
{
vec4 bvhBound[];
};
layout(std430, binding = 2) buffer elementOffsetBuffer
layout(std430, binding = 2) buffer elementTranformBuffer
{
mat3x2 elementTranform[];
};
layout(std430, binding = 3) buffer elementOffsetBuffer
{
/**
* @[0] elementBvhRoot
* @[1] styleOffset
* @[2] pointsOffset
* @[3] linesOffset
*/
uint elementOffset[][5];
uint elementOffset[][4];
};
layout(std430, binding = 3) buffer elementIndexBuffer
layout(std430, binding = 4) buffer elementIndexBuffer
{
uint elementIndexs[]; // ÏߺÍÃæ
};
layout(std430, binding = 4) buffer elementDataBuffer
layout(std430, binding = 5) buffer elementDataBuffer
{
float elementData[]; // µãºÍStyle
};
const float PI = 3.14159265358979;
const uint STACK_SIZE = 10;
struct Stack
@ -77,7 +84,6 @@ struct Stack
}
} stack, elementStack;
// Modified from http://tog.acm.org/resources/GraphicsGems/gems/Roots3And4.c
// Credits to Doublefresh for hinting there
int solve_quadric(vec2 coeffs, inout vec2 roots)
@ -151,7 +157,6 @@ int solve_cubic(vec3 coeffs, inout vec3 r)
return 3;
}
int segment_int_test(vec2 uv, vec2 p0, vec2 p1)
{
p0 -= uv;
@ -299,15 +304,19 @@ bvec3 cubic_bezier_sign_test(vec2 uv, vec2 p0, vec2 p1, vec2 p2, vec2 p3)
// int n_ints = 0;
bvec3 result = bvec3(false);
for(int i=0;i<3;i++){
if(i < n_roots){
if(roots[i] >= 0. && roots[i] <= 1.){
for (int i = 0; i < 3; i++)
{
if (i < n_roots)
{
if (roots[i] >= 0. && roots[i] <= 1.)
{
float x_pos = -p0.x + 3. * p1.x - 3. * p2.x + p3.x;
x_pos = x_pos * roots[i] + 3. * p0.x - 6. * p1.x + 3. * p2.x;
x_pos = x_pos * roots[i] + -3. * p0.x + 3. * p1.x;
x_pos = x_pos * roots[i] + p0.x;
if(x_pos > uv.x){
if (x_pos > uv.x)
{
result[1] = !result[1];
}
}
@ -320,24 +329,32 @@ bvec3 cubic_bezier_sign_test(vec2 uv, vec2 p0, vec2 p1, vec2 p2, vec2 p3)
vec2 nor1 = vec2(tang1.y, -tang1.x);
vec2 nor2 = vec2(tang2.y, -tang2.x);
if(p0.y < p1.y){
if((uv.y<=p0.y) && (dot(uv-p0.xy,nor1)>0.)){
if (p0.y < p1.y)
{
if ((uv.y <= p0.y) && (dot(uv - p0.xy, nor1) > 0.))
{
result[0] = !result[0];
}
}
else{
if(!(uv.y<=p0.y) && !(dot(uv-p0.xy,nor1)>0.)){
else
{
if (!(uv.y <= p0.y) && !(dot(uv - p0.xy, nor1) > 0.))
{
result[0] = !result[0];
}
}
if(p2.y<p3.y){
if(!(uv.y<=p3.y) && dot(uv-p3.xy,nor2)>0.){
if (p2.y < p3.y)
{
if (!(uv.y <= p3.y) && dot(uv - p3.xy, nor2) > 0.)
{
result[2] = !result[2];
}
}
else{
if((uv.y<=p3.y) && !(dot(uv-p3.xy,nor2)>0.)){
else
{
if ((uv.y <= p3.y) && !(dot(uv - p3.xy, nor2) > 0.))
{
result[2] = !result[2];
}
}
@ -355,7 +372,8 @@ const float eps = .000005;
const int halley_iterations = 8;
// lagrange positive real root upper bound
// see for example: https://doi.org/10.1016/j.jsc.2014.09.038
float upper_bound_lagrange5(float a0, float a1, float a2, float a3, float a4){
float upper_bound_lagrange5(float a0, float a1, float a2, float a3, float a4)
{
vec4 coeffs1 = vec4(a0, a1, a2, a3);
@ -383,7 +401,8 @@ float upper_bound_lagrange5(float a0, float a1, float a2, float a3, float a4){
}
// lagrange upper bound applied to f(-x) to get lower bound
float lower_bound_lagrange5(float a0, float a1, float a2, float a3, float a4){
float lower_bound_lagrange5(float a0, float a1, float a2, float a3, float a4)
{
vec4 coeffs1 = vec4(-a0, a1, -a2, a3);
@ -410,7 +429,8 @@ float lower_bound_lagrange5(float a0, float a1, float a2, float a3, float a4){
return -max_max - max_max2;
}
vec2 parametric_cub_bezier(float t, vec2 p0, vec2 p1, vec2 p2, vec2 p3){
vec2 parametric_cub_bezier(float t, vec2 p0, vec2 p1, vec2 p2, vec2 p3)
{
vec2 a0 = (-p0 + 3. * p1 - 3. * p2 + p3);
vec2 a1 = (3. * p0 - 6. * p1 + 3. * p2);
vec2 a2 = (-3. * p0 + 3. * p1);
@ -419,7 +439,8 @@ vec2 parametric_cub_bezier(float t, vec2 p0, vec2 p1, vec2 p2, vec2 p3){
return (((a0 * t) + a1) * t + a2) * t + a3;
}
void sort_roots3(inout vec3 roots){
void sort_roots3(inout vec3 roots)
{
vec3 tmp;
tmp[0] = min(roots[0], min(roots[1], roots[2]));
@ -429,7 +450,8 @@ void sort_roots3(inout vec3 roots){
roots = tmp;
}
void sort_roots4(inout vec4 roots){
void sort_roots4(inout vec4 roots)
{
vec4 tmp;
vec2 min1_2 = min(roots.xz, roots.yw);
@ -446,7 +468,8 @@ void sort_roots4(inout vec4 roots){
roots = tmp;
}
float eval_poly5(float a0, float a1, float a2, float a3, float a4, float x){
float eval_poly5(float a0, float a1, float a2, float a3, float a4, float x)
{
float f = ((((x + a4) * x + a3) * x + a2) * x + a1) * x + a0;
@ -457,7 +480,8 @@ float eval_poly5(float a0, float a1, float a2, float a3, float a4, float x){
// basically a variant of newton raphson which converges quicker and has bigger basins of convergence
// see http://mathworld.wolfram.com/HalleysMethod.html
// or https://en.wikipedia.org/wiki/Halley%27s_method
float halley_iteration5(float a0, float a1, float a2, float a3, float a4, float x){
float halley_iteration5(float a0, float a1, float a2, float a3, float a4, float x)
{
float f = ((((x + a4) * x + a3) * x + a2) * x + a1) * x + a0;
float f1 = (((5. * x + 4. * a4) * x + 3. * a3) * x + 2. * a2) * x + a1;
@ -466,7 +490,8 @@ float halley_iteration5(float a0, float a1, float a2, float a3, float a4, float
return x - (2. * f * f1) / (2. * f1 * f1 - f * f2);
}
float halley_iteration4(vec4 coeffs, float x){
float halley_iteration4(vec4 coeffs, float x)
{
float f = (((x + coeffs[3]) * x + coeffs[2]) * x + coeffs[1]) * x + coeffs[0];
float f1 = ((4. * x + 3. * coeffs[3]) * x + 2. * coeffs[2]) * x + coeffs[1];
@ -477,7 +502,8 @@ float halley_iteration4(vec4 coeffs, float x){
// Modified from http://tog.acm.org/resources/GraphicsGems/gems/Roots3And4.c
// Credits to Doublefresh for hinting there
int solve_quartic(vec4 coeffs, inout vec4 s){
int solve_quartic(vec4 coeffs, inout vec4 s)
{
float a = coeffs[3];
float b = coeffs[2];
@ -529,17 +555,21 @@ int solve_quartic(vec4 coeffs, inout vec4 s){
float u = z * z - r;
float v = 2. * z - p;
if(u > -eps){
if (u > -eps)
{
u = sqrt(abs(u));
}
else{
else
{
return 0;
}
if(v > -eps){
if (v > -eps)
{
v = sqrt(abs(v));
}
else{
else
{
return 0;
}
@ -557,12 +587,15 @@ int solve_quartic(vec4 coeffs, inout vec4 s){
int old_num = num;
num += solve_quadric(quad_coeffs, tmp);
if(old_num!=num){
if(old_num == 0){
if (old_num != num)
{
if (old_num == 0)
{
s[0] = tmp[0];
s[1] = tmp[1];
}
else{//old_num == 2
else
{ // old_num == 2
s[2] = tmp[0];
s[3] = tmp[1];
}
@ -574,8 +607,10 @@ int solve_quartic(vec4 coeffs, inout vec4 s){
float sub = 1. / 4. * a;
/* single halley iteration to fix cancellation */
for(int i=0;i<4;i+=2){
if(i < num){
for (int i = 0; i < 4; i += 2)
{
if (i < num)
{
s[i] -= sub;
s[i] = halley_iteration4(coeffs, s[i]);
@ -586,7 +621,8 @@ int solve_quartic(vec4 coeffs, inout vec4 s){
return num;
}
float cubic_bezier_dis(vec2 uv, vec2 p0, vec2 p1, vec2 p2, vec2 p3, bool roundEnd){
float cubic_bezier_dis(vec2 uv, vec2 p0, vec2 p1, vec2 p2, vec2 p3, bool roundEnd)
{
// switch points when near to end point to minimize numerical error
// only needed when control point(s) very far away
@ -659,57 +695,71 @@ float cubic_bezier_dis(vec2 uv, vec2 p0, vec2 p1, vec2 p2, vec2 p3, bool roundEn
// compute root isolating intervals by roots of derivative and outer root bounds
// only roots going form - to + considered, because only those result in a minimum
if(num_roots_drv==4){
if(eval_poly5(b0,b1,b2,b3,b4,roots_drv[0]) > 0.){
if (num_roots_drv == 4)
{
if (eval_poly5(b0, b1, b2, b3, b4, roots_drv[0]) > 0.)
{
a[0] = lb;
b[0] = roots_drv[0];
num_roots = 1;
}
if(sign(eval_poly5(b0,b1,b2,b3,b4,roots_drv[1])) != sign(eval_poly5(b0,b1,b2,b3,b4,roots_drv[2]))){
if(num_roots == 0){
if (sign(eval_poly5(b0, b1, b2, b3, b4, roots_drv[1])) != sign(eval_poly5(b0, b1, b2, b3, b4, roots_drv[2])))
{
if (num_roots == 0)
{
a[0] = roots_drv[1];
b[0] = roots_drv[2];
num_roots = 1;
}
else{
else
{
a[1] = roots_drv[1];
b[1] = roots_drv[2];
num_roots = 2;
}
}
if(eval_poly5(b0,b1,b2,b3,b4,roots_drv[3]) < 0.){
if(num_roots == 0){
if (eval_poly5(b0, b1, b2, b3, b4, roots_drv[3]) < 0.)
{
if (num_roots == 0)
{
a[0] = roots_drv[3];
b[0] = ub;
num_roots = 1;
}
else if(num_roots == 1){
else if (num_roots == 1)
{
a[1] = roots_drv[3];
b[1] = ub;
num_roots = 2;
}
else{
else
{
a[2] = roots_drv[3];
b[2] = ub;
num_roots = 3;
}
}
}
else{
if(num_roots_drv==2){
if(eval_poly5(b0,b1,b2,b3,b4,roots_drv[0]) < 0.){
else
{
if (num_roots_drv == 2)
{
if (eval_poly5(b0, b1, b2, b3, b4, roots_drv[0]) < 0.)
{
num_roots = 1;
a[0] = roots_drv[1];
b[0] = ub;
}
else if(eval_poly5(b0,b1,b2,b3,b4,roots_drv[1]) > 0.){
else if (eval_poly5(b0, b1, b2, b3, b4, roots_drv[1]) > 0.)
{
num_roots = 1;
a[0] = lb;
b[0] = roots_drv[0];
}
else{
else
{
num_roots = 2;
a[0] = lb;
@ -718,9 +768,9 @@ float cubic_bezier_dis(vec2 uv, vec2 p0, vec2 p1, vec2 p2, vec2 p3, bool roundEn
a[1] = roots_drv[1];
b[1] = ub;
}
}
else{//num_roots_drv==0
else
{ // num_roots_drv==0
vec3 roots_snd_drv = vec3(1e38);
int num_roots_snd_drv = solve_cubic(c2, roots_snd_drv);
@ -741,31 +791,44 @@ float cubic_bezier_dis(vec2 uv, vec2 p0, vec2 p1, vec2 p2, vec2 p3, bool roundEn
int num_roots_trd_drv = 0;
vec2 roots_trd_drv = vec2(1e38);
if(num_roots_snd_drv!=3){
if (num_roots_snd_drv != 3)
{
num_roots_trd_drv = solve_quadric(c3, roots_trd_drv);
}
for(int i=0;i<3;i++){
if(i < num_roots){
for(int j=0;j<3;j+=2){
if(j < num_roots_snd_drv){
if(a[i] < roots_snd_drv[j] && b[i] > roots_snd_drv[j]){
if(eval_poly5(b0,b1,b2,b3,b4,roots_snd_drv[j]) > 0.){
for (int i = 0; i < 3; i++)
{
if (i < num_roots)
{
for (int j = 0; j < 3; j += 2)
{
if (j < num_roots_snd_drv)
{
if (a[i] < roots_snd_drv[j] && b[i] > roots_snd_drv[j])
{
if (eval_poly5(b0, b1, b2, b3, b4, roots_snd_drv[j]) > 0.)
{
b[i] = roots_snd_drv[j];
}
else{
else
{
a[i] = roots_snd_drv[j];
}
}
}
}
for(int j=0;j<2;j++){
if(j < num_roots_trd_drv){
if(a[i] < roots_trd_drv[j] && b[i] > roots_trd_drv[j]){
if(eval_poly5(b0,b1,b2,b3,b4,roots_trd_drv[j]) > 0.){
for (int j = 0; j < 2; j++)
{
if (j < num_roots_trd_drv)
{
if (a[i] < roots_trd_drv[j] && b[i] > roots_trd_drv[j])
{
if (eval_poly5(b0, b1, b2, b3, b4, roots_trd_drv[j]) > 0.)
{
b[i] = roots_trd_drv[j];
}
else{
else
{
a[i] = roots_trd_drv[j];
}
}
@ -779,15 +842,17 @@ float cubic_bezier_dis(vec2 uv, vec2 p0, vec2 p1, vec2 p2, vec2 p3, bool roundEn
// compute roots with halley's method
for(int i=0;i<3;i++){
if(i < num_roots){
for (int i = 0; i < 3; i++)
{
if (i < num_roots)
{
roots[i] = .5 * (a[i] + b[i]);
for(int j=0;j<halley_iterations;j++){
for (int j = 0; j < halley_iterations; j++)
{
roots[i] = halley_iteration5(b0, b1, b2, b3, b4, roots[i]);
}
// compute squared distance to nearest point on curve
if (roundEnd)
{
@ -797,21 +862,20 @@ float cubic_bezier_dis(vec2 uv, vec2 p0, vec2 p1, vec2 p2, vec2 p3, bool roundEn
}
else
{
if(roots[i]<0.||roots[i]>1.) d0=min(d0,1e38);
if (roots[i] < 0. || roots[i] > 1.)
d0 = min(d0, 1e38);
else
{
vec2 to_curve = uv - parametric_cub_bezier(roots[i], p0, p1, p2, p3);
d0 = min(d0, dot(to_curve, to_curve));
}
}
}
}
return sqrt(d0);
}
int cubic_bezier_int_test2(vec2 uv, vec2 p0, vec2 p1, vec2 p2, vec2 p3, bool reverse)
{
float cu = (-p0.y + 3. * p1.y - 3. * p2.y + p3.y);
@ -831,8 +895,10 @@ int cubic_bezier_int_test2(vec2 uv, vec2 p0, vec2 p1, vec2 p2, vec2 p3, bool rev
}
else
{
if (abs(cu) < .0001) n_roots = solve_quadric(vec2(co / qu, li / qu), roots.xy);
else n_roots = solve_cubic(vec3(co / cu, li / cu, qu / cu), roots);
if (abs(cu) < .0001)
n_roots = solve_quadric(vec2(co / qu, li / qu), roots.xy);
else
n_roots = solve_cubic(vec3(co / cu, li / cu, qu / cu), roots);
for (int i = 0; i < n_roots; i++)
{
@ -843,7 +909,8 @@ int cubic_bezier_int_test2(vec2 uv, vec2 p0, vec2 p1, vec2 p2, vec2 p3, bool rev
x_pos = x_pos * roots[i] + -3. * p0.x + 3. * p1.x;
x_pos = x_pos * roots[i] + p0.x;
if (reverse? x_pos < uv.x: x_pos > uv.x) n_ints++;
if (reverse ? x_pos < uv.x : x_pos > uv.x)
n_ints++;
}
}
}
@ -858,10 +925,12 @@ int ray_int_test(vec2 uv, vec2 p0, vec2 direction, bool reverse)
vec2 nor = -direction;
nor = vec2(nor.y, -nor.x);
float sgn = p0.y > direction.y ? 1. : -1.;
if(reverse) sgn = -sgn;
if (reverse)
sgn = -sgn;
return dot(nor, p0) * sgn < 0. ? 0 : 1;
}
else return 0;
else
return 0;
}
vec2 bezierTangent(float t, vec2 p0, vec2 p1, vec2 p2, vec2 p3)
@ -962,65 +1031,63 @@ void drawLine(in float d, in uint styleIndex, out vec4 elementColor, out vec2 me
}
}
bool drawElement(uint elementIndex, vec2 localUV, vec2 scale, out vec3 color, out vec2 metallicRoughness, inout vec3 debugBVH = vec3(0))
bool shouldFillBeginCap(vec2 localUV, bool onVeryBegin, int endType, vec2 p0, vec2 tangentBegin, vec2 tangentEndLast)
{
vec2 normal;
if (onVeryBegin)
{
if (endType % 2 == 0)
return true;
else if (endType % 2 == 1)
normal = normalize(mat2(0, 1, -1, 0) * (-tangentBegin));
}
else
{
vec2 normalLast = normalize(mat2(0, 1, -1, 0) * tangentEndLast);
vec2 normalNow = normalize(mat2(0, 1, -1, 0) * (-tangentBegin));
normal = normalLast + normalNow;
}
return angleLargeThanPi(normal, localUV - p0);
}
bool shouldFillEndCap(vec2 localUV, bool onVeryEnd, int endType, vec2 p3, vec2 tangentEnd, vec2 tangentBeginNext)
{
vec2 normal;
if (onVeryEnd)
{
if ((endType / 2) % 2 == 0)
return true;
else if ((endType / 2) % 2 == 1)
normal = normalize(mat2(0, 1, -1, 0) * tangentEnd);
}
else
{
vec2 normalLast = normalize(mat2(0, 1, -1, 0) * tangentEnd);
vec2 normalNow = normalize(mat2(0, 1, -1, 0) * (-tangentBeginNext));
normal = normalLast + normalNow;
}
return angleLargeThanPi(localUV - p3, normal);
}
bool fillElement(vec2 localUV, uint contourIndex, uint linesOffset, uint pointsOffset, uint styleIndex,
inout vec4 elementColor, inout vec2 metallicRoughness)
{
bool hitElement = false;
vec4 elementColor = vec4(-1);
metallicRoughness = vec2(0, 0.8);
uint currentOffset[] = elementOffset[elementIndex];
uint elementBvhRoot = currentOffset[0];
uint styleIndex = currentOffset[1];
uint elementBvhLength = 0x80000000;
uint pointsOffset = currentOffset[2];
uint linesOffset = currentOffset[3];
elementStack.top = 0;
uint elementBvhIndex = 0;
while (elementBvhIndex < elementBvhLength || !elementStack.empty())
{
while (elementBvhIndex < elementBvhLength)
{
vec4 bound = bvhBound[elementBvhRoot + elementBvhIndex];
uint leftChild = bvhChildren[elementBvhRoot + elementBvhIndex].x;
if (all(lessThan(bound.xy, localUV)) && all(lessThan(localUV, bound.zw)))
{
if (leftChild >= elementBvhLength)
{
if (any(greaterThan(bound.xy+vec2(0.003), localUV)) || any(greaterThan(localUV, bound.zw-vec2(0.003))))
{
debugBVH.g = 0;
debugBVH.r += 1;
}
//uint styleIndex = bvhChildren[elementBvhRoot + elementBvhIndex].y;
//uint elementType = bvhChildren[elementBvhRoot + elementBvhIndex].y;
//float elementType = elementData[styleIndex];
bool isFillStyle = elementData[styleIndex]<=0;
// for(int i = 0; i<200;i++)
if (isFillStyle) //Ãæ
{
uint contourIndex = linesOffset + leftChild - 0x80000000;
uint num_its = 0;
uint lineCount = elementIndexs[contourIndex];
for (uint contourIterator = contourIndex + 1; contourIterator < contourIndex + 1 + lineCount; contourIterator++)
{
uint lineIndex = elementIndexs[contourIterator];
uint pLocation = linesOffset + 2 * lineIndex;
uvec4 pxIndex = uvec4(pointsOffset)+2*uvec4(elementIndexs[pLocation]>>16, elementIndexs[pLocation]&0xFFFF, elementIndexs[pLocation+1]>>16, elementIndexs[pLocation+1]&0xFFFF);
uvec4 pxIndex =
uvec4(pointsOffset) + 2 * uvec4(elementIndexs[pLocation] >> 16, elementIndexs[pLocation] & 0xFFFF,
elementIndexs[pLocation + 1] >> 16, elementIndexs[pLocation + 1] & 0xFFFF);
uvec4 pyIndex = uvec4(1) + pxIndex;
mat4x2 p = mat4x2(elementData[pxIndex[0]], elementData[pyIndex[0]],
elementData[pxIndex[1]], elementData[pyIndex[1]],
elementData[pxIndex[2]],elementData[pyIndex[2]],
elementData[pxIndex[3]], elementData[pyIndex[3]]);
// vec2 p[4] = {vec2(elementData[pxIndex[0]], elementData[pyIndex[0]]),
// vec2(elementData[pxIndex[1]], elementData[pyIndex[1]]),
// vec2(elementData[pxIndex[2]],elementData[pyIndex[2]]),
// vec2(elementData[pxIndex[3]], elementData[pyIndex[3]])};
mat4x2 p =
mat4x2(elementData[pxIndex[0]], elementData[pyIndex[0]], elementData[pxIndex[1]], elementData[pyIndex[1]],
elementData[pxIndex[2]], elementData[pyIndex[2]], elementData[pxIndex[3]], elementData[pyIndex[3]]);
// if (bound.z == p[0].x && distance(localUV, p[3]) < 0.01)
// {
// debugBVH = vec3(0, 0, 1);
@ -1031,7 +1098,6 @@ bool drawElement(uint elementIndex, vec2 localUV, vec2 scale, out vec3 color, ou
// {
// num_its += segment_int_test(localUV, p0, p3);
// }
//
// else
num_its += cubic_bezier_int_test(localUV, p[0], p[1], p[2], p[3]);
}
@ -1047,48 +1113,70 @@ bool drawElement(uint elementIndex, vec2 localUV, vec2 scale, out vec3 color, ou
elementColor = vec4(unpackUnorm4x8(floatBitsToUint(elementData[styleIndex + 1])).rgb, 0);
metallicRoughness = head.xy;
}
}
return hitElement;
}
else //Ïß
bool strokeElement(vec2 localUV, uint contourIndex, uint linesOffset, uint pointsOffset, uint styleIndex,
inout vec4 elementColor, inout vec2 metallicRoughness)
{
bool hitElement = false;
float strokeWidth = elementData[styleIndex];
float widthHeightRatio = uintBitsToFloat(currentOffset[4]);
vec2 size = normalize(vec2(widthHeightRatio, 1)) + vec2(2 * strokeWidth);
vec2 ratio = widthHeightRatio < 1 ? vec2(widthHeightRatio, 1) : vec2(1, 1 / widthHeightRatio);
localUV *= ratio;
uint contourIndex = linesOffset + leftChild - 0x80000000;
float minDistance = 1e38;
uint lineCount = elementIndexs[contourIndex];
vec4 styleHead = unpackUnorm4x8(floatBitsToUint(elementData[styleIndex + 1]));
float lineType = floor(styleHead.b * 10);
//float lineType = 2;
int endType = int(round(styleHead.b * 100)) % 10;
vec2 p3Last = vec2(1e38);
vec2 p2Last = vec2(1e38);
vec2 tangentEndLast;
int debugBegin = 0;
for ( uint contourIterator_ = contourIndex + 1;contourIterator_ <= contourIndex + 1 + lineCount; contourIterator_++)
for (uint contourIterator_ = contourIndex + 1; contourIterator_ < contourIndex + 1 + lineCount; contourIterator_++)
{
uint contourIterator = contourIterator_;
if (contourIterator_ == contourIndex + 1 + lineCount)
contourIterator = contourIndex + 1;
uint lineIndex = elementIndexs[contourIterator];
uint pLocation = linesOffset + 3 * lineIndex;
uvec4 pxIndex = uvec4(pointsOffset)+2*uvec4(elementIndexs[pLocation]>>16, elementIndexs[pLocation]&0xFFFF, elementIndexs[pLocation+1]>>16, elementIndexs[pLocation+1]&0xFFFF);
vec2 percent = unpackUnorm2x16(elementIndexs[pLocation + 2]);
uvec4 pxIndex =
uvec4(pointsOffset) + 2 * uvec4(elementIndexs[pLocation] >> 16, elementIndexs[pLocation] & 0xFFFF,
elementIndexs[pLocation + 1] >> 16, elementIndexs[pLocation + 1] & 0xFFFF);
uvec4 pyIndex = uvec4(1) + pxIndex;
mat4x2 p = mat4x2(elementData[pxIndex[0]], elementData[pyIndex[0]],
elementData[pxIndex[1]], elementData[pyIndex[1]],
elementData[pxIndex[2]], elementData[pyIndex[2]],
mat4x2 p =
mat4x2(elementData[pxIndex[0]], elementData[pyIndex[0]], elementData[pxIndex[1]], elementData[pyIndex[1]],
elementData[pxIndex[2]], elementData[pyIndex[2]], elementData[pxIndex[3]], elementData[pyIndex[3]]);
vec2 tangentBeginNext = vec2(0);
if (contourIterator + 1 < contourIndex + 1 + lineCount)
{
uint lineIndex = elementIndexs[contourIterator + 1];
uint pLocation = linesOffset + 3 * lineIndex;
// vec2 percent = unpackUnorm2x16(elementIndexs[pLocation + 2]);
uvec4 pxIndex = uvec4(pointsOffset) +
2 * uvec4(elementIndexs[pLocation] >> 16, elementIndexs[pLocation] & 0xFFFF,
elementIndexs[pLocation + 1] >> 16, elementIndexs[pLocation + 1] & 0xFFFF);
uvec4 pyIndex = uvec4(1) + pxIndex;
mat4x2 pNext = mat4x2(elementData[pxIndex[0]], elementData[pyIndex[0]], elementData[pxIndex[1]],
elementData[pyIndex[1]], elementData[pxIndex[2]], elementData[pyIndex[2]],
elementData[pxIndex[3]], elementData[pyIndex[3]]);
p[0] *= ratio;
p[1] *= ratio;
p[2] *= ratio;
p[3] *= ratio;
if (pNext[0] == pNext[1] && pNext[2] == pNext[3])
{
pNext[1] = (pNext[0] + pNext[3]) / 2;
pNext[2] = pNext[1];
}
// if(pNext[0]!=p[3])
// break;
if (pNext[0] != pNext[1])
tangentBeginNext = normalize(pNext[0] - pNext[1]);
else
tangentBeginNext = normalize(pNext[0] - pNext[2]);
}
if (p[0] == p[1] && p[2] == p[3])
{
@ -1098,52 +1186,70 @@ bool drawElement(uint elementIndex, vec2 localUV, vec2 scale, out vec3 color, ou
if (distance(localUV, p[0]) <= 0.001)
{
if(p3Last==p[0]) debugBegin = 2;
else debugBegin = 1;
if (p3Last == p[0])
debugBegin = 2;
else
debugBegin = 1;
}
float d = cubic_bezier_dis(localUV, p[0], p[1], p[2], p[3], true);
if (d <= strokeWidth)
{
bool onBegin = distance(localUV,p[0])<=strokeWidth&&p3Last==p[0];
bool fill = true;
bool onBegin =
distance(localUV, p[0]) <= strokeWidth; //&& (p3Last == p[0] || contourIterator == contourIndex + 1);
bool onEnd = distance(localUV, p[3]) <= strokeWidth;
vec2 tangentBegin;
vec2 tangentEnd;
if (p[0] != p[1])
tangentBegin = normalize(p[0] - p[1]);
else
tangentBegin = normalize(p[0] - p[2]);
if (p[3] != p[2])
tangentEnd = normalize(p[3] - p[2]);
else
tangentEnd = normalize(p[3] - p[1]);
bool hit = d < minDistance;
if (onBegin)
hit = hit && shouldFillBeginCap(localUV, contourIterator == contourIndex + 1, endType, p[0],
tangentBegin, p3Last - p2Last);
if (onEnd)
hit = hit && shouldFillEndCap(localUV, tangentBeginNext == vec2(0), endType, p[3], tangentEnd,
tangentBeginNext);
if (hit)
{
vec2 normalLast = normalize(mat2(0,1,-1,0)*(p3Last-p2Last));
vec2 normalNow = normalize(mat2(0,1,-1,0)*(p[1]-p[0]));
vec2 normal = normalLast+normalNow;
fill = angleLargeThanPi(normal, localUV-p[0]);
}
if(onBegin?fill:d<minDistance)
{
minDistance = min(minDistance, d);
bool reverse = p[3].y - p[0].y < 0.;
vec2 tangentBegin = normalize(p[0]-p[1]);
vec2 tangentEnd = normalize(p[3]-p[2]);
if(tangentBegin.y==0.) tangentBegin.y=reverse?eps:-eps;
if(tangentEnd.y==0.) tangentEnd.y=reverse?-eps:eps;
int intTest = cubic_bezier_int_test2(localUV, p[0], p[1], p[2], p[3], reverse)
+ ray_int_test(localUV, p[0], tangentBegin, reverse)
+ ray_int_test(localUV, p[3], tangentEnd, reverse);
if(lineType==2 || intTest%2==int(lineType))
// if (tangentBegin.y == 0.)
// tangentBegin.y = reverse ? eps : -eps;
// if (tangentEnd.y == 0.)
// tangentEnd.y = reverse ? -eps : eps;
int intTest = cubic_bezier_int_test2(localUV, p[0], p[1], p[2], p[3], reverse) +
ray_int_test(localUV, p[0], tangentBegin, reverse) +
ray_int_test(localUV, p[3], tangentEnd, reverse);
if (lineType == 2 || (intTest % 2 == int(lineType)))
{
minDistance = min(minDistance, d);
hitElement = true;
// elementColor = vec4(1, 1, 0, 1);
vec2 metallicRoughness;
drawLine(minDistance / strokeWidth, styleIndex, elementColor, metallicRoughness);
}
else if(p3Last==p[0]) hitElement = false;
// if(distance(localUV,p[0])<=strokeWidth&&p3Last==p[0]&&fill)
// {
// hitElement = true;
// elementColor = vec4(0,0,1,1);
// }
// else if (p3Last == p[0])
// hitElement = false;
}
tangentEndLast = tangentEnd;
}
p3Last = p[3];
p2Last = p[2];
}
if (hitElement && distance(localUV, p3Last) <= strokeWidth)
{
// hitElement = shouldFillEndCap(localUV, percent[1] > 1 - 1e-5, endType, p3Last, tangentEndLast, vec2(0));
}
// if (minDistance <= 0.001)
// {
@ -1154,7 +1260,55 @@ bool drawElement(uint elementIndex, vec2 localUV, vec2 scale, out vec3 color, ou
// else if (debugBegin == 2)
// elementColor = vec4(0, 1, 0, 1);
// }
return hitElement;
}
bool drawElement(uint elementIndex, vec2 localUV, out vec3 color, out vec2 metallicRoughness,
inout vec3 debugBVH = vec3(0))
{
bool hitElement = false;
vec4 elementColor = vec4(-1);
metallicRoughness = vec2(0, 0.8);
uint currentOffset[] = elementOffset[elementIndex];
uint elementBvhRoot = currentOffset[0];
uint styleIndex = currentOffset[1];
uint pointsOffset = currentOffset[2];
uint linesOffset = currentOffset[3];
// float widthHeightRatio = uintBitsToFloat(currentOffset[4]);
elementStack.top = 0;
uint elementBvhIndex = 0;
uint elementBvhLength = 0x80000000;
while (elementBvhIndex < elementBvhLength || !elementStack.empty())
{
while (elementBvhIndex < elementBvhLength)
{
vec4 bound = bvhBound[elementBvhRoot + elementBvhIndex];
uint leftChild = bvhChildren[elementBvhRoot + elementBvhIndex].x;
if (all(lessThan(bound.xy, localUV)) && all(lessThan(localUV, bound.zw)))
{
if (leftChild >= elementBvhLength)
{
if (any(greaterThan(bound.xy + vec2(0.003), localUV)) ||
any(greaterThan(localUV, bound.zw - vec2(0.003))))
{
debugBVH.g = 0;
debugBVH.r += 1;
}
uint contourIndex = linesOffset + leftChild - 0x80000000;
bool isFillStyle = elementData[styleIndex] <= 0;
if (isFillStyle) // Ãæ
{
hitElement = fillElement(localUV, contourIndex, linesOffset, pointsOffset, styleIndex,
elementColor, metallicRoughness);
}
else // Ïß
{
hitElement = strokeElement(localUV, contourIndex, linesOffset, pointsOffset, styleIndex,
elementColor, metallicRoughness);
}
elementBvhIndex = elementBvhLength;
@ -1182,7 +1336,6 @@ bool drawElement(uint elementIndex, vec2 localUV, vec2 scale, out vec3 color, ou
return hitElement;
}
void main()
{
ivec2 pixelLocation = ivec2(pixelOffset + gl_GlobalInvocationID.xy);
@ -1190,12 +1343,13 @@ void main()
// imageStore(gBaseColor, pixelLocation, vec4(uv,1,1));
// imageStore(gMetallicRoughness, pixelLocation, vec4(uv,1,1));
// return;
uv = vec2(1)-uv*2;
uv = uv * 2 - vec2(1);
// vec2 uv = imageLoad(gPaintingTexCoord, pixelLocation).rg;
vec3 debugBVH = vec3(0);
// bool debugHit = false;
vec4 color = vec4(1,1,1,-1);
// vec4 color = vec4(0.76, 0.33, 0.15, -1);
vec4 color = vec4(backgroundColor, -1);
vec2 metallicRoughness = vec2(0, 0.8);
stack.top = 0;
uint index = 0, visitTime = 0;
@ -1208,47 +1362,40 @@ void main()
visitTime++;
vec4 bound = bvhBound[index];
uint leftChild = bvhChildren[index].x;
if (all(lessThan(bound.xy, uv)) && all(lessThan(uv, bound.zw)))
{
if (any(greaterThan(bound.xy + vec2(0.005), uv)) || any(greaterThan(uv, bound.zw - vec2(0.005))))
debugBVH.g += 0.3;
if (leftChild >= bvhLength)
{
uint transformIndex = leftChild - 0x80000000;
uint zIndex = bvhChildren[index].y >> 18;
bvec2 flip = bvec2(bvhChildren[index].y & (1<<16), bvhChildren[index].y & (1<<17));
float angle = (float(bvhChildren[index].y&((1<<16)-1)) / 65535.0) * 2 * PI;
mat2 rotation = {{cos(angle), -sin(angle)}, {sin(angle), cos(angle)}};
vec2 localUV = uv - (bound.xy + bound.zw) / 2;
localUV = rotation * localUV;
vec2 scale = (bound.zw - bound.xy) / 2;
localUV /= scale;
if (all(lessThan(vec2(-1), localUV)) && all(lessThan(localUV, vec2(1))) && zIndex>color.w)
{
//if (any(greaterThan(bound.xy+vec2(0.005), uv)) || any(greaterThan(uv, bound.zw-vec2(0.005))))
if (any(greaterThan(vec2(-1)+vec2(0.005), localUV)) || any(greaterThan(localUV, vec2(1)-vec2(0.005))))
debugBVH.g += 0.3;
//uint elementIndex = leftChild - bvhLength;
//debugBVH.bg += 0.5 * (localUV + vec2(1));
uint elementIndex = bvhChildren[index].y - zIndex;
mat3x2 transform = elementTranform[transformIndex];
vec2 localUV =
(mat3(vec3(transform[0], 0), vec3(transform[1], 0), vec3(transform[2], 1)) * vec3(uv, 1)).xy;
//debugBVH = vec3(0);
if(flip.x) localUV.x = -localUV.x;
if(flip.y) localUV.y = -localUV.y;
vec3 elementColor;
vec2 elementMetallicRoughness;
if(drawElement(leftChild - 0x80000000, localUV, scale, elementColor, elementMetallicRoughness, debugBVH))
if (drawElement(elementIndex, localUV, elementColor, elementMetallicRoughness,
debugBVH))
{
color = vec4(elementColor, zIndex);
metallicRoughness = elementMetallicRoughness;
}
}
//if(elementIndex == 1 && transformIndex==1)
// color = vec4(1,1,0,1);
index = bvhLength;
}
else if (all(lessThan(bound.xy, uv)) && all(lessThan(uv, bound.zw)))
else
{
if (any(greaterThan(bound.xy+vec2(0.005), uv)) || any(greaterThan(uv, bound.zw-vec2(0.005))))
debugBVH.g += 0.3;
//debugBVH.r += 0.02;
stack.push(index);
index = leftChild;
}
}
else
index = bvhLength;
}
@ -1272,5 +1419,4 @@ void main()
imageStore(gBaseColor, pixelLocation, vec4(debugBVH, 1));
imageStore(gMetallicRoughness, pixelLocation, vec4(0, 0.8, 0, 1));
}
}

View File

@ -36,11 +36,6 @@ void main()
lod++;
gMetallicRoughness = mt.rg;
// int pageSize = textureSize(texture_basecolor, levels-1).x;
// uint pageId = 0;
// for(uint i = 0; i < lodExpect; i++)
// pageId += 1<<(2*(levels-1-i));
uint w = 1<<(levels-1-lodExpect);
ivec2 page = ivec2(TexCoords * vec2(w));
page = clamp(page, ivec2(0), ivec2(w-1));
@ -49,7 +44,6 @@ void main()
gPaintingIndex = uvec2(0);
else
gPaintingIndex = uvec2((paintingId<<4)+lodExpect, (page.y<<8)+page.x);
gPaintingTexCoord = vec2(1., 1.) - TexCoords * 2;
return;
}

View File

@ -1,14 +0,0 @@
//{{NO_DEPENDENCIES}}
// Microsoft Visual C++ generated include file.
// Used by ArchitectureColoredPainting.rc
// 新对象的下一组默认值
//
#ifdef APSTUDIO_INVOKED
#ifndef APSTUDIO_READONLY_SYMBOLS
#define _APS_NEXT_RESOURCE_VALUE 101
#define _APS_NEXT_COMMAND_VALUE 40001
#define _APS_NEXT_CONTROL_VALUE 1001
#define _APS_NEXT_SYMED_VALUE 101
#endif
#endif

View File

@ -1,261 +0,0 @@
/*
The MIT License (MIT)
Copyright © 2018-2022 Antonio Dias
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
*/
#include "CaptionButton.h"
CaptionButton::CaptionButton(QWidget *parent) : QWidget(parent)
{
m_is_active = false;
m_is_under_mouse = false;
m_is_pressed = false;
m_icon_dark = false;
setAttribute(Qt::WA_Hover);
}
CaptionButton::~CaptionButton()
{
}
QPixmap CaptionButton::drawIcon(const QPixmap &icon, bool active, bool force_light)
{
QImage tmp = icon.toImage();
if (!active)
{
for (int i = 0; i < tmp.height(); i++)
{
for (int j = 0; j < tmp.width(); j++)
{
QColor pixel = QColor::fromRgba(tmp.pixel(j, i));
pixel.setRedF(pixel.redF() * 0.5f);
pixel.setGreenF(pixel.greenF() * 0.5f);
pixel.setBlueF(pixel.blueF() * 0.5f);
tmp.setPixel(j, i, pixel.rgba());
}
}
}
if (m_icon_dark && !force_light)
tmp.invertPixels();
return QPixmap::fromImage(tmp);
}
void CaptionButton::init(IconType type)
{
m_type = type;
setColors();
drawIcons();
}
void CaptionButton::drawIcons()
{
switch (m_type)
{
case IconType::Minimize:
{
QPixmap icon = QPixmap(":/images/icon_window_minimize.png");
m_active_icon = drawIcon(icon, true);
m_inactive_icon = drawIcon(icon, false);
break;
}
case IconType::Restore:
{
QPixmap icon = QPixmap(":/images/icon_window_restore.png");
m_active_icon = drawIcon(icon, true);
m_inactive_icon = drawIcon(icon, false);
break;
}
case IconType::Maximize:
{
QPixmap icon = QPixmap(":/images/icon_window_maximize.png");
m_active_icon = drawIcon(icon, true);
m_inactive_icon = drawIcon(icon, false);
break;
}
case IconType::Close:
{
QPixmap icon = QPixmap(":/images/icon_window_close.png");
m_active_icon = drawIcon(icon, true);
m_inactive_icon = drawIcon(icon, false);
m_close_icon_hover = drawIcon(icon, true, true);
break;
}
}
}
void CaptionButton::setColors()
{
if (m_icon_dark)
{
if (m_type == IconType::Close)
{
m_normal = QColor("transparent");
m_hover = QColor("#F00000");
m_pressed = QColor("#F1707A");
}
else
{
m_normal = QColor("transparent");
m_hover = QColor("#E5E5E5");
m_pressed = QColor("#CACACB");
}
}
else
{
if (m_type == IconType::Close)
{
m_normal = QColor("transparent");
m_hover = QColor("#F00000");
m_pressed = QColor("#F1707A");
}
else
{
m_normal = QColor("transparent");
m_hover = QColor("#505050");
m_pressed = QColor("#3F3F3F");
}
}
repaint();
}
void CaptionButton::setIconMode(bool icon_dark)
{
m_icon_dark = icon_dark;
drawIcons();
setColors();
repaint();
}
void CaptionButton::setActive(bool is_active)
{
m_is_active = is_active;
repaint();
}
void CaptionButton::setState(int state)
{
switch (state)
{
case QEvent::HoverEnter:
{
m_is_under_mouse = true;
repaint();
break;
}
case QEvent::HoverLeave:
{
m_is_under_mouse = false;
repaint();
break;
}
case QEvent::MouseButtonPress:
{
m_is_pressed = true;
m_is_under_mouse = true;
repaint();
break;
}
case QEvent::MouseButtonRelease:
{
m_is_pressed = false;
m_is_under_mouse = false;
repaint();
break;
}
default:
break;
}
}
void CaptionButton::paintEvent(QPaintEvent *event)
{
Q_UNUSED(event)
QPixmap current_icon = m_active_icon;
QColor current_color = m_normal;
//Change icon if needed
if (m_is_under_mouse)
{
if (m_type == IconType::Close)
current_icon = m_close_icon_hover;
}
else
{
if (!m_is_active)
current_icon = m_inactive_icon;
}
//Change background color if needed
if (m_is_pressed)
{
if (m_is_under_mouse)
current_color = m_pressed;
}
else
{
if (m_is_under_mouse)
current_color = m_hover;
}
QPainter painter(this);
painter.setRenderHints(QPainter::Antialiasing | QPainter::SmoothPixmapTransform);
painter.setCompositionMode(QPainter::CompositionMode_SourceOver);
painter.fillRect(rect(), current_color);
QRect target_rect;
target_rect = current_icon.rect();
target_rect.setSize(QSize(16, 16));
target_rect = QRect(rect().center() - target_rect.center(), target_rect.size());
painter.drawPixmap(target_rect, current_icon);
}

View File

@ -1,81 +0,0 @@
/*
The MIT License (MIT)
Copyright © 2018-2022 Antonio Dias
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
*/
#ifndef CAPTIONBUTTON_H
#define CAPTIONBUTTON_H
#include <QtCore>
#include <QtGui>
#include <QtWidgets>
class CaptionButton : public QWidget
{
Q_OBJECT
public:
explicit CaptionButton(QWidget *parent = nullptr);
~CaptionButton();
enum class IconType
{
Minimize,
Restore,
Maximize,
Close
};
void init(IconType type);
signals:
void clicked();
public slots:
void setIconMode(bool icon_dark);
void setActive(bool is_active);
void setState(int state);
private:
//Functions
QPixmap drawIcon(const QPixmap &icon, bool active, bool force_light = false);
void setColors();
void drawIcons();
void paintEvent(QPaintEvent *event);
//Variables
QPixmap m_inactive_icon;
QPixmap m_active_icon;
QPixmap m_close_icon_hover;
QColor m_normal;
QColor m_hover;
QColor m_pressed;
IconType m_type;
bool m_is_active;
bool m_is_under_mouse;
bool m_is_pressed;
bool m_icon_dark;
};
#endif // CAPTIONBUTTON_H

View File

@ -0,0 +1,49 @@
#pragma once
#include <lib/qtmaterialstyle.h>
#include <lib/qtmaterialtheme.h>
#include <QColorDialog>
class ColorHelper
{
QtMaterialTheme theme;
QColor primary1;
public:
void setPrimary1(const QColor& color)
{
theme.setColor("primary1", color);
primary1 = color;
}
[[nodiscard]] QColor getPrimary1() const
{
return primary1;
}
ColorHelper()
{
setPrimary1(QColor::fromRgb(0, 90, 158));
QtMaterialStyle::instance().setTheme(&theme);
}
static ColorHelper& instance()
{
static ColorHelper instance;
return instance;
}
static QColor execColorDialog(
const QColor& initial = instance().getPrimary1(),
QWidget* parent = nullptr,
const QString& title = ""
) {
auto dialog = QColorDialog(initial, parent);
if (!title.isEmpty())
{
dialog.setWindowTitle(title);
}
dialog.adjustSize();
dialog.exec();
return dialog.selectedColor();
}
};

View File

@ -0,0 +1,38 @@
#include "ProjectDataManager.h"
using namespace std;
ProjectDataManager* ProjectDataManager::instance = nullptr;
ProjectDataManager* ProjectDataManager::Instance()
{
if (instance == nullptr)
instance = new ProjectDataManager();
return instance;
}
void ProjectDataManager::addProjectData(ProjectData data)
{
projectDataList.push_back(data);
}
void ProjectDataManager::setZoom(double x, double y, EditorWidgetItem* item)
{
for (auto& data : projectDataList) {
if (data.item == item) {
data.zoomX = x;
data.zoomY = y;
return;
}
}
}
QPointF ProjectDataManager::getZoomByPainterDeivce(QPaintDevice* device)
{
for (auto& data : projectDataList) {
if (data.item->previewWindow == device) {
return QPointF(data.zoomX, data.zoomY);
}
}
return QPointF(1, 1);
}

View File

@ -0,0 +1,30 @@
#pragma once
#include <map>
#include <QPaintDevice>
#include <QFile>
#include <vector>
#include "EditorWidgetItem.h"
struct ProjectData
{
int width, height;
double zoomX, zoomY;
QString fileHome;
EditorWidgetItem* item;
};
class ProjectDataManager
{
private:
std::vector<ProjectData> projectDataList;
ProjectDataManager() = default;
~ProjectDataManager() = default;
static ProjectDataManager* instance;
public:
static ProjectDataManager* Instance();
void addProjectData(ProjectData data);
void setZoom(double x, double y, EditorWidgetItem* item);
QPointF getZoomByPainterDeivce(QPaintDevice* device);
};

View File

@ -1,62 +1,57 @@
#include "EditorWidget.h"
#include "EditorWidgetItem.h"
#include <QMouseEvent>
#include <QInputDialog>
#include <QMenu>
#include <QFileDialog>
EditorWidget::EditorWidget(QWidget* parent) : QWidget(parent)
{
QImage x;
displayLayer = nullptr;
displayElement = nullptr;
ui.setupUi(this);
previewWindow = ui.Preview;
treeWidget = ui.LayerTree;
tabWidget = ui.DisplayTab;
layerInfoDisplayWidget = dynamic_cast<InfoDisplayWidget *>(tabWidget->widget(0));
elementInfoDisplayWidget = dynamic_cast<InfoDisplayWidget *>(tabWidget->widget(1));
connect(treeWidget, &LayerTreeWidget::displayLayerChange, this, &EditorWidget::onLayerChange);
connect(layerInfoDisplayWidget, &InfoDisplayWidget::requireRefreshPreview, this,
&EditorWidget::triggerRefreshPreview);
connect(treeWidget, &LayerTreeWidget::requireRefreshPreview, this,
&EditorWidget::triggerRefreshPreview);
connect(layerInfoDisplayWidget, &InfoDisplayWidget::requireSelfRefresh, layerInfoDisplayWidget, &InfoDisplayWidget::triggerSelfRefresh);
// &EditorWidget::triggerRefreshPreview);
// test
QFile settingFile;
settingFile.setFileName("../data.json");
settingFile.open(QFile::ReadOnly);
QByteArray setting = settingFile.readAll().trimmed();
QJsonParseError jError;
QJsonDocument jsonDoc(QJsonDocument::fromJson(setting, &jError));
qDebug() << jsonDoc.object().value("height").toDouble();
qDebug() << jError.errorString();
// end test
QJsonObject source = jsonDoc.object();
elementManager = new ElementManager(source,previewWindow->getRenderer());
layerManager = new LayerManager(source, elementManager);
previewWindow->initialize(layerManager,QSize(jsonDoc.object().value("width").toDouble(),jsonDoc.object().value("height").toDouble()));
if (layerManager->getRoot() != nullptr)
this->createButton = ui.createButton;
this->closeButton = ui.closeButton;
this->saveButton = ui.saveButton;
this->saveAsButton = ui.saveAsButton;
this->openButton = ui.openButton;
this->tabWidget = ui.tabWidget;
while (this->tabWidget->count() > 0)
{
treeWidget->root = layerManager->getRoot();
treeWidget->refresh();
treeWidget->addTopLevelItem(treeWidget->root->getQTreeItem());
this->tabWidget->removeTab(0);
}
connect(this->createButton, &QPushButton::clicked, this, [this]() {
static int count = 0;
this->tabWidget->addTab(new EditorWidgetItem("../data.json",this), "untitled" + QString::number(count++));
});
connect(this->openButton, &QPushButton::clicked, this, [this]() {
QString fileName = QFileDialog::getOpenFileName(this->saveAsButton, QString::fromLocal8Bit("´ò¿ª"), "", QString::fromLocal8Bit("JSONÎļþ(*.json)"));
if(!fileName.isEmpty())
this->tabWidget->addTab(new EditorWidgetItem(fileName, this), fileName);
});
connect(this->closeButton, &QPushButton::clicked, this, [this]() {
this->tabWidget->removeTab(this->tabWidget->currentIndex());
});
connect(this->saveButton, &QPushButton::clicked, this, [this]() {
EditorWidgetItem* item = dynamic_cast<EditorWidgetItem*>(this->tabWidget->currentWidget());
if (item != nullptr)
{
//item->save();
}
});
connect(this->saveAsButton, &QPushButton::clicked, this, [this]() {
EditorWidgetItem* item = dynamic_cast<EditorWidgetItem*>(this->tabWidget->currentWidget());
if (item != nullptr)
{
QString fileName = QFileDialog::getSaveFileName(this->saveAsButton, QString::fromLocal8Bit("Áí´æΪ"), "", QString::fromLocal8Bit("JSONÎļþ(*.json)"));
item->saveAs(fileName);
}
});
}
EditorWidget::~EditorWidget()
void EditorWidget::renameTab(QWidget* target, QString name)
{
}
void EditorWidget::paintEvent(QPaintEvent *event)
int index = this->tabWidget->indexOf(target);
if (index != -1)
{
this->tabWidget->setTabText(index, name);
}
void EditorWidget::onLayerChange(LayerWrapper *layer)
{
displayLayer = layer;
// TODO : notify InfoDisplayWidget and update
dynamic_cast<InfoDisplayWidget *>(tabWidget->widget(0))->setLayer(layer);
this->update();
}
void EditorWidget::triggerRefreshPreview()
{
previewWindow->update();
}

View File

@ -1,39 +1,23 @@
#pragma once
#include "ElementManager.h"
#include "InfoDisplayWidget.h"
#include "LayerManager.h"
#include "LayerTreeWidget.h"
#include "PreviewWindow.h"
#include <qwidget.h>
#include "ui_EditorWidget.h"
#include <QPainter>
#include <QTreeWidget>
#include <QWidget>
class EditorWidget : public QWidget
class EditorWidget :
public QWidget
{
Q_OBJECT
private:
// DATA PART
PreviewWindow *previewWindow;
ElementManager *elementManager;
LayerManager *layerManager;
// QT GUI PART
Ui::EditorWidgetClass ui;
LayerTreeWidget *treeWidget;
Ui::EditorWidget ui;
QTabWidget* tabWidget;
InfoDisplayWidget *layerInfoDisplayWidget, *elementInfoDisplayWidget;
// QT DATA PART
LayerWrapper *displayLayer;
GraphicElement *displayElement;
QPushButton* createButton;
QPushButton* closeButton;
QPushButton* saveButton;
QPushButton* saveAsButton;
QPushButton* openButton;
public:
EditorWidget(QWidget* parent = nullptr);
~EditorWidget();
void paintEvent(QPaintEvent *event) override;
private slots:
void onLayerChange(LayerWrapper *layer);
void triggerRefreshPreview();
~EditorWidget()=default;
void renameTab(QWidget* target, QString name);
};

View File

@ -0,0 +1,35 @@
#include "ColorPicker.h"
QString getStyleSheet(const QColor& color)
{
return
"QPushButton#colorPicker {"
" border-style: solid;"
" border-width: 1px;"
" border-color: black;"
" background-color: " + color.name() + ";"
"}";
}
ColorPicker::ColorPicker(const QColor& color, QWidget* parent) : QPushButton(parent), color(color)
{
this->setObjectName("colorPicker");
this->setStyleSheet(getStyleSheet(color));
connect(this, &QPushButton::clicked, this, &ColorPicker::onClicked);
}
QColor ColorPicker::getColor() const
{
return color;
}
void ColorPicker::onClicked()
{
const QColor newColor = ColorHelper::execColorDialog(this->color, this);
if (newColor.isValid() && this->color != newColor)
{
this->color = newColor;
this->setStyleSheet(getStyleSheet(newColor));
emit colorChanged(newColor);
}
}

View File

@ -0,0 +1,17 @@
#pragma once
#include "../ColorHelper.hpp"
#include <QPushButton>
class ColorPicker : public QPushButton
{
Q_OBJECT
private:
QColor color;
public:
ColorPicker(const QColor& color = ColorHelper::instance().getPrimary1(), QWidget* parent = nullptr);
QColor getColor() const;
public slots:
void onClicked();
signals:
void colorChanged(QColor newColor);
};

View File

@ -0,0 +1,48 @@
#include "FillStyleWidget.h"
#include "ColorPicker.h"
#include <QGridLayout>
#include <QLabel>
#include <qtmaterialtextfield.h>
FillStyleWidget::FillStyleWidget(std::shared_ptr<MaterialStyleFill> fill, QWidget* parent)
: QWidget(parent), fill(fill)
{
auto* layout = new QGridLayout(this);
this->setLayout(layout);
// ÑÕÉ«
auto* material = &plainFill(fill)->material;
auto* colorLabel = new QLabel(QStringLiteral("ÑÕÉ«"), this);
layout->addWidget(colorLabel, 0, 0);
auto* colorPicker = new ColorPicker(material->color);
connect(colorPicker, &ColorPicker::colorChanged,
[material](QColor color)
{
material->color = color;
});
layout->addWidget(colorPicker, 0, 1);
// ½ðÊô¶È
auto* metallicLabel = new QLabel(QStringLiteral("½ðÊô¶È"), this);
layout->addWidget(metallicLabel, 1, 0);
auto* metallicInput = new QtMaterialTextField(this);
metallicInput->setText(QString::number(material->metallicF(), 'g', 3));
connect(metallicInput, &QtMaterialTextField::textChanged,
[material](const QString& text)
{
material->setMetallicF(text.toFloat());
});
layout->addWidget(metallicInput, 1, 1);
// ´Ö²Ú¶È
auto* roughnessLabel = new QLabel(QStringLiteral("´Ö²Ú¶È"), this);
layout->addWidget(roughnessLabel, 2, 0);
auto* roughnessInput = new QtMaterialTextField(this);
roughnessInput->setText(QString::number(material->roughnessF(), 'g', 3));
connect(roughnessInput, &QtMaterialTextField::textChanged,
[material](const QString& text)
{
material->setRoughnessF(text.toFloat());
});
layout->addWidget(roughnessInput, 2, 1);
}

View File

@ -0,0 +1,12 @@
#pragma once
#include "../Renderer/Painting/MaterialStyleFill.h"
#include "LayerStyle.h"
#include <QWidget>
class FillStyleWidget : public QWidget
{
Q_OBJECT
public:
FillStyleWidget(std::shared_ptr<Renderer::MaterialStyleFill> fill, QWidget* parent = nullptr);
std::shared_ptr<Renderer::MaterialStyleFill> fill;
};

View File

@ -0,0 +1,99 @@
#include "LayerCreateWidget.h"
#include <QComboBox>
LayerCreateWidget::LayerCreateWidget(ElementManager* elementManager, FolderLayerWrapper* folderLayer, QWidget* parent) :
QDialog(parent)
{
this->elementManager = elementManager;
ui.setupUi(this);
ui.name->setPlaceholderText(QString::fromLocal8Bit(""));
ui.name->setText(QString::fromLocal8Bit("×Óͼ²ã-") + QString::number(folderLayer->children.size() + 1));
connect(ui.buttonBox, SIGNAL(accepted()), this, SLOT(accept()));
connect(ui.buttonBox, SIGNAL(rejected()), this, SLOT(reject()));
connect(ui.comboBox, SIGNAL(currentIndexChanged(int)), this, SLOT(onCurrentIndexChanged(int)));
elementPool = new ElementPoolWidget(ui.elementPool);
elements = elementManager->elements;
elementsCheck(folderLayer);
elementPool->setElementList(elements);
}
void LayerCreateWidget::elementsCheck(FolderLayerWrapper* parent)
{
std::set<LayerWrapper*> upSet, downSet;
parent->collectUpReachable(upSet);
for (auto it = elements.begin(); it != elements.end();)
{
bool valid = true;
auto ele = dynamic_cast<GroupElement*>(*it);
if (ele != nullptr)
{
downSet.clear();
ele->collectReachable(downSet);
for (auto& layer : downSet)
{
if (upSet.find(layer) != upSet.end())
{
valid = false;
break;
}
}
}
if (valid)
++it;
else
it = elements.erase(it);
}
}
LayerCreateWidget::~LayerCreateWidget()
{
}
void LayerCreateWidget::accept()
{
QJsonObject jsonObj;
jsonObj.insert("name", ui.name->text());
if (ui.comboBox->currentIndex() == 0) {
auto currentEle = elements[elementPool->currentIndex];
int index = -1;
for (int i = 0; i < elementManager->elements.size(); i++)
{
if (elementManager->elements[i] == currentEle)
{
index = i;
break;
}
}
if (index == -1)
return;
jsonObj.insert("is-folder", false);
jsonObj.insert("element", index);
}
else {
jsonObj.insert("is-folder", true);
jsonObj.insert("children", QJsonArray());
}
QJsonObject transform;
QJsonObject offset;
QJsonObject scale;
scale.insert("x", 1);
scale.insert("y", 1);
offset.insert("x", 0);
offset.insert("y", 0);
transform.insert("offset", offset);
transform.insert("scale", scale);
transform.insert("rotation", 0);
jsonObj.insert("transform", transform);
jsonObj.insert("referenced-by", QJsonValue());
emit LayerInfoReturned(jsonObj);
QDialog::accept();
}
void LayerCreateWidget::onCurrentIndexChanged(int index) {
if (index == 0) {// leaf layer
elementPool->setVisible(true);
}
else {// folder layer
elementPool->setVisible(false);
}
}

View File

@ -0,0 +1,32 @@
#pragma once
#include <QDialog>
#include <QJsonObject>
#include "ElementPoolWidget.h"
#include "ElementManager.h"
#include "ui_LayerCreateWidget.h"
class LayerCreateWidget :
public QDialog
{
Q_OBJECT
private:
ElementManager* elementManager;
std::vector<GraphicElement*> elements;
Ui::LayerCreateWidget ui;
ElementPoolWidget* elementPool;
void elementsCheck(FolderLayerWrapper*);
public:
LayerCreateWidget(ElementManager* elementManager, FolderLayerWrapper* folderLayer, QWidget* parent = nullptr);
~LayerCreateWidget();
void accept() override;
public slots:
void onCurrentIndexChanged(int index);
signals:
void LayerInfoReturned(QJsonObject jsonObj);
};

View File

@ -0,0 +1,124 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>LayerCreateWidget</class>
<widget class="QWidget" name="LayerCreateWidget">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>326</width>
<height>355</height>
</rect>
</property>
<property name="windowTitle">
<string>Form</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout_2">
<item>
<layout class="QVBoxLayout" name="verticalLayout" stretch="0,1,5,1,0">
<item>
<widget class="QComboBox" name="comboBox">
<property name="maximumSize">
<size>
<width>90</width>
<height>16777215</height>
</size>
</property>
<property name="currentIndex">
<number>0</number>
</property>
<property name="maxVisibleItems">
<number>2</number>
</property>
<property name="maxCount">
<number>2</number>
</property>
<property name="insertPolicy">
<enum>QComboBox::NoInsert</enum>
</property>
<property name="sizeAdjustPolicy">
<enum>QComboBox::AdjustToContents</enum>
</property>
<item>
<property name="text">
<string>叶子节点</string>
</property>
</item>
<item>
<property name="text">
<string>组合节点</string>
</property>
</item>
</widget>
</item>
<item>
<widget class="QWidget" name="widget" native="true">
<layout class="QHBoxLayout" name="horizontalLayout_2">
<property name="leftMargin">
<number>0</number>
</property>
<property name="topMargin">
<number>0</number>
</property>
<property name="rightMargin">
<number>0</number>
</property>
<property name="bottomMargin">
<number>0</number>
</property>
<item>
<layout class="QHBoxLayout" name="horizontalLayout">
<property name="leftMargin">
<number>11</number>
</property>
<property name="rightMargin">
<number>11</number>
</property>
<item>
<widget class="QLabel" name="label">
<property name="text">
<string>图层名:</string>
</property>
</widget>
</item>
<item>
<widget class="QLineEdit" name="name"/>
</item>
</layout>
</item>
</layout>
</widget>
</item>
<item>
<widget class="QWidget" name="elementPool" native="true"/>
</item>
<item>
<widget class="QDialogButtonBox" name="buttonBox">
<property name="standardButtons">
<set>QDialogButtonBox::Close|QDialogButtonBox::Ok</set>
</property>
<property name="centerButtons">
<bool>false</bool>
</property>
</widget>
</item>
<item>
<spacer name="horizontalSpacer">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>40</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
</layout>
</item>
</layout>
</widget>
<resources/>
<connections/>
</ui>

View File

@ -0,0 +1,71 @@
#include "LayerStyleDialog.h"
#include <QComboBox>
#include <QDialogButtonBox>
#include <QGridLayout>
LayerStyleDialog::LayerStyleDialog(
LayerStyleContainer& styles,
const std::shared_ptr<LayerStyle>& existedStyle,
QWidget* parent
) : QDialog(parent), styles(&styles)
{
dialogLayout = new QGridLayout;
dialogLayout->setAlignment(Qt::AlignmentFlag::AlignHCenter);
this->setLayout(dialogLayout);
if (existedStyle)
{
this->modifyingStyle = existedStyle->clone();
this->styleWidget = modifyingStyle->getInputWidget();
this->styleWidget->setParent(this);
dialogLayout->addWidget(styleWidget, 1, 0);
}
else
{
QStringList unusedStyleNames = styles.unusedStyleNames();
auto* typeSelector = new QComboBox(this);
if (!unusedStyleNames.empty())
{
typeSelector->addItems(unusedStyleNames);
this->modifyingStyle = std::move(styles.makeUnusedStyle(unusedStyleNames[0]));
dialogLayout->addWidget(typeSelector, 0, 0);
this->styleWidget = this->modifyingStyle->getInputWidget();
this->styleWidget->setParent(this);
this->dialogLayout->addWidget(styleWidget, 1, 0);
connect(typeSelector, &QComboBox::currentTextChanged,
this, &LayerStyleDialog::onStyleTypeSelectorChanged);
}
}
auto* buttonBox = new QDialogButtonBox(QDialogButtonBox::Ok | QDialogButtonBox::Cancel, this);
connect(buttonBox, &QDialogButtonBox::accepted, this, &LayerStyleDialog::accept);
connect(buttonBox, &QDialogButtonBox::rejected, this, &LayerStyleDialog::reject);
dialogLayout->addWidget(buttonBox, 2, 0);
this->adjustSize();
}
void LayerStyleDialog::accept()
{
this->layerStyle = std::move(this->modifyingStyle);
QDialog::accept();
}
void LayerStyleDialog::onStyleTypeSelectorChanged(const QString& current)
{
if (this->styleWidget)
{
this->dialogLayout->removeWidget(this->styleWidget);
this->styleWidget->setParent(nullptr);
delete styleWidget;
}
this->modifyingStyle = std::move(styles->makeUnusedStyle(current));
this->styleWidget = this->modifyingStyle->getInputWidget();
this->styleWidget->setParent(this);
this->dialogLayout->addWidget(styleWidget, 1, 0);
this->styleWidget->adjustSize();
this->adjustSize();
}

View File

@ -0,0 +1,25 @@
#pragma once
#include "LayerStyle.h"
#include <QDialog>
#include <QGridLayout>
class LayerStyleDialog : public QDialog
{
Q_OBJECT
private:
QWidget* styleWidget;
QGridLayout* dialogLayout;
LayerStyleContainer* styles;
std::unique_ptr<LayerStyle> modifyingStyle;
public:
LayerStyleDialog(
LayerStyleContainer& styles,
const std::shared_ptr<LayerStyle>& existedStyle = nullptr,
QWidget* parent = nullptr
);
std::shared_ptr<LayerStyle> layerStyle;
private slots:
void onStyleTypeSelectorChanged(const QString& current);
void accept() override;
};

View File

@ -0,0 +1,108 @@
#include "StrokeStyleListView.h"
#include "ColorPicker.h"
#include <qtmaterialraisedbutton.h>
constexpr int COLUMN_WIDTH = 0;
constexpr int COLUMN_COLOR = 1;
constexpr int COLUMN_METALLIC = 2;
constexpr int COLUMN_ROUGHNESS = 3;
constexpr int COLUMN_OPERATIONS = 4;
// TODO: 将这个类改为继承QWidget把table转为其中的一个元素加上新增行按钮和宽度设置
StrokeStyleListView::StrokeStyleListView(
std::shared_ptr<Renderer::StrokeRadialGradient> stroke,
QWidget* parent
) : QTableWidget(parent), stroke(stroke)
{
this->setSizeAdjustPolicy(QAbstractScrollArea::AdjustToContentsOnFirstShow);
this->setColumnCount(5);
this->setRowCount(stroke->materialMap.size());
QStringList headers;
headers.append(QStringLiteral("离心距离占比"));
headers.append(QStringLiteral("颜色"));
headers.append(QStringLiteral("金属度"));
headers.append(QStringLiteral("粗糙度"));
headers.append(QStringLiteral("其他操作"));
this->setHorizontalHeaderLabels(headers);
for (int row = 0; auto& strokePair : stroke->materialMap)
{
QTableWidgetItem* widthItem = new QTableWidgetItem;
widthItem->setData(Qt::EditRole, strokePair.first);
this->setItem(row, COLUMN_WIDTH, widthItem);
QColor* colorPtr = &(strokePair.second.color);
QTableWidgetItem* colorItem = new QTableWidgetItem;
colorItem->setData(Qt::DisplayRole, *colorPtr);
this->setItem(row, COLUMN_COLOR, colorItem);
ColorPicker* colorPicker = new ColorPicker(*colorPtr, this);
this->setCellWidget(row, COLUMN_COLOR, colorPicker);
connect(colorPicker, &ColorPicker::colorChanged, [this, colorPtr](QColor color) {
*colorPtr = color;
this->update();
});
QTableWidgetItem* metallicItem = new QTableWidgetItem;
metallicItem->setData(Qt::EditRole, strokePair.second.metallic);
this->setItem(row, COLUMN_METALLIC, metallicItem);
QTableWidgetItem* roughnessItem = new QTableWidgetItem;
roughnessItem->setData(Qt::EditRole, strokePair.second.roughness);
this->setItem(row, COLUMN_ROUGHNESS, roughnessItem);
QtMaterialRaisedButton* removeButton = new QtMaterialRaisedButton("-", this);
removeButton->setFixedSize(20, 20);
this->setCellWidget(row, COLUMN_OPERATIONS, removeButton);
connect(removeButton, &QtMaterialRaisedButton::clicked, [this, row]() {
this->stroke->materialMap.erase(this->item(row, COLUMN_WIDTH)->text().toFloat());
this->removeRow(row);
});
row++;
}
connect(this, &StrokeStyleListView::currentItemChanged, this, &StrokeStyleListView::onCurrentItemChanged);
connect(this, &StrokeStyleListView::cellChanged, this, &StrokeStyleListView::onCellChanged);
this->adjustSize();
}
void StrokeStyleListView::onCurrentItemChanged(QTableWidgetItem* current, QTableWidgetItem* previous)
{
if (!current) return;
int column = current->column();
if (column != COLUMN_COLOR && column != COLUMN_OPERATIONS)
{
this->currentItemValue = current->text();
}
}
void StrokeStyleListView::onCellChanged(int row, int column)
{
auto changedItem = this->item(row, column);
auto changedItemValue = changedItem->text().toFloat();
if (changedItemValue < 0 || 1 < changedItemValue)
{
changedItem->setData(Qt::EditRole, this->currentItemValue.toFloat());
return;
}
auto changedWidth = this->item(row, COLUMN_WIDTH)->text().toFloat();
switch (row)
{
case COLUMN_WIDTH:
{
float oldWidth = this->currentItemValue.toFloat();
auto node = stroke->materialMap.extract(oldWidth);
node.key() = changedWidth;
stroke->materialMap.insert(std::move(node));
break;
}
case COLUMN_METALLIC:
{
stroke->materialMap[changedWidth].metallic = changedItemValue;
break;
}
case COLUMN_ROUGHNESS:
{
stroke->materialMap[changedWidth].roughness = changedItemValue;
break;
}
}
}

View File

@ -0,0 +1,21 @@
#pragma once
#include "LayerStyle.h"
#include "../Renderer/Painting/MaterialStyleStroke.h"
#include <QListView>
#include <QListWidget>
#include <QTableWidget>
class StrokeStyleListView : public QTableWidget
{
Q_OBJECT
private:
QString currentItemValue;
public:
StrokeStyleListView(std::shared_ptr<Renderer::StrokeRadialGradient> stroke, QWidget* parent = nullptr);
std::shared_ptr<Renderer::StrokeRadialGradient> stroke;
protected slots:
void onCurrentItemChanged(QTableWidgetItem* current, QTableWidgetItem* previous);
void onCellChanged(int row, int column);
};

View File

@ -0,0 +1,236 @@
#include "StrokeStyleWidget.h"
#include "ColorPicker.h"
#include "../ColorHelper.hpp"
#include <qtmaterialraisedbutton.h>
#include <limits>
#include <array>
#include <QHeaderView>
#include <ranges>
#include <utility>
constexpr int COLUMN_WIDTH = 0;
constexpr int COLUMN_COLOR = 1;
constexpr int COLUMN_METALLIC = 2;
constexpr int COLUMN_ROUGHNESS = 3;
constexpr int COLUMN_OPERATIONS = 4;
inline Renderer::Material newMaterial()
{
return {ColorHelper::instance().getPrimary1()};
}
inline bool isClosedStroke(const std::shared_ptr<MaterialStyleStroke>& stroke)
{
return stroke->endType == Renderer::StrokeEndType::kClosed;
}
StrokeStyleWidget::StrokeStyleWidget(
const std::shared_ptr<MaterialStyleStroke>& stroke,
QWidget* parent
) : QWidget(parent), stroke(stroke)
{
auto* viewLayout = new QVBoxLayout(this);
this->setLayout(viewLayout);
initStrokeSettings();
auto* strokeProperties = new QWidget(this);
auto* strokePropertiesLayout = new QHBoxLayout(strokeProperties);
strokePropertiesLayout->setMargin(0);
strokeProperties->setLayout(strokePropertiesLayout);
strokePropertiesLayout->addWidget(enableGradual);
if (!isClosedStroke(stroke))
{
strokePropertiesLayout->addWidget(endTypeBox);
}
viewLayout->addWidget(strokeProperties);
viewLayout->addWidget(widthField);
initTable(radialStroke(stroke));
viewLayout->addWidget(strokeTable);
initAddButton();
viewLayout->addWidget(addButton);
this->adjustSize();
}
void StrokeStyleWidget::initStrokeSettings()
{
this->enableGradual = new QtMaterialCheckBox(this);
enableGradual->setText(QStringLiteral("渐变"));
enableGradual->setChecked(radialStroke(stroke)->gradual);
connect(enableGradual, &QtMaterialCheckBox::toggled, [this](bool checked) {
radialStroke(this->stroke)->gradual = checked;
});
if (!isClosedStroke(stroke))
{
this->endTypeBox = new QComboBox(this);
for (const auto& displayName : MaterialStyleStroke::strokeEndTypeNames | std::views::keys)
{
endTypeBox->addItem(displayName);
}
endTypeBox->setCurrentIndex(static_cast<int>(this->stroke->endType));
connect(endTypeBox, QOverload<int>::of(&QComboBox::currentIndexChanged), [this](int index) {
const auto& [displayName, endType] = MaterialStyleStroke::strokeEndTypeNames[index];
this->stroke->endType = endType;
});
}
else
{
this->endTypeBox = nullptr;
}
this->widthField = new QtMaterialTextField(this);
widthField->setLabel(QStringLiteral("本侧描边宽度"));
widthField->setText(QString::number(stroke->halfWidth));
auto* widthValidator = new QDoubleValidator(0.1, std::numeric_limits<float>::max(), 3, widthField);
widthValidator->setNotation(QDoubleValidator::StandardNotation);
widthField->setValidator(widthValidator);
connect(widthField, &QtMaterialTextField::textChanged, [this](const QString& changed) {
if (this->widthField->hasAcceptableInput())
{
this->stroke->halfWidth = changed.toFloat();
}
});
}
void StrokeStyleWidget::initTable(const std::shared_ptr<Renderer::StrokeRadialGradient>& materialStroke)
{
this->strokeTable = new QTableWidget(this);
strokeTable->setSizeAdjustPolicy(QAbstractScrollArea::AdjustToContentsOnFirstShow);
strokeTable->setColumnCount(5);
strokeTable->setRowCount(materialStroke->materialMap.size());
QStringList headers;
headers << QStringLiteral("离心距离占比")
<< QStringLiteral("颜色")
<< QStringLiteral("金属度")
<< QStringLiteral("粗糙度")
<< QStringLiteral("其他操作");
strokeTable->setHorizontalHeaderLabels(headers);
strokeTable->setMinimumHeight(strokeTable->horizontalHeader()->height() * 6);
strokeTable->setMinimumWidth(strokeTable->sizeHint().width());
int row = 0;
// 内容
for (auto& [width, material] : std::views::reverse(materialStroke->materialMap))
{
setTableRow(row, width, material);
row++;
}
connect(strokeTable, &QTableWidget::currentItemChanged, this, &StrokeStyleWidget::onCurrentItemChanged);
connect(strokeTable, &QTableWidget::cellChanged, this, &StrokeStyleWidget::onCellChanged);
}
void StrokeStyleWidget::initAddButton()
{
this->addButton = new QtMaterialRaisedButton("+", strokeTable);
addButton->setFixedHeight(30);
addButton->setBackgroundColor(ColorHelper::instance().getPrimary1());
connect(addButton, &QtMaterialRaisedButton::clicked, [this] {
handlingRowInsert = true;
auto& materialMap = radialStroke(this->stroke)->materialMap;
float newWidth;
if (materialMap.empty())
{
newWidth = 1.f;
}
else
{
const auto firstPair = materialMap.begin();
newWidth = firstPair->first / 2;
}
const Renderer::Material newMaterial(ColorHelper::instance().getPrimary1());
materialMap[newWidth] = newMaterial;
const int newRow = this->strokeTable->rowCount();
this->strokeTable->insertRow(newRow);
setTableRow(newRow, newWidth, materialMap[newWidth]);
this->strokeTable->update();
handlingRowInsert = false;
});
}
void StrokeStyleWidget::setTableRow(int row, float width, Renderer::Material& material)
{
auto* widthItem = new QTableWidgetItem;
widthItem->setData(Qt::EditRole, width);
strokeTable->setItem(row, COLUMN_WIDTH, widthItem);
QColor* colorPtr = &material.color;
auto* colorItem = new QTableWidgetItem;
colorItem->setData(Qt::DisplayRole, *colorPtr);
strokeTable->setItem(row, COLUMN_COLOR, colorItem);
auto* colorPicker = new ColorPicker(*colorPtr, strokeTable);
strokeTable->setCellWidget(row, COLUMN_COLOR, colorPicker);
connect(colorPicker, &ColorPicker::colorChanged, [this, colorPtr](QColor color) {
*colorPtr = std::move(color);
this->strokeTable->update();
});
auto* metallicItem = new QTableWidgetItem;
metallicItem->setData(Qt::EditRole, material.metallicF());
strokeTable->setItem(row, COLUMN_METALLIC, metallicItem);
auto* roughnessItem = new QTableWidgetItem;
roughnessItem->setData(Qt::EditRole, material.roughnessF());
strokeTable->setItem(row, COLUMN_ROUGHNESS, roughnessItem);
auto* removeButton = new QtMaterialRaisedButton("-", strokeTable);
removeButton->setBackgroundColor(ColorHelper::instance().getPrimary1());
removeButton->setFixedSize(20, 20);
strokeTable->setCellWidget(row, COLUMN_OPERATIONS, removeButton);
connect(removeButton, &QtMaterialRaisedButton::clicked, [this, row] {
radialStroke(this->stroke)->materialMap.erase(this->strokeTable->item(row, COLUMN_WIDTH)->text().toFloat());
this->strokeTable->removeRow(row);
});
}
void StrokeStyleWidget::onCurrentItemChanged(QTableWidgetItem* current, QTableWidgetItem* previous)
{
if (!current) return;
const int column = current->column();
if (column != COLUMN_COLOR && column != COLUMN_OPERATIONS)
{
this->currentItemValue = current->data(Qt::EditRole);
}
}
void StrokeStyleWidget::onCellChanged(int row, int column)
{
if (handlingRowInsert) return;
const auto changedItem = strokeTable->item(row, column);
const auto changedItemValue = changedItem->text().toFloat();
if (changedItemValue < 0 || 1 < changedItemValue)
{
changedItem->setData(Qt::EditRole, this->currentItemValue.toFloat());
return;
}
const auto changedWidth = strokeTable->item(row, COLUMN_WIDTH)->data(Qt::EditRole).toFloat();
switch (column)
{
case COLUMN_WIDTH:
{
float oldWidth = this->currentItemValue.toFloat();
auto node = radialStroke(stroke)->materialMap.extract(oldWidth);
if (node.empty())
{
break;
}
node.key() = changedWidth;
radialStroke(stroke)->materialMap.insert(std::move(node));
strokeTable->sortItems(COLUMN_WIDTH, Qt::DescendingOrder);
break;
}
case COLUMN_METALLIC:
{
radialStroke(stroke)->materialMap[changedWidth].setMetallicF(changedItemValue);
break;
}
case COLUMN_ROUGHNESS:
{
radialStroke(stroke)->materialMap[changedWidth].setRoughnessF(changedItemValue);
break;
}
}
}

View File

@ -0,0 +1,36 @@
#pragma once
#include "LayerStyle.h"
#include "../Renderer/Painting/MaterialStyleStroke.h"
#include <QTableWidget>
#include <QVBoxLayout>
#include <QComboBox>
#include <qtmaterialtextfield.h>
#include <qtmaterialcheckbox.h>
#include <qtmaterialraisedbutton.h>
class StrokeStyleWidget : public QWidget
{
Q_OBJECT
private:
QVariant currentItemValue;
QtMaterialCheckBox* enableGradual;
QComboBox* endTypeBox;
QtMaterialTextField* widthField;
QTableWidget* strokeTable;
QtMaterialRaisedButton* addButton;
bool handlingRowInsert = false;
void initStrokeSettings();
void initTable(const std::shared_ptr<Renderer::StrokeRadialGradient>& materialStroke);
void initAddButton();
void setTableRow(int row, float width, Renderer::Material& material);
public:
StrokeStyleWidget(const std::shared_ptr<MaterialStyleStroke>& stroke, QWidget* parent = nullptr);
std::shared_ptr<MaterialStyleStroke> stroke;
protected slots:
void onCurrentItemChanged(QTableWidgetItem* current, QTableWidgetItem* previous);
void onCellChanged(int row, int column);
};

View File

@ -0,0 +1,182 @@
#include "EditorWidgetItem.h"
#include "EditorWidget.h"
#include "DataManager/ProjectDataManager.h"
#include <QTimer>
#include <QFileInfo>
#include <QFile>
#include <QDir>
EditorWidgetItem::EditorWidgetItem(QString filePath,QWidget *parent) : QWidget(parent)
{
QImage x;
this->parent = parent;
displayLayer = nullptr;
displayElement = nullptr;
ui.setupUi(this);
previewWindow = ui.Preview;
treeWidget = ui.LayerTree;
tabWidget = ui.DisplayTab;
this->filePath = filePath;
layerInfoDisplayWidget = dynamic_cast<InfoDisplayWidget *>(tabWidget->widget(0));
elementInfoDisplayWidget = dynamic_cast<ElementPoolWidget *>(tabWidget->widget(1));
editorSettingWidget = dynamic_cast<EditorSettingWidget*>(tabWidget->widget(2));
elementInfoDisplayWidget->enableEdit();
qDebug() << layerInfoDisplayWidget;
qDebug() << elementInfoDisplayWidget;
auto centralRefresh = [this]() {
layerInfoDisplayWidget->refresh();
elementInfoDisplayWidget->refresh();
treeWidget->refresh();
previewWindow->refresh();
};
connect(previewWindow, &PreviewWindow::triggerCentralRefresh, centralRefresh);
connect(layerInfoDisplayWidget, &InfoDisplayWidget::triggerCentralRefresh, centralRefresh);
connect(elementInfoDisplayWidget, &ElementPoolWidget::triggerCentralRefresh, centralRefresh);
connect(treeWidget, &LayerTreeWidget::triggerCentralRefresh, centralRefresh);
connect(editorSettingWidget, &EditorSettingWidget::backgroundColorChanged, this, &EditorWidgetItem::handleBackgroundColorChange);
connect(editorSettingWidget, &EditorSettingWidget::projectNameChanged, this, &EditorWidgetItem::handleProjectNameChange);
connect(previewWindow, &PreviewWindow::refreshElementPreviewByIndex, elementInfoDisplayWidget, &ElementPoolWidget::refreshPictureByIndex);
connect(previewWindow, &PreviewWindow::layerInfoChanged, layerInfoDisplayWidget, &InfoDisplayWidget::triggerSelfRefresh);
connect(treeWidget, &LayerTreeWidget::displayLayerChange, previewWindow, &PreviewWindow::currentLayerChanged);
//connect(treeWidget, &LayerTreeWidget::requireRefreshElementWidget, elementInfoDisplayWidget, &ElementPoolWidget::refresh);
// connect(layerInfoDisplayWidget, &InfoDisplayWidget::requireRefreshElementWidget, elementInfoDisplayWidget, &ElementPoolWidget::refresh);
connect(treeWidget, &LayerTreeWidget::displayLayerChange, this, &EditorWidgetItem::onLayerChange);
// connect(layerInfoDisplayWidget, &InfoDisplayWidget::requireRefreshPreview, this,
// &EditorWidgetItem::triggerRefreshPreview);
// connect(treeWidget, &LayerTreeWidget::requireRefreshPreview, this,
// &EditorWidgetItem::triggerRefreshPreview);
//connect(layerInfoDisplayWidget, &InfoDisplayWidget::requireSelfRefresh, layerInfoDisplayWidget, &InfoDisplayWidget::triggerSelfRefresh);
// connect(elementInfoDisplayWidget, &ElementPoolWidget::refreshLayerTree, treeWidget, &LayerTreeWidget::refresh);
// &EditorWidget::triggerRefreshPreview);
// test
QFile settingFile;
//settingFile.setFileName("../data.json");
settingFile.setFileName(filePath);
settingFile.open(QFile::ReadOnly);
QByteArray setting = settingFile.readAll().trimmed();
QJsonParseError jError;
QJsonDocument jsonDoc(QJsonDocument::fromJson(setting, &jError));
qDebug() << jsonDoc.object().value("height").toDouble();
qDebug() << jError.errorString();
ProjectData data;
data.fileHome = QFileInfo(filePath).absolutePath();
data.width = jsonDoc.object().value("height").toInt();
data.height = jsonDoc.object().value("width").toInt();
data.zoomX = 1.0;
data.zoomY = 1.0;
data.item = this;
ProjectDataManager::Instance()->addProjectData(data);
// end test
QJsonObject source = jsonDoc.object();
elementManager = new ElementManager(source, QFileInfo(filePath).absolutePath());
layerManager = new LayerManager(source, elementManager);
elementInfoDisplayWidget->setElementManager(elementManager);
treeWidget->elementManager = elementManager;
//qDebug() << layerManager->toJson();
previewWindow->initialize(layerManager,QSize(jsonDoc.object().value("width").toDouble(),jsonDoc.object().value("height").toDouble()));
if (layerManager->getRoot() != nullptr)
{
treeWidget->root = layerManager->getRoot();
treeWidget->refresh();
treeWidget->addTopLevelItem(treeWidget->root->getQTreeItem());
}
this->backgroundColor = source.value("background-color").toVariant().value<QColor>();
this->projectName = source.value("project-name").toString();
qDebug() << this->backgroundColor;
qDebug() << this->projectName;
QTimer::singleShot(300, this, [this, centralRefresh]() {
handleBackgroundColorChange(this->backgroundColor);
handleProjectNameChange(this->projectName);
centralRefresh();
});
}
EditorWidgetItem::~EditorWidgetItem()
{
}
void EditorWidgetItem::paintEvent(QPaintEvent *event)
{
QPainter painter(this);
// 设置画刷的颜色为灰色,并填充整个窗口区域
painter.setBrush(Qt::gray);
painter.drawRect(this->rect());
}
void EditorWidgetItem::onLayerChange(LayerWrapper *layer)
{
displayLayer = layer;
// TODO : notify InfoDisplayWidget and update
dynamic_cast<InfoDisplayWidget *>(tabWidget->widget(0))->setLayer(layer);
this->update();
}
void EditorWidgetItem::triggerRefreshPreview()
{
previewWindow->update();
}
void EditorWidgetItem::save() const
{
saveImpl(this->filePath);
}
void EditorWidgetItem::saveAs(QString filePath) const
{
saveImpl(filePath);
QString srcHome = QFileInfo(this->filePath).absolutePath();
if (!QDir(QFileInfo(filePath).absolutePath() + "/svg").exists())
{
QDir().mkdir(QFileInfo(filePath).absolutePath() + "/svg");
}
for (auto& ele : elementManager->elements)
{
auto e = dynamic_cast<SimpleElement*>(ele);
if (e != nullptr)
{
QString fileName = e->jsonSource["data"].toObject()["include"].toString();
QString src = srcHome + fileName;
QString dst = QFileInfo(filePath).absolutePath() + fileName;
QFile::copy(src, dst);
}
}
}
void EditorWidgetItem::saveImpl(QString filePath) const
{
QJsonObject source1 = layerManager->toJson();
QJsonObject source2 = elementManager->toJson();
QJsonObject json;
json.insert("width", 1080);
json.insert("height", 1080);
json.insert("root-layer", source1.value("root-layer"));
json.insert("elements", source2.value("elements"));
json.insert("project-name", this->projectName);
json.insert("background-color", QJsonValue::fromVariant(QVariant::fromValue(this->backgroundColor)));
QJsonDocument doc(json);
QFile file(filePath);
file.open(QIODevice::WriteOnly);
file.write(doc.toJson());
file.close();
}
void EditorWidgetItem::handleBackgroundColorChange(QColor color)
{
this->backgroundColor = color;
previewWindow->setBackgroundColor(color);
}
void EditorWidgetItem::handleProjectNameChange(QString name)
{
this->projectName = name;
auto parent = dynamic_cast<EditorWidget*>(this->parent);
qDebug() << name << " " << parent<<" "<<this;
if (parent != nullptr)
{
parent->renameTab(this, name);
}
}

View File

@ -0,0 +1,54 @@
#pragma once
#include "ElementManager.h"
#include "ElementPoolWidget.h"
#include "InfoDisplayWidget.h"
#include "LayerManager.h"
#include "LayerTreeWidget.h"
#include "PreviewWindow.h"
#include "EditorSettingWidget.h"
#include "ui_EditorWidgetItem.h"
#include <QPainter>
#include <QTreeWidget>
#include <QWidget>
class EditorWidgetItem : public QWidget
{
Q_OBJECT
public:
// DATA PART
PreviewWindow *previewWindow;
ElementManager *elementManager;
LayerManager *layerManager;
// QT GUI PART
Ui::EditorWidgetItem ui;
LayerTreeWidget *treeWidget;
QTabWidget *tabWidget;
InfoDisplayWidget* layerInfoDisplayWidget;
ElementPoolWidget* elementInfoDisplayWidget;
EditorSettingWidget* editorSettingWidget;
// QT DATA PART
LayerWrapper *displayLayer;
GraphicElement *displayElement;
QWidget* parent;
void saveImpl(QString filePath)const;
public:
// PROJECT INFO
QString filePath;
QString projectName;
QColor backgroundColor;
public:
EditorWidgetItem(QString filePath, QWidget *parent = nullptr);
~EditorWidgetItem();
void paintEvent(QPaintEvent *event) override;
void save() const;
void saveAs(QString filePath)const;
void handleBackgroundColorChange(QColor color);
void handleProjectNameChange(QString name);
private slots:
void onLayerChange(LayerWrapper *layer);
void triggerRefreshPreview();
};

View File

@ -1,25 +1,40 @@
#include "ElementManager.h"
ElementManager::ElementManager(QJsonObject source,Renderer::ElementRenderer* renderer)
#include <QFileInfo>
#include <QDir>
#include <QFile>
ElementManager::ElementManager(QJsonObject source, QString fileHome)
{
auto elementsJson = source.value("elements").toArray();
qDebug() << elementsJson.size();
this->fileHome = fileHome;
int index = 0;
for (auto elementJson : elementsJson)
{
if (elementJson.toObject().value("type").toString() == "group")
elements.push_back(new GroupElement());
else
elements.push_back(new SimpleElement(elementJson.toObject()));
(*elements.rbegin())->renderer = renderer;
elements.push_back(new SimpleElement(elementJson.toObject(), fileHome));
(*elements.rbegin())->name = elementJson.toObject().value("name").toString();
}
for (auto element : elements)
element->index = index++;
}
void ElementManager::addElement(GraphicElement *element)
{
this->elements.push_back(element);
}
void ElementManager::removeElement(GraphicElement *pElement)
{
for (auto& element : elements)
{
if (element == pElement)
{
elements.erase(std::find(elements.begin(), elements.end(), element));
break;
}
}
}
GraphicElement *ElementManager::getElementById(int index)
@ -33,3 +48,57 @@ GraphicElement *ElementManager::getElementById(int index)
ElementManager::~ElementManager()
{
}
QJsonObject ElementManager::toJson() const
{
QJsonArray elementsJson;
for (auto element : elements)
elementsJson.push_back(element->toJson());
QJsonObject result;
result.insert("elements", elementsJson);
return result;
}
int ElementManager::getElementIndex(GraphicElement* pelement)
{
for (int i = 0; i < elements.size(); i++)
if (elements[i] == pelement)
return i;
return -1;
}
int ElementManager::getLayerReferencedBy(const FolderLayerWrapper* layer)
{
for (int i = 0; i < elements.size(); i++)
if (typeid(*elements[i]) == typeid(GroupElement)) {
if (((GroupElement*)elements[i])->sourceLayer == layer)
return i;
}
return -1;
}
void ElementManager::removeElement(int index)
{
if (index < elements.size())
elements.erase(elements.begin() + index);
}
void ElementManager::createGroupElement(QString name, FolderLayerWrapper* sourceLayer) {
auto element = new GroupElement();
element->name = name;
element->setSourceLayer(sourceLayer);
addElement(element);
}
void ElementManager::createSimpleElement(QString name, QString filePath) {
QJsonObject json;
QJsonObject data;
QFile fileSrc(filePath);
QFile::copy(filePath, QDir::cleanPath(fileHome + "/svg/" + QFileInfo(filePath).fileName()));
data.insert("include", "/svg/"+QFileInfo(filePath).fileName());
json.insert("data", data);
auto element = new SimpleElement(json, fileHome);
element->name = name;
addElement(element);
}

View File

@ -8,19 +8,27 @@ using std::vector;
class LayerManager;
class GraphicElement;
class Renderer::ElementRenderer;
class FolderLayerWrapper;
class ElementManager
{
private:
public:
vector<GraphicElement *> elements;
QString fileHome;
public:
ElementManager(QJsonObject source,Renderer::ElementRenderer *renderer);
ElementManager(QJsonObject source, QString fileHome);
~ElementManager();
void addElement(GraphicElement *element);
void removeElement(GraphicElement *pElement);
void removeElement(int index);
void createGroupElement(QString name, FolderLayerWrapper* sourceLayer);
void createSimpleElement(QString name, QString filePath);
QJsonObject toJson()const;
/**
* only used in initialization
*/
GraphicElement *getElementById(int index);
int getElementIndex(GraphicElement* pElement);
int getLayerReferencedBy(const FolderLayerWrapper* layer);
};

View File

@ -0,0 +1,136 @@
#include "ElementPoolWidget.h"
#include <QMenu>
#include <QInputDialog>
#include <QFileDialog>
#include <QDialogButtonBox>
ElementPoolWidget::ElementPoolWidget(QWidget* parent)
: QWidget(parent)
{
elementManager = nullptr;
iconWidth = 120, iconHeight = 90;
pictureList = new QListWidget(this);
pictureList->setContextMenuPolicy(Qt::CustomContextMenu);
pictureList->setIconSize(QSize(iconWidth, iconHeight));
pictureList->setWindowFlags(Qt::FramelessWindowHint);
pictureList->setResizeMode(QListWidget::Adjust);
pictureList->setViewMode(QListWidget::IconMode);
pictureList->setMovement(QListWidget::Static);
pictureList->setVerticalScrollMode(QAbstractItemView::ScrollPerPixel);
pictureList->setWrapping(true);
pictureList->setSpacing(5);
pictureList->setStyleSheet("QListWidget::Item:hover{background-color: #f5f5f5;border-radius:5px; }"
"QListWidget::item:selected{background-color:rgba(234,234,234,1);color:rgb(61,61,61);border:1px solid #778899;border-radius:2px; }"
"QScrollBar:vertical{width:6px}"
"QListWidget{outline:none;border:0px}");
pictureList->setFocusPolicy(Qt::NoFocus);
QLayout* layout = new QHBoxLayout();
layout->addWidget(pictureList);
setLayout(layout);
//pictureList->setFixedSize(600, 800);
connect(pictureList, SIGNAL(itemClicked(QListWidgetItem*)), this, SLOT(pictureItemClicked(QListWidgetItem*)));
}
void ElementPoolWidget::setElementList(std::vector<GraphicElement*> elements) {
pictureList->clear();
this->elements = elements;
for (int index = 0; index < elements.size(); index++) {
QListWidgetItem* pItem = new QListWidgetItem(
elements[index]->getPreview(QSize(iconWidth - 25, iconHeight - 25)),
elements[index]->name);
pItem->setSizeHint(QSize(iconWidth, iconHeight));
pictureList->insertItem(index, pItem);
}
if(elements.size() > 0)
pictureList->setCurrentRow(0),
currentIndex = 0;
}
ElementPoolWidget::~ElementPoolWidget() {
}
int ElementPoolWidget::pictureItemClicked(QListWidgetItem* item) {
//qDebug() << pictureList->currentRow();
currentIndex = pictureList->currentRow();
emit elementSelected(this->elements[pictureList->currentRow()]);
return pictureList->currentRow();
}
void ElementPoolWidget::setElementManager(ElementManager* element)
{
this->elementManager = element;
this->setElementList(this->elementManager->elements);
}
void ElementPoolWidget::refresh() {
this->setElementList(this->elementManager->elements);
// update();
}
void ElementPoolWidget::refreshPicture(GraphicElement* element) {
for (int i = 0; i < elements.size(); i++) {
if (element == elements[i]) {
pictureList->item(i)->setIcon(elements[i]->getPreview(QSize(iconWidth - 25, iconHeight - 25)));
// update();
return;
}
}
}
void ElementPoolWidget::refreshPictureByIndex(int index) {
if (index >= 0 && index < elements.size())
{
pictureList->item(index)->setIcon(elements[index]->getPreview(QSize(iconWidth - 25, iconHeight - 25)));
// update();
}
}
void ElementPoolWidget::enableEdit()
{
connect(this->pictureList, &QListWidget::customContextMenuRequested, this, &ElementPoolWidget::popMenu);
}
void ElementPoolWidget::popMenu(const QPoint& pos)
{
QListWidgetItem* item = pictureList->itemAt(pos);
int currentIndex = -1;
if (item != nullptr)
{
currentIndex = pictureList->row(item);
}
QMenu* menu = new QMenu(this);
if (currentIndex >= 0 && currentIndex < elementManager->elements.size())
{
auto currentElement = this->elementManager->elements[currentIndex];
menu->addAction(QString::fromLocal8Bit("重命名"), this, [this, currentElement]() {
bool bOk = false;
QString sName =
QInputDialog::getText(this, QString::fromLocal8Bit("重命名"), QString::fromLocal8Bit("新名称:"), QLineEdit::Normal, currentElement->name, &bOk);
if (bOk && !sName.isEmpty())
{
currentElement->name = sName;
emit triggerCentralRefresh();
}
});
menu->addAction(QString::fromLocal8Bit("删除"), this, [this, currentElement]() {
this->elementManager->removeElement(currentElement);
emit triggerCentralRefresh();
});
menu->actions().last()->setDisabled(currentElement->referencedCount > 0);
}
else
{
menu->addAction(QString::fromLocal8Bit("添加元素从svg导入"), this, [this]() {
QString filePath = QFileDialog::getOpenFileName(this, QString::fromLocal8Bit("打开文件"), "", "SVG Files (*.svg)");
QFileInfo fileInfo(filePath);
QString fileName = fileInfo.fileName();
qDebug() << fileName << " " << filePath;
this->elementManager->createSimpleElement(fileName, filePath);
emit triggerCentralRefresh();
});
}
menu->popup(mapToGlobal(pos));
}

View File

@ -0,0 +1,38 @@
#pragma once
#include <QWidget>
#include <vector>
#include <GraphicElement.h>
#include <QListWidget>
#include <ElementManager.h>
#include <QLayout>
class ElementPoolWidget : public QWidget
{
Q_OBJECT
private:
std::vector<GraphicElement*> elements;
QListWidget* pictureList;
int iconWidth, iconHeight;
ElementManager* elementManager;
public:
int currentIndex = -1;
ElementPoolWidget(QWidget* parent = nullptr);
void setElementList(std::vector<GraphicElement*> elementList);
void setElementManager(ElementManager* element);
~ElementPoolWidget();
void enableEdit();
signals:
void elementSelected(GraphicElement* element);
void refreshLayerTree();
void triggerCentralRefresh();
public slots:
int pictureItemClicked(QListWidgetItem* item);
void refresh();
void refreshPicture(GraphicElement* element);
void refreshPictureByIndex(int index);
void popMenu(const QPoint& pos);
};

View File

@ -1,11 +1,16 @@
#include "GraphicElement.h"
#include "util/SvgFileLoader.h"
#include <QGuiApplication>
#include <QScreen>
#include <QDir>
using namespace std;
PixelPath SimpleElement::getPaintObject() const
{
PixelPath result;
result.addPath(painterPath);
//qDebug() << result.getPainterPath();
return result;
}
@ -14,14 +19,19 @@ void SimpleElement::loadSvgFile(const QString& filePath)
// TODO ÑùʽÎÊÌâ
SvgFileLoader loader;
loader.loadSvgFile(filePath, painterPath);
qDebug() << "load svg file success " << painterPath.elementCount();
auto startPoint = static_cast<QPointF>(painterPath.elementAt(0));
auto endPoint = static_cast<QPointF>(painterPath.elementAt(painterPath.elementCount() - 1));
this->closed = startPoint == endPoint;
qDebug() << "load svg file success " << painterPath.elementCount() << (isClosed() ? "is" : "not") << "closed";
}
SimpleElement::SimpleElement(QJsonObject jsonSource) : jsonSource(jsonSource)
SimpleElement::SimpleElement(QJsonObject jsonSource, QString fileHome) : jsonSource(jsonSource)
{
painterPath.clear();
filePath = jsonSource["data"].toObject()["include"].toString();
//loadSvgFile("D:\\Projects\\BigC\\svg\\3.svg");
loadSvgFile("../"/*TODO: 改成json文件所在文件夹路径*/ + jsonSource.value("data").toObject().value("include").toString());
QFileInfo info(QDir::cleanPath(fileHome + "/" + filePath));
loadSvgFile(info.absoluteFilePath());
}
GroupElement::GroupElement(FolderLayerWrapper* sourceLayer)
@ -43,23 +53,11 @@ PixelPath GroupElement::getPaintObject() const
}
//TODO: apply styles and send back
PixelPath SimpleElement::getPaintObject(std::vector<Renderer::ElementStyleStrokeDemo> styles) const {
PixelPath result;
Renderer::ElementStyleStrokeDemo demo(2);
auto [img, mov] = renderer->drawElement(painterPath, demo, 1.0, false);
//qDebug() << img << " ------";
result.addImage(img, mov);
//result.addPath(painterPath);
// QImage img(80,80,QImage::Format_ARGB32);
// QPainter pt(&img);
//pt.setPen(QPen(Qt::red, 2));
//pt.drawLine(0, 0, 80, 80);
//pt.end();
//result.addImage(img, QPoint(0, 0));
return result;
PixelPath SimpleElement::getPaintObject(const LayerStyleContainer& styles) const {
return this->getPaintObject();
}
PixelPath GroupElement::getPaintObject(std::vector<Renderer::ElementStyleStrokeDemo> styles) const {
PixelPath GroupElement::getPaintObject(const LayerStyleContainer& styles) const {
return getPaintObject();
}
@ -76,3 +74,101 @@ PixelPath GroupElement::getPaintObject(std::vector<Renderer::ElementStyleStrokeD
// rawPath.addPath(path);
// }
//}
//TODO : Ìí¼Óϸ½Ú
QJsonObject SimpleElement::toJson() const
{
QJsonObject result;
QJsonObject data;
data["include"] = filePath;
result["type"] = "svg-file";
result["data"] = data;
result["name"] = name;
return result;
}
QJsonObject GroupElement::toJson() const
{
QJsonObject result;
QJsonObject data;
data["reference-layer"] = "0.0";
result["type"] = "group";
result["data"] = data;
result["name"] = name;
return result;
}
void SimpleElement::paint(QPainter* painter, QTransform transform, const LayerStyleContainer& styles)
{
painter->save();
painter->setTransform(transform);
if (styles.empty())
{
painter->drawPath(painterPath);
}
else
{
double angle = atan(transform.m12() / transform.m11());
double maxScale;
if (fabs(cos(angle))>1e-5)
maxScale = std::max(fabs(transform.m11() / cos(angle)), fabs(transform.m22() / cos(angle)));
else
maxScale = std::max(fabs(transform.m12() / sin(angle)), fabs(transform.m21() / sin(angle)));
double pixelRatio = maxScale * QGuiApplication::primaryScreen()->devicePixelRatio();
auto [img, mov] = Renderer::ElementRenderer::instance()->drawElement(painterPath, styles, pixelRatio);
transform.translate(mov.x(), mov.y());
painter->setTransform(transform.scale(1 / pixelRatio, 1 / pixelRatio));
painter->drawImage(0, 0, img);
}
painter->restore();
}
bool SimpleElement::isClosed() const
{
return closed;
}
void GroupElement::paint(QPainter* painter, QTransform transform, const LayerStyleContainer& styles)
{
sourceLayer->paint(painter, transform, true);
}
bool GroupElement::isClosed() const
{
return false;
}
QPixmap SimpleElement::getPreview(QSize size)
{
QPixmap result(size + QSize(5, 5));
QPainter painter(&result);
painter.setRenderHint(QPainter::Antialiasing);
painter.setRenderHint(QPainter::SmoothPixmapTransform);
painter.scale(size.width() / painterPath.boundingRect().width(), size.height() / painterPath.boundingRect().height());
painter.drawPath(painterPath);
return result;
}
QPixmap GroupElement::getPreview(QSize size)
{
auto cache = sourceLayer->getCache();
QPixmap result(QSize(1024, 1024));
QPainter painter(&result);
painter.setRenderHint(QPainter::Antialiasing);
painter.setRenderHint(QPainter::SmoothPixmapTransform);
sourceLayer->paint(&painter, QTransform(), true);
painter.end();
QRect rect(cache.getBoundingRect().toRect());
rect.setTopLeft(rect.topLeft() - QPoint(5, 5));
rect.setBottomRight(rect.bottomRight() + QPoint(5, 5));
result = result.copy(rect);
return result.scaled(size, Qt::KeepAspectRatio, Qt::SmoothTransformation);
}
void GroupElement::collectReachable(std::set<LayerWrapper*>& set) const
{
if (sourceLayer != nullptr)
{
sourceLayer->collectDownReachable(set);
}
}

View File

@ -1,7 +1,7 @@
#pragma once
#include "LayerWrapper.h"
#include <QJsonArray>
#include "LayerStyle.h"
#include <QJsonObject>
#include <QPainterPath>
#include <QImage>
@ -20,26 +20,40 @@ class ComposedPainterPath;
class GraphicElement
{
public:
size_t referencedCount = 0;
Renderer::ElementRenderer *renderer;
QString name = "";
int index;
// TODO: ¸ÄΪBitmapPath
virtual QJsonObject toJson() const = 0;
virtual PixelPath getPaintObject() const = 0;
virtual PixelPath getPaintObject(std::vector<Renderer::ElementStyleStrokeDemo>) const = 0;
virtual PixelPath getPaintObject(const LayerStyleContainer& styles) const = 0;
virtual void paint(QPainter* painter, QTransform transform, const LayerStyleContainer& styles) = 0;
virtual bool isClosed() const = 0;
virtual QPixmap getPreview(QSize size) = 0;
};
class SimpleElement : public GraphicElement
{
private:
bool closed;
public:
QJsonObject jsonSource;
// TODO: ¸ÄΪComposedPainterPath
QPainterPath painterPath;
QString filePath;
void loadSvgFile(const QString& filePath);
public:
SimpleElement(QJsonObject jsonSource);
QJsonObject toJson() const override;
SimpleElement(QJsonObject jsonSource, QString fileHome);
SimpleElement(QString filePath);
~SimpleElement() = default;
PixelPath getPaintObject() const override;
PixelPath getPaintObject(std::vector<Renderer::ElementStyleStrokeDemo>) const override;
PixelPath getPaintObject(const LayerStyleContainer& styles) const override;
void paint(QPainter* painter, QTransform transform, const LayerStyleContainer& styles) override;
bool isClosed() const override;
QPixmap getPreview(QSize size) override;
};
class GroupElement : public GraphicElement
@ -48,12 +62,17 @@ public:
FolderLayerWrapper* sourceLayer;
public:
QJsonObject toJson() const override;
GroupElement() = default;
GroupElement(FolderLayerWrapper* mSourceLayer);
~GroupElement() = default;
PixelPath getPaintObject() const override;
PixelPath getPaintObject(std::vector<Renderer::ElementStyleStrokeDemo>) const override;
PixelPath getPaintObject(const LayerStyleContainer& styles) const override;
void setSourceLayer(FolderLayerWrapper* sourceLayer);
void paint(QPainter* painter, QTransform transform, const LayerStyleContainer& styles) override;
bool isClosed() const override;
QPixmap getPreview(QSize size) override;
void collectReachable(std::set<LayerWrapper*>& set) const;
};
//******************************** BitmapPath ********************************//

View File

@ -11,10 +11,23 @@ LayerWrapper *LayerManager::getRoot() const
{
return root;
}
void LayerManager::paint(QPainter *painter, QSize size) const
void LayerManager::paint(QPainter *painter, QSize size,LayerWrapper* selecetedLayer) const
{
auto p = root->getCache().resizedPixel(size);
painter->drawPixmap(0, 0, p);
painter->save();
root->getCache();
root->paint(painter);
painter->restore();
painter->save();
// painter->setBrush(QBrush(Qt::white));
//painter->setCompositionMode(QPainter::CompositionMode_Difference);
if (selecetedLayer != nullptr)
{
painter->setPen(QPen(Qt::gray, 2, Qt::DashLine));
selecetedLayer->paintVisualBounding(painter);
//painter->setPen(QPen(Qt::gray, 2, Qt::DashDotLine));
//selecetedLayer->paintVisualBounding(painter);
}
painter->restore();
}
bool LayerManager::singleSelectedCheck() const
{
@ -62,11 +75,20 @@ bool LayerManager::changeParent(FolderLayerWrapper *newParent) const
selectedLayers[0]->setParent(newParent);
return true;
}
void LayerManager::addLayer(LayerWrapper *layer)
{
layerSet.insert(layer);
}
void LayerManager::removeLayer(LayerWrapper *layer)
{
layerSet.erase(layer);
}
QJsonObject LayerManager::toJson() const
{
QJsonObject result;
result.insert("root-layer", root->toJson());
return result;
}

View File

@ -31,7 +31,8 @@ class LayerManager
LayerWrapper *getRoot() const;
LayerManager() = default;
LayerManager(QJsonObject source, ElementManager* elementManager);
void paint(QPainter *painter, QSize size) const;
QJsonObject toJson() const;
void paint(QPainter *painter, QSize size, LayerWrapper* selecetedLayer=nullptr) const;
bool rename(QString newName) const;
bool combine() const;
// bool seperate() const;

View File

@ -1,6 +1,381 @@
#include "LayerStyle.h"
#include "./EditorWidgetComponent/StrokeStyleWidget.h"
#include "./EditorWidgetComponent/FillStyleWidget.h"
#include "./util/EncodeUtil.hpp"
#include "../ColorHelper.hpp"
#include <qtmaterialcheckbox.h>
#include <QHBoxLayout>
#include <QPushButton>
#include <QLabel>
#include <QObject>
#include <QDebug>
#define _USE_JOIN_VIEW_INPUT_RANGE
#include <QJsonArray>
#include <ranges>
std::vector<Renderer::BaseStyle> StrokeElementStyle::toBaseStyles() const
std::vector<Renderer::BaseStyle> StrokeElementLayerStyle::toBaseStyles() const
{
return std::vector<Renderer::BaseStyle>();
std::vector<Renderer::BaseStyle> baseStyles;
if (enableEachSideIndependent)
{
if (!radialStroke(strokePair.first)->materialMap.empty())
{
baseStyles.push_back({ std::make_shared<Renderer::TransformStyle>(), strokePair.first });
}
if (!radialStroke(strokePair.second)->materialMap.empty())
{
baseStyles.push_back({ std::make_shared<Renderer::TransformStyle>(), strokePair.second });
}
}
else if (!radialStroke(strokePair.first)->materialMap.empty())
{
const auto material = std::shared_ptr(std::move(strokePair.first->clone()));
std::static_pointer_cast<MaterialStyleStroke>(material)->strokeType = Renderer::StrokeType::kBothSides;
baseStyles.push_back({ std::make_shared<Renderer::TransformStyle>(), material });
}
return baseStyles;
}
QWidget* StrokeElementLayerStyle::getInputWidget()
{
auto* w = new QWidget;
auto* materialList = new QListView;
auto* layout = new QVBoxLayout(w);
layout->setMargin(0);
auto* leftStrokeView = new StrokeStyleWidget(this->strokePair.first, w);
layout->addWidget(leftStrokeView);
auto* checkEachSideIndependent = new QtMaterialCheckBox(w);
checkEachSideIndependent->setText(QStringLiteral("ÓÒ²à¶ÀÁ¢Ãè±ß"));
checkEachSideIndependent->setChecked(enableEachSideIndependent);
layout->addWidget(checkEachSideIndependent);
auto* rightStrokeView = new StrokeStyleWidget(this->strokePair.second, w);
layout->addWidget(rightStrokeView);
rightStrokeView->setDisabled(!this->enableEachSideIndependent);
QObject::connect(checkEachSideIndependent, &QtMaterialCheckBox::toggled, [this, rightStrokeView](bool toggled) {
this->enableEachSideIndependent = toggled;
rightStrokeView->setDisabled(!toggled);
});
return w;
}
QWidget* StrokeElementLayerStyle::getListDisplayWidget() const
{
auto* w = new QWidget;
auto* name = new QLabel(w);
name->setText(QStringLiteral("Ãè±ß"));
auto* layout = new QHBoxLayout(w);
layout->setMargin(0);
layout->addWidget(name);
return w;
}
void LayerStyleContainer::computeNewHash()
{
hash = 0;
for (auto& f : styles
| std::views::values
| std::views::transform(&LayerStyle::toBaseStyles)
| std::views::join
| std::views::transform(&Renderer::BaseStyle::material)
| std::views::transform(&MaterialStyle::encoded)
| std::views::join)
{
const unsigned int u = *reinterpret_cast<unsigned int*>(&f);
hash ^= u + 0x9e3779b9 + (hash << 6) + (hash >> 2);
}
}
LayerStyleContainer LayerStyleContainer::fromJson(bool isClosedElement, const QJsonArray& jsonArray)
{
LayerStyleContainer container(isClosedElement);
for (const auto& style : jsonArray)
{
container.useStyle(LayerStyle::fromJson(style.toObject()));
}
return container;
}
LayerStyleContainer::LayerStyleContainer(bool isClosedElement) : hash(0)
{
for (const auto& style : commonStyles)
{
unusedStyles.insert(style);
}
if (isClosedElement)
{
for (const auto& style : closedOnlyStyles)
{
unusedStyles.insert(style);
}
}
else
{
for (const auto& style : unclosedOnlyStyles)
{
unusedStyles.insert(style);
}
}
}
std::vector<Renderer::BaseStyle> LayerStyleContainer::toBaseStyles() const
{
std::vector<Renderer::BaseStyle> result;
for (const auto& style : styles | std::views::values)
{
auto baseStyles = style->toBaseStyles();
result.insert(result.end(),
std::make_move_iterator(baseStyles.begin()),
std::make_move_iterator(baseStyles.end()));
}
return result;
}
QJsonArray LayerStyleContainer::toJson() const
{
QJsonArray json;
for (const auto& style : styles | std::views::values)
{
json.append(style->toJson());
}
return json;
}
QStringList LayerStyleContainer::unusedStyleNames() const
{
QStringList result;
for(const auto& name : unusedStyles | std::views::keys)
{
result << name;
}
return result;
}
std::unique_ptr<LayerStyle> LayerStyleContainer::makeUnusedStyle(const QString& styleName) const
{
return unusedStyles.at(styleName)();
}
bool LayerStyleContainer::empty() const
{
return styles.empty();
}
bool LayerStyleContainer::full() const
{
return unusedStyles.empty();
}
std::map<QString, std::shared_ptr<LayerStyle>>::iterator LayerStyleContainer::begin()
{
return styles.begin();
}
std::map<QString, std::shared_ptr<LayerStyle>>::iterator LayerStyleContainer::end()
{
return styles.end();
}
bool LayerStyleContainer::useStyle(const std::shared_ptr<LayerStyle>& style)
{
auto styleNode = unusedStyles.extract(style->getDisplayName());
if (styleNode.empty())
{
return false;
}
styles[styleNode.key()] = style;
usedStyles.insert(std::move(styleNode));
return true;
}
bool LayerStyleContainer::dropStyle(const QString& styleName)
{
auto styleNode = usedStyles.extract(styleName);
if (styleNode.empty())
{
return false;
}
styles.erase(styleName);
unusedStyles.insert(std::move(styleNode));
return true;
}
float LayerStyleContainer::boundingBoxAffectValue() const {
float maxLineWidth = 0;
const auto strokeStyle = styles.find(StrokeElementLayerStyle::displayName());
if (strokeStyle != styles.end())
{
if (const auto strokeElementLayerStyle =
std::dynamic_pointer_cast<StrokeElementLayerStyle>(strokeStyle->second);
strokeElementLayerStyle != nullptr)
{
const auto& leftStyleStroke = strokeElementLayerStyle->strokePair.first;
const auto& rightStyleStroke = strokeElementLayerStyle->strokePair.second;
if (leftStyleStroke != nullptr)
{
maxLineWidth = std::max(maxLineWidth, leftStyleStroke->halfWidth);
}
if (rightStyleStroke != nullptr)
{
maxLineWidth = std::max(maxLineWidth, rightStyleStroke->halfWidth);
}
}
}
return maxLineWidth;
}
size_t LayerStyleContainer::getHash() const
{
return hash;
}
std::unique_ptr<StrokeElementLayerStyle> StrokeElementLayerStyle::fromJson(const QJsonObject& json)
{
auto ptr = std::make_unique<StrokeElementLayerStyle>(
std::static_pointer_cast<MaterialStyleStroke>(
std::shared_ptr(std::move(MaterialStyle::decoded(EncodeUtil::fromBase64<GLfloat>(json["left"].toString()))))
),
std::static_pointer_cast<MaterialStyleStroke>(
std::shared_ptr(std::move(MaterialStyle::decoded(EncodeUtil::fromBase64<GLfloat>(json["right"].toString()))))
)
);
ptr->enableEachSideIndependent = json["enableEachSideIndependent"].toBool();
return ptr;
}
StrokeElementLayerStyle::StrokeElementLayerStyle(bool isClosed)
{
const auto materialMap = std::map<float, Renderer::Material>();
this->strokePair.first = std::make_shared<MaterialStyleStroke>(
7,
Renderer::StrokeType::kLeftSide, isClosed ? Renderer::StrokeEndType::kClosed : Renderer::StrokeEndType::kFlat,
std::make_shared<Renderer::StrokeRadialGradient>(materialMap, false)
);
this->strokePair.second = std::make_shared<MaterialStyleStroke>(
7,
Renderer::StrokeType::kRightSide, isClosed ? Renderer::StrokeEndType::kClosed : Renderer::StrokeEndType::kFlat,
std::make_shared<Renderer::StrokeRadialGradient>(materialMap, false)
);
}
StrokeElementLayerStyle::StrokeElementLayerStyle(const PMaterialStyleStroke& left, const PMaterialStyleStroke& right)
{
this->strokePair.first = left;
this->strokePair.second = right ? right : std::static_pointer_cast<MaterialStyleStroke>(
std::shared_ptr(std::move(left->clone()))
);
}
StrokeElementLayerStyle::StrokeElementLayerStyle(const StrokeElementLayerStyle& other)
{
strokePair.first = std::static_pointer_cast<MaterialStyleStroke>(
std::shared_ptr(std::move(other.strokePair.first->clone()))
);
strokePair.second = std::static_pointer_cast<MaterialStyleStroke>(
std::shared_ptr(std::move(other.strokePair.second->clone()))
);
enableEachSideIndependent = other.enableEachSideIndependent;
}
QJsonObject StrokeElementLayerStyle::toJson() const
{
auto json = LayerStyle::toJson();
json["enableEachSideIndependent"] = enableEachSideIndependent;
json["left"] = EncodeUtil::toBase64<GLfloat>(strokePair.first->encoded());
json["right"] = EncodeUtil::toBase64<GLfloat>(strokePair.second->encoded());
return json;
}
std::unique_ptr<LayerStyle> StrokeElementLayerStyle::clone() const
{
return std::make_unique<StrokeElementLayerStyle>(StrokeElementLayerStyle(*this));
}
std::unique_ptr<FillElementLayerStyle> FillElementLayerStyle::fromJson(const QJsonObject& json)
{
auto ptr = std::make_unique<FillElementLayerStyle>(
std::static_pointer_cast<MaterialStyleFill>(
std::shared_ptr(std::move(MaterialStyle::decoded(EncodeUtil::fromBase64<GLfloat>(json["material"].toString()))))
)
);
return ptr;
}
std::vector<Renderer::BaseStyle> FillElementLayerStyle::toBaseStyles() const
{
return { {std::make_shared<Renderer::TransformStyle>(), fillMaterialStyle} };
}
QWidget* FillElementLayerStyle::getInputWidget()
{
return new FillStyleWidget(fillMaterialStyle);
}
QWidget* FillElementLayerStyle::getListDisplayWidget() const
{
auto* w = new QWidget;
auto* name = new QLabel(w);
name->setText(QStringLiteral("Ìî³ä"));
auto* layout = new QHBoxLayout(w);
layout->setMargin(0);
layout->addWidget(name);
return w;
}
FillElementLayerStyle::FillElementLayerStyle(const PMaterialStyleFill& fillMaterialStyle)
: fillMaterialStyle(fillMaterialStyle)
{
if (!fillMaterialStyle)
{
this->fillMaterialStyle = std::make_shared<MaterialStyleFill>(
std::make_shared<Renderer::FillPlain>(ColorHelper::instance().getPrimary1())
);
}
}
FillElementLayerStyle::FillElementLayerStyle(const FillElementLayerStyle& other)
{
this->fillMaterialStyle = std::static_pointer_cast<MaterialStyleFill>(
std::shared_ptr(std::move(other.fillMaterialStyle->clone()))
);
}
QJsonObject FillElementLayerStyle::toJson() const
{
auto json = LayerStyle::toJson();
json["material"] = EncodeUtil::toBase64<GLfloat>(fillMaterialStyle->encoded());
return json;
}
std::unique_ptr<LayerStyle> FillElementLayerStyle::clone() const
{
return std::make_unique<FillElementLayerStyle>(FillElementLayerStyle(*this));
}
std::unique_ptr<LayerStyle> LayerStyle::fromJson(const QJsonObject& json)
{
QString type = json["type"].toString();
if (type == StrokeElementLayerStyle::typeName())
{
return StrokeElementLayerStyle::fromJson(json);
}
if (type == FillElementLayerStyle::typeName())
{
return FillElementLayerStyle::fromJson(json);
}
return nullptr;
}
QJsonObject LayerStyle::toJson() const
{
QJsonObject json;
json["type"] = this->getTypeName();
return json;
}

View File

@ -1,24 +1,136 @@
#pragma once
#include <functional>
#include <utility>
#include <set>
#include <map>
#include <QListWidget>
#include <QJsonObject>
#include <QJsonArray>
#include "../Renderer/Painting/ElementStyle.h"
#include "../Renderer/Painting/MaterialStyleStroke.h"
#include "../Renderer/Painting/MaterialStyleFill.h"
class LayerStyle
using Renderer::MaterialStyle;
using Renderer::MaterialStyleStroke;
using Renderer::MaterialStyleFill;
#define STYLE_NAME(display_name, type_name) \
static QString displayName() { return QStringLiteral(display_name); } \
QString getDisplayName() const override { return QStringLiteral(display_name); } \
static QString typeName() { return type_name; } \
QString getTypeName() const override { return type_name; }
#define radialStroke(stroke) std::static_pointer_cast<Renderer::StrokeRadialGradient>((stroke)->materialStroke)
#define plainFill(fill) std::static_pointer_cast<Renderer::FillPlain>((fill)->materialFill)
class LayerStyle : public Renderer::ElementStyle
{
public:
virtual void apply() = 0;
static std::unique_ptr<LayerStyle> fromJson(const QJsonObject& json);
virtual ~LayerStyle() = default;
virtual QString getDisplayName() const = 0;
virtual QString getTypeName() const = 0;
virtual QWidget* getInputWidget() = 0;
virtual QWidget* getListDisplayWidget() const = 0;
virtual QJsonObject toJson() const;
virtual std::unique_ptr<LayerStyle> clone() const = 0;
};
struct EditorStrokeMaterialStyle
class StrokeElementLayerStyle : public LayerStyle
{
float applyWidth;
Renderer::StrokeType strokeType;
Renderer::StrokeEndType endType;
std::shared_ptr<Renderer::MaterialStroke> materialStroke;
};
using PMaterialStyleStroke = std::shared_ptr<MaterialStyleStroke>;
class StrokeElementStyle : Renderer::ElementStyle
{
public:
virtual std::vector<Renderer::BaseStyle> toBaseStyles() const override;
std::vector<EditorStrokeMaterialStyle> materialStyles;
STYLE_NAME("描边", "stroke")
static std::unique_ptr<StrokeElementLayerStyle> fromJson(const QJsonObject& json);
StrokeElementLayerStyle(bool isClosed);
StrokeElementLayerStyle(const PMaterialStyleStroke& left, const PMaterialStyleStroke& right = nullptr);
StrokeElementLayerStyle(const StrokeElementLayerStyle& other);
~StrokeElementLayerStyle() override = default;
std::vector<Renderer::BaseStyle> toBaseStyles() const override;
QWidget* getInputWidget() override;
QWidget* getListDisplayWidget() const override;
QJsonObject toJson() const override;
std::unique_ptr<LayerStyle> clone() const override;
std::pair<PMaterialStyleStroke, PMaterialStyleStroke> strokePair;
bool enableEachSideIndependent = false;
};
class FillElementLayerStyle : public LayerStyle
{
using PMaterialStyleFill = std::shared_ptr<MaterialStyleFill>;
public:
STYLE_NAME("填充", "fill")
static std::unique_ptr<FillElementLayerStyle> fromJson(const QJsonObject& json);
FillElementLayerStyle(const PMaterialStyleFill& fillMaterialStyle = nullptr);
FillElementLayerStyle(const FillElementLayerStyle& other);
~FillElementLayerStyle() override = default;
std::vector<Renderer::BaseStyle> toBaseStyles() const override;
QWidget* getInputWidget() override;
QWidget* getListDisplayWidget() const override;
QJsonObject toJson() const override;
std::unique_ptr<LayerStyle> clone() const override;
PMaterialStyleFill fillMaterialStyle;
};
/**
* LayerStylecomputeNewHash()
*/
class LayerStyleContainer : public Renderer::ElementStyle
{
using DisplayNameWithSupplier = std::map<QString, std::function<std::unique_ptr<LayerStyle>()>>;
private:
inline const static DisplayNameWithSupplier commonStyles = { };
inline const static DisplayNameWithSupplier closedOnlyStyles = {
{
FillElementLayerStyle::displayName(),
[] { return std::make_unique<FillElementLayerStyle>(); }
},
{
StrokeElementLayerStyle::displayName(),
[] { return std::make_unique<StrokeElementLayerStyle>(true); }
}
};
inline const static DisplayNameWithSupplier unclosedOnlyStyles = { {
StrokeElementLayerStyle::displayName(),
[] { return std::make_unique<StrokeElementLayerStyle>(false); }
} };
DisplayNameWithSupplier unusedStyles;
DisplayNameWithSupplier usedStyles;
std::map<QString, std::shared_ptr<LayerStyle>> styles;
size_t hash;
public:
static LayerStyleContainer fromJson(bool isClosedElement, const QJsonArray& jsonArray);
LayerStyleContainer(bool isClosedElement);
std::vector<Renderer::BaseStyle> toBaseStyles() const override;
QJsonArray toJson() const;
bool empty() const;
bool full() const;
std::map<QString, std::shared_ptr<LayerStyle>>::iterator begin();
std::map<QString, std::shared_ptr<LayerStyle>>::iterator end();
QStringList unusedStyleNames() const;
std::unique_ptr<LayerStyle> makeUnusedStyle(const QString& styleName) const;
bool useStyle(const std::shared_ptr<LayerStyle>& style);
bool dropStyle(const QString& styleName);
float boundingBoxAffectValue() const;
size_t getHash() const;
/**
*
*/
void computeNewHash();
};

View File

@ -23,16 +23,22 @@ FolderLayerWrapper*LayerWrapper::getParent() const
return this == nullptr ? nullptr : this->parent;
}
PixelPath LayerWrapper::getCache()
PixelPath LayerWrapper::getCache(LayerWrapper* selectedLayer)
{
this->refresh();
this->refresh(selectedLayer);
if (selectedLayer == this)
{
this->cache.highLight();
}
return cache;
}
// TODO: undone
LayerWrapper::LayerWrapper(QJsonObject json, FolderLayerWrapper*parent)
LayerWrapper::LayerWrapper(QJsonObject json, FolderLayerWrapper*parent, ElementManager* elementManager)
{
this->parent = parent;
this->elementManager = elementManager;
this->qTreeWidgetItem = new QTreeWidgetItem();
auto transformJson = json.value("transform").toObject();
property.name = json.value("name").toString();
property.offset = {transformJson.value("offset").toObject().value("x").toDouble(),
@ -40,10 +46,12 @@ LayerWrapper::LayerWrapper(QJsonObject json, FolderLayerWrapper*parent)
property.scale = {transformJson.value("scale").toObject().value("x").toDouble(),
transformJson.value("scale").toObject().value("y").toDouble()};
property.rotation = {transformJson.value("rotation").toDouble()};
selected = false;
hidden = false;
}
FolderLayerWrapper::FolderLayerWrapper(QJsonObject json, ElementManager *elementManager, FolderLayerWrapper*parent)
: LayerWrapper(json, parent)
: LayerWrapper(json, parent, elementManager)
{
qDebug() << json.value("name").toString() << " " << this;
QJsonArray childrenJson = json.value("children").toArray();
@ -53,6 +61,7 @@ FolderLayerWrapper::FolderLayerWrapper(QJsonObject json, ElementManager *element
auto p = reinterpret_cast<GroupElement *>(elementManager->getElementById(referencedJson.toInt()));
if (p != nullptr)
p->setSourceLayer(this);
this->referencedBy = referencedJson.toInt();
}
for (auto childJson : childrenJson)
{
@ -66,13 +75,37 @@ FolderLayerWrapper::FolderLayerWrapper(QJsonObject json, ElementManager *element
}
LeafLayerWrapper::LeafLayerWrapper(QJsonObject json, ElementManager* elementManager, FolderLayerWrapper* parent)
: LayerWrapper(json, parent)
: LayerWrapper(json, parent, elementManager),
wrappedElement(elementManager->getElementById(json.value("element").toInt())),
styles(LayerStyleContainer::fromJson(wrappedElement->isClosed(), json.value("styles").toArray()))
{
qDebug() << json.value("name").toString() << " " << this;
int elementIndex = json.value("element").toInt();
wrappedElement = elementManager->getElementById(elementIndex);
if(wrappedElement != nullptr)
wrappedElement->referencedCount++;
}
void LayerWrapper::SimpleProperty::apply(PixelPath&cache) const
LeafLayerWrapper::~LeafLayerWrapper()
{
if (wrappedElement != nullptr)
wrappedElement->referencedCount--;
}
void LayerWrapper::SimpleProperty::apply(PixelPath&cache)
{
transform.reset();
double centerX = cache.getBoundingRect().center().x();
double centerY = cache.getBoundingRect().center().y();
//qDebug() << name << " " << cache.boundingRect().center();
//qDebug() << name << " " << cache.boundingRect();
transform.translate(centerX, centerY);
transform.translate(offset.x(), offset.y());
transform.rotate(rotation);
transform.scale(scale.x(), scale.y());
transform.translate(-centerX, -centerY);
cache = cache.trans(transform);
}
QTransform LayerWrapper::getTransform()
{
QTransform trans;
double centerX = cache.getBoundingRect().center().x();
@ -80,28 +113,28 @@ void LayerWrapper::SimpleProperty::apply(PixelPath&cache) const
//qDebug() << name << " " << cache.boundingRect().center();
//qDebug() << name << " " << cache.boundingRect();
trans.translate(centerX, centerY);
trans.scale(scale.x(), scale.y());
trans.rotate(rotation);
trans.translate(property.offset.x(), property.offset.y());
trans.rotate(property.rotation);
trans.scale(property.scale.x(), property.scale.y());
trans.translate(-centerX, -centerY);
trans.translate(offset.x(), offset.y());
cache = cache.trans(trans);
return trans;
}
void LayerWrapper::refresh()
void LayerWrapper::refresh(LayerWrapper* layer)
{
property.apply(cache);
}
void FolderLayerWrapper::refresh()
void FolderLayerWrapper::refresh(LayerWrapper* layer)
{
cache.clear();
for (auto& child : children) {
qDebug() << child.get();
cache.addPath(child.get()->getCache());
cache.addPath(child.get()->getCache(layer));
}
LayerWrapper::refresh();
}
void LeafLayerWrapper::refresh()
void LeafLayerWrapper::refresh(LayerWrapper* layer)
{
cache.clear();
if (wrappedElement != nullptr)
@ -119,22 +152,22 @@ void LayerWrapper::setParent(FolderLayerWrapper* newParent)
void FolderLayerWrapper::removeAllChild()
{
children.clear();
qTreeWidgetItem.takeChildren();
qTreeWidgetItem->takeChildren();
}
void LayerWrapper::del() {
qDebug() << "LayerWrapper::del()";
if (parent != nullptr){
qTreeWidgetItem.takeChildren();
parent->qTreeWidgetItem.removeChild(&qTreeWidgetItem);
qTreeWidgetItem->takeChildren();
parent->qTreeWidgetItem->removeChild(qTreeWidgetItem);
}
}
void LayerWrapper::delSelf() {
qDebug() << "LayerWrapper::delSelf()";
if (parent != nullptr) {
qTreeWidgetItem.takeChildren();
parent->qTreeWidgetItem.removeChild(&qTreeWidgetItem);
qTreeWidgetItem->takeChildren();
parent->qTreeWidgetItem->removeChild(qTreeWidgetItem);
}
}
@ -154,7 +187,7 @@ void FolderLayerWrapper::delSelf() {
qDebug() << this;
for (auto& child : this->children) {
this->parent->addChild(child);
this->parent->qTreeWidgetItem.addChild(&child.get()->qTreeWidgetItem);
this->parent->qTreeWidgetItem->addChild(child.get()->qTreeWidgetItem);
child->setParent(this->parent);
}
while (!this->children.empty()) {
@ -166,18 +199,258 @@ void FolderLayerWrapper::delSelf() {
QTreeWidgetItem* LayerWrapper::getQTreeItem()
{
this->qTreeWidgetItem.setText(0, this->property.name);
this->qTreeWidgetItem.setData(0, Qt::UserRole, QVariant::fromValue(this));
return &this->qTreeWidgetItem;
this->qTreeWidgetItem->setData(0, Qt::UserRole, QVariant::fromValue(this));
this->qTreeWidgetItem->setCheckState(0, Qt::Checked);
return this->qTreeWidgetItem;
}
QTreeWidgetItem* LeafLayerWrapper::getQTreeItem()
{
if (this->qTreeWidgetItem == nullptr)
this->qTreeWidgetItem = new QTreeWidgetItem();
if (typeid(*wrappedElement) == typeid(GroupElement))
{
this->qTreeWidgetItem->setText(0, "@ "+this->property.name);
}
else
{
this->qTreeWidgetItem->setText(0, this->property.name);
}
this->qTreeWidgetItem->setText(1,">> "+this->wrappedElement->name);
this->qTreeWidgetItem->setTextColor(1, Qt::blue);
return LayerWrapper::getQTreeItem();
}
QTreeWidgetItem* FolderLayerWrapper::getQTreeItem()
{
while (this->qTreeWidgetItem.childCount() > 0) {
this->qTreeWidgetItem.removeChild(this->qTreeWidgetItem.child(0));
}
for (auto& child : this->children) {
this->qTreeWidgetItem.addChild(child->getQTreeItem());
this->qTreeWidgetItem->addChild(child->getQTreeItem());
}
this->qTreeWidgetItem->setText(0, this->property.name);
auto ele = this->elementManager->getElementById(this->getReferencedBy());
if (ele != nullptr)
{
this->qTreeWidgetItem->setText(1, "<< " + ele->name);
this->qTreeWidgetItem->setTextColor(1, Qt::darkGreen);
}
return LayerWrapper::getQTreeItem();
}
//TODO: add effects
QJsonObject LayerWrapper::toJson() const
{
QJsonObject json;
json.insert("name", property.name);
QJsonObject transformJson;
transformJson.insert("offset", QJsonObject({ {"x", property.offset.x()}, {"y", property.offset.y()} }));
transformJson.insert("scale", QJsonObject({ {"x", property.scale.x()}, {"y", property.scale.y()} }));
transformJson.insert("rotation", property.rotation);
json.insert("transform", transformJson);
return json;
}
QJsonObject FolderLayerWrapper::toJson() const
{
QJsonObject json = LayerWrapper::toJson();
QJsonArray childrenJson;
for (auto& child : children)
if(child != nullptr)
childrenJson.push_back(child->toJson());
json.insert("children", childrenJson);
json.insert("is-folder", true);
if(this->getReferencedBy() != -1)
json.insert("referenced-by", this->getReferencedBy());
else
json.insert("referenced-by", QJsonValue());
return json;
}
QJsonObject LeafLayerWrapper::toJson() const
{
QJsonObject json = LayerWrapper::toJson();
json.insert("element", elementManager->getElementIndex(wrappedElement));
json.insert("is-folder", false);
json.insert("styles", styles.toJson());
return json;
}
int FolderLayerWrapper::getReferencedBy()const
{
if (this->elementManager != nullptr)
return this->elementManager->getLayerReferencedBy(this);
else
return -1;
}
void LayerWrapper::paint(QPainter* painter, QTransform transform, bool force)
{
// if (this->selected)
// {
// painter->save();
//painter->setPen(QPen(Qt::red, 2));
//painter->setTransform(transform);
//painter->drawRect(this->cache.getBoundingRect());
//painter->restore();
// }
}
void FolderLayerWrapper::paint(QPainter* painter, QTransform transform, bool force)
{
if (hidden && !force)
return;
LayerWrapper::paint(painter, transform, force);
transform = property.transform * transform;
//qDebug() << transform;
for (auto& child : children)
child->paint(painter, transform, force);
}
void LeafLayerWrapper::paint(QPainter* painter, QTransform transform, bool force)
{
if (hidden && !force)
return;
LayerWrapper::paint(painter, transform, force);
transform = property.transform * transform;
//qDebug() << transform;
if (wrappedElement != nullptr)
{
wrappedElement->paint(painter, transform, styles);
}
}
void LayerWrapper::collectUpReachable(std::set<LayerWrapper*>& reachable)
{
auto cPos = this;
while (cPos != nullptr)
{
reachable.insert(cPos);
cPos = cPos->parent;
}
}
void LayerWrapper::collectDownReachable(std::set<LayerWrapper*>& reachable)
{
reachable.insert(this);
}
void LeafLayerWrapper::collectDownReachable(std::set<LayerWrapper*>& reachable)
{
LayerWrapper::collectDownReachable(reachable);
auto ele = dynamic_cast<GroupElement*>(wrappedElement);
if (ele != nullptr)
{
ele->collectReachable(reachable);
}
}
void FolderLayerWrapper::collectDownReachable(std::set<LayerWrapper*>& reachable)
{
LayerWrapper::collectDownReachable(reachable);
for (auto& child : children)
child->collectDownReachable(reachable);
}
void LayerWrapper::refreshTreeItem()
{
hidden = qTreeWidgetItem->checkState(0) == Qt::Unchecked;
}
void LeafLayerWrapper::refreshTreeItem()
{
LayerWrapper::refreshTreeItem();
if (typeid(*wrappedElement) == typeid(GroupElement))
{
this->qTreeWidgetItem->setText(0, "@ " + this->property.name);
}
else
{
this->qTreeWidgetItem->setText(0, this->property.name);
}
this->qTreeWidgetItem->setText(1, ">> " + this->wrappedElement->name);
this->qTreeWidgetItem->setTextColor(1, Qt::blue);
}
void FolderLayerWrapper::refreshTreeItem()
{
LayerWrapper::refreshTreeItem();
for (auto& child : this->children) {
child->refreshTreeItem();
}
this->qTreeWidgetItem->setText(0, this->property.name);
auto ele = this->elementManager->getElementById(this->getReferencedBy());
if (ele != nullptr)
{
this->qTreeWidgetItem->setText(1, "<< " + ele->name);
this->qTreeWidgetItem->setTextColor(1, Qt::darkGreen);
}
if (this->referencedCount() > 0)
{
this->qTreeWidgetItem->setToolTip(0,QString::fromLocal8Bit("子树被引用计数:") + QString::number(this->referencedCount(true)) + "\n"
+ QString::fromLocal8Bit("当前节点被引用计数:") + QString::number(this->getReferencedBy() != -1));
}
else
{
this->qTreeWidgetItem->setToolTip(0, "");
}
}
size_t LayerWrapper::referencedCount(bool excludeSelf) const
{
return 0;
}
size_t FolderLayerWrapper::referencedCount(bool excludeSelf) const
{
size_t count = 0;
for (auto& child : children)
count += child->referencedCount();
if (!excludeSelf && this->getReferencedBy() != -1)
count++;
return count;
}
bool LayerWrapper::deleteable(bool excludeSubTree) const
{
return true;
}
bool FolderLayerWrapper::deleteable(bool excludeSubTree) const
{
if (excludeSubTree)
return this->getReferencedBy() == -1;
else
return this->referencedCount() == 0;
}
bool LayerWrapper::referencingGroupElement() const
{
return false;
}
bool LeafLayerWrapper::referencingGroupElement() const
{
return typeid(*wrappedElement) == typeid(GroupElement);
}
bool LayerWrapper::canApplyStyles() const
{
return typeid(*this) == typeid(LeafLayerWrapper) && !referencingGroupElement();
}
void LayerWrapper::paintVisualBounding(QPainter* painter) const
{
if (hidden)
return;
QTransform transform;
auto layer = this->parent;
while (layer != nullptr)
{
transform = transform * layer->property.transform;
layer = layer->parent;
}
painter->save();
painter->setTransform(transform);
painter->drawRect(cache.getBoundingRect());
painter->restore();
}

View File

@ -6,6 +6,7 @@
#include <QGraphicsItemGroup>
#include <QGraphicsScene>
#include <QJSonObject>
#include <QJsonArray>
#include <QLine>
#include <QObject>
#include <QPoint>
@ -28,12 +29,15 @@ class LayerWrapper
protected:
FolderLayerWrapper* parent;
QPointF referencePoint;
ElementManager* elementManager;
// vector<LayerStyle> styles;
// TODO: 将cache移到子类对Leaf用ComposedPainterPath对Folder用FolderBitmapPath
PixelPath cache;
public:
QTreeWidgetItem qTreeWidgetItem;
QTreeWidgetItem* qTreeWidgetItem;
bool selected;
bool hidden;
struct SimpleProperty
{
QString name = "";
@ -42,17 +46,20 @@ class LayerWrapper
double rotation = 0;
bool flipHorizontally = 0;
bool flipVertically = 0;
QTransform transform;
// TODO: 将QPainterPath改为BitmapPath
void apply(PixelPath&cache) const;
void apply(PixelPath&cache);
} property;
virtual void setParent(FolderLayerWrapper*newParent);
virtual void refresh();
virtual void refresh(LayerWrapper* layer = nullptr);
virtual QTreeWidgetItem* getQTreeItem();
// TODO: 将QPainterPath改为BitmapPath/QImage或者直接将其删除绘制时直接使用BitmapPath的paint方法
virtual PixelPath getCache();
virtual PixelPath getCache(LayerWrapper* selectedLayer=nullptr);
QTransform getTransform();
FolderLayerWrapper*getParent() const; // invoke by manager, then invoke parent's applyStyles
LayerWrapper(QJsonObject json, FolderLayerWrapper*parent);
LayerWrapper(QJsonObject json, FolderLayerWrapper*parent, ElementManager* elementManager=nullptr);
LayerWrapper() = default;
virtual void paint(QPainter* painter, QTransform transform=QTransform(), bool force = false);
// TODO : export Function
// virtual LayerWrapper *addChild() = 0; // Leaf Child Only
// virtual LayerWrapper *addParent() = 0; // Folder Parent Only
@ -60,18 +67,28 @@ class LayerWrapper
// virtual void deleteAll() const = 0;
virtual void del();
virtual void delSelf();
virtual QJsonObject toJson() const;
~LayerWrapper() = default;
virtual void collectUpReachable(std::set<LayerWrapper*>& reachable);
virtual void collectDownReachable(std::set<LayerWrapper*>& reachable);
virtual void refreshTreeItem();
virtual size_t referencedCount(bool excludeSelf = false) const;
virtual bool deleteable(bool excludeSubTree = false) const;
virtual bool referencingGroupElement() const;
virtual void paintVisualBounding(QPainter* painter) const;
bool canApplyStyles() const;
};
class FolderLayerWrapper : public LayerWrapper
{
public:
vector<shared_ptr<LayerWrapper>> children;
int referencedBy = -1;
public:
~FolderLayerWrapper() = default;
void refresh() override;
void refresh(LayerWrapper* layer=nullptr) override;
FolderLayerWrapper() = default;
FolderLayerWrapper(QJsonObject json, ElementManager *elementManager, FolderLayerWrapper*parent);
void addChild(shared_ptr<LayerWrapper> child);
@ -80,19 +97,31 @@ class FolderLayerWrapper : public LayerWrapper
void del() override;
void delSelf() override;
QTreeWidgetItem* getQTreeItem() override;
QJsonObject toJson() const override;
int getReferencedBy()const;
void paint(QPainter* painter, QTransform transform = QTransform(), bool force = false) override;
void collectDownReachable(std::set<LayerWrapper*>& reachable) override;
void refreshTreeItem() override;
size_t referencedCount(bool excludeSelf = false) const override;
bool deleteable(bool excludeSubTree = false) const override;
};
class LeafLayerWrapper : public LayerWrapper
{
public:
GraphicElement *wrappedElement;
const vector<Renderer::ElementStyleStrokeDemo> styles;
LayerStyleContainer styles;
public:
~LeafLayerWrapper() = default;
void refresh() override;
LeafLayerWrapper() = default;
~LeafLayerWrapper();
void refresh(LayerWrapper* layer = nullptr) override;
LeafLayerWrapper(QJsonObject json, ElementManager *elementManager, FolderLayerWrapper*parent);
QJsonObject toJson() const override;
void paint(QPainter* painter, QTransform transform = QTransform(), bool force = false) override;
void collectDownReachable(std::set<LayerWrapper*>& reachable) override;
QTreeWidgetItem* getQTreeItem() override;
void refreshTreeItem() override;
bool referencingGroupElement() const override;
};
Q_DECLARE_METATYPE(LayerWrapper *)

View File

@ -32,10 +32,15 @@ QPixmap PixelPath::getPixmap() const
return pixmap;
}
QPainterPath PixelPath::getPainterPath() const {
return painterPath;
}
void PixelPath::addPath(const PixelPath& path)
{
QPainter painter(&pixmap);
painter.drawPixmap(0, 0, path.getPixmap());
this->painterPath.addPath(path.getPainterPath());
boundingRect = boundingRect.united(path.getBoundingRect());
}
@ -46,6 +51,7 @@ void PixelPath::addPath(const QPainterPath& path)
painter.setRenderHint(QPainter::HighQualityAntialiasing);
painter.setPen(QPen(Qt::black,1));
painter.drawPath(path);
this->painterPath.addPath(path);
boundingRect = boundingRect.united(path.boundingRect());
}
@ -60,6 +66,7 @@ void PixelPath::clear()
{
pixmap.fill(Qt::transparent);
boundingRect = QRectF(0, 0, 0, 0);
painterPath.clear();
}
PixelPath PixelPath::trans(QTransform& mat)const
@ -70,7 +77,9 @@ PixelPath PixelPath::trans(QTransform& mat)const
painter.setRenderHint(QPainter::HighQualityAntialiasing);
painter.setTransform(mat);
painter.drawPixmap(0, 0, pixmap);
result.boundingRect = mat.mapRect(boundingRect);
result.painterPath.addPath(this->painterPath);
result.painterPath = mat.map(result.painterPath);
result.boundingRect = result.painterPath.boundingRect();
return result;
}
@ -83,5 +92,32 @@ QPixmap PixelPath::resizedPixel(QSize size)const
painter.setRenderHint(QPainter::SmoothPixmapTransform);
painter.setRenderHint(QPainter::HighQualityAntialiasing);
painter.drawPixmap(0, 0, pixmap.scaled(size, Qt::IgnoreAspectRatio, Qt::SmoothTransformation));
return result;
}
QPixmap PixelPath::getDetail()const
{
QPixmap result;
auto rect = boundingRect.toRect();
rect.setHeight(rect.height() + 20);
rect.setWidth(rect.width() + 20);
qDebug() << rect;
result = pixmap.copy(rect);
return result;
}
void PixelPath::highLight()
{
// 创建一个QPainter对象关联到QPixmap对象
QPainter painter(&pixmap);
// 设置画笔的颜色、宽度和样式
painter.setPen(QPen(Qt::black, 1, Qt::DashLine));
// 绘制一个矩形,指定左上角和右下角的坐标
painter.drawRect(boundingRect);
// 结束绘制
painter.end();
}

View File

@ -7,20 +7,24 @@
class PixelPath
{
private:
public:
QRectF boundingRect;
QPixmap pixmap;
QPainterPath painterPath;
int w,h;
public:
PixelPath(int w=1920, int h= 1080);
PixelPath(QPainterPath painterPath,int w = 1920, int h = 1080);
PixelPath(int w=16, int h= 16);
PixelPath(QPainterPath painterPath,int w = 16, int h = 16);
~PixelPath() = default;
QRectF getBoundingRect() const;
QPixmap getPixmap() const;
QPainterPath getPainterPath() const;
void addPath(const PixelPath& path);
void addPath(const QPainterPath& path);
void addImage(const QImage& image,const QPointF& pos);
void clear();
PixelPath trans(QTransform& mat)const;
QPixmap resizedPixel(QSize size)const;
QPixmap getDetail()const;
void highLight();
};

View File

@ -1,8 +1,11 @@
#include "PreviewWindow.h"
#include <QApplication>
PreviewWindow::PreviewWindow(QWidget *parent) : QOpenGLWidget(parent)
{
this->renderer = new Renderer::ElementRenderer(this);
//this->setFixedSize(QSize(108, 108));
this->setStyleSheet("border: 1px solid black");
this->renderer = Renderer::ElementRenderer::instance();
QSurfaceFormat surfaceFormat;
surfaceFormat.setSamples(16);
setFormat(surfaceFormat);
@ -10,12 +13,16 @@ PreviewWindow::PreviewWindow(QWidget *parent) : QOpenGLWidget(parent)
painter->setRenderHint(QPainter::SmoothPixmapTransform);
painter->setRenderHint(QPainter::HighQualityAntialiasing);
layerManager = nullptr;
currentLayer = nullptr;
zoomStep = 0;
backgroundColor = QColor(255, 255, 255, 255);
}
void PreviewWindow::initialize(LayerManager *layerManager,QSize windowSize)
{
this->logicalSize = windowSize;
this->layerManager = layerManager;
this->setFixedSize(windowSize);
}
void PreviewWindow::show()
@ -36,18 +43,18 @@ void PreviewWindow::show()
void PreviewWindow::initializeGL()
{
this->renderer->initialize();
initializeOpenGLFunctions();
}
void PreviewWindow::paintGL()
{
glClearColor(1.0, 1.0, 1.0, 1.0);
glClearColor(backgroundColor.redF(), backgroundColor.greenF(), backgroundColor.blueF(), backgroundColor.alphaF());
glClear(GL_COLOR_BUFFER_BIT);
painter->begin(this);
painter->setWindow(0, 0, logicalSize.width(), logicalSize.height());
painter->setRenderHint(QPainter::Antialiasing);
painter->setRenderHint(QPainter::HighQualityAntialiasing);
layerManager->paint(painter,this->size());
layerManager->paint(painter,this->size(),currentLayer);
painter->end();
}
@ -58,3 +65,89 @@ void PreviewWindow::resizeGL(int w, int h)
Renderer::ElementRenderer* const PreviewWindow::getRenderer()const {
return this->renderer;
}
void PreviewWindow::currentLayerChanged(LayerWrapper* layer)
{
this->currentLayer = layer;
}
void PreviewWindow::refresh()
{
this->repaint();
}
void PreviewWindow::mousePressEvent(QMouseEvent* event)
{
// 当鼠标按下时,记录当前的位置
m_lastPos = event->pos();
}
void PreviewWindow::mouseMoveEvent(QMouseEvent* event)
{
// 当鼠标移动时,计算移动的距离,并根据需要更新图形的状态
int dx = event->x() - m_lastPos.x();
int dy = event->y() - m_lastPos.y();
if (currentLayer != nullptr) {
if (QApplication::keyboardModifiers() == Qt::ControlModifier && (event->buttons() & Qt::LeftButton))
{
currentLayer->property.scale.setX(std::max(0.0, currentLayer->property.scale.x() + dx / 50.0));
currentLayer->property.scale.setY(std::max(0.0, currentLayer->property.scale.y() + dy / 50.0));
}
else if (QApplication::keyboardModifiers() == (Qt::ControlModifier | Qt::ShiftModifier) && (event->buttons() & Qt::LeftButton))
{
currentLayer->property.scale.setX(std::max(0.0, currentLayer->property.scale.x() * (1.0 + dx / 50.0)));
currentLayer->property.scale.setY(std::max(0.0, currentLayer->property.scale.y() * (1.0 + dx / 50.0)));
}
else if (event->buttons() & Qt::LeftButton) {
// 如果按下的是左键,那么平移图形
currentLayer->property.offset.setX(currentLayer->property.offset.x() + dx);
currentLayer->property.offset.setY(currentLayer->property.offset.y() + dy);
}
else if (event->buttons() & Qt::RightButton) {
// 如果按下的是右键,那么旋转图形
qreal angle = dx;
currentLayer->property.rotation += angle;
}
auto layer = currentLayer;
while (layer != nullptr)
{
auto index = -1;
if (typeid(*layer) == typeid(FolderLayerWrapper))
index = dynamic_cast<FolderLayerWrapper*>(layer)->getReferencedBy();
layer = layer->getParent();
}
}
// 更新上一次的位置
emit triggerCentralRefresh();
m_lastPos = event->pos();
this->repaint();
}
void PreviewWindow::mouseReleaseEvent(QMouseEvent* event)
{
//emit layerInfoChanged();
}
void PreviewWindow::setBackgroundColor(QColor color)
{
this->backgroundColor = color;
this->repaint();
}
void PreviewWindow::wheelEvent(QWheelEvent* event)
{
if (QApplication::keyboardModifiers() == Qt::ControlModifier)
{
if (event->delta() > 0 && zoomStep < ZOOM_STEP_MAX)
{
zoomStep++;
this->setFixedSize(logicalSize * (1 + zoomStep * ZOOM_RATE));
}
else if(event->delta() < 0 && zoomStep > ZOOM_STEP_MIN)
{
zoomStep--;
this->setFixedSize(logicalSize * (1 + zoomStep * ZOOM_RATE));
}
this->repaint();
}
}

View File

@ -7,6 +7,7 @@
#include <QJsonObject>
#include <QJsonValue>
#include <QOpenGLFunctions>
#include <QMouseEvent>
#include <QOpenGLWidget>
#include "../Renderer/Preview/ElementRenderer.h"
@ -15,10 +16,26 @@ class PreviewWindow : public QOpenGLWidget, protected QOpenGLFunctions
Q_OBJECT
private:
constexpr static double ZOOM_RATE = 0.25;
constexpr static double ZOOM_DEFAULT = 1.0;
constexpr static int ZOOM_STEP_MIN = -2;
constexpr static int ZOOM_STEP_MAX = 4;
private:
int zoomStep;
QPainter *painter;
LayerManager *layerManager;
Renderer::ElementRenderer* renderer;
QSize logicalSize;
QRectF viewportRect;
LayerWrapper* currentLayer;
QPointF m_lastPos;
QColor backgroundColor;
void mousePressEvent(QMouseEvent* event) override;
void mouseMoveEvent(QMouseEvent* event) override;
void mouseReleaseEvent(QMouseEvent* event) override;
void wheelEvent(QWheelEvent* event) override;
public:
PreviewWindow(QWidget *parent = nullptr);
@ -28,4 +45,15 @@ class PreviewWindow : public QOpenGLWidget, protected QOpenGLFunctions
void paintGL() override;
void resizeGL(int w, int h) override;
Renderer::ElementRenderer* const getRenderer()const;
void setBackgroundColor(QColor color);
public slots:
void currentLayerChanged(LayerWrapper*);
void refresh();
signals:
void layerInfoChanged();
void refreshElementPreview(GraphicElement*);
void refreshElementPreviewByIndex(int);
void triggerCentralRefresh();
};

View File

@ -0,0 +1,21 @@
#include "EditorSettingWidget.h"
#include "../ColorHelper.hpp"
#include <QPushButton>
#include <QInputDialog>
EditorSettingWidget::EditorSettingWidget(QWidget* parent)
: QWidget(parent)
{
ui.setupUi(this);
connect(ui.backgroundColorButton, &QPushButton::clicked, this, [this]() {
QColor color = ColorHelper::execColorDialog(Qt::white, this, QString::fromLocal8Bit("选择背景颜色"));
if (color.isValid())
{
emit backgroundColorChanged(color);
}
});
connect(ui.renameButton, &QPushButton::clicked, this, [this]() {
QString name = QInputDialog::getText(this, QString::fromLocal8Bit("重命名"), QString::fromLocal8Bit("请输入新的项目名称"));
emit projectNameChanged(name);
});
}

View File

@ -0,0 +1,21 @@
#pragma once
#include <qwidget.h>
#include "ui_EditorSettingWidget.h"
class EditorSettingWidget :
public QWidget
{
Q_OBJECT;
private:
Ui::EditorSettingWidget ui;
public:
EditorSettingWidget(QWidget* parent = nullptr);
signals:
void backgroundColorChanged(QColor);
void projectNameChanged(QString);
};

View File

@ -1,34 +1,31 @@
#include "InfoDisplayWidget.h"
#include "./EditorWidgetComponent/LayerStyleDialog.h"
#include <QLineEdit>
#include <QTextBlock>
#include <QListWidget>
#include <QPushButton>
#include <QDialog>
#include <QDialogButtonBox>
#include <QComboBox>
#include <qtmaterialraisedbutton.h>
#include <qtmaterialflatbutton.h>
void InfoDisplayWidget::setLayer(LayerWrapper *layer)
{
this->displayLayer = layer;
generateLayerForm();
}
void InfoDisplayWidget::setElement(GraphicElement *element)
{
this->displayElement = element;
generateElementForm();
}
void InfoDisplayWidget::generateLayerForm()
{
QLayoutItem *item;
if (this->layout() != nullptr)
{
while ((item = this->layout()->takeAt(0)) != nullptr)
while (this->layout()->count() > 0 && (item = this->layout()->takeAt(0)) != nullptr)
{
delete item->widget();
item->widget()->deleteLater();
delete item;
}
delete this->layout();
}
QFormLayout *layout = new QFormLayout();
layout->setRowWrapPolicy(QFormLayout::WrapAllRows);
if (this->displayLayer == nullptr)
@ -47,27 +44,27 @@ void InfoDisplayWidget::generateLayerForm()
rotation->setValidator(new QIntValidator(-10000, 10000, this));
connect(rotation, &QLineEdit::textChanged, [=](QString content) {
this->displayLayer->property.rotation = content.toDouble();
emit requireRefreshPreview();
emit triggerCentralRefresh();
});
offsetX->setValidator(new QIntValidator(-10000, 10000, this));
connect(offsetX, &QLineEdit::textChanged, [=](QString content) {
this->displayLayer->property.offset = {content.toDouble(), this->displayLayer->property.offset.y()};
emit requireRefreshPreview();
emit triggerCentralRefresh();
});
offsetY->setValidator(new QIntValidator(-10000, 10000, this));
connect(offsetY, &QLineEdit::textChanged, [=](QString content) {
this->displayLayer->property.offset = {this->displayLayer->property.offset.x(), content.toDouble()};
emit requireRefreshPreview();
emit triggerCentralRefresh();
});
scaleX->setValidator(new QDoubleValidator(0.001, 1000, 4, this));
scaleX->setValidator(new QDoubleValidator(-1000, 1000, 4, this));
connect(scaleX, &QLineEdit::textChanged, [=](QString content) {
this->displayLayer->property.scale = {content.toDouble(), this->displayLayer->property.scale.y()};
emit requireRefreshPreview();
emit triggerCentralRefresh();
});
scaleY->setValidator(new QDoubleValidator(0.001, 1000, 4, this));
scaleY->setValidator(new QDoubleValidator(-1000, 1000, 4, this));
connect(scaleY, &QLineEdit::textChanged, [=](QString content) {
this->displayLayer->property.scale = {this->displayLayer->property.scale.x(), content.toDouble()};
emit requireRefreshPreview();
emit triggerCentralRefresh();
});
layout->addRow("layer name:", name);
@ -78,78 +75,113 @@ void InfoDisplayWidget::generateLayerForm()
layout->addRow("scale-Y:", scaleY);
layout->setRowWrapPolicy(QFormLayout::DontWrapRows);
bool styleEnabled = true;
if (styleEnabled) {
QListWidget* styleList = new QListWidget(this);
QListWidgetItem* item = new QListWidgetItem("样式列表");
item->setFlags(Qt::NoItemFlags);
styleList->addItem(item);
static vector<QString> styleNames = { "样例1", "样例2", "样例3" };
auto createStyleItem = [this, styleList](int index) {
QListWidgetItem* item = new QListWidgetItem;
QWidget* w = new QWidget;
item->setSizeHint(QSize(50, 40));
QHBoxLayout* layout = new QHBoxLayout;
QPushButton* deleteButton = new QPushButton(w);
QPushButton* detailButton = new QPushButton(w);
QLabel* name = new QLabel(w);
name->setText(styleNames[index]);
detailButton->setText("...");
detailButton->setFixedSize(QSize(20, 20));
deleteButton->setText("×");
deleteButton->setFixedSize(QSize(20, 20));
connect(detailButton, &QPushButton::clicked, [styleList, item, this, index]() {
QDialog dlg(this);
dlg.setWindowTitle("样式详情");
dlg.resize(400, 200);
QGridLayout *contentLayout = new QGridLayout(&dlg);
QLineEdit* name = new QLineEdit(styleNames[index], &dlg);
auto buttonBox = new QDialogButtonBox(QDialogButtonBox::Save | QDialogButtonBox::Cancel);
contentLayout->addWidget(buttonBox);
connect(buttonBox, &QDialogButtonBox::accepted, &dlg, &QDialog::accept);
connect(buttonBox, &QDialogButtonBox::rejected, &dlg, &QDialog::reject);
bool updateStyle = dlg.exec();
if (updateStyle) {
styleNames[index] = name->text();
qDebug() << name->text();
// 在此处修改新样式信息至内存
emit requireRefreshPreview();
emit requireSelfRefresh();
if (auto* leafP = dynamic_cast<LeafLayerWrapper*>(this->displayLayer); leafP) {
auto* styleList = new QListWidget(this);
auto* header = new QListWidgetItem;
auto* headerWidget = new QWidget(styleList);
auto* headerLayout = new QHBoxLayout;
auto* headerLabel = new QLabel(headerWidget);
headerLabel->setText("样式列表");
headerLabel->setSizePolicy(QSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding));
//QtMaterialRaisedButton* addStyleButton = new QtMaterialRaisedButton("+", headerWidget);
auto* addStyleButton = new QPushButton("+", headerWidget);
addStyleButton->setFixedSize(QSize(20, 20));
if (leafP->styles.full())
{
addStyleButton->setDisabled(true);
}
else
{
connect(addStyleButton, &QPushButton::clicked, [&, leafP] {
auto* dialog = new LayerStyleDialog(leafP->styles, nullptr, this);
dialog->exec();
if (dialog->layerStyle)
{
leafP->styles.useStyle(dialog->layerStyle);
leafP->styles.computeNewHash();
emit triggerCentralRefresh();
}
});
connect(deleteButton, &QPushButton::clicked, [styleList,item,this]() {
styleList->removeItemWidget(item);
delete item;
// 删除layer对应样式
emit requireRefreshPreview();
emit requireSelfRefresh();
}
headerLayout->addWidget(headerLabel);
headerLayout->addWidget(addStyleButton);
headerLayout->setContentsMargins(5, 0, 5, 0);
headerWidget->setLayout(headerLayout);
header->setFlags(Qt::NoItemFlags);
styleList->addItem(header);
styleList->setItemWidget(header, headerWidget);
auto* styles = &leafP->styles;
for (auto styleIterator = styles->begin(); styleIterator != styles->end(); ++styleIterator)
{
auto* item = new QListWidgetItem;
auto* w = new QWidget(this);
item->setSizeHint(QSize(50, 40));
auto* layout = new QHBoxLayout(w);
layout->setAlignment(Qt::AlignmentFlag::AlignRight);
//QtMaterialFlatButton* detailButton = new QtMaterialFlatButton(w);
//QtMaterialFlatButton* removeButton = new QtMaterialFlatButton(w);
auto* detailButton = new QPushButton(w);
auto* removeButton = new QPushButton(w);
detailButton->setText("...");
detailButton->setFixedSize(QSize(20, 20));
removeButton->setText("×");
removeButton->setFixedSize(QSize(20, 20));
connect(detailButton, &QPushButton::clicked, this,
[this, styles, styleIterator]
{
auto* dialog =
new LayerStyleDialog(*styles, styleIterator->second, this);
dialog->exec();
if (dialog->layerStyle)
{
styleIterator->second = dialog->layerStyle;
styles->computeNewHash();
emit triggerCentralRefresh();
}
});
layout->addWidget(name);
connect(removeButton, &QPushButton::clicked, this,
[this, styleIterator, styles]
{
styles->dropStyle(styleIterator->first);
styles->computeNewHash();
emit triggerCentralRefresh();
});
QWidget* styleDisplayWidget = styleIterator->second->getListDisplayWidget();
styleDisplayWidget->setParent(w);
styleDisplayWidget->setSizePolicy(QSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding));
layout->addWidget(styleDisplayWidget);
layout->addWidget(detailButton);
layout->addWidget(deleteButton);
layout->addWidget(removeButton);
w->setLayout(layout);
styleList->addItem(item);
styleList->setItemWidget(item, w);
};
for (int i = 0; i < styleNames.size(); i++)
createStyleItem(i);
}
layout->addRow(styleList);
}
}
this->setLayout(layout);
}
void InfoDisplayWidget::generateElementForm()
{
}
void InfoDisplayWidget::triggerSelfRefresh()
{
if (this->displayLayer != nullptr)
this->generateLayerForm();
else
this->generateElementForm();
}
void InfoDisplayWidget::refresh()
{
if (this->displayLayer != nullptr)
this->generateLayerForm();
}

View File

@ -4,26 +4,27 @@
#include <QFormLayout>
#include <QLabel>
#include <QWidget>
#include "ElementPoolWidget.h"
class InfoDisplayWidget : public QWidget
{
Q_OBJECT
private:
LayerWrapper *displayLayer;
GraphicElement *displayElement;
public:
void setLayer(LayerWrapper *layer);
void setElement(GraphicElement *element);
void generateLayerForm();
void generateElementForm();
void refresh();
public slots:
void triggerSelfRefresh();
signals:
void triggerCentralRefresh();
void requireRefreshPreview();
void requireSelfRefresh();
void requireRefreshElementWidget();
};

View File

@ -1,21 +1,36 @@
#include "LayerTreeWidget.h"
#include <QInputDialog>
#include <QMenu>
#include "./EditorWidgetComponent/LayerCreateWidget.h"
#include <QTimer>
LayerTreeWidget::LayerTreeWidget(QWidget *parent)
{
emit displayLayerChange(nullptr);
this->selectedItem = nullptr;
this->copiedItem = nullptr;
this->setContextMenuPolicy(Qt::CustomContextMenu);
this->setHeaderLabel("Layer Content");
this->setColumnWidth(0, 240);
connect(this, &QTreeWidget::customContextMenuRequested, this, &LayerTreeWidget::popMenu);
connect(this, &QTreeWidget::currentItemChanged, [=](QTreeWidgetItem *currentItem) {
if (this->selectedItem != nullptr) {
this->selectedItem->data(0, Qt::UserRole).value<LayerWrapper*>()->selected = false;
}
this->selectedItem = currentItem;
if(this->selectedItem !=nullptr)
emit displayLayerChange(this->selectedItem->data(0, Qt::UserRole).value<LayerWrapper *>());
else
if (this->selectedItem != nullptr) {
auto layer = this->selectedItem->data(0, Qt::UserRole).value<LayerWrapper*>();
layer->selected = true;
emit displayLayerChange(layer);
}
else {
emit displayLayerChange(nullptr);
}
emit triggerCentralRefresh();
});
connect(this, &QTreeWidget::itemChanged, this, [=]() {
emit triggerCentralRefresh();
});
// connect(this, &QTreeWidget::itemDoubleClicked, this, &LayerTreeWidget::onItemDoubleClicked);
}
@ -35,27 +50,66 @@ void LayerTreeWidget::popMenu(const QPoint &pos)
QMenu menu;
QTreeWidgetItem *item = itemAt(pos);
this->selectedItem = item;
// TODO
menu.addAction(QString::fromLocal8Bit("´´½¨×Ó½Úµã"), this, &LayerTreeWidget::onRenameEvent);
if (item != nullptr) {
auto layer = this->selectedItem->data(0, Qt::UserRole).value<LayerWrapper*>();
if (layer != nullptr) {
if (typeid(*layer) == typeid(FolderLayerWrapper)) {
menu.addAction(QString::fromLocal8Bit("创建子节点"), this, [this, layer]() {
auto dialog = new LayerCreateWidget(elementManager, dynamic_cast<FolderLayerWrapper*>(layer), this);
connect(dialog, &LayerCreateWidget::LayerInfoReturned, this, [this, layer](QJsonObject jsonObj) {
auto folderLayer = dynamic_cast<FolderLayerWrapper*>(layer);
LayerWrapper* newLayer;
qDebug() << this->elementManager;
if(jsonObj.value("is-folder").toBool())
newLayer = new FolderLayerWrapper(jsonObj, this->elementManager, folderLayer);
else
newLayer = new LeafLayerWrapper(jsonObj, this->elementManager, folderLayer);
folderLayer->addChild(std::shared_ptr<LayerWrapper>(newLayer));
folderLayer->qTreeWidgetItem->addChild(newLayer->getQTreeItem());
qDebug() << jsonObj<<"----------------------";
emit triggerCentralRefresh();
});
dialog->exec();
});
}
if (layer != root) {
menu.addAction(QString::fromLocal8Bit("删除"), this, [this]() {
auto layer = this->selectedItem->data(0, Qt::UserRole).value<LayerWrapper*>();
layer->del();
layer->getParent()->removeChild(layer);
emit triggerCentralRefresh();
});
menu.actions().last()->setEnabled(layer->deleteable());
menu.addAction(QString::fromLocal8Bit("重命名"), this, &LayerTreeWidget::onRenameEvent);
// menu.addAction("Copy", this, &LayerTreeWidget::onRenameEvent);
if (item != nullptr && item->childCount() > 0)
if(typeid(*layer) == typeid(FolderLayerWrapper))
{
menu.addAction(QString::fromLocal8Bit("删除(保留子节点)"), this, [this]() {
auto layer = this->selectedItem->data(0, Qt::UserRole).value<LayerWrapper*>();
layer->delSelf();
layer->getParent()->removeChild(layer);
this->refresh();
emit requireRefreshPreview();
emit triggerCentralRefresh();
});
menu.addAction(QString::fromLocal8Bit("ɾ³ý"), this, [this]() {
if (this->selectedItem == nullptr)
return;
auto layer = this->selectedItem->data(0, Qt::UserRole).value<LayerWrapper*>();
layer->del();
layer->getParent()->removeChild(layer);
this->refresh();
emit requireRefreshPreview();
menu.actions().last()->setEnabled(layer->deleteable(true));
}
}
if (typeid(*layer) == typeid(FolderLayerWrapper) && ((FolderLayerWrapper*)layer)->getReferencedBy() == -1) {
menu.addAction(QString::fromLocal8Bit("创建组合元素"), this, [this, layer]() {
auto layer = dynamic_cast<FolderLayerWrapper*>(this->selectedItem->data(0, Qt::UserRole).value<LayerWrapper*>());
if (layer != nullptr) {
bool ok;
QString name = QInputDialog::getText(this, QString::fromLocal8Bit("创建组合元素"),
QString::fromLocal8Bit("组合元素名称:"), QLineEdit::Normal,
QString::fromLocal8Bit("图源") + layer->property.name, &ok);
if (ok && !name.isEmpty()) {
elementManager->createGroupElement(name, layer);
emit triggerCentralRefresh();
}
}
});
}
}
}
menu.exec(mapToGlobal(pos));
}
@ -76,9 +130,5 @@ void LayerTreeWidget::onRenameEvent()
}
void LayerTreeWidget::refresh() {
// if(this->root!=nullptr)
//{
// this->clear();
// this->addTopLevelItem(this->root->getQTreeItem());
//}
this->root->refreshTreeItem();
}

View File

@ -1,5 +1,6 @@
#pragma once
#include "LayerWrapper.h"
#include "ElementManager.h"
#include <QPoint>
#include <QTreeWidget>
class LayerTreeWidget : public QTreeWidget
@ -10,6 +11,7 @@ class LayerTreeWidget : public QTreeWidget
LayerWrapper *copiedItem;
public:
ElementManager* elementManager;
LayerWrapper* root;
LayerTreeWidget(QWidget *parent = nullptr);
void onRenameEvent();
@ -19,6 +21,8 @@ class LayerTreeWidget : public QTreeWidget
// void onItemDoubleClicked(QTreeWidgetItem *item, int column = 0);
signals:
void triggerCentralRefresh();
void displayLayerChange(LayerWrapper *);
void requireRefreshPreview();
void requireRefreshElementWidget();
};

View File

@ -0,0 +1,25 @@
#pragma once
#include <QByteArray>
namespace EncodeUtil
{
#include <vector>
template<typename S>
QString toBase64(const std::vector<S>& vec)
{
QByteArray ba;
ba.resize(vec.size() * sizeof(S));
memcpy_s(ba.data(), ba.size(), vec.data(), vec.size() * sizeof(S));
return ba.toBase64();
}
template<typename T>
std::vector<T> fromBase64(const QString& str)
{
QByteArray ba = QByteArray::fromBase64(str.toUtf8());
std::vector<T> vec;
vec.resize(ba.size() / sizeof(T));
memcpy_s(vec.data(), vec.size() * sizeof(T), ba.data(), ba.size());
return vec;
}
}

View File

@ -0,0 +1,14 @@
#pragma once
#include <QJsonArray>
namespace JsonUtil
{
#include <vector>
template<typename T>
QJsonArray toQJsonArray(const std::vector<T>& vec)
{
QJsonArray array;
std::copy(vec.begin(), vec.end(), std::back_inserter(array));
return array;
}
}

View File

@ -15,7 +15,7 @@ vector<vector<Point>> PainterPathUtil::transformToLines(const QPainterPath& pai
for (int elementIndex = 0; elementIndex < painterPath.elementCount(); elementIndex++) {
auto element = painterPath.elementAt(elementIndex);
point = element;
qDebug() << element;
//qDebug() << element;
if (element.isMoveTo()) {
if (line.size() >= 2) {
lines.push_back(line);
@ -43,7 +43,7 @@ vector<vector<Point>> PainterPathUtil::transformToLines(const QPainterPath& pai
return lines;
}
QPainterPath PainterPathUtil::monotonization(QPainterPath& painterPath) {
QPainterPath PainterPathUtil::monotonization(const QPainterPath& painterPath) {
QPainterPath resPath;
vector<vector<Point> > lines = transformToLines(painterPath);
vector<shared_ptr<Line>> linePtrVector;
@ -62,15 +62,15 @@ QPainterPath PainterPathUtil::monotonization(QPainterPath& painterPath) {
return resPath;
}
std::pair<QPainterPath, float> PainterPathUtil::normalized(const QPainterPath& path)
std::pair<QPainterPath, float> PainterPathUtil::normalized(const QPainterPath& path, float width)
{
auto rect = path.boundingRect();
return { (QTransform::fromTranslate(-rect.center().x(), -rect.center().y()) * QTransform::fromScale(1 / (rect.width() / 1.999999), -1 / (rect.height() / 1.999999))).map(path),
return { (QTransform::fromTranslate(-rect.center().x(), -rect.center().y()) * QTransform::fromScale(1 / (rect.width() / 1.999999), -1 / (rect.height() / 1.999999)) * QTransform::fromScale(1 / (1 + width),1 / (1 + width))).map(path),
rect.width() / rect.height() };
}
std::pair<std::vector<std::vector<Renderer::Point>>, float> PainterPathUtil::toNormalizedLines(const QPainterPath& path)
std::pair<std::vector<std::vector<Renderer::Point>>, float> PainterPathUtil::toNormalizedLines(const QPainterPath& path, float width)
{
auto [p, ratio] = normalized(path);
auto [p, ratio] = normalized(path, width);
return { transformToLines(p), ratio };
}

View File

@ -7,8 +7,8 @@ class PainterPathUtil
{
public:
static std::vector<std::vector<Renderer::Point>> transformToLines(const QPainterPath& painterPath);
static QPainterPath monotonization(QPainterPath& painterPath);
static std::pair<QPainterPath, float> normalized(const QPainterPath& path);
static std::pair<std::vector<std::vector<Renderer::Point>>, float> toNormalizedLines(const QPainterPath& path);
static QPainterPath monotonization(const QPainterPath& painterPath);
static std::pair<QPainterPath, float> normalized(const QPainterPath& path, float width = 0);
static std::pair<std::vector<std::vector<Renderer::Point>>, float> toNormalizedLines(const QPainterPath& path, float width = 0);
};

View File

@ -0,0 +1,135 @@
#include "PaintingUtil.h"
#include <QFile>
#include <QJsondocument>
#include "PainterPathUtil.h"
#include <queue>
#include <QFileInfo>
using Renderer::Painting;
using Renderer::BaseElement;
using Renderer::ElementTransform;
using glm::bvec2;
using std::max;
using std::shared_ptr;
using std::make_shared;
using std::min;
using std::queue;
struct LayerNode {
LayerWrapper* nowLayer;
QTransform transfrom;
};
QJsonObject PaintingUtil::readJsonFile(QString jsonFilePath) {
QFile jsonFile(jsonFilePath);
qDebug() << jsonFilePath;
jsonFile.open(QFile::ReadOnly);
QByteArray fileContent = jsonFile.readAll().trimmed();
jsonFile.close();
QJsonParseError jError;
QJsonDocument jsonDoc(QJsonDocument::fromJson(fileContent, &jError));
return jsonDoc.object();
}
Painting PaintingUtil::transfromToPainting(QString jsonFilePath) {
Painting painting;
glm::bvec2 flip(0, 0);
QJsonObject jsonObj = readJsonFile(jsonFilePath);
qDebug() << jsonObj;
shared_ptr<ElementManager> elementManager = make_shared<ElementManager>(jsonObj, QFileInfo(jsonFilePath).absolutePath());
shared_ptr<LayerManager> layerManager = make_shared<LayerManager>(jsonObj, elementManager.get());
//qDebug() << elementManager->toJson();
//qDebug() << layerManager->toJson();
//qDebug() << ((SimpleElement*)((LeafLayerWrapper*)((FolderLayerWrapper*)((FolderLayerWrapper*)layerManager->getRoot())->children[0].get())->children[0].get())->wrappedElement)->painterPath;
//qDebug() << ((LeafLayerWrapper*)((FolderLayerWrapper*)((FolderLayerWrapper*)layerManager->getRoot())->children[0].get())->children[0].get())->getCache().painterPath;
//qDebug() << elementManager->toJson();
//qDebug() << layerManager->toJson();
//qDebug() << ((SimpleElement*)((LeafLayerWrapper*)((FolderLayerWrapper*)((FolderLayerWrapper*)layerManager->getRoot())->children[0].get())->children[0].get())->wrappedElement)->painterPath;
//qDebug() << ((LeafLayerWrapper*)((FolderLayerWrapper*)((FolderLayerWrapper*)layerManager->getRoot())->children[0].get())->children[0].get())->getCache().painterPath;
queue<LayerNode> layerQueue;
LayerWrapper* root = layerManager->getRoot();
root->getCache();
//double maxLineWidth = getMaxLineWidth(root);
layerQueue.push({ root, root->property.transform });
while (!layerQueue.empty()) {
auto layerNode = layerQueue.front();
layerQueue.pop();
FolderLayerWrapper* nowLayer = handleLayerWrapper(layerNode.nowLayer, layerNode.transfrom, painting);
if (nowLayer != nullptr) {
for (auto sonLayer : nowLayer->children) {
layerQueue.push({ sonLayer.get(), layerNode.transfrom });
}
}
}
return painting;
}
FolderLayerWrapper* PaintingUtil::handleLayerWrapper(LayerWrapper* nowLayer, QTransform& transform, Painting& painting) {
LeafLayerWrapper* leafLayer = dynamic_cast<LeafLayerWrapper*>(nowLayer);
transform = nowLayer->property.transform * transform;
if (leafLayer != nullptr) {
GroupElement* wrapperElement = dynamic_cast<GroupElement*>(leafLayer->wrappedElement);
if (wrapperElement != nullptr) {
transform = wrapperElement->sourceLayer->property.transform * transform;
return wrapperElement->sourceLayer;
}
PixelPath pixelPath = nowLayer->getCache();
QPainterPath painterPath = pixelPath.getPainterPath();
QRectF bound = painterPath.boundingRect();
//qDebug() << leafLayer<<"------" << painterPath;
// transform to -1£¬ 1
QTransform trans;
double maxLen = std::max(bound.width(), bound.height());
qDebug() << maxLen << bound;
trans.scale(1 / maxLen, 1 / maxLen);
trans.translate(-bound.center().x(), -bound.center().y());
painterPath = trans.map(painterPath);
shared_ptr<vector<vector<Renderer::Point> >> contour = std::make_shared<vector<vector<Renderer::Point> >>(PainterPathUtil::transformToLines(painterPath));
QSize screenSize = QSize(1024, 1024);
ElementTransform elementTransform;
transform = trans.inverted() * transform * QTransform::fromScale(2. / screenSize.width(), 2. / screenSize.height()) * QTransform::fromTranslate(-1, -1) * QTransform::fromScale(1, -1);
auto baseStyles = leafLayer->styles.toBaseStyles();
Renderer::BaseElement element;
element.contour = contour;
for (auto& baseStyle : baseStyles) {
double lineWidth = 0;
if (baseStyle.material->type() == Renderer::MaterialStyleType::kStroke) {
auto material = std::static_pointer_cast<MaterialStyleStroke>(baseStyle.material);
material->halfWidth /= maxLen;
lineWidth = material->halfWidth;
qDebug() << material->halfWidth;
}
QPainterPathStroker stroker;
stroker.setWidth(lineWidth * 2);
stroker.setCapStyle(Qt::RoundCap);
stroker.setJoinStyle(Qt::RoundJoin);
QPainterPath strokePath = stroker.createStroke(painterPath);
auto rect = transform.map(strokePath).boundingRect();
elementTransform.bound = glm::vec4(rect.x(), rect.y(), rect.x() + rect.width(), rect.y() + rect.height());
qDebug() << elementTransform.bound.x << elementTransform.bound.y << elementTransform.bound.z << elementTransform.bound.z;
transform = transform.inverted();
elementTransform.transform = glm::mat3x2(
transform.m11(), transform.m12(), transform.m21(),
transform.m22(), transform.m31(), transform.m32()
);
//qDebug() << transform;
elementTransform.zIndex = 0;
element.style = baseStyle.material;
painting.addElement(element, elementTransform);
}
return nullptr;
}
FolderLayerWrapper* folderLayer = dynamic_cast<FolderLayerWrapper*>(nowLayer);
return folderLayer;
}

View File

@ -0,0 +1,15 @@
#pragma once
#include "../../Renderer/Painting/Painting.h"
#include <QJsonObject>
#include <ElementManager.h>
class PaintingUtil
{
private:
//static const double pi;
static QJsonObject readJsonFile(QString jsonFilePath);
static FolderLayerWrapper* handleLayerWrapper(LayerWrapper* nowLayer, QTransform& transform, Renderer::Painting& painting);
//static double getMaxLineWidth(LayerWrapper* root);
public:
static Renderer::Painting transfromToPainting(QString jsonFilePath);
};

View File

@ -86,7 +86,6 @@ QPolygonF SvgFileLoader::handleAttrPoints(QString points) {
void SvgFileLoader::handleAttrTransform(QString transformStyle, QPainterPath& painterPath) {
QTransform trans;
QStringList ops = transformStyle.split(')');
reverse(ops.begin(), ops.end());
vector<double> numbers;
for (auto op : ops) {
op = op.simplified();

View File

@ -0,0 +1,29 @@
#include "FluentMenu.h"
#include <QPainter>
#include <glm/glm.hpp>
::FluentMenu::FluentMenu(QWidget* parent) : QMenu(parent)
{
setWindowFlags(windowFlags() | Qt::FramelessWindowHint | Qt::NoDropShadowWindowHint);
setAttribute(Qt::WA_TranslucentBackground);
setStyleSheet(QString::fromStdString(std::format("QMenu {{margin:{}px;border-radius:{}px;padding:4px;border: 1px solid #c1c1c1;}}"
"QMenu::item {{height:30px;width:100px;padding-left:20px;border-radius:{}px;}}"
"QMenu::item:selected {{background-color:#dedede;}}", shadowRadius, borderRadius, itemBorderRadius)));
}
void ::FluentMenu::paintEvent(QPaintEvent* event)
{
QPainter painter(this);
painter.setRenderHint(QPainter::Antialiasing, true);
for (qreal i = 0; i < shadowRadius; i += 1 / devicePixelRatioF())
{
painter.setPen(QColor(0, 0, 0, glm::mix(0.22 * 255, 0., glm::pow(i / shadowRadius, 0.2))));
painter.drawRoundedRect(QRectF(shadowRadius - i, shadowRadius - i, width() - (shadowRadius - i) * 2, height() - (shadowRadius - i) * 2), borderRadius + i, borderRadius + i);
}
QMenu::paintEvent(event);
}
QAction* FluentMenu::exec(const QPoint& pos, QAction* at)
{
return QMenu::exec(parentWidget()->mapToGlobal(parentWidget()->mapFromGlobal(pos) + QPoint(-shadowRadius, -shadowRadius)), at);
}

View File

@ -0,0 +1,15 @@
#pragma once
#include <QMenu>
class FluentMenu : public QMenu
{
Q_OBJECT
public:
explicit FluentMenu(QWidget* parent = nullptr);
void paintEvent(QPaintEvent* event) override;
QAction* exec(const QPoint& pos, QAction* at = nullptr);
int shadowRadius = 16;
int borderRadius = 6;
int itemBorderRadius = 4;
};

View File

@ -1,75 +0,0 @@
/*
The MIT License (MIT)
Copyright © 2018-2022 Antonio Dias
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
*/
#include "iconwidget.h"
#define ICONWIDTH 16
#define ICONHEIGHT 16
IconWidget::IconWidget(QWidget *parent) : QWidget(parent)
{
m_active = true;
}
void IconWidget::setPixmap(const QPixmap &pixmap)
{
m_pixmap = pixmap;
QImage tmp = m_pixmap.toImage();
for (int i = 0; i < m_pixmap.width(); i++)
{
for (int j = 0; j < m_pixmap.height(); j++)
{
int gray = qGray(tmp.pixel(i, j));
int alpha = qAlpha(tmp.pixel(i, j));
tmp.setPixel(i, j, qRgba(gray, gray, gray, alpha));
}
}
m_grayed_pixmap = QPixmap::fromImage(tmp);
repaint();
}
void IconWidget::setActive(bool active)
{
m_active = active;
repaint();
}
void IconWidget::paintEvent(QPaintEvent *event)
{
Q_UNUSED(event)
QPainter painter(this);
painter.setRenderHints(QPainter::Antialiasing | QPainter::SmoothPixmapTransform);
painter.drawPixmap((width() - ICONWIDTH)/ 2,
(height() - ICONHEIGHT) / 2,
ICONWIDTH, ICONHEIGHT,
m_active ? m_pixmap : m_grayed_pixmap);
}

View File

@ -1,52 +0,0 @@
/*
The MIT License (MIT)
Copyright © 2018-2022 Antonio Dias
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
*/
#ifndef ICONWIDGET_H
#define ICONWIDGET_H
#include <QtCore>
#include <QtGui>
#include <QtWidgets>
class IconWidget : public QWidget
{
Q_OBJECT
public:
explicit IconWidget(QWidget *parent = nullptr);
public slots:
void setPixmap(const QPixmap &pixmap);
void setActive(bool active);
private:
//Functions
void paintEvent(QPaintEvent *event);
//Variables
QPixmap m_pixmap;
QPixmap m_grayed_pixmap;
bool m_active;
};
#endif // ICONWIDGET_H

View File

@ -2,6 +2,7 @@
#include "Renderer/RendererGLWidget.h"
#include "qslider.h"
#include <QPushButton>
#include <QScreen>
#include "NavigationBarWidget.h"
#include <FramelessHelper/Core/utils.h>
#include <FramelessHelper/Widgets/standardtitlebar.h>
@ -11,19 +12,13 @@
FRAMELESSHELPER_USE_NAMESPACE
CentralWidget::CentralWidget(QWidget* parent) : QMainWindow(parent)
CentralWidget::CentralWidget(QWidget* parent) : QWidget(parent)
{
ui.setupUi(this);
NavigationBarWidget* navigationBarWidget = new NavigationBarWidget();
QHBoxLayout* tabBarLayout = new QHBoxLayout(ui.tabWidget);
tabBarLayout->setSpacing(0);
tabBarLayout->setMargin(0);
tabBarLayout->addWidget(navigationBarWidget, 0, Qt::AlignTop | Qt::AlignHCenter);
QObject::connect(navigationBarWidget->tabs, &QtMaterialTabs::currentChanged, ui.tabWidget, &QTabWidget::setCurrentIndex);
QObject::connect(ui.tabWidget, &QTabWidget::currentChanged,
ui.rendererWidget, &Renderer::RendererWidget::currentTabChanged);
NavigationBarWidget* navigationBarWidget = new NavigationBarWidget(this);
ui.gridLayout->addWidget(navigationBarWidget, 0, 0, 1, 1, Qt::AlignTop | Qt::AlignHCenter);
QObject::connect(navigationBarWidget->tabs, &QtMaterialTabs::currentChanged, ui.stackedWidget, &QStackedWidget::setCurrentIndex);
QObject::connect(ui.stackedWidget, &QStackedWidget::currentChanged, ui.rendererWidget, &Renderer::RendererWidget::currentTabChanged);
}
CentralWidget::~CentralWidget()
@ -44,7 +39,7 @@ MainWindow::MainWindow(QWidget* parent, const Qt::WindowFlags flags)
m_titleBar->chromePalette()->setTitleBarActiveBackgroundColor(QColor(0, 0, 0, 0));
m_titleBar->chromePalette()->setTitleBarInactiveBackgroundColor(QColor(0, 0, 0, 0));
m_central_widget = new CentralWidget();
m_central_widget = new CentralWidget(this);
setMenuWidget(m_titleBar);
setCentralWidget(m_central_widget);
@ -55,15 +50,15 @@ MainWindow::MainWindow(QWidget* parent, const Qt::WindowFlags flags)
helper->setSystemButton(m_titleBar->maximizeButton(), Global::SystemButtonType::Maximize);
helper->setSystemButton(m_titleBar->closeButton(), Global::SystemButtonType::Close);
/*connect(helper, &FramelessWidgetsHelper::ready, this, [this, helper]() {
helper->moveWindowToDesktopCenter();
});*/
//connect(helper, &FramelessWidgetsHelper::ready, this, [this, helper]() {
// helper->moveWindowToDesktopCenter();
// });
setWindowIcon(QIcon(":/images/icon.png"));
setWindowTitle("ArchitectureColoredPainting");
resize(m_central_widget->size());
//move(QGuiApplication::primaryScreen()->availableGeometry().center() - rect().center());
move(QGuiApplication::primaryScreen()->availableGeometry().center() - rect().center());
}
@ -77,4 +72,5 @@ void MainWindow::closeEvent(QCloseEvent* event)
if (result != QMessageBox::Yes)
event->ignore();*/
qDebug() << "closeEvent";
}

View File

@ -9,7 +9,7 @@ FRAMELESSHELPER_BEGIN_NAMESPACE
class StandardTitleBar;
FRAMELESSHELPER_END_NAMESPACE
class CentralWidget : public QMainWindow
class CentralWidget : public QWidget
{
Q_OBJECT
public:

View File

@ -1,25 +1,24 @@
#pragma once
#include <QOpenGLFunctions_4_5_Core>
#include <QString>
#include <QVector>
#include <QVector2D>
#include <QVector3D>
#include <vector>
#include <QOpenGLShaderProgram>
#include <QOpenGLBuffer>
#include <QOpenGLVertexArrayObject>
#include <QOpenGLTexture>
#include <QOpenGLWidget>
#include <assimp/vector3.h>
#include <glm/glm.hpp>
#include "Drawable.h"
namespace Renderer
{
struct Vertex
{
QVector3D Position;
QVector3D Normal;
QVector2D TexCoords;
Vertex(const aiVector3D& position, const aiVector3D& Normal, const aiVector3D& TexCoords);
glm::vec3 Position;
glm::vec3 Normal;
glm::vec2 TexCoords;
Vertex(const aiVector3D& position, const aiVector3D& normal, const aiVector3D& texCoords);
};
struct Texture
@ -38,8 +37,8 @@ namespace Renderer
class Mesh : public Drawable
{
public:
QVector<Vertex> vertices;
QVector<unsigned int> indices;
std::vector<Vertex> vertices;
std::vector<unsigned int> indices;
//QVector<Texture*> textures;
GLuint textureBasecolor = 0;
GLuint textureMetallicRoughness = 0;

View File

@ -14,6 +14,9 @@
#include "../Editor/util/PainterPathUtil.h"
#include "../Editor/util/SvgFileLoader.h"
#include <ThirdPartyLib/qquick/qquicksvgparser_p.h>
#include <util/PaintingUtil.h>
#include "Painting/MaterialStyleStroke.h"
#include <glm/gtc/matrix_transform.hpp>
using namespace Renderer;
using std::vector;
@ -30,14 +33,12 @@ Model::Model(QOpenGLContext* context, QOpenGLShaderProgram* shaderProgram,
}
void Model::draw() {
//shaderProgram->bind();
for (auto& mesh : meshes) {
mesh->draw();
}
}
void Model::drawShadow() {
//shaderProgram->bind();
for (auto& mesh : meshes) {
mesh->drawShadow();
}
@ -45,34 +46,73 @@ void Model::drawShadow() {
void Renderer::Model::loadModel(QString path)
{
directory = path;
QFileInfo modelFile(path);
name = modelFile.baseName();
directory = modelFile.dir();
Assimp::Importer importer;
const aiScene* scene = importer.ReadFile(directory.absolutePath().toUtf8(), aiProcess_Triangulate | aiProcess_FlipUVs);
const aiScene* scene = importer.ReadFile(modelFile.absoluteFilePath().toUtf8(), aiProcess_Triangulate);
if (!scene || scene->mFlags & AI_SCENE_FLAGS_INCOMPLETE || !scene->mRootNode)
{
qDebug() << "ERROR::ASSIMP::" << importer.GetErrorString() << endl;
qCritical() << "ERROR::ASSIMP::" << importer.GetErrorString();
return;
}
qDebug() << directory.absolutePath() << "Loaded Successfully";
qDebug() << modelFile.absoluteFilePath() << "Loaded Successfully";
qDebug() << "NumMeshes: " << scene->mNumMeshes;
qDebug() << "NumMaterials: " << scene->mNumMaterials;
qDebug() << "NumTextures: " << scene->mNumTextures;
directory.cdUp();
unloadModel();
if (QFile paintingConfigFile(directory.filePath(name + ".txt")); paintingConfigFile.open(QFile::ReadOnly | QIODevice::Text))
{
QTextStream stream(&paintingConfigFile);
while (!stream.atEnd())
{
QString source, target;
glm::vec2 leftBottom, rightTop;
stream >> source >> target >> leftBottom.x >> leftBottom.y >> rightTop.x >> rightTop.y;
qDebug() << source << target << leftBottom.x << leftBottom.y << rightTop.x << rightTop.y;
paintingMap.emplace(source.toStdString(), std::forward_as_tuple(target.toStdString(), leftBottom, rightTop));
}
paintingConfigFile.close();
}
else
{
qWarning() << "Painting Config Not Found!";
}
aiMatrix4x4 transform;
aiMatrix4x4::Scaling(aiVector3D(1 / 0.008), transform);
processNode(scene->mRootNode, scene, transform * scene->mRootNode->mTransformation);
AABB.clear();
AABB.emplace_back(minX, minY, minZ);
AABB.emplace_back(minX, minY, maxZ);
AABB.emplace_back(minX, maxY, minZ);
AABB.emplace_back(minX, maxY, maxZ);
AABB.emplace_back(maxX, minY, minZ);
AABB.emplace_back(maxX, minY, maxZ);
AABB.emplace_back(maxX, maxY, minZ);
AABB.emplace_back(maxX, maxY, maxZ);
}
void Renderer::Model::unloadModel()
{
paintingMap.clear();
for (auto& i : paintingLoaded)
vtManager->deleteVirtualTexture(i.second);
paintingLoaded.clear();
texturesLoaded.clear();
meshes.clear();
minX = std::numeric_limits<float>::max();
maxX = std::numeric_limits<float>::min();
minY = std::numeric_limits<float>::max();
maxY = std::numeric_limits<float>::min();
minZ = std::numeric_limits<float>::max();
maxZ = std::numeric_limits<float>::min();
processNode(scene->mRootNode, scene);
AABB.push_back(QVector3D(minX, minY, minZ));
AABB.push_back(QVector3D(minX, minY, maxZ));
AABB.push_back(QVector3D(minX, maxY, minZ));
AABB.push_back(QVector3D(minX, maxY, maxZ));
AABB.push_back(QVector3D(maxX, minY, minZ));
AABB.push_back(QVector3D(maxX, minY, maxZ));
AABB.push_back(QVector3D(maxX, maxY, minZ));
AABB.push_back(QVector3D(maxX, maxY, maxZ));
}
void Model::processNode(aiNode* node, const aiScene* scene, aiMatrix4x4 mat4)
@ -93,86 +133,76 @@ void Model::processNode(aiNode* node, const aiScene* scene, aiMatrix4x4 mat4)
std::unique_ptr<Drawable> Model::processMesh(aiMesh* mesh, const aiScene* scene, aiMatrix4x4 model)
{
aiMaterial* material = scene->mMaterials[mesh->mMaterialIndex];
QMatrix4x4 modelQ((float*)&model);
aiString str;
material->GetTexture(aiTextureType_BASE_COLOR, 0, &str);
if (paintingProgram != nullptr && (std::strcmp(str.C_Str(), "17876391417123941155.jpg") == 0 || std::strcmp(str.C_Str(), "11474523244911310074.jpg") == 0))
{
qDebug() << str.C_Str() << "Replaced";
// 初始化网格
auto m_mesh = std::make_unique<PaintingMesh>(glFunc, paintingProgram, shadowProgram, modelQ);
// 遍历网格的每个顶点
std::vector<Vertex> vertices;
for (unsigned int i = 0; i < mesh->mNumVertices; i++)
{
if (mesh->mNormals && mesh->mTextureCoords[0])
{
Vertex vertex(mesh->mVertices[i], mesh->mNormals[i], mesh->mTextureCoords[0][i]);
minX = std::min(minX, vertex.Position.x());
maxX = std::max(maxX, vertex.Position.x());
minY = std::min(minY, vertex.Position.y());
maxY = std::max(maxY, vertex.Position.y());
minZ = std::min(minZ, vertex.Position.z());
maxZ = std::max(maxZ, vertex.Position.z());
m_mesh->vertices.push_back(vertex);
auto pos = mesh->mVertices[i];
vertices.push_back(Vertex(pos, mesh->mNormals[i], mesh->mTextureCoords[0][i]));
auto worldPos = model * pos;
minX = std::min(minX, worldPos.x);
maxX = std::max(maxX, worldPos.x);
minY = std::min(minY, worldPos.y);
maxY = std::max(maxY, worldPos.y);
minZ = std::min(minZ, worldPos.z);
maxZ = std::max(maxZ, worldPos.z);
}
}
for (unsigned int i = 0; i < mesh->mNumFaces; i++)
std::vector<unsigned int> indices;
for (auto face = mesh->mFaces; face < mesh->mFaces + mesh->mNumFaces; face++)
indices.insert(indices.end(), face->mIndices, face->mIndices + face->mNumIndices);
aiMaterial* material = scene->mMaterials[mesh->mMaterialIndex];
if (auto iter = paintingMap.find([&] {
aiString str;
material->GetTexture(aiTextureType_BASE_COLOR, 0, &str);
return std::string(str.C_Str());
}()); paintingProgram != nullptr && iter != paintingMap.end())
{
aiFace face = mesh->mFaces[i];
// 将所有面的索引数据添加到索引数组中
for (unsigned int j = 0; j < face.mNumIndices; j++) {
m_mesh->indices.push_back(face.mIndices[j]);
}
qDebug() << iter->first.c_str() << "Replaced";
auto mesh = std::make_unique<PaintingMesh>(glFunc, paintingProgram, shadowProgram, modelQ);
auto& [paintingPath, leftBottom, rightTop] = iter->second;
for (auto& v : vertices)
{
v.TexCoords = (v.TexCoords - leftBottom) / (rightTop - leftBottom);
//qDebug() << v.TexCoords.x << v.TexCoords.y;
}
m_mesh->paintingId = loadPainting(std::string(str.C_Str()));
auto& handle = vtManager->getPaintingHandle(m_mesh->paintingId);
m_mesh->textureBasecolor = handle.baseColor;
m_mesh->textureMetallicRoughness = handle.metallicRoughness;
m_mesh->setupMesh();
return m_mesh;
mesh->vertices = vertices;
mesh->indices = indices;
mesh->paintingId = loadPainting(paintingPath);
auto& handle = vtManager->getPaintingHandle(mesh->paintingId);
mesh->textureBasecolor = handle.baseColor;
mesh->textureMetallicRoughness = handle.metallicRoughness;
mesh->setupMesh();
return mesh;
}
else
{
// 初始化网格
auto m_mesh = std::make_unique<Mesh>(glFunc, shaderProgram, shadowProgram, modelQ);
// 遍历网格的每个顶点
for (unsigned int i = 0; i < mesh->mNumVertices; i++)
{
if (mesh->mNormals && mesh->mTextureCoords[0])
{
Vertex vertex(mesh->mVertices[i], mesh->mNormals[i], mesh->mTextureCoords[0][i]);
minX = std::min(minX, vertex.Position.x());
maxX = std::max(maxX, vertex.Position.x());
minY = std::min(minY, vertex.Position.y());
maxY = std::max(maxY, vertex.Position.y());
minZ = std::min(minZ, vertex.Position.z());
maxZ = std::max(maxZ, vertex.Position.z());
m_mesh->vertices.push_back(vertex);
}
}
auto mesh = std::make_unique<Mesh>(glFunc, shaderProgram, shadowProgram, modelQ);
mesh->vertices = vertices;
mesh->indices = indices;
// 将所有面的索引数据添加到索引数组中
for (auto face = mesh->mFaces; face < mesh->mFaces + mesh->mNumFaces; face++)
for (auto indice = face->mIndices; indice < face->mIndices + face->mNumIndices; indice++)
m_mesh->indices.push_back(*indice);
// 处理材质
if (!(m_mesh->textureBasecolor = loadMaterialTextures(material, aiTextureType_BASE_COLOR)))
if (!(mesh->textureBasecolor = loadMaterialTextures(material, aiTextureType_BASE_COLOR)))
qWarning() << "Basecolor Texture Loading Failed!";
if (!(m_mesh->textureMetallicRoughness = loadMaterialTextures(material, aiTextureType_METALNESS)))
if (!(mesh->textureMetallicRoughness = loadMaterialTextures(material, aiTextureType_METALNESS)))
qWarning() << "MetallicRoughness Texture Loading Failed!";
if (!(m_mesh->textureNormal = loadMaterialTextures(material, aiTextureType_NORMALS)))
if (!(mesh->textureNormal = loadMaterialTextures(material, aiTextureType_NORMALS)))
qWarning() << "Normal Texture Loading Failed!";
if (m_mesh->textureBasecolor && m_mesh->textureMetallicRoughness && m_mesh->textureNormal)
if (mesh->textureBasecolor && mesh->textureMetallicRoughness && mesh->textureNormal)
{
m_mesh->setupMesh();
return m_mesh;
mesh->setupMesh();
return mesh;
}
else
return nullptr;
@ -203,7 +233,7 @@ GLuint Model::loadMaterialTextures(aiMaterial* mat, aiTextureType type)
texture.setWrapMode(QOpenGLTexture::DirectionS, QOpenGLTexture::Repeat);
texture.setWrapMode(QOpenGLTexture::DirectionT, QOpenGLTexture::Repeat);
texture.setMinMagFilters(QOpenGLTexture::LinearMipMapLinear, QOpenGLTexture::Linear);
texture.setData(data);
texture.setData(data.mirrored());
return texture.textureId();
}
@ -213,12 +243,13 @@ GLuint Renderer::Model::loadPainting(std::string path)
if (iter != paintingLoaded.end())
return iter->second;
//vector<std::shared_ptr<Contour>> contour = {
// std::make_shared<Contour>(SvgParser("M100,100C-.5,100,0,100.5,0,0L40,.07C40,59.5,39.5,60,100,60Z", 100, 100).parse()),
// std::make_shared<Contour>(SvgParser("M308.49,212.25l23,28.38-82,78.31c-14.28,13.64-26.34-20.6-53.44,9.32l-30.24-13.4,63.56-51.59L190.71,215.6l-32.92,26.72L149.5,232.1l32.92-26.72L173.2,194l-32.91,26.72-7.38-9.08L165.83,185l-38.69-47.66L94.22,164,85,152.65l32.91-26.72-9.21-11.35L75.79,141.3l-5.53-6.81,32.92-26.72L94,96.42,61.05,123.14l-12-14.76L37.72,117.6l12,14.75L30.41,148,0,110.55,136.2,0l30.4,37.46L147.31,53.12l-12-14.76L124,47.58l12,14.75L103.05,89.05l9.21,11.35,32.92-26.72,5.52,6.81-32.91,26.72L127,118.56l32.92-26.72,9.21,11.35-32.91,26.72,38.69,47.67,32.91-26.72,7.37,9.08-32.91,26.72L191.49,198l32.92-26.72,8.29,10.22-32.92,26.71,38.7,47.68L302,204.3l6.45,7.95Z", 331.52, 328.26).parse()),
// std::make_shared<Contour>(SvgParser("M377,459.61a11.26,11.26,0,0,1,11.27-11.27H696.12a11.27,11.27,0,0,0,11-8.62A359.84,359.84,0,0,0,708,280.56a11.26,11.26,0,0,0-11-8.73H388.27A11.26,11.26,0,0,1,377,260.57h0a11.26,11.26,0,0,1,11.27-11.26H683.71A11.32,11.32,0,0,0,694.28,234C649.8,113.69,542.57,23.85,412.3,4.12a11.22,11.22,0,0,0-12.76,11.17v158.9a11.26,11.26,0,0,0,11.26,11.27H583.12a11.32,11.32,0,0,0,9.26-17.75c-31.67-46.59-78.51-75.2-109.11-90.07a11.25,11.25,0,0,0-16.13,10.17V115.2a11.24,11.24,0,0,0,6.22,10.07l7.51,3.76a11.28,11.28,0,0,1,5,15.12h0a11.27,11.27,0,0,1-15.11,5l-20-10a11.27,11.27,0,0,1-6.22-10.07V54a11.27,11.27,0,0,1,14.62-10.75c5.11,1.59,125.66,40.35,172.24,149A11.27,11.27,0,0,1,621.11,208H388.27A11.26,11.26,0,0,1,377,196.73V11.36A11.32,11.32,0,0,0,365.89.08C363.34,0,360.79,0,358.22,0s-5.11,0-7.66.08a11.32,11.32,0,0,0-11.11,11.28V196.74A11.26,11.26,0,0,1,328.18,208H95.35A11.27,11.27,0,0,1,85,192.3c46.57-108.67,167.12-147.42,172.23-149A11.26,11.26,0,0,1,271.86,54v75.11a11.25,11.25,0,0,1-6.23,10.07l-20,10a11.27,11.27,0,0,1-15.11-5h0a11.26,11.26,0,0,1,5-15.11l7.52-3.76a11.27,11.27,0,0,0,6.22-10.07V87.82a11.25,11.25,0,0,0-16.14-10.16c-30.6,14.87-77.45,43.48-109.1,90.07a11.3,11.3,0,0,0,9.25,17.74H305.66a11.26,11.26,0,0,0,11.27-11.26V15.31A11.22,11.22,0,0,0,304.17,4.14C173.88,23.86,66.66,113.71,22.17,234a11.32,11.32,0,0,0,10.56,15.29H328.18a11.26,11.26,0,0,1,11.27,11.26v0a11.26,11.26,0,0,1-11.27,11.26H19.52a11.26,11.26,0,0,0-11,8.72,359.84,359.84,0,0,0,.83,159.16,11.26,11.26,0,0,0,11,8.61H328.18a11.26,11.26,0,0,1,11.27,11.27h0a11.26,11.26,0,0,1-11.27,11.26h-294a11.32,11.32,0,0,0-10.53,15.4C69,604.65,175.3,692.78,304.16,712.3a11.21,11.21,0,0,0,12.76-11.16V542.22A11.26,11.26,0,0,0,305.66,531h-166c-9.53,0-14.89,11.22-8.69,18.47,34.09,39.77,74.45,65.66,101.77,80.18a11.25,11.25,0,0,0,16.53-10V591a11.26,11.26,0,0,1,11.26-11.26h0A11.26,11.26,0,0,1,271.85,591v63.85A11.27,11.27,0,0,1,256.8,665.5c-4.45-1.59-109.58-40-171-139.9a11.27,11.27,0,0,1,9.59-17.17H328.18a11.26,11.26,0,0,1,11.27,11.26V705.08a11.32,11.32,0,0,0,11.11,11.28q3.82.07,7.66.08c2.57,0,5.12,0,7.67-.08A11.32,11.32,0,0,0,377,705.08V519.69a11.25,11.25,0,0,1,11.27-11.26H621.1a11.26,11.26,0,0,1,9.59,17.16c-61.46,99.87-166.59,138.3-171,139.9a11.27,11.27,0,0,1-15-10.61V591a11.26,11.26,0,0,1,11.26-11.26h0A11.26,11.26,0,0,1,467.14,591v28.6a11.25,11.25,0,0,0,16.53,10c27.33-14.53,67.68-40.42,101.77-80.19,6.2-7.23.85-18.46-8.69-18.46h-166a11.26,11.26,0,0,0-11.26,11.26V701.12a11.21,11.21,0,0,0,12.76,11.17c128.86-19.51,235.14-107.66,280.48-226a11.33,11.33,0,0,0-10.53-15.41h-294A11.25,11.25,0,0,1,377,459.61ZM35.27,399.53V316.9a11.26,11.26,0,0,1,11.27-11.26H669.92a11.25,11.25,0,0,1,11.26,11.26v82.63a11.25,11.25,0,0,1-11.26,11.26H46.54a11.27,11.27,0,0,1-11.27-11.26Z", 716.45, 716.44).parse())
//};
Painting painting;
path = "../test.json";
if (auto file = QFileInfo(QString(path.c_str())); file.isFile())
painting = PaintingUtil::transfromToPainting(file.filePath());
else
{
qDebug() << path.c_str() << "Not Found, Using Default Painting";
vector<std::pair<std::shared_ptr<Contour>, float>> contours;
QPainterPath painterPaths[3];
@ -248,13 +279,84 @@ GLuint Renderer::Model::loadPainting(std::string path)
std::make_shared<Element>(Element{ contours[1].first, style[2], contours[1].second}),
std::make_shared<Element>(Element{ contours[2].first, style[0], contours[2].second}),
};
Painting painting;
if (path == "17876391417123941155.jpg")
if (path == "0.json")
{
painting.addElement(*element[0], ElementTransform{ glm::vec2(-0.45,-0.45), glm::vec2(0.5,0.5) / 2.f, 0, glm::bvec2(false), 0 });
painting.addElement(*element[1], ElementTransform{ glm::vec2(-0.45, 0.45), glm::vec2(0.5,0.5) / 2.f, 0, glm::bvec2(false), 0 });
painting.addElement(*element[2], ElementTransform{ glm::vec2(0.50,-0.45), glm::vec2(0.6,0.7) / 2.f, 0, glm::bvec2(false), 0 });
//painting.addElement(*element[0], ElementTransform{ glm::vec2(-0.45,-0.45), glm::vec2(0.5,0.5) / 2.f, 0, glm::bvec2(false), 0 });
//painting.addElement(*element[1], ElementTransform{ glm::vec2(-0.45, 0.45), glm::vec2(0.5,0.5) / 2.f, 0, glm::bvec2(false), 0 });
//painting.addElement(*element[2], ElementTransform{ glm::vec2(0.50,-0.45), glm::vec2(0.6,0.7) / 2.f, 0, glm::bvec2(false), 0 });
}
else if (path == "1.json")
{
//painting.backgroundColor = QColor(196, 81, 35);
float widths[] = { 0.22, 0.22 * 0.25 / 0.15, 0.22 * 0.25 / 0.15 };
QPainterPath painterPaths[6];
for (int i = 0; i < 6; i++)
if (!SvgFileLoader().loadSvgFile(QString(std::format("../svg/{}.svg", i + 1).c_str()), painterPaths[i]))
qCritical() << "load error";
vector<std::pair<std::shared_ptr<Contour>, float>> contours;
for (int i = 0; i < 3; i++)
{
auto [contour, ratio] = PainterPathUtil::toNormalizedLines(painterPaths[i], widths[i]);
contours.emplace_back(std::make_shared<Contour>(contour), ratio);
}
class StyleStrokeRadialGradient : public Renderer::ElementStyle
{
public:
float width;
StrokeType type;
StyleStrokeRadialGradient(float width, StrokeType type) :width(width), type(type) {};
virtual std::vector<Renderer::BaseStyle> toBaseStyles() const override
{
std::map<float, Material> materialMap = {
{0.09, Material{QColor(255,255,255),0,0.8}},
{0.63, Material{QColor(165,176,207),0,0.8}},
{1.00, Material{QColor(58,64,151),0,0.8}}
};
return { BaseStyle(std::make_shared<TransformStyle>(),
std::make_shared<MaterialStyleStroke>(width, type, StrokeEndType::kFlat,
std::make_shared<StrokeRadialGradient>(materialMap, false))) };
}
};
vector<std::shared_ptr<ElementStyle>> style = {
std::make_shared<StyleStrokeRadialGradient>(widths[0], StrokeType::kLeftSide),
std::make_shared<StyleStrokeRadialGradient>(widths[1], StrokeType::kRightSide),
std::make_shared<StyleStrokeRadialGradient>(widths[2], StrokeType::kLeftSide),
};
std::map<float, Material> materialMap = {
{0.09, Material{QColor(255,255,255),0,0.8}},
{0.63, Material{QColor(165,176,207),0,0.8}},
{1.00, Material{QColor(58,64,151),0,0.8}}
};
vector<std::shared_ptr<BaseElement>> element = {
std::make_shared<BaseElement>(contours[0].first, std::make_shared<MaterialStyleStroke>(
widths[0], StrokeType::kLeftSide, StrokeEndType::kFlat, std::make_shared<StrokeRadialGradient>(materialMap, false))),
std::make_shared<BaseElement>(contours[1].first, std::make_shared<MaterialStyleStroke>(
widths[1], StrokeType::kRightSide, StrokeEndType::kFlat, std::make_shared<StrokeRadialGradient>(materialMap, false))),
std::make_shared<BaseElement>(contours[2].first, std::make_shared<MaterialStyleStroke>(
widths[2], StrokeType::kLeftSide, StrokeEndType::kFlat, std::make_shared<StrokeRadialGradient>(materialMap, false))),
};
QTransform trans = QTransform::fromScale(0.3, 0.3).inverted();
glm::mat3x2 mat(trans.m11(), trans.m12(), trans.m21(), trans.m22(), trans.m31(), trans.m32());
trans = QTransform::fromTranslate(0.5, 0).scale(0.3, 0.3).inverted();
glm::mat3x2 mat2(trans.m11(), trans.m12(), trans.m21(), trans.m22(), trans.m31(), trans.m32());
//mat = glm::mat3x2(11, 22, 33, 44, 55, 66);
//mat2 = glm::mat3x2(1, 2, 3, 4, 5, 6);
qDebug() << std::format("\n{} {} {}\n {} {} {}\n{} {} {}",
trans.m11(), trans.m21(), trans.m31(),
trans.m12(), trans.m22(), trans.m32(),
trans.m13(), trans.m23(), trans.m33()).c_str();
//painting.addElement(*element[0], ElementTransform{ glm::vec4(-1,-1,1,1), mat, 0});
painting.addElement(*element[1], ElementTransform{ glm::vec4(-1,-1,0,1), mat, 0 });
painting.addElement(*element[2], ElementTransform{ glm::vec4(0,-1,1,1), mat2, 0 });
//painting.addElement(*element[0], ElementTransform{ glm::vec2(-0.45,0.45), glm::vec2(0.25), 0, glm::bvec2(false), 0 });
//painting.addElement(*element[1], ElementTransform{ glm::vec2(-0.535,0.33), glm::vec2(0.15), 0, glm::bvec2(false), 0 });
//painting.addElement(*element[2], ElementTransform{ glm::vec2(-0.535,0.23), glm::vec2(0.15), 0, glm::bvec2(false), 0 });
}
else
{
@ -262,11 +364,10 @@ GLuint Renderer::Model::loadPainting(std::string path)
{
float x = (float)rand() / RAND_MAX * 2 - 1;
float y = (float)rand() / RAND_MAX * 2 - 1;
painting.addElement(*element[i % 3], ElementTransform{ glm::vec2(x,y), glm::vec2(0.025), (float)rand() / RAND_MAX * 360, glm::bvec2(false), 0 });
//painting.addElement(*element[i % 3], ElementTransform{ glm::vec2(x,y), glm::vec2(0.025), (float)rand() / RAND_MAX * 360, glm::bvec2(false), 0 });
}
}
}
painting.generateBuffers(glFunc);
auto index = vtManager->createVirtualTexture(painting);

View File

@ -15,22 +15,27 @@ namespace Renderer
void draw();
void drawShadow();
void loadModel(QString path);
void unloadModel();
Model(QOpenGLContext* context, QOpenGLShaderProgram* shaderProgram, QOpenGLShaderProgram* paintingProgram, QOpenGLShaderProgram* shadowProgram, VirtualTextureManager* vtManager);
private:
QOpenGLContext* context;
QOpenGLContext* context = nullptr;
QOpenGLFunctions_4_5_Core* glFunc = nullptr;
QOpenGLShaderProgram* shaderProgram = nullptr;
QOpenGLShaderProgram* paintingProgram = nullptr;
QOpenGLShaderProgram* shadowProgram = nullptr;
VirtualTextureManager* vtManager = nullptr;
/* 模型数据 */
/**
* @brief key BaseColor \n
* value json,
*/
std::unordered_map<std::string, std::tuple<std::string, glm::vec2, glm::vec2>> paintingMap;
std::unordered_map<std::string, GLuint> paintingLoaded;
std::unordered_map<std::string, QOpenGLTexture> texturesLoaded;
std::vector<std::unique_ptr<Drawable>> meshes;
/// 模型所在路径
QDir directory;
QDir directory; /// 模型所在路径
QString name; /// 模型文件名
float minX, maxX, minY, maxY, minZ, maxZ;

View File

@ -1,6 +1,43 @@
#include "BaseStyle.h"
#include "MaterialStyleFill.h"
#include "MaterialStyleStroke.h"
using namespace Renderer;
using namespace glm;
Renderer::Material::Material(const QColor& color, float metallic, float roughness)
: color(color)
{
setMetallicF(metallic);
setRoughnessF(roughness);
}
Renderer::Material::Material(const glm::vec4& color, const glm::vec2& metallicRoughness)
: color(QColor::fromRgbF(color.r, color.g, color.b, color.a))
{
setMetallicF(metallicRoughness.r);
setRoughnessF(metallicRoughness.g);
}
float Renderer::Material::metallicF() const
{
return metallic / 255.f;
}
float Renderer::Material::roughnessF() const
{
return roughness / 255.f;
}
void Renderer::Material::setMetallicF(float metallic)
{
this->metallic = metallic * 255;
}
void Renderer::Material::setRoughnessF(float roughness)
{
this->roughness = roughness * 255;
}
bool Renderer::Material::operator==(const Material& m) const
{
@ -9,5 +46,52 @@ bool Renderer::Material::operator==(const Material& m) const
std::pair<glm::vec4, glm::vec2> Renderer::Material::toVec() const
{
return { glm::vec4(color.redF(), color.greenF(), color.blueF(), color.alphaF()), glm::vec2(metallic, roughness)};
return { glm::vec4(color.redF(), color.greenF(), color.blueF(), color.alphaF()), glm::vec2(metallicF(), roughnessF())};
}
std::unique_ptr<MaterialStyle> Renderer::MaterialStyle::decoded(const std::vector<GLfloat>& encoded)
{
if (encoded[0] <= 0) /// MaterialStyleFill
{
std::unique_ptr<MaterialFill> materiallFill;
glm::vec4 head = glm::unpackUnorm4x8(glm::floatBitsToUint(encoded[0]));
if (head.z == 0)
{
materiallFill = std::make_unique<FillPlain>(Material(glm::unpackUnorm4x8(glm::floatBitsToUint(encoded[1])), glm::vec2(head.r, head.g)));
}
return std::make_unique<MaterialStyleFill>(std::move(materiallFill));
}
else /// MaterialStyleStroke
{
std::unique_ptr<MaterialStroke> materialStroke;
uint headUint = floatBitsToUint(encoded[1]);
vec4 head = unpackUnorm4x8(headUint);
StrokeType strokeType = (StrokeType)floor(head.b * 10);
StrokeEndType endType = (StrokeEndType)(int(round(head.b * 100)) % 10);
switch (int(head.a * 100) % 10)
{
/// Plain
case 0: {
materialStroke = std::make_unique<StrokePlain>(Material(glm::unpackUnorm4x8(glm::floatBitsToUint(encoded[2])), glm::vec2(head.r, head.g)));
break;
}
/// RadialGradient
case 1: {
uint size = headUint % (1 << 15);
bool gradual = (headUint & (1 << 15)) != 0;
std::map<float, Material> materialMap;
for (uint i = 0; i < size; i++)
{
auto data = floatBitsToUint(encoded[2 + i * 2]);
auto level = unpackUnorm2x16(data).y;
auto color = unpackUnorm4x8(floatBitsToUint(encoded[3 + i * 2]));
auto metallicRoughness = unpackUnorm4x8(data);
materialMap.emplace(level, Material(color, glm::vec2(metallicRoughness.r, metallicRoughness.g)));
}
materialStroke = std::make_unique<StrokeRadialGradient>(materialMap, gradual);
break;
}
}
return std::make_unique<MaterialStyleStroke>(encoded[0], strokeType, endType, std::move(materialStroke));
}
}

View File

@ -23,7 +23,9 @@ namespace Renderer
public:
virtual MaterialStyleType type() const = 0;
virtual std::vector<GLfloat> encoded() const = 0;
virtual std::unique_ptr<MaterialStyle> clone() const = 0;
virtual bool operator==(const MaterialStyle&) const = 0;
static std::unique_ptr<MaterialStyle> decoded(const std::vector<GLfloat>& encoded);
};
struct BaseStyle
@ -35,8 +37,16 @@ namespace Renderer
struct Material
{
QColor color;
float metallic;
float roughness;
std::uint8_t metallic;
std::uint8_t roughness;
Material() = default;
Material(const QColor& color, float metallic = 0, float roughness = 0.5);
Material(const glm::vec4& color, const glm::vec2& metallicRoughness);
float metallicF() const;
float roughnessF() const;
void setMetallicF(float metallic);
void setRoughnessF(float roughness);
bool operator==(const Material&) const;
std::pair<glm::vec4, glm::vec2> toVec() const;
};

View File

@ -20,12 +20,12 @@ QVector4D BvhTree::Union(QVector4D a, QVector4D b) {
QVector4D BvhTree::merge(BvhPtr lp, BvhPtr rp) {
QVector4D a = lp->bound, b = rp->bound;
if (lp->isLeaf) {
/*if (lp->isLeaf) {
a = BvhTreeData::boundWithRotation(a, lp->getRightSon());
}
if (rp->isLeaf) {
b = BvhTreeData::boundWithRotation(b, rp->getRightSon());
}
}*/
return Union(a, b);
}

View File

@ -1,16 +1,16 @@
#include "Element.h"
void Renderer::ElementTransform::applyTransformStyle(const TransformStyle& t)
{
center += t.translation;
scale *= t.scale;
rotation += t.rotation;
flip ^= t.flip;
}
Renderer::ElementTransform Renderer::ElementTransform::appliedTransformStyle(const TransformStyle& t) const
{
ElementTransform result = *this;
result.applyTransformStyle(t);
return result;
}
//void Renderer::ElementTransform::applyTransformStyle(const TransformStyle& t)
//{
// /*center += t.translation;
// scale *= t.scale;
// rotation += t.rotation;
// flip ^= t.flip;*/
//}
//
//Renderer::ElementTransform Renderer::ElementTransform::appliedTransformStyle(const TransformStyle& t) const
//{
// ElementTransform result = *this;
// result.applyTransformStyle(t);
// return result;
//}

View File

@ -18,14 +18,15 @@ namespace Renderer
struct ElementTransform
{
glm::vec2 center;
//glm::vec2 size;
glm::vec2 scale; /// 相对于画布
float rotation; /// 角度制
glm::bvec2 flip;
//glm::vec2 center;
//glm::vec2 scale; /// 相对于画布
//float rotation; /// 角度制
//glm::bvec2 flip;
glm::vec4 bound; /// 包围盒,不影响变换
glm::mat3x2 transform; /// 逆变换
GLuint zIndex;
void applyTransformStyle(const TransformStyle& t);
ElementTransform appliedTransformStyle(const TransformStyle& t) const;
//void applyTransformStyle(const TransformStyle& t);
//ElementTransform appliedTransformStyle(const TransformStyle& t) const;
};
}

View File

@ -141,12 +141,13 @@ bool LineTree::handleShortCutNode(LineTreeNode& fa, LineTreeNode& nowTreeNode, d
nowTreeNode.lineSet.push_back(lineIndex);
}
}
if (nowTreeNode.lineSet.size() <= requiredLineMin) {
if (nowTreeNode.lineSet.size() <= requiredLineMin
|| (nowTreeNode.bound.z()-nowTreeNode.bound.x())*sqrt(2) <= lineWidth) {
if (nowTreeNode.lineSet.empty())
return false;
restOfTreeNodes.push_back(nowTreeNode);
nowTreeNode.divided = false;
v.push_back(nowTreeNode);
//v.push_back(nowTreeNode);
return false;
}
else {

View File

@ -3,7 +3,12 @@
using namespace Renderer;
Renderer::FillPlain::FillPlain(QColor color, float metallic, float roughness)
: color(color), metallic(metallic), roughness(roughness)
: material(color, metallic, roughness)
{
}
Renderer::FillPlain::FillPlain(const Material& material)
: material(material)
{
}
@ -14,16 +19,20 @@ MaterialFillType Renderer::FillPlain::type() const
std::vector<GLfloat> Renderer::FillPlain::encoded() const
{
return { glm::uintBitsToFloat(glm::packUnorm4x8(glm::vec4(metallic, roughness, 0, 1))),
glm::uintBitsToFloat(glm::packUnorm4x8(glm::vec4(color.redF(), color.greenF(), color.blueF(), color.alphaF())))};
auto pair = material.toVec();
return { glm::uintBitsToFloat(glm::packUnorm4x8(glm::vec4(pair.second, 0, 1))),
glm::uintBitsToFloat(glm::packUnorm4x8(pair.first))};
}
std::unique_ptr<MaterialFill> Renderer::FillPlain::clone() const
{
return std::make_unique<FillPlain>(*this);
}
bool Renderer::FillPlain::operator==(const MaterialFill& m) const
{
return type() == m.type()
&& color == static_cast<const FillPlain&>(m).color
&& metallic == static_cast<const FillPlain&>(m).metallic
&& roughness == static_cast<const FillPlain&>(m).roughness;
&& material == static_cast<const FillPlain&>(m).material;
}
@ -42,6 +51,11 @@ std::vector<GLfloat> Renderer::MaterialStyleFill::encoded() const
return materialFill->encoded();
}
std::unique_ptr<MaterialStyle> Renderer::MaterialStyleFill::clone() const
{
return std::make_unique<MaterialStyleFill>(materialFill->clone());
}
bool Renderer::MaterialStyleFill::operator==(const MaterialStyle& m) const
{
return type() == m.type() && *materialFill == *static_cast<const MaterialStyleFill&>(m).materialFill;

View File

@ -10,6 +10,7 @@ namespace Renderer
public:
virtual MaterialFillType type() const = 0;
virtual std::vector<GLfloat> encoded() const = 0;
virtual std::unique_ptr<MaterialFill> clone() const = 0;
virtual bool operator==(const MaterialFill&) const = 0;
};
@ -17,13 +18,13 @@ namespace Renderer
{
public:
FillPlain(QColor color, float metallic, float roughness);
FillPlain(const Material& material);
virtual MaterialFillType type() const override;
virtual std::vector<GLfloat> encoded() const override;
virtual std::unique_ptr<MaterialFill> clone() const override;
virtual bool operator==(const MaterialFill&) const override;
QColor color;
float metallic;
float roughness;
Material material;
};
class MaterialStyleFill : public MaterialStyle
@ -32,8 +33,9 @@ namespace Renderer
MaterialStyleFill(std::shared_ptr<MaterialFill> materialFill);
virtual MaterialStyleType type() const override;
virtual std::vector<GLfloat> encoded() const override;
virtual std::unique_ptr<MaterialStyle> clone() const override;
virtual bool operator==(const MaterialStyle&) const override;
protected:
//protected:
std::shared_ptr<MaterialFill> materialFill;
};

View File

@ -1,5 +1,6 @@
#include "MaterialStyleStroke.h"
#include <QDebug>
#include <array>
using namespace Renderer;
@ -18,8 +19,14 @@ MaterialStrokeType Renderer::StrokePlain::type() const
return MaterialStrokeType::kPlain;
}
std::unique_ptr<MaterialStroke> Renderer::StrokePlain::clone() const
{
return std::make_unique<StrokePlain>(*this);
}
std::vector<GLfloat> Renderer::StrokePlain::encoded() const
{
qDebug() << material.color;
return { glm::uintBitsToFloat(glm::packUnorm4x8(glm::vec4(material.toVec().second, 0.f, 0.f))),
glm::uintBitsToFloat(glm::packUnorm4x8(material.toVec().first)) };
}
@ -53,11 +60,16 @@ std::vector<GLfloat> Renderer::StrokeRadialGradient::encoded() const
v.y = pair.first;
result.push_back(glm::uintBitsToFloat(glm::packUnorm2x16(v)));
result.push_back(glm::uintBitsToFloat(glm::packUnorm4x8(color)));
qDebug() << pair.first;
//qDebug() << pair.first;
}
return result;
}
std::unique_ptr<MaterialStroke> Renderer::StrokeRadialGradient::clone() const
{
return std::make_unique<StrokeRadialGradient>(*this);
}
bool Renderer::StrokeRadialGradient::operator==(const MaterialStroke& m) const
{
return type() == m.type()
@ -65,8 +77,8 @@ bool Renderer::StrokeRadialGradient::operator==(const MaterialStroke& m) const
&& materialMap == static_cast<const StrokeRadialGradient&>(m).materialMap;
}
Renderer::MaterialStyleStroke::MaterialStyleStroke(float width, StrokeType strokeType, StrokeEndType endType, std::shared_ptr<MaterialStroke> materialStroke)
: halfWidth(width/2), strokeType(strokeType), endType(endType), materialStroke(materialStroke)
Renderer::MaterialStyleStroke::MaterialStyleStroke(float halfWidth, StrokeType strokeType, StrokeEndType endType, std::shared_ptr<MaterialStroke> materialStroke)
: halfWidth(halfWidth), strokeType(strokeType), endType(endType), materialStroke(materialStroke)
{
}
@ -87,9 +99,18 @@ std::vector<GLfloat> Renderer::MaterialStyleStroke::encoded() const
return v;
}
std::unique_ptr<MaterialStyle> Renderer::MaterialStyleStroke::clone() const
{
return std::make_unique<MaterialStyleStroke>(halfWidth, strokeType, endType, materialStroke->clone());
}
bool Renderer::MaterialStyleStroke::operator==(const MaterialStyle& m) const
{
return type() == m.type() && *materialStroke == *static_cast<const MaterialStyleStroke&>(m).materialStroke;
return type() == m.type()
&& halfWidth == static_cast<const MaterialStyleStroke&>(m).halfWidth
&& strokeType == static_cast<const MaterialStyleStroke&>(m).strokeType
&& endType == static_cast<const MaterialStyleStroke&>(m).endType
&& *materialStroke == *static_cast<const MaterialStyleStroke&>(m).materialStroke;
}
float Renderer::MaterialStyleStroke::getHalfWidth() const
@ -97,4 +118,10 @@ float Renderer::MaterialStyleStroke::getHalfWidth() const
return halfWidth;
}
#define endTypeBoxLabel(start, end) QStringLiteral(start##" -> "##end)
const std::array<std::pair<QString, StrokeEndType>, 4> Renderer::MaterialStyleStroke::strokeEndTypeNames = {
std::pair{endTypeBoxLabel("Բͷ", "Բͷ"), StrokeEndType::kRound},
std::pair{endTypeBoxLabel("ƽͷ", "Բͷ"), StrokeEndType::kFlatRound},
std::pair{endTypeBoxLabel("Բͷ", "ƽͷ"), StrokeEndType::kRoundFlat},
std::pair{endTypeBoxLabel("ƽͷ", "ƽͷ"), StrokeEndType::kFlat}
};

View File

@ -11,6 +11,7 @@ namespace Renderer
public:
virtual MaterialStrokeType type() const = 0;
virtual std::vector<GLfloat> encoded() const = 0;
virtual std::unique_ptr<MaterialStroke> clone() const = 0;
virtual bool operator==(const MaterialStroke&) const = 0;
};
@ -20,7 +21,9 @@ namespace Renderer
StrokePlain(QColor color, float metallic, float roughness);
StrokePlain(const Material& material);
virtual MaterialStrokeType type() const override;
virtual std::unique_ptr<MaterialStroke> clone() const override;
virtual std::vector<GLfloat> encoded() const override;
virtual bool operator==(const MaterialStroke&) const override;
Material material;
@ -32,6 +35,7 @@ namespace Renderer
StrokeRadialGradient(const std::map<float, Material>& materialMap, bool gradual);
virtual MaterialStrokeType type() const override;
virtual std::vector<GLfloat> encoded() const override;
virtual std::unique_ptr<MaterialStroke> clone() const override;
virtual bool operator==(const MaterialStroke&) const override;
std::map<float, Material> materialMap;
@ -39,7 +43,7 @@ namespace Renderer
};
enum class StrokeType { kBothSides = 2, kLeftSide = 1, kRightSide = 0 };
enum class StrokeEndType { kRound = 0, kFlat = 1 };
enum class StrokeEndType { kRound = 0b00, kFlat = 0b11, kRoundFlat = 0b10, kFlatRound = 0b01, kClosed = 0b100/*用于封闭图形*/ };
class MaterialStyleStroke : public MaterialStyle
{
@ -47,12 +51,14 @@ namespace Renderer
MaterialStyleStroke(float width, StrokeType strokeType, StrokeEndType endType, std::shared_ptr<MaterialStroke> materialStroke);
virtual MaterialStyleType type() const override;
virtual std::vector<GLfloat> encoded() const override;
virtual std::unique_ptr<MaterialStyle> clone() const override;
virtual bool operator==(const MaterialStyle&) const override;
float getHalfWidth() const;
protected:
float halfWidth;
StrokeType strokeType;
StrokeEndType endType;
std::shared_ptr<MaterialStroke> materialStroke;
static const std::array<std::pair<QString, StrokeEndType>, 4> strokeEndTypeNames;
};
}

View File

@ -9,7 +9,7 @@ using namespace Renderer;
constexpr int kMaxLineCount = 20;
Painting::Painting()
Painting::Painting(QColor backgroundColor) : backgroundColor(backgroundColor)
{
}
@ -53,87 +53,40 @@ void Renderer::Painting::addElement(ElementWithTransform elementWithTransform)
elementPool.insert({ element, 0 });
}
elements.push_back(elementWithTransform);
elementTransformPool.emplace(elementWithTransform.transform.transform, 0);
}
void Renderer::Painting::addElement(const Element& element, const ElementTransform& transform)
void Renderer::Painting::addElement(const BaseElement& element, const ElementTransform& transform)
{
auto contour = element.contour;
auto it = elementStyleMap.find(element.style);
if (it == elementStyleMap.end())
{
std::vector<BaseStyle> baseStyles;
for (auto& style : element.style->toBaseStyles())
{
auto [iter, _] = styleSet.insert(style.material);
baseStyles.push_back(BaseStyle{ style.transform, *iter });
}
it = elementStyleMap.insert({ element.style, baseStyles }).first;
}
for (int i = 0; i < it->second.size(); i++)
{
auto& style = it->second[i];
ElementTransform trans = transform;
trans.applyTransformStyle(*style.transform);
trans.zIndex = trans.zIndex * 10 + i;
addElement(ElementWithTransform{ BaseElement{element.contour, style.material, element.ratio}, trans });
}
addElement({ element , transform });
}
Renderer::BaseTransform::BaseTransform(ElementTransform t)
: bound(glm::vec4(t.center - t.scale, t.center + t.scale))
, rotation(t.rotation)
, flip(t.flip)
, zIndex(t.zIndex)
BvhTreeData Painting::encodeElementLeaf(const ElementWithTransform& e)
{
}
void Renderer::Painting::addElement(std::shared_ptr<Element> element, QVector4D bound, float rotation, int zIndex)
{
}
BvhTreeData Painting::encodeElementLeaf(ElementWithTransform e)
{
glm::vec4 bound;
switch (e.element.style->type())
{
case MaterialStyleType::kStroke:
{
auto w = std::static_pointer_cast<MaterialStyleStroke>(e.element.style)->getHalfWidth();
glm::vec2 size = e.element.ratio < 1 ? glm::vec2(e.element.ratio, 1) : glm::vec2(1, 1 / e.element.ratio);
glm::vec2 scale = size * e.transform.scale;
bound = glm::vec4(e.transform.center - scale, e.transform.center + scale);
break;
}
case MaterialStyleType::kFill:
{
bound = glm::vec4(e.transform.center - e.transform.scale, e.transform.center + e.transform.scale);
break;
}
}
//bound = glm::vec4(e.transform.center - e.transform.scale, e.transform.center + e.transform.scale);
GLuint rightSon = (GLuint)(glm::mod(e.transform.rotation, 360.f) / 360.f * (1 << 16))
+ (e.transform.flip.x << 16) + (e.transform.flip.y << 17) + (e.transform.zIndex << 18);
return BvhTreeData(QVector4D(bound.x, bound.y, bound.z, bound.w), elementPool[e.element], rightSon);
QVector4D bound(e.transform.bound.x, e.transform.bound.y, e.transform.bound.z, e.transform.bound.w);
GLuint rightSon = elementPool[e.element] + (e.transform.zIndex << 18);
return { bound, elementTransformPool[e.transform.transform], rightSon };
}
void Painting::generateBuffers(QOpenGLFunctions_4_5_Core* glFunc)
{
qDebug() << "Element Count: " << elementPool.size();
qDebug() << "Coutour Count: " << contourPool.size();
qDebug() << "Contour Count: " << contourPool.size();
qDebug() << " Style Count: " << stylePool.size();
bvhChildren.clear();
bvhBounds.clear();
elementTransform.clear();
elementOffsets.clear();
elementIndex.clear();
elementData.clear();
for (int index = 0; auto & i : elementPool)
{
i.second = index++;
}
for (int index = 0; auto & i : elementTransformPool)
i.second = index++;
std::vector<BvhTreeData> rootBvhTreeData;
for (auto& i : elements)
@ -162,27 +115,35 @@ void Painting::generateBuffers(QOpenGLFunctions_4_5_Core* glFunc)
elementData.insert(elementData.end(), encodedStyle.begin(), encodedStyle.end());
}
for (auto & i : elementTransformPool)
elementTransform.emplace_back(i.first);
for (auto& i : elementPool)
{
//qDebug() <<"element:" << i.second;
//std::shared_ptr<ContourBuffer> contourBuffer = i.first.style->type() == MaterialStyleType::kStroke ? contourPool[i.first.contour].second : contourPool[i.first.contour].first;
auto& contourBuffer = contourPool[{i.first.contour, i.first.style->type()}];
elementOffsets.push_back({ contourBuffer.bvhOffset, stylePool[i.first.style], contourBuffer.pointsOffset, contourBuffer.linesOffset, glm::floatBitsToUint(i.first.ratio) });
elementOffsets.push_back({ contourBuffer.bvhOffset, stylePool[i.first.style], contourBuffer.pointsOffset, contourBuffer.linesOffset });
//std::cout << std::format("{} {} {} {}\n", contourBuffer->bvhOffset, stylePool[i.first->style], contourBuffer->pointsOffset, contourBuffer->linesOffset);
}
glFunc->glCreateBuffers(5, buffers.data());
glFunc->glCreateBuffers(7, buffers.data());
GLuint& bvhSSBO = buffers[0];
GLuint& bvhBoundSSBO = buffers[1];
GLuint& elementOffsetSSBO = buffers[2];
GLuint& elementIndexSSBO = buffers[3];
GLuint& elementDataSSBO = buffers[4];
GLuint& elementTransformSSBO = buffers[2];
GLuint& elementOffsetSSBO = buffers[3];
GLuint& elementIndexSSBO = buffers[4];
GLuint& elementDataSSBO = buffers[5];
GLuint& backgroundColorUBO = buffers[6];
glFunc->glNamedBufferData(buffers[0], bvhChildren.size() * sizeof(bvhChildren[0]), bvhChildren.data(), GL_STATIC_READ);
glFunc->glNamedBufferData(buffers[1], bvhBounds.size() * sizeof(bvhBounds[0]), bvhBounds.data(), GL_STATIC_READ);
glFunc->glNamedBufferData(buffers[2], elementOffsets.size() * sizeof(elementOffsets[0]), elementOffsets.data(), GL_STATIC_READ);
glFunc->glNamedBufferData(buffers[3], elementIndex.size() * sizeof(elementIndex[0]), elementIndex.data(), GL_STATIC_READ);
glFunc->glNamedBufferData(buffers[4], elementData.size() * sizeof(elementData[0]), elementData.data(), GL_STATIC_READ);
glFunc->glNamedBufferData(buffers[2], elementTransform.size() * sizeof(elementTransform[0]), elementTransform.data(), GL_STATIC_READ);
glFunc->glNamedBufferData(buffers[3], elementOffsets.size() * sizeof(elementOffsets[0]), elementOffsets.data(), GL_STATIC_READ);
glFunc->glNamedBufferData(buffers[4], elementIndex.size() * sizeof(elementIndex[0]), elementIndex.data(), GL_STATIC_READ);
glFunc->glNamedBufferData(buffers[5], elementData.size() * sizeof(elementData[0]), elementData.data(), GL_STATIC_READ);
glm::vec3 color(backgroundColor.redF(), backgroundColor.greenF(), backgroundColor.blueF());
glFunc->glNamedBufferData(buffers[6], sizeof(glm::vec3), &color, GL_STATIC_READ);
}
GLuint Renderer::Painting::getElementCount()
@ -220,3 +181,17 @@ bool Renderer::Painting::CompareMaterialStyle::operator()(const std::shared_ptr<
else
return left < right;
}
std::size_t Renderer::Painting::HashMat3x2::operator()(const glm::mat3x2& mat) const
{
std::size_t result = 0;
constexpr long long P = 998244353;
long long base = 1;
for (int i = 0; i < 3; i++)
for (int j = 0; j < 2; j++)
{
result += base * mat[i][j];
base *= P;
}
return result;
}

View File

@ -15,24 +15,13 @@ namespace Renderer
{
std::shared_ptr<Contour> contour;
std::shared_ptr<MaterialStyle> style;
float ratio; /// ¿í¸ß±È
bool operator<(const BaseElement& e) const;
};
struct BaseTransform
{
glm::vec4 bound;
float rotation;
glm::bvec2 flip;
GLuint zIndex;
BaseTransform(ElementTransform t);
};
struct ElementWithTransform
{
BaseElement element;
ElementTransform transform;
//BaseTransform transform;
};
struct ContourBuffer
@ -52,7 +41,6 @@ namespace Renderer
GLuint styleOffset;
GLuint pointsOffset;
GLuint linesOffset;
GLuint ratio;
};
class Painting
@ -60,15 +48,16 @@ namespace Renderer
public:
std::vector<GLuint> bvhChildren;
std::vector<QVector4D> bvhBounds;
std::vector<glm::mat3x2> elementTransform;
std::vector<ElementOffset> elementOffsets;
std::vector<GLuint> elementIndex;
std::vector<GLfloat> elementData;
int paintingId = 0;
std::array<GLuint, 5> buffers;
std::array<GLuint, 7> buffers;
QColor backgroundColor;
Painting();
void addElement(const Element& element, const ElementTransform& transform);
void addElement(std::shared_ptr<Element> element, QVector4D bound, float rotation, int zIndex);
Painting(QColor backgroundColor = Qt::white);
void addElement(const BaseElement& element, const ElementTransform& transform);
void generateBuffers(QOpenGLFunctions_4_5_Core* glFunc);
GLuint getElementCount();
private:
@ -76,14 +65,15 @@ namespace Renderer
std::unordered_map<std::tuple<std::shared_ptr<Contour>, MaterialStyleType>, ContourBuffer, ContourHash> contourPool;
struct CompareMaterialStyle { bool operator()(const std::shared_ptr<MaterialStyle>&, const std::shared_ptr<MaterialStyle>&) const; };
std::set<std::shared_ptr<MaterialStyle>, CompareMaterialStyle> styleSet;
std::unordered_map<std::shared_ptr<ElementStyle>, std::vector<BaseStyle>> elementStyleMap;
std::unordered_map<std::shared_ptr<MaterialStyle>, GLuint> stylePool;
struct HashMat3x2 { std::size_t operator()(const glm::mat3x2&) const; };
std::unordered_map<glm::mat3x2, GLuint, HashMat3x2> elementTransformPool;
std::map<BaseElement, GLuint> elementPool;
std::vector<ElementWithTransform> elements;
void addElement(ElementWithTransform element);
void insertContourBuffer(ContourBuffer& buffer);
BvhTreeData encodeElementLeaf(ElementWithTransform e);
BvhTreeData encodeElementLeaf(const ElementWithTransform& e);
};
}

View File

@ -1,7 +1,7 @@
#pragma once
#include <QOpenGLFunctions_4_5_Core>
#include <QString>
#include <QVector>
#include <vector>
#include <QVector2D>
#include <QVector3D>
#include <QOpenGLShaderProgram>
@ -18,8 +18,8 @@ namespace Renderer
class PaintingMesh : public Drawable
{
public:
QVector<Vertex> vertices;
QVector<GLuint> indices;
std::vector<Vertex> vertices;
std::vector<GLuint> indices;
GLuint textureBasecolor;
GLuint textureMetallicRoughness;
QMatrix4x4 model;

View File

@ -10,24 +10,9 @@
#include "../Painting/Element.h"
#include "../Painting/Painting.h"
#include "../Painting/MaterialStyleStroke.h"
#include <util/PainterPathUtil.h>
using namespace Renderer;
Renderer::ElementRenderer::ElementRenderer(QOpenGLWidget* glWidget)
: glWidget(glWidget)
{
}
void Renderer::ElementRenderer::initialize()
{
initializeOpenGLFunctions();
shader = std::make_unique<QOpenGLShaderProgram>();
if (!shader->addShaderFromSourceFile(QOpenGLShader::Compute, ":/Shaders/element.comp"))
qDebug() << "ERROR: " << shader->log();
if (!shader->link())
qDebug() << "ERROR: " << shader->log();
}
std::vector<glm::vec2> generatePathBuffer(const QPainterPath& path)
{
std::vector<glm::vec2> pathBuffer;
@ -91,8 +76,10 @@ std::vector<GLfloat> generateStyleBuffer(const std::vector<BaseStyle>& styles)
styleBuffer.push_back(style.transform->scale.y);
styleBuffer.push_back(style.transform->rotation);
styleBuffer.push_back(glm::uintBitsToFloat(glm::packUnorm2x16(style.transform->flip)));
auto encoded = style.material->encoded();
styleBuffer.insert(styleBuffer.end(), encoded.begin(), encoded.end());
//qDebug() << "style size" << styleBuffer.size();
}
return styleBuffer;
}
@ -101,13 +88,23 @@ QRectF calcBoundingRect(const QPainterPath& path, const std::vector<BaseStyle>&
{
QRectF bound = path.boundingRect();
ElementTransform originTransform{ glm::vec2(bound.center().x(), bound.center().y()), glm::vec2(bound.width(), bound.height()), 0, glm::bvec2(false), 0 };
//ElementTransform originTransform{ glm::vec2(bound.center().x(), bound.center().y()), glm::vec2(bound.width(), bound.height()), 0, glm::bvec2(false), 0 };
glm::vec2 leftTop(std::numeric_limits<float>::max()), rightBottom(std::numeric_limits<float>::min());
for (auto& baseStyle : styles)
{
BaseTransform transform(originTransform.appliedTransformStyle(*baseStyle.transform));
struct BaseTransform
{
glm::vec4 bound;
float rotation = 0;
glm::bvec2 flip = glm::bvec2(false);
GLuint zIndex = 0;
};
//BaseTransform transform(originTransform.appliedTransformStyle(*baseStyle.transform));
glm::vec2 center(bound.center().x(), bound.center().y());
glm::vec2 scale(bound.width(), bound.height());
BaseTransform transform{ glm::vec4(center - scale, center + scale) };
if (baseStyle.material->type() == MaterialStyleType::kStroke)
{
float halfWidth = std::static_pointer_cast<MaterialStyleStroke>(baseStyle.material)->getHalfWidth();
@ -130,27 +127,51 @@ QRectF calcBoundingRect(const QPainterPath& path, const std::vector<BaseStyle>&
return QRectF(QPointF(leftTop.x, leftTop.y), QPointF(rightBottom.x, rightBottom.y));
}
std::pair<QImage, QPointF> Renderer::ElementRenderer::drawElement(const QPainterPath& path, const ElementStyle& style, float pixelRatio, bool releaseContext)
Renderer::ElementRenderer::ElementRenderer()
{
auto baseStyles = style.toBaseStyles();
QRectF bound = calcBoundingRect(path, baseStyles);
surface.create();
thread = std::jthread([&](std::stop_token stop) {
std::stop_callback cb(stop, [&] {
draw.notify_all();
});
QOpenGLContext context;
context.create();
context.makeCurrent(&surface);
auto gl = context.versionFunctions<QOpenGLFunctions_4_5_Core>();
shader = std::make_unique<QOpenGLShaderProgram>();
if (!shader->addShaderFromSourceFile(QOpenGLShader::Compute, ":/Shaders/element.comp"))
qDebug() << "ERROR: " << shader->log();
if (!shader->link())
qDebug() << "ERROR: " << shader->log();
initialized = true;
while (!stop.stop_requested())
{
std::unique_lock<std::mutex> lock(drawMutex);
draw.wait(lock, [&] {return needDraw || stop.stop_requested(); });
if (needDraw)
{
needDraw = false;
auto baseStyles = style->toBaseStyles();
QRectF bound = calcBoundingRect(*path, baseStyles);
QPointF leftTop = bound.topLeft();
QSize size(ceil(bound.width() * pixelRatio), ceil(bound.height() * pixelRatio));
auto pathBuffer = generatePathBuffer(path);
auto pathBuffer = generatePathBuffer(*path);
auto styleBuffer = generateStyleBuffer(baseStyles);
if (releaseContext) glWidget->makeCurrent();
GLuint ssbo[2];
glCreateBuffers(2, ssbo);
glNamedBufferData(ssbo[0], pathBuffer.size() * sizeof(glm::vec2), pathBuffer.data(), GL_STATIC_READ);
glNamedBufferData(ssbo[1], styleBuffer.size() * sizeof(GLfloat), styleBuffer.data(), GL_STATIC_READ);
gl->glCreateBuffers(2, ssbo);
gl->glNamedBufferData(ssbo[0], pathBuffer.size() * sizeof(glm::vec2), pathBuffer.data(), GL_STATIC_READ);
gl->glNamedBufferData(ssbo[1], styleBuffer.size() * sizeof(GLfloat), styleBuffer.data(), GL_STATIC_READ);
auto fbo = QOpenGLFramebufferObject(size, QOpenGLFramebufferObject::NoAttachment, GL_TEXTURE_2D);
fbo.bind();
glClearColor(0, 0, 0, 0);
glClear(GL_COLOR_BUFFER_BIT);
gl->glClearColor(0, 0, 0, 0);
gl->glClear(GL_COLOR_BUFFER_BIT);
fbo.release();
shader->bind();
@ -158,14 +179,43 @@ std::pair<QImage, QPointF> Renderer::ElementRenderer::drawElement(const QPainter
shader->setUniformValue("styleSize", (GLint)styleBuffer.size());
shader->setUniformValue("leftTop", leftTop);
shader->setUniformValue("pixelRatio", pixelRatio);
glBindBufferBase(GL_SHADER_STORAGE_BUFFER, 10, ssbo[0]);
glBindBufferBase(GL_SHADER_STORAGE_BUFFER, 11, ssbo[1]);
glBindImageTexture(0, fbo.texture(), 0, GL_FALSE, 0, GL_READ_WRITE, GL_RGBA8);
glDispatchCompute(ceil(size.width() / 8.), ceil(size.height() / 8.), 1);
gl->glBindBufferBase(GL_SHADER_STORAGE_BUFFER, 10, ssbo[0]);
gl->glBindBufferBase(GL_SHADER_STORAGE_BUFFER, 11, ssbo[1]);
gl->glBindImageTexture(0, fbo.texture(), 0, GL_FALSE, 0, GL_READ_WRITE, GL_RGBA8);
gl->glDispatchCompute(ceil(size.width() / 8.), ceil(size.height() / 8.), 1);
shader->release();
auto image = fbo.toImage(false);
glDeleteBuffers(2, ssbo);
if (releaseContext) glWidget->doneCurrent();
return { image, leftTop };
gl->glDeleteBuffers(2, ssbo);
result = { image, leftTop };
drawFinished = true;
}
draw.notify_all();
}
context.doneCurrent();
});
while (!initialized)
std::this_thread::yield();
}
ElementRenderer* Renderer::ElementRenderer::instance()
{
static ElementRenderer renderer;
return &renderer;
}
std::pair<QImage, QPointF> Renderer::ElementRenderer::drawElement(const QPainterPath& path, const ElementStyle& style, float pixelRatio)
{
std::unique_lock<std::mutex> lock(drawMutex);
draw.wait(lock, [&] {return drawFinished; });
drawFinished = false;
QPainterPath monotonized = PainterPathUtil::monotonization(path);
this->path = &monotonized;
this->style = &style;
this->pixelRatio = pixelRatio;
needDraw = true;
draw.notify_all();
draw.wait(lock, [&] {return drawFinished; });
return result;
}

View File

@ -3,32 +3,40 @@
#include <QOpenGLFunctions_4_5_Core>
#include <QOPenGLShaderProgram>
#include "../Painting/ElementStyle.h"
#include <QOffscreenSurface>
namespace Renderer
{
class ElementRenderer : protected QOpenGLFunctions_4_5_Core
class ElementRenderer
{
public:
ElementRenderer(QOpenGLWidget* glWidget);
/**
* @brief initializeGL
*/
void initialize();
static ElementRenderer* instance();
/**
* @brief QImage
* @param path
* @param style
* @param pixelRatio path
* @param releaseContext initializeGL, resizeGLpaintGLfalse
* @return QImage
* @return QPointF QPainterPath
*/
std::pair<QImage, QPointF> drawElement(const QPainterPath& path, const ElementStyle& style, float pixelRatio, bool releaseContext = true);
std::pair<QImage, QPointF> drawElement(const QPainterPath& path, const ElementStyle& style, float pixelRatio);
protected:
QOpenGLWidget* glWidget;
std::unique_ptr<QOpenGLShaderProgram> shader;
std::jthread thread;
QOffscreenSurface surface;
QOpenGLContext context;
std::atomic<bool> initialized = false;
bool drawFinished = true;
bool needDraw = false;
std::condition_variable draw;
std::mutex drawMutex;
const QPainterPath* path;
const ElementStyle* style;
float pixelRatio;
std::pair<QImage, QPointF> result;
ElementRenderer();
};
}

View File

@ -73,17 +73,14 @@ void RendererGLWidget::stopTimer()
timerId = -1;
}
void RendererGLWidget::setModel()
void RendererGLWidget::setModel(QString path)
{
makeCurrent();
model->loadModel("Models/Sponza/Sponza.gltf");
//model = new Model("E:\\3D Objects\\gallery_gltf\\gallery_gltf.gltf", context(), modelProgramPtr, paintingProgramPtr, shadowProgramPtr, paintingHelper);
//model->loadModel("Models/Sponza/Sponza.gltf");
//model->loadModel("E:\\3D Objects\\Gate\\gltf\\Gate.gltf");
model->loadModel(path);
light.model = model;
qDebug() << model->AABB;
//paintingHelper->allocateBuffers();
//paintingCompProgramPtr->bind();
//paintingHelper->bindPaintingBuffers();
//paintingCompProgramPtr->release();
doneCurrent();
}

View File

@ -26,7 +26,7 @@ namespace Renderer
void startTimer();
void stopTimer();
public slots:
void setModel();
void setModel(QString path);
void setMainLightPitch(float pitch);
void setMainLightYaw(float yaw);
void setExposure(float exposure);

View File

@ -1,12 +1,37 @@
#include "RendererWidget.h"
#include "RendererGLWidget.h"
#include "../FluentMenu.h"
#include <QFileDialog>
Renderer::RendererWidget::RendererWidget(QWidget* parent)
: QWidget(parent)
{
ui.setupUi(this);
FluentMenu* menu = new FluentMenu(this);
auto openAction = new QAction(QStringLiteral("´ò¿ª"), menu);
auto saveAction = new QAction(QStringLiteral("±£´æ"), menu);
auto testAction = new QAction(QStringLiteral("²âÊÔ"), menu);
auto test2Action = new QAction(QStringLiteral("²âÊÔ2"), menu);
menu->addAction(openAction);
menu->addAction(saveAction);
menu->addAction(testAction);
menu->addAction(test2Action);
ui.openButton->setHaloVisible(false);
ui.openButton->setOverlayStyle(::Material::TintedOverlay);
ui.openButton->setCheckable(true);
QObject::connect(menu, &QMenu::aboutToShow, [&]() {
ui.openButton->setChecked(true);
});
QObject::connect(menu, &QMenu::aboutToHide, [&]() {
ui.openButton->setChecked(false);
});
QObject::connect(ui.openButton, &QPushButton::clicked, [&, menu]() {
menu->exec(ui.openButton->mapToGlobal(QPoint(0, ui.openButton->height())));
});
QObject::connect(ui.horizontalSlider, &QSlider::valueChanged,
ui.openGLWidget, &Renderer::RendererGLWidget::setMainLightPitch);
QObject::connect(ui.horizontalSlider_2, &QSlider::valueChanged,
@ -15,11 +40,21 @@ Renderer::RendererWidget::RendererWidget(QWidget *parent)
ui.openGLWidget->setExposure(value / 100.f);
});
QObject::connect(openAction, &QAction::triggered, [&] {
QString fileName = QFileDialog::getOpenFileName(this, QStringLiteral("´ò¿ªÄ£ÐÍ"), QString(), QStringLiteral("glTF 2.0 (*.gltf)"));
qDebug() << fileName;
if (fileName != QString())
ui.openGLWidget->setModel(fileName);
});
QObject::connect(testAction, &QAction::triggered, [&] {
ui.openGLWidget->setModel("Models/Sponza/Sponza.gltf");
});
QObject::connect(test2Action, &QAction::triggered, [&] {
ui.openGLWidget->setModel("E:\\3D Objects\\Gate\\Gate.gltf");
});
ui.horizontalSlider->setValue(105);
ui.horizontalSlider_2->setValue(80);
ui.exposureSlider->setValue(60);
QObject::connect(ui.openButton, &QPushButton::clicked,
ui.openGLWidget, &Renderer::RendererGLWidget::setModel);
}
Renderer::RendererWidget::~RendererWidget()
@ -36,3 +71,4 @@ void Renderer::RendererWidget::currentTabChanged(int index)
ui.openGLWidget->stopTimer();
}
}

View File

@ -1,19 +1,19 @@
#pragma once
#include <QWidget>
#include <QMenu>
#include "ui_RendererWidget.h"
namespace Renderer
{
class RendererWidget : public QWidget
{
Q_OBJECT
public:
RendererWidget(QWidget* parent = nullptr);
~RendererWidget();
public slots:
void currentTabChanged(int index);
private:
Ui::RendererWidgetClass ui;
};

View File

@ -18,7 +18,7 @@ Renderer::VirtualTextureManager::VirtualTextureManager(GladGLContext* glMain)
surface.create();
mainContext->doneCurrent();
thread = std::jthread([&] {
thread = std::jthread([&](std::stop_token stop) {
QOpenGLContext context;
context.setFormat(mainContext->format());
context.setShareContext(mainContext);
@ -67,7 +67,7 @@ Renderer::VirtualTextureManager::VirtualTextureManager(GladGLContext* glMain)
GLuint pageLoadTimeQuery;
gl->GenQueries(1, &pageLoadTimeQuery);
while (true)
while (!stop.stop_requested())
{
if (needUpdate)
{
@ -85,6 +85,7 @@ Renderer::VirtualTextureManager::VirtualTextureManager(GladGLContext* glMain)
//qDebug() << duration;
pageLoadDuration += duration;
}
std::this_thread::yield();
}
});
@ -123,8 +124,9 @@ std::uint16_t Renderer::VirtualTextureManager::createVirtualTexture(Painting pai
glMain->TextureParameteri(metallicRoughness, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
glMain->TextureStorage2D(metallicRoughness, levels, GL_RG8, GLsizei(textureSize), GLsizei(textureSize));
for (int i = 0; i < 5; i++)
for (int i = 0; i < 6; i++)
glMain->BindBufferBase(GL_SHADER_STORAGE_BUFFER, i, painting.buffers[i]);
glMain->BindBufferBase(GL_UNIFORM_BUFFER, 1, painting.buffers[6]);
for (auto level = levels - 1; level < levels; ++level)
{
@ -142,7 +144,7 @@ std::uint16_t Renderer::VirtualTextureManager::createVirtualTexture(Painting pai
GL_TRUE);
program.bind();
glMain->Uniform2i(gl->GetUniformLocation(program.programId(), "pixelOffset"), static_cast<GLsizei>(pageSize) * i, static_cast<GLsizei>(pageSize) * j);
glMain->Uniform2i(0 /*pixelOffset*/, static_cast<GLsizei>(pageSize) * i, static_cast<GLsizei>(pageSize) * j);
glMain->BindImageTexture(0, baseColor, level, GL_FALSE, 0, GL_READ_WRITE, GL_RGBA8);
glMain->BindImageTexture(1, metallicRoughness, level, GL_FALSE, 0, GL_READ_WRITE, GL_RG8);
glMain->DispatchCompute(pageSize / 8, pageSize / 8, 1);
@ -150,10 +152,30 @@ std::uint16_t Renderer::VirtualTextureManager::createVirtualTexture(Painting pai
}
}
if (const auto it = std::find_if(paintings.begin(), paintings.end(), [](const PaintingHandle& i) { return i.baseColor == 0; });
it != paintings.end())
{
*it = { baseColor, metallicRoughness, painting.buffers };
return it - paintings.begin() + 1;
}
else
{
paintings.emplace_back(baseColor, metallicRoughness, painting.buffers);
return paintings.size();
}
}
void Renderer::VirtualTextureManager::deleteVirtualTexture(std::uint16_t id)
{
auto& painting = getPaintingHandle(id);
glMain->DeleteTextures(2, (GLuint*)&painting);
glMain->DeleteBuffers(7, painting.buffers.data());
painting.baseColor = 0;
painting.metallicRoughness = 0;
painting.buffers.fill(0);
}
Renderer::PaintingHandle& Renderer::VirtualTextureManager::getPaintingHandle(std::uint16_t id)
{
return paintings[id - 1];
@ -187,8 +209,9 @@ void Renderer::VirtualTextureManager::pageCommitmentById(const glm::u16vec2& pag
if (commit)
{
program.bind();
for (int i = 0; i < 5; i++)
for (int i = 0; i < 6; i++)
gl->BindBufferBase(GL_SHADER_STORAGE_BUFFER, i, painting.buffers[i]);
glMain->BindBufferBase(GL_UNIFORM_BUFFER, 1, painting.buffers[6]);
gl->Uniform2i(gl->GetUniformLocation(program.programId(), "pixelOffset"), static_cast<GLsizei>(pageSize) * page.x, static_cast<GLsizei>(pageSize) * page.y);
gl->BindImageTexture(0, painting.baseColor, level, GL_FALSE, 0, GL_READ_WRITE, GL_RGBA8);
gl->BindImageTexture(1, painting.metallicRoughness, level, GL_FALSE, 0, GL_READ_WRITE, GL_RG8);

View File

@ -15,7 +15,7 @@ namespace Renderer
{
GLuint baseColor;
GLuint metallicRoughness;
std::array<GLuint, 5> buffers;
std::array<GLuint, 7> buffers;
};
class VirtualTextureManager
@ -24,11 +24,12 @@ namespace Renderer
VirtualTextureManager(GladGLContext* glMain);
/**
* @brief
* @param painting
* @brief
* @param painting
* @return ÐéÄâÎÆÀíid
*/
std::uint16_t createVirtualTexture(Painting painting);
void deleteVirtualTexture(std::uint16_t id);
PaintingHandle& getPaintingHandle(std::uint16_t id);
void tryUpdatePages(const std::vector<glm::u16vec2>& pageIds);

View File

@ -1,80 +0,0 @@
/*
The MIT License (MIT)
Copyright © 2018-2022 Antonio Dias
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
*/
#include "titlewidget.h"
TitleWidget::TitleWidget(QWidget *parent) : QWidget(parent)
{
m_active = false;
}
void TitleWidget::setText(const QString &text)
{
m_title = text;
repaint();
}
void TitleWidget::setActive(bool active)
{
m_active = active;
repaint();
}
void TitleWidget::setTitleColor(const QColor &active_color, const QColor &inactive_color)
{
m_active_color = active_color;
m_inactive_color = inactive_color;
repaint();
}
void TitleWidget::paintEvent(QPaintEvent *event)
{
Q_UNUSED(event)
QPainter painter(this);
painter.setRenderHints(QPainter::Antialiasing);
QFont font;
font.setPointSize(10);
#ifdef Q_OS_WIN
font.setFamily("Segoe UI, Microsoft YaHei UI");
#else
font.setFamily(qApp->font().family());
#endif
painter.setFont(font);
QPen pen;
pen.setColor(m_active ? m_active_color : m_inactive_color);
painter.setPen(pen);
QFontMetrics metrics(painter.font());
QSize title_size = metrics.size(0, m_title);
QString title = metrics.elidedText(m_title, Qt::ElideRight, width());
painter.drawText(0, (height() - title_size.height()) / 2, title_size.width(), title_size.height(), 0, title);
}

View File

@ -1,54 +0,0 @@
/*
The MIT License (MIT)
Copyright © 2018-2022 Antonio Dias
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
*/
#ifndef TITLEWIDGET_H
#define TITLEWIDGET_H
#include <QtCore>
#include <QtGui>
#include <QtWidgets>
class TitleWidget : public QWidget
{
Q_OBJECT
public:
explicit TitleWidget(QWidget *parent = nullptr);
public slots:
void setText(const QString &text);
void setActive(bool active);
void setTitleColor(const QColor &active_color, const QColor &inactive_color);
private:
//Functions
void paintEvent(QPaintEvent *event);
//Variables
QString m_title;
bool m_active;
QColor m_active_color;
QColor m_inactive_color;
};
#endif // TITLEWIDGET_H

View File

@ -1,4 +1,5 @@
#include "MainWindow.h"
#include "ColorHelper.hpp"
#include <QGuiApplication>
#include <QtWidgets/QApplication>
#include <FramelessHelper/Core/private/framelessconfig_p.h>
@ -49,10 +50,12 @@ int main(int argc, char* argv[])
//QGuiApplication::setAttribute(Qt::AA_EnableHighDpiScaling);
QApplication::setHighDpiScaleFactorRoundingPolicy(Qt::HighDpiScaleFactorRoundingPolicy::PassThrough);
QApplication a(argc, argv);
Q_INIT_RESOURCE(resources);
ColorHelper::instance();
//FramelessHelper::Core::setApplicationOSThemeAware();
FramelessConfig::instance()->set(Global::Option::ForceNonNativeBackgroundBlur);
//FramelessConfig::instance()->set(Global::Option::EnableBlurBehindWindow);
FramelessConfig::instance()->set(Global::Option::CenterWindowBeforeShow);
//FramelessConfig::instance()->set(Global::Option::CenterWindowBeforeShow);
MainWindow w;
w.show();
return a.exec();

Some files were not shown because too many files have changed in this diff Show More