Compare commits

..

40 Commits

Author SHA1 Message Date
wuyize b2e49dae17 提供变宽样式的接口,未实现 2023-03-23 23:17:53 +08:00
karlis a3d39b32e5 添加了所有Layer的styles成员 2023-03-23 22:36:22 +08:00
wuyize a55a38385e Fix: GraphicElement中pixelRatio的计算 2023-03-23 20:28:19 +08:00
karlis 5d5ea2307f 添加单向渲染逻辑和内存强制回收措施 2023-03-23 16:17:01 +08:00
karlis aa9743d4e7 修正创建LeafLayer引用组合元素后,无法析构的问题 2023-03-23 15:59:33 +08:00
ArgonarioD 214fa0f82f [editor] 修改了style相关的一些东西 #30
* 将LayerStyleContainer的覆盖运算符从<<改为了 |
 * 调整了LayerContainerListWidget的UI
 * fix: 移除描边时可能导致空指针异常
2023-03-23 15:02:44 +08:00
karlis 483afb9fb4 修正flip拼写错误的问题 2023-03-23 11:57:28 +08:00
ArgonarioD 2f9b988dac [editor] 为LayerStyleContainer重载了<<运算符 | #30 2023-03-23 01:44:28 +08:00
karlis ba4be72918 bug fix 2023-03-22 22:47:37 +08:00
karlis b739f4506b 修正BoundRect显示巨大化的问题 2023-03-22 20:37:09 +08:00
karlis 578fd5da8e 修正图元更改后列表不正确的问题 2023-03-22 20:17:43 +08:00
karlis f175e50b7b merge 2023-03-22 19:59:42 +08:00
karlis beee94ebe0 图元预览改为懒加载;添加帧缓存;修改图元预览渲染机制;固定渲染倍率* 2023-03-22 19:58:17 +08:00
ArgonarioD 62d887aa07 [editor] 将图层的旋转角度限制在了(-360, 360) | #27 2023-03-22 17:01:09 +08:00
wuyize 512181f6a3 Fix: painting.comp中zIndex计算错误,PaintingUtil包围盒计算错误 2023-03-22 16:58:28 +08:00
wuyize 60c7695545 Fix: PreviewWindow显示不完整 | #26 2023-03-22 14:54:52 +08:00
karlis 7e0e07292f merge 2023-03-22 14:46:59 +08:00
karlis b2f37cc4eb Experimental:添加了【图层提升/下放功能】 2023-03-22 14:45:43 +08:00
karlis 0b63e8e737 Experimental:添加了【图层提升/下放功能】 2023-03-22 13:55:06 +08:00
yang.yongquan 56777c1f00 增加zIndex,减少重复contour的生成 2023-03-22 13:02:43 +08:00
ArgonarioD 2e7d20d0d7 [editor] style接入新的UI实现 | #10 2023-03-22 00:09:44 +08:00
karlis 3edca0287b Merge branch 'main' of http://101.34.228.45:3000/BigC/ArchitectureColoredPainting 2023-03-21 22:58:58 +08:00
karlis d1aeb28b97 添加ALT+↑↓切换Layer顺序的快捷功能 2023-03-21 22:58:42 +08:00
wuyize 492f0a12d7 Fix: Painting背景材质以及zIndex改为小的在上层 2023-03-21 22:30:19 +08:00
wuyize 88dc039fe8 Fix: PaintingUtil中root矩阵计算错误 2023-03-21 19:11:54 +08:00
karlis c77114b7f3 Merge branch 'main' of http://101.34.228.45:3000/BigC/ArchitectureColoredPainting 2023-03-21 15:19:13 +08:00
karlis 2b5598a81e 完成除样式外新ui接入 | #10 2023-03-21 15:18:57 +08:00
wuyize 3a693de9fe 为FluentMenu提供更完善的封装类型FluentMenuButton | #24 2023-03-21 15:15:41 +08:00
karlis 291d112d11 Merge branch 'main' of http://101.34.228.45:3000/BigC/ArchitectureColoredPainting 2023-03-21 14:35:40 +08:00
karlis fff1ce3424 Merge branch 'main' of http://101.34.228.45:3000/BigC/ArchitectureColoredPainting 2023-03-21 14:35:37 +08:00
karlis 9057500da8 Merge branch 'main' of http://101.34.228.45:3000/BigC/ArchitectureColoredPainting 2023-03-21 14:25:12 +08:00
karlis 4d16b47edd merge 2023-03-21 14:25:04 +08:00
karlis 5fbf46faf2 merge 2023-03-21 14:13:41 +08:00
karlis 16671ae71f 添加Element渲染缓存 2023-03-21 14:11:47 +08:00
yang.yongquan 182284c60f Merge branch 'main' of http://101.34.228.45:3000/BigC/ArchitectureColoredPainting 2023-03-21 13:57:20 +08:00
yang.yongquan 93537954a9 Fix: 选择文件时未对“未选择”的情况做处理 | #19 2023-03-21 13:57:16 +08:00
wuyize d3cf84f479 Fix: style宽度转换问题 2023-03-21 13:55:46 +08:00
ArgonarioD 2581624388 添加了证明markdown 2023-03-21 13:50:27 +08:00
wuyize 35e7648206 FIX: setWindow参数设置错误 2023-03-21 12:24:42 +08:00
yang.yongquan 793402e3c5 增加了originPath以便transform操作 2023-03-21 10:23:11 +08:00
58 changed files with 2054 additions and 1258 deletions

581
4_L0.json
View File

@ -1,33 +1,40 @@
{ {
"background-color": "#b7a386", "background-color": "#ae9f86",
"elements": [ "elements": [
{ {
"data": { "data": {
"include": "../svg/2.svg" "include": "/svg/2.svg"
}, },
"name": "ababa", "name": "ababa",
"type": "svg-file" "type": "svg-file"
}, },
{ {
"data": { "data": {
"reference-layer": "0.0" "include": "/svg/0.svg"
},
"name": "ababa-group",
"type": "group"
},
{
"data": {
"include": "../svg/0.svg"
}, },
"name": "ababa2", "name": "ababa2",
"type": "svg-file" "type": "svg-file"
}, },
{ {
"data": { "data": {
"include": "D:/BigC2022/temp/ArchitectureColoredPainting/svg/4_L0.svg" "include": "/svg/4_L0.svg"
}, },
"name": "4_L0-fill.svg", "name": "4_L0.svg",
"type": "svg-file" "type": "svg-file"
},
{
"data": {
"reference-layer": "0.0"
},
"name": "图源工",
"type": "group"
},
{
"data": {
"reference-layer": "0.0"
},
"name": "图源万",
"type": "group"
} }
], ],
"height": 1080, "height": 1080,
@ -37,41 +44,47 @@
{ {
"children": [ "children": [
{ {
"element": 3, "children": [
{
"element": 2,
"is-folder": false, "is-folder": false,
"name": "4_L0", "name": "4_L0",
"styles": [ "styles": [
{ {
"material": "AH8A/2pkiv8=", "material": "AH8A/1JOff8=",
"type": "fill" "type": "fill"
}, },
{ {
"enableEachSideIndependent": true, "enableEachSideIndependent": true,
"left": "AADAQAEAJJwAf///9dKG/w==", "left": "AAAAQAEAJJwAf///9c19/w==",
"right": "AADgQAAACpw=", "right": "AADgQAAACpw=",
"type": "stroke" "type": "stroke"
} }
], ],
"transform": { "transform": {
"filpX": false,
"filpY": false,
"offset": { "offset": {
"x": -230, "x": 0,
"y": -533 "y": 0
}, },
"rotation": 0, "rotation": 0,
"scale": { "scale": {
"x": 0.32341007644113307, "x": 1,
"y": 0.32341007644113307 "y": 1
} }
} }
} }
], ],
"is-folder": true, "is-folder": true,
"name": "GroupFolderExample", "name": "工",
"referenced-by": 1, "referenced-by": 3,
"transform": { "transform": {
"filpX": false,
"filpY": false,
"offset": { "offset": {
"x": 50, "x": 0,
"y": 50 "y": 0
}, },
"rotation": 0, "rotation": 0,
"scale": { "scale": {
@ -81,87 +94,17 @@
} }
}, },
{ {
"element": 1, "element": 3,
"is-folder": false, "is-folder": false,
"name": "aaaa2", "name": "1",
"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": [ "styles": [
], ],
"transform": { "transform": {
"filpX": false,
"filpY": false,
"offset": { "offset": {
"x": 103, "x": 103,
"y": 130 "y": -1
},
"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, "rotation": 90,
"scale": { "scale": {
@ -171,51 +114,17 @@
} }
}, },
{ {
"element": 1, "element": 3,
"is-folder": false, "is-folder": false,
"name": "bbbb3", "name": "2",
"styles": [ "styles": [
], ],
"transform": { "transform": {
"filpX": false,
"filpY": false,
"offset": { "offset": {
"x": 432, "x": 104,
"y": 285 "y": 100
},
"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, "rotation": 0,
"scale": { "scale": {
@ -225,15 +134,17 @@
} }
}, },
{ {
"element": 1, "element": 3,
"is-folder": false, "is-folder": false,
"name": "cccc2", "name": "3",
"styles": [ "styles": [
], ],
"transform": { "transform": {
"filpX": false,
"filpY": false,
"offset": { "offset": {
"x": 361, "x": 3,
"y": -70 "y": 102
}, },
"rotation": 90, "rotation": 90,
"scale": { "scale": {
@ -241,104 +152,14 @@
"y": 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": [
], ],
"is-folder": true,
"name": "子图层-1",
"referenced-by": 4,
"transform": { "transform": {
"filpX": false,
"filpY": false,
"offset": { "offset": {
"x": 0, "x": 0,
"y": 0 "y": 0
@ -351,14 +172,16 @@
} }
}, },
{ {
"element": 1, "element": 4,
"is-folder": false, "is-folder": false,
"name": "eeee1", "name": "子图层-2",
"styles": [ "styles": [
], ],
"transform": { "transform": {
"filpX": false,
"filpY": false,
"offset": { "offset": {
"x": 0, "x": 204,
"y": 0 "y": 0
}, },
"rotation": 0, "rotation": 0,
@ -369,17 +192,19 @@
} }
}, },
{ {
"element": 1, "element": 4,
"is-folder": false, "is-folder": false,
"name": "eeee2", "name": "子图层-3",
"styles": [ "styles": [
], ],
"transform": { "transform": {
"filpX": false,
"filpY": false,
"offset": { "offset": {
"x": -149, "x": 3,
"y": 85 "y": 205
}, },
"rotation": 90, "rotation": 0,
"scale": { "scale": {
"x": 1, "x": 1,
"y": 1 "y": 1
@ -387,17 +212,259 @@
} }
}, },
{ {
"element": 1, "element": 4,
"is-folder": false, "is-folder": false,
"name": "eeee3", "name": "子图层-4",
"styles": [ "styles": [
], ],
"transform": { "transform": {
"filpX": false,
"filpY": false,
"offset": { "offset": {
"x": 166, "x": 207,
"y": 482 "y": 203
}, },
"rotation": 180, "rotation": 0,
"scale": {
"x": 1,
"y": 1
}
}
},
{
"element": 4,
"is-folder": false,
"name": "子图层-5",
"styles": [
],
"transform": {
"filpX": false,
"filpY": false,
"offset": {
"x": 407,
"y": -2
},
"rotation": 0,
"scale": {
"x": 1,
"y": 1
}
}
},
{
"element": 4,
"is-folder": false,
"name": "子图层-6",
"styles": [
],
"transform": {
"filpX": false,
"filpY": false,
"offset": {
"x": 411,
"y": 201
},
"rotation": 0,
"scale": {
"x": 1,
"y": 1
}
}
},
{
"element": 4,
"is-folder": false,
"name": "子图层-7",
"styles": [
],
"transform": {
"filpX": false,
"filpY": false,
"offset": {
"x": 6,
"y": 408
},
"rotation": 0,
"scale": {
"x": 1,
"y": 1
}
}
},
{
"element": 4,
"is-folder": false,
"name": "子图层-8",
"styles": [
],
"transform": {
"filpX": false,
"filpY": false,
"offset": {
"x": 210,
"y": 408
},
"rotation": 0,
"scale": {
"x": 1,
"y": 1
}
}
},
{
"element": 4,
"is-folder": false,
"name": "子图层-9",
"styles": [
],
"transform": {
"filpX": false,
"filpY": false,
"offset": {
"x": 414,
"y": 405
},
"rotation": 0,
"scale": {
"x": 1,
"y": 1
}
}
},
{
"element": 4,
"is-folder": false,
"name": "子图层-10",
"styles": [
],
"transform": {
"filpX": false,
"filpY": false,
"offset": {
"x": 610,
"y": -3
},
"rotation": 0,
"scale": {
"x": 1,
"y": 1
}
}
},
{
"element": 4,
"is-folder": false,
"name": "子图层-11",
"styles": [
],
"transform": {
"filpX": false,
"filpY": false,
"offset": {
"x": 614,
"y": 200
},
"rotation": 0,
"scale": {
"x": 1,
"y": 1
}
}
},
{
"element": 4,
"is-folder": false,
"name": "子图层-12",
"styles": [
],
"transform": {
"filpX": false,
"filpY": false,
"offset": {
"x": 617,
"y": 405
},
"rotation": 0,
"scale": {
"x": 1,
"y": 1
}
}
},
{
"element": 4,
"is-folder": false,
"name": "子图层-13",
"styles": [
],
"transform": {
"filpX": false,
"filpY": false,
"offset": {
"x": -3,
"y": -202
},
"rotation": 0,
"scale": {
"x": 1,
"y": 1
}
}
},
{
"element": 4,
"is-folder": false,
"name": "子图层-14",
"styles": [
],
"transform": {
"filpX": false,
"filpY": false,
"offset": {
"x": 200,
"y": -203
},
"rotation": 0,
"scale": {
"x": 1,
"y": 1
}
}
},
{
"element": 4,
"is-folder": false,
"name": "子图层-15",
"styles": [
],
"transform": {
"filpX": false,
"filpY": false,
"offset": {
"x": 402,
"y": -205
},
"rotation": 0,
"scale": {
"x": 1,
"y": 1
}
}
},
{
"element": 4,
"is-folder": false,
"name": "子图层-16",
"styles": [
],
"transform": {
"filpX": false,
"filpY": false,
"offset": {
"x": 606,
"y": -206
},
"rotation": 0,
"scale": { "scale": {
"x": 1, "x": 1,
"y": 1 "y": 1
@ -409,14 +476,16 @@
"name": "root", "name": "root",
"referenced-by": null, "referenced-by": null,
"transform": { "transform": {
"filpX": false,
"filpY": false,
"offset": { "offset": {
"x": 8, "x": 116,
"y": 20 "y": 288
}, },
"rotation": 0, "rotation": 60,
"scale": { "scale": {
"x": 1.7159367435419115, "x": 1.85202,
"y": 1.7159367435419115 "y": 1.85202
} }
} }
}, },

View File

@ -72,6 +72,7 @@
<PreprocessorDefinitions>FRAMELESSHELPER_WIDGETS_STATIC;%(PreprocessorDefinitions)</PreprocessorDefinitions> <PreprocessorDefinitions>FRAMELESSHELPER_WIDGETS_STATIC;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<WarningLevel>Level1</WarningLevel> <WarningLevel>Level1</WarningLevel>
<FavorSizeOrSpeed>Speed</FavorSizeOrSpeed> <FavorSizeOrSpeed>Speed</FavorSizeOrSpeed>
<TreatSpecificWarningsAsErrors>4715;</TreatSpecificWarningsAsErrors>
</ClCompile> </ClCompile>
<Link> <Link>
<AdditionalDependencies>opengl32.lib;%(AdditionalDependencies)</AdditionalDependencies> <AdditionalDependencies>opengl32.lib;%(AdditionalDependencies)</AdditionalDependencies>
@ -104,6 +105,7 @@
</Link> </Link>
</ItemDefinitionGroup> </ItemDefinitionGroup>
<ItemGroup> <ItemGroup>
<ClCompile Include="src\Editor\EditorWidgetComponent\LayerContainerListWidget.cpp" />
<ClCompile Include="src\Editor\DataManager\ProjectDataManager.cpp" /> <ClCompile Include="src\Editor\DataManager\ProjectDataManager.cpp" />
<ClCompile Include="src\Editor\EditorWidgetComponent\FillStyleWidget.cpp" /> <ClCompile Include="src\Editor\EditorWidgetComponent\FillStyleWidget.cpp" />
<ClCompile Include="src\Editor\RightBar\EditorSettingWidget.cpp" /> <ClCompile Include="src\Editor\RightBar\EditorSettingWidget.cpp" />
@ -128,6 +130,7 @@
<ClCompile Include="src\Editor\util\PaintingUtil.cpp" /> <ClCompile Include="src\Editor\util\PaintingUtil.cpp" />
<ClCompile Include="src\Editor\util\SvgFileLoader.cpp" /> <ClCompile Include="src\Editor\util\SvgFileLoader.cpp" />
<ClCompile Include="src\FluentMenu.cpp" /> <ClCompile Include="src\FluentMenu.cpp" />
<ClCompile Include="src\FluentMenuButton.cpp" />
<ClCompile Include="src\gl.c" /> <ClCompile Include="src\gl.c" />
<ClCompile Include="src\main.cpp" /> <ClCompile Include="src\main.cpp" />
<ClCompile Include="src\MainWindow.cpp" /> <ClCompile Include="src\MainWindow.cpp" />
@ -204,12 +207,13 @@
<QtMoc Include="src\Editor\EditorWidgetComponent\ColorPicker.h" /> <QtMoc Include="src\Editor\EditorWidgetComponent\ColorPicker.h" />
<QtMoc Include="src\Editor\RightBar\EditorSettingWidget.h" /> <QtMoc Include="src\Editor\RightBar\EditorSettingWidget.h" />
<QtMoc Include="src\Editor\EditorWidgetComponent\FillStyleWidget.h" /> <QtMoc Include="src\Editor\EditorWidgetComponent\FillStyleWidget.h" />
<QtMoc Include="src\Editor\EditorWidgetComponent\LayerContainerListWidget.h" />
<ClInclude Include="src\Editor\DataManager\ProjectDataManager.h" /> <ClInclude Include="src\Editor\DataManager\ProjectDataManager.h" />
<ClInclude Include="src\ColorHelper.hpp" /> <ClInclude Include="src\ColorHelper.hpp" />
<ClInclude Include="src\Editor\LayerWrapper.h" /> <ClInclude Include="src\Editor\LayerWrapper.h" />
<ClInclude Include="src\Editor\util\EncodeUtil.hpp" /> <ClInclude Include="src\Editor\util\EncodeUtil.hpp" />
<ClInclude Include="src\Editor\util\JsonUtil.hpp" /> <ClInclude Include="src\Editor\util\JsonUtil.hpp" />
<ClInclude Include="src\Editor\ElementManager.h" /> <QtMoc Include="src\Editor\ElementManager.h" />
<QtMoc Include="src\Editor\ElementPoolWidget.h" /> <QtMoc Include="src\Editor\ElementPoolWidget.h" />
<ClInclude Include="src\Editor\GraphicElement.h" /> <ClInclude Include="src\Editor\GraphicElement.h" />
<ClInclude Include="src\Editor\LayerManager.h" /> <ClInclude Include="src\Editor\LayerManager.h" />
@ -224,6 +228,7 @@
<ClInclude Include="src\Editor\util\PaintingUtil.h" /> <ClInclude Include="src\Editor\util\PaintingUtil.h" />
<ClInclude Include="src\Editor\util\SvgFileLoader.h" /> <ClInclude Include="src\Editor\util\SvgFileLoader.h" />
<QtMoc Include="src\FluentMenu.h" /> <QtMoc Include="src\FluentMenu.h" />
<ClInclude Include="src\FluentMenuButton.h" />
<ClInclude Include="src\Renderer\IblUtils.h" /> <ClInclude Include="src\Renderer\IblUtils.h" />
<ClInclude Include="src\Renderer\Painting\BaseStyle.h" /> <ClInclude Include="src\Renderer\Painting\BaseStyle.h" />
<ClInclude Include="src\Renderer\Painting\CubicBezierSignedDistance.h" /> <ClInclude Include="src\Renderer\Painting\CubicBezierSignedDistance.h" />

View File

@ -264,6 +264,12 @@
<ClCompile Include="src\Editor\EditorWidgetComponent\ColorPicker.cpp"> <ClCompile Include="src\Editor\EditorWidgetComponent\ColorPicker.cpp">
<Filter>Source Files\Editor</Filter> <Filter>Source Files\Editor</Filter>
</ClCompile> </ClCompile>
<ClCompile Include="src\FluentMenuButton.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="src\Editor\EditorWidgetComponent\LayerContainerListWidget.cpp">
<Filter>Source Files</Filter>
</ClCompile>
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<QtMoc Include="src\Renderer\RendererGLWidget.h"> <QtMoc Include="src\Renderer\RendererGLWidget.h">
@ -320,6 +326,12 @@
<QtMoc Include="src\Editor\EditorWidgetComponent\FillStyleWidget.h"> <QtMoc Include="src\Editor\EditorWidgetComponent\FillStyleWidget.h">
<Filter>Header Files</Filter> <Filter>Header Files</Filter>
</QtMoc> </QtMoc>
<QtMoc Include="src\Editor\EditorWidgetComponent\LayerContainerListWidget.h">
<Filter>Header Files</Filter>
</QtMoc>
<QtMoc Include="src\Editor\ElementManager.h">
<Filter>Header Files\Editor\Element</Filter>
</QtMoc>
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<None Include="..\data.json" /> <None Include="..\data.json" />
@ -510,9 +522,6 @@
<ClInclude Include="src\Editor\util\EncodeUtil.hpp"> <ClInclude Include="src\Editor\util\EncodeUtil.hpp">
<Filter>Header Files\Editor\util</Filter> <Filter>Header Files\Editor\util</Filter>
</ClInclude> </ClInclude>
<ClInclude Include="src\Editor\ElementManager.h">
<Filter>Header Files\Editor\Element</Filter>
</ClInclude>
<ClInclude Include="src\Editor\GraphicElement.h"> <ClInclude Include="src\Editor\GraphicElement.h">
<Filter>Header Files\Editor\Element</Filter> <Filter>Header Files\Editor\Element</Filter>
</ClInclude> </ClInclude>
@ -531,6 +540,9 @@
<ClInclude Include="src\Editor\DataManager\ProjectDataManager.h"> <ClInclude Include="src\Editor\DataManager\ProjectDataManager.h">
<Filter>Header Files</Filter> <Filter>Header Files</Filter>
</ClInclude> </ClInclude>
<ClInclude Include="src\FluentMenuButton.h">
<Filter>Header Files</Filter>
</ClInclude>
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<QtRcc Include="res\MainWindow.qrc"> <QtRcc Include="res\MainWindow.qrc">

View File

@ -71,7 +71,7 @@
</widget> </widget>
</item> </item>
<item row="5" column="0" colspan="2"> <item row="5" column="0" colspan="2">
<widget class="QListWidget" name="styleList"/> <widget class="LayerContainerListWidget" name="styleList" native="true"/>
</item> </item>
</layout> </layout>
</widget> </widget>
@ -86,6 +86,11 @@
<extends>QCheckBox</extends> <extends>QCheckBox</extends>
<header location="global">qtmaterialcheckbox.h</header> <header location="global">qtmaterialcheckbox.h</header>
</customwidget> </customwidget>
<customwidget>
<class>LayerContainerListWidget</class>
<extends>QWidget</extends>
<header location="global">EditorWidgetComponent/LayerContainerListWidget.h</header>
</customwidget>
</customwidgets> </customwidgets>
<resources/> <resources/>
<connections/> <connections/>

View File

@ -41,7 +41,7 @@
<number>0</number> <number>0</number>
</property> </property>
<item> <item>
<layout class="QHBoxLayout" name="horizontalLayout_2" stretch="1,1,1,1,1,0"> <layout class="QHBoxLayout" name="horizontalLayout_2" stretch="1,1,0">
<property name="spacing"> <property name="spacing">
<number>0</number> <number>0</number>
</property> </property>
@ -49,7 +49,7 @@
<number>0</number> <number>0</number>
</property> </property>
<item> <item>
<widget class="QPushButton" name="createButton"> <widget class="FluentMenuButton" name="fileMenuButton">
<property name="minimumSize"> <property name="minimumSize">
<size> <size>
<width>80</width> <width>80</width>
@ -63,12 +63,12 @@
</size> </size>
</property> </property>
<property name="text"> <property name="text">
<string>新建</string> <string>文件</string>
</property> </property>
</widget> </widget>
</item> </item>
<item> <item>
<widget class="QPushButton" name="openButton"> <widget class="FluentMenuButton" name="projectMenuButton">
<property name="minimumSize"> <property name="minimumSize">
<size> <size>
<width>80</width> <width>80</width>
@ -82,70 +82,7 @@
</size> </size>
</property> </property>
<property name="text"> <property name="text">
<string>打开</string> <string>项目</string>
</property>
</widget>
</item>
<item>
<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="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="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> </property>
</widget> </widget>
</item> </item>
@ -178,6 +115,9 @@
<property name="currentIndex"> <property name="currentIndex">
<number>0</number> <number>0</number>
</property> </property>
<property name="tabsClosable">
<bool>true</bool>
</property>
<widget class="QWidget" name="tab"> <widget class="QWidget" name="tab">
<attribute name="title"> <attribute name="title">
<string>Tab 1</string> <string>Tab 1</string>
@ -194,6 +134,13 @@
</item> </item>
</layout> </layout>
</widget> </widget>
<customwidgets>
<customwidget>
<class>FluentMenuButton</class>
<extends>QPushButton</extends>
<header location="global">../FluentMenuButton.h</header>
</customwidget>
</customwidgets>
<resources/> <resources/>
<connections/> <connections/>
</ui> </ui>

View File

@ -39,7 +39,7 @@
<widget class="QWidget" name="MainWindow" native="true"> <widget class="QWidget" name="MainWindow" native="true">
<layout class="QVBoxLayout" name="verticalLayout_2" stretch="20,1"> <layout class="QVBoxLayout" name="verticalLayout_2" stretch="20,1">
<item> <item>
<layout class="QHBoxLayout" name="horizontalLayout" stretch="0,12,5"> <layout class="QHBoxLayout" name="horizontalLayout" stretch="0,7,4,3">
<item> <item>
<widget class="QWidget" name="LeftBar" native="true"/> <widget class="QWidget" name="LeftBar" native="true"/>
</item> </item>
@ -66,11 +66,11 @@
<property name="widgetResizable"> <property name="widgetResizable">
<bool>true</bool> <bool>true</bool>
</property> </property>
<widget class="QWidget" name="scrollAreaWidgetContents"> <widget class="PreviewWindow" name="Preview">
<property name="geometry"> <property name="geometry">
<rect> <rect>
<x>0</x> <x>0</x>
<y>0</y> <y>-17</y>
<width>1024</width> <width>1024</width>
<height>1024</height> <height>1024</height>
</rect> </rect>
@ -88,35 +88,11 @@
</size> </size>
</property> </property>
<property name="maximumSize"> <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> <size>
<width>1024</width> <width>1024</width>
<height>1024</height> <height>1024</height>
</size> </size>
</property> </property>
<property name="maximumSize">
<size>
<width>1024</width>
<height>1024</height>
</size>
</property>
</widget>
</item>
</layout>
</widget> </widget>
</widget> </widget>
</item> </item>
@ -147,18 +123,13 @@
</size> </size>
</property> </property>
<property name="currentIndex"> <property name="currentIndex">
<number>2</number> <number>0</number>
</property> </property>
<widget class="InfoDisplayWidget" name="LayerDisplay"> <widget class="InfoDisplayWidget" name="LayerDisplay">
<attribute name="title"> <attribute name="title">
<string>图层信息</string> <string>图层信息</string>
</attribute> </attribute>
</widget> </widget>
<widget class="ElementPoolWidget" name="ElementDisplay">
<attribute name="title">
<string>图元池</string>
</attribute>
</widget>
<widget class="EditorSettingWidget" name="EditorSetting"> <widget class="EditorSettingWidget" name="EditorSetting">
<attribute name="title"> <attribute name="title">
<string>设置</string> <string>设置</string>
@ -196,16 +167,28 @@
<string>关联图元</string> <string>关联图元</string>
</property> </property>
</column> </column>
<column>
<property name="text">
<string>可见</string>
</property>
</column>
</widget> </widget>
</item> </item>
</layout> </layout>
</widget> </widget>
</item> </item>
<item>
<layout class="QVBoxLayout" name="ElementBar">
<item>
<widget class="QLabel" name="ElementDisplayLabel">
<property name="text">
<string>图元</string>
</property>
<property name="alignment">
<set>Qt::AlignCenter</set>
</property>
</widget>
</item>
<item>
<widget class="ElementPoolWidget" name="ElementDisplay" native="true"/>
</item>
</layout>
</item>
</layout> </layout>
</item> </item>
<item> <item>

View File

@ -41,7 +41,7 @@
<number>5</number> <number>5</number>
</property> </property>
<item> <item>
<widget class="QtMaterialFlatButton" name="openButton"> <widget class="FluentMenuButton" name="openButton">
<property name="sizePolicy"> <property name="sizePolicy">
<sizepolicy hsizetype="Fixed" vsizetype="Fixed"> <sizepolicy hsizetype="Fixed" vsizetype="Fixed">
<horstretch>0</horstretch> <horstretch>0</horstretch>
@ -181,9 +181,9 @@
<header>RendererGLWidget.h</header> <header>RendererGLWidget.h</header>
</customwidget> </customwidget>
<customwidget> <customwidget>
<class>QtMaterialFlatButton</class> <class>FluentMenuButton</class>
<extends>QPushButton</extends> <extends>QPushButton</extends>
<header location="global">qtmaterialflatbutton.h</header> <header>../FluentMenuButton.h</header>
</customwidget> </customwidget>
</customwidgets> </customwidgets>
<resources/> <resources/>

View File

@ -557,7 +557,7 @@ int solve_quartic(vec4 coeffs, inout vec4 s)
return num; 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, out float t)
{ {
// switch points when near to end point to minimize numerical error // switch points when near to end point to minimize numerical error
@ -794,7 +794,13 @@ float cubic_bezier_dis(vec2 uv, vec2 p0, vec2 p1, vec2 p2, vec2 p3, bool roundEn
{ {
roots[i] = clamp(roots[i], 0., 1.); roots[i] = clamp(roots[i], 0., 1.);
vec2 to_curve = uv - parametric_cub_bezier(roots[i], p0, p1, p2, p3); vec2 to_curve = uv - parametric_cub_bezier(roots[i], p0, p1, p2, p3);
d0 = min(d0, dot(to_curve, to_curve)); float d = dot(to_curve, to_curve);
if (d < d0)
{
d0 = d;
t = roots[i];
}
// d0 = min(d0, dot(to_curve, to_curve));
} }
else else
{ {
@ -893,11 +899,11 @@ bool angleLargeThanPi(vec2 a, vec2 b)
/************************************************************************************/ /************************************************************************************/
void drawLine(in float d, uint styleIndex, out vec4 elementColor, out vec2 metallicRoughness) void drawLine(in float d, uint styleHeadIndex, out vec4 elementColor, out vec2 metallicRoughness)
{ {
elementColor = vec4(1); elementColor = vec4(1);
metallicRoughness = vec2(0.8); metallicRoughness = vec2(0.8);
uint headUint = floatBitsToUint(style[styleIndex + 1]); uint headUint = floatBitsToUint(style[styleHeadIndex]);
vec4 head = unpackUnorm4x8(headUint); vec4 head = unpackUnorm4x8(headUint);
switch (int(head.a * 100) % 10) switch (int(head.a * 100) % 10)
// switch (2) // switch (2)
@ -905,7 +911,7 @@ void drawLine(in float d, uint styleIndex, out vec4 elementColor, out vec2 metal
/// Plain /// Plain
case 0: { case 0: {
metallicRoughness = head.rg; metallicRoughness = head.rg;
elementColor = vec4(unpackUnorm4x8(floatBitsToUint(style[styleIndex + 2])).rgb, 1); elementColor = vec4(unpackUnorm4x8(floatBitsToUint(style[styleHeadIndex + 1])).rgb, 1);
break; break;
} }
/// RadialGradient /// RadialGradient
@ -913,16 +919,16 @@ void drawLine(in float d, uint styleIndex, out vec4 elementColor, out vec2 metal
uint size = headUint % (1 << 15); uint size = headUint % (1 << 15);
bool gradual = (headUint & (1 << 15)) != 0; bool gradual = (headUint & (1 << 15)) != 0;
uint lastData = floatBitsToUint(style[styleIndex + 2 + 0 * 2]); uint lastData = floatBitsToUint(style[styleHeadIndex + 1 + 0 * 2]);
float lastLevel = 0; float lastLevel = 0;
vec2 lastMetallicRoughness = unpackUnorm4x8(lastData).rg; vec2 lastMetallicRoughness = unpackUnorm4x8(lastData).rg;
vec4 lastColor = vec4(unpackUnorm4x8(floatBitsToUint(style[styleIndex + 3 + 0 * 2])).rgb, 1); vec4 lastColor = vec4(unpackUnorm4x8(floatBitsToUint(style[styleHeadIndex + 2 + 0 * 2])).rgb, 1);
for (uint i = 0; i < size; i++) for (uint i = 0; i < size; i++)
{ {
uint data = floatBitsToUint(style[styleIndex + 2 + i * 2]); uint data = floatBitsToUint(style[styleHeadIndex + 1 + i * 2]);
float level = unpackUnorm2x16(data).y; float level = unpackUnorm2x16(data).y;
vec4 currentColor = vec4(unpackUnorm4x8(floatBitsToUint(style[styleIndex + 3 + i * 2])).rgb, 1); vec4 currentColor = vec4(unpackUnorm4x8(floatBitsToUint(style[styleHeadIndex + 2 + i * 2])).rgb, 1);
vec2 currentMetallicRoughness = unpackUnorm4x8(data).rg; vec2 currentMetallicRoughness = unpackUnorm4x8(data).rg;
if (d <= level) if (d <= level)
{ {
@ -969,21 +975,24 @@ void drawLine(in float d, uint styleIndex, out vec4 elementColor, out vec2 metal
} }
} }
/**
* @param styleIndex ÊäÈëstyleHeadµÄIndex£¬·µ»ØÏÂÒ»¸östyleµÄindex
*/
void nextStyleIndex(inout uint styleIndex) void nextStyleIndex(inout uint styleIndex)
{ {
uint headUint = floatBitsToUint(style[styleIndex + 1]); uint headUint = floatBitsToUint(style[styleIndex]);
vec4 head = unpackUnorm4x8(headUint); vec4 head = unpackUnorm4x8(headUint);
switch (int(head.a * 100) % 10) switch (int(head.a * 100) % 10)
{ {
/// Plain /// Plain
case 0: { case 0: {
styleIndex += 3; styleIndex += 2;
break; break;
} }
/// RadialGradient /// RadialGradient
case 1: { case 1: {
uint size = headUint % (1 << 15); uint size = headUint % (1 << 15);
styleIndex += 2 + size * 2; styleIndex += 1 + size * 2;
break; break;
} }
case 2: { case 2: {
@ -998,9 +1007,9 @@ bool shouldFillBeginCap(vec2 localUV, bool onVeryBegin, int endType, vec2 p0, ve
vec2 normal; vec2 normal;
if (onVeryBegin) if (onVeryBegin)
{ {
if (endType%2 == 0) if (endType % 2 == 0)
return true; return true;
else if (endType%2 == 1) else if (endType % 2 == 1)
normal = normalize(mat2(0, 1, -1, 0) * (-tangentBegin)); normal = normalize(mat2(0, 1, -1, 0) * (-tangentBegin));
} }
else else
@ -1017,9 +1026,9 @@ bool shouldFillEndCap(vec2 localUV, bool onVeryEnd, int endType, vec2 p3, vec2 t
vec2 normal; vec2 normal;
if (onVeryEnd) if (onVeryEnd)
{ {
if ((endType/2)%2 == 0) if ((endType / 2) % 2 == 0)
return true; return true;
else if ((endType/2)%2 == 1) else if ((endType / 2) % 2 == 1)
normal = normalize(mat2(0, 1, -1, 0) * tangentEnd); normal = normalize(mat2(0, 1, -1, 0) * tangentEnd);
} }
else else
@ -1034,11 +1043,7 @@ bool shouldFillEndCap(vec2 localUV, bool onVeryEnd, int endType, vec2 p3, vec2 t
void main() void main()
{ {
uvec2 pixelLocation = gl_GlobalInvocationID.xy; uvec2 pixelLocation = gl_GlobalInvocationID.xy;
// imageStore(gBaseColor, ivec2(pixelLocation), vec4(vec2(pixelLocation)/vec2(imageSize(gBaseColor)), 1,1));
vec4 color = vec4(0); 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;) for (uint styleIndex = 0; styleIndex < styleSize;)
{ {
styleIndex += 6; styleIndex += 6;
@ -1063,7 +1068,7 @@ void main()
continue; continue;
} }
mat4x2 p = mat4x2(p3Last, pTemp, path[++pathIndex], path[++pathIndex]); mat4x2 p = mat4x2(p3Last, pTemp, path[++pathIndex], path[++pathIndex]);
++pathIndex;
num_its += cubic_bezier_int_test(localUV, p[0], p[1], p[2], p[3]); num_its += cubic_bezier_int_test(localUV, p[0], p[1], p[2], p[3]);
p3Last = p[3]; p3Last = p[3];
p2Last = p[2]; p2Last = p[2];
@ -1075,46 +1080,41 @@ void main()
vec4 head = unpackUnorm4x8(floatBitsToUint(style[styleIndex])); vec4 head = unpackUnorm4x8(floatBitsToUint(style[styleIndex]));
if (head.z == 0) if (head.z == 0)
{ {
elementColor = vec4(unpackUnorm4x8(floatBitsToUint(style[styleIndex+1])).rgb, 1); elementColor = vec4(unpackUnorm4x8(floatBitsToUint(style[styleIndex + 1])).rgb, 1);
} }
} }
styleIndex += 2; styleIndex += 2;
} }
else // Stroke else // Stroke
{ {
uint widthMapSize = floatBitsToUint(style[styleIndex + 1]);
float minDistance = 1e38; float minDistance = 1e38;
vec4 styleHead = unpackUnorm4x8(floatBitsToUint(style[styleIndex + 1])); styleIndex += widthMapSize + 2;
vec4 styleHead = unpackUnorm4x8(floatBitsToUint(style[styleIndex]));
float lineType = floor(styleHead.b * 10); float lineType = floor(styleHead.b * 10);
// float lineType = 2;
int endType = int(round(styleHead.b * 100)) % 10; int endType = int(round(styleHead.b * 100)) % 10;
// endType = 1;
int debugBegin = 0; int debugBegin = 0;
bool onVeryBegin = false; bool onVeryBegin = false;
bool onVeryEnd = false; bool onVeryEnd = false;
vec2 tangentEndLast; vec2 tangentEndLast = vec2(0);
vec2 tangentFirstBegin; vec2 tangentFirstBegin;
uint lastHitIndex = 0; uint lastHitIndex = 0;
bool lastHitElement = false; bool lastHitElement = false;
hitElement = false; hitElement = false;
for (uint pathIndex = 0; pathIndex < pathSize; pathIndex++) for (uint pathIndex = 0; pathIndex < pathSize; pathIndex++)
//for (uint pathIndex = 0; pathIndex < 46; pathIndex++)
{ {
vec2 pTemp = path[pathIndex]; vec2 pTemp = path[pathIndex];
if (isinf(pTemp.x)) if (isinf(pTemp.x))
{ {
// TODO: ¼ì²âÊÇ·ñ·â±Õ²¢´¦Àí
pBegin = path[++pathIndex]; pBegin = path[++pathIndex];
p3Last = pBegin; p3Last = pBegin;
p2Last = pBegin; p2Last = pBegin;
if(endType == 4) if (endType == 4 /*StrokeEndType::kClosed*/)
{ {
//onVeryBegin = false; // onVeryBegin = false;
vec2 lastP1 = path[pathSize-3]; vec2 lastP1 = path[pathSize - 4];
vec2 lastP2 = path[pathSize-2]; vec2 lastP2 = path[pathSize - 3];
vec2 lastP3 = path[pathSize-1]; vec2 lastP3 = path[pathSize - 2];
if (lastP3 != lastP2) if (lastP3 != lastP2)
tangentEndLast = normalize(lastP3 - lastP2); tangentEndLast = normalize(lastP3 - lastP2);
else else
@ -1125,7 +1125,7 @@ void main()
continue; continue;
} }
mat4x2 p = mat4x2(p3Last, pTemp, path[++pathIndex], path[++pathIndex]); mat4x2 p = mat4x2(p3Last, pTemp, path[++pathIndex], path[++pathIndex]);
vec2 lengthRate = path[++pathIndex];
vec2 tangentBeginNext; vec2 tangentBeginNext;
if (pathIndex + 1 < pathSize) if (pathIndex + 1 < pathSize)
{ {
@ -1146,32 +1146,36 @@ void main()
} }
else else
{ {
if(endType == 4) if (endType == 4 /*StrokeEndType::kClosed*/)
{ {
//onVeryEnd = false; // onVeryEnd = false;
tangentBeginNext = tangentFirstBegin; tangentBeginNext = tangentFirstBegin;
} }
else else
onVeryEnd = true; 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;
bool onEnd = distance(localUV, p[3]) <= strokeWidth;
vec2 tangentBegin;
vec2 tangentEnd; 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]) if (p[3] != p[2])
tangentEnd = normalize(p[3] - p[2]); tangentEnd = normalize(p[3] - p[2]);
else else
tangentEnd = normalize(p[3] - p[1]); tangentEnd = normalize(p[3] - p[1]);
float t;
float d = cubic_bezier_dis(localUV, p[0], p[1], p[2], p[3], true, t);
float localWidth = strokeWidth; // * mix(lengthRate.x, lengthRate.y, t);
if (d <= localWidth)
{
bool onBegin = t<1e-5;
bool onEnd = t>1-1e-5;
//bool onBegin = true;
//bool onEnd = true;
vec2 tangentBegin;
if (p[0] != p[1])
tangentBegin = normalize(p[0] - p[1]);
else
tangentBegin = normalize(p[0] - p[2]);
bool hit = d < minDistance; bool hit = d < minDistance;
if (onBegin) if (onBegin)
hit = hit && hit = hit &&
@ -1198,19 +1202,19 @@ void main()
hitElement = true; hitElement = true;
// elementColor = vec4(1, 1, 0, 1); // elementColor = vec4(1, 1, 0, 1);
vec2 metallicRoughness; vec2 metallicRoughness;
drawLine(minDistance / strokeWidth, styleIndex, elementColor, metallicRoughness); drawLine(minDistance / localWidth, styleIndex, elementColor, metallicRoughness);
} }
} }
tangentEndLast = tangentEnd; if (pathIndex == 5)
if(pathIndex == 4)
tangentFirstBegin = tangentBegin; tangentFirstBegin = tangentBegin;
} }
tangentEndLast = tangentEnd;
p3Last = p[3]; p3Last = p[3];
p2Last = p[2]; p2Last = p[2];
onVeryBegin = false; onVeryBegin = false;
} }
nextStyleIndex(styleIndex); nextStyleIndex(styleIndex);
} }
if (hitElement) if (hitElement)
color = elementColor; color = elementColor;

View File

@ -6,7 +6,11 @@ layout(location = 0) uniform ivec2 pixelOffset;
layout(std140, binding = 1) uniform ubo layout(std140, binding = 1) uniform ubo
{ {
vec3 backgroundColor; float backgroundRed;
float backgroundGreen;
float backgroundBlue;
float backgroundMetallic;
float backgroundRoughness;
}; };
layout(rgba8, binding = 0) uniform image2D gBaseColor; layout(rgba8, binding = 0) uniform image2D gBaseColor;
@ -955,11 +959,11 @@ bool angleLargeThanPi(vec2 a, vec2 b)
return a.x * b.y - b.x * a.y < 0; return a.x * b.y - b.x * a.y < 0;
} }
void drawLine(in float d, in uint styleIndex, out vec4 elementColor, out vec2 metallicRoughness) void drawLine(in float d, uint styleHeadIndex, out vec4 elementColor, out vec2 metallicRoughness)
{ {
elementColor = vec4(1); elementColor = vec4(1);
metallicRoughness = vec2(0.8); metallicRoughness = vec2(0.8);
uint headUint = floatBitsToUint(elementData[styleIndex + 1]); uint headUint = floatBitsToUint(elementData[styleHeadIndex]);
vec4 head = unpackUnorm4x8(headUint); vec4 head = unpackUnorm4x8(headUint);
switch (int(head.a * 100) % 10) switch (int(head.a * 100) % 10)
// switch (2) // switch (2)
@ -967,7 +971,7 @@ void drawLine(in float d, in uint styleIndex, out vec4 elementColor, out vec2 me
/// Plain /// Plain
case 0: { case 0: {
metallicRoughness = head.rg; metallicRoughness = head.rg;
elementColor = vec4(unpackUnorm4x8(floatBitsToUint(elementData[styleIndex + 2])).rgb, 1); elementColor = vec4(unpackUnorm4x8(floatBitsToUint(elementData[styleHeadIndex + 1])).rgb, 1);
break; break;
} }
/// RadialGradient /// RadialGradient
@ -975,16 +979,16 @@ void drawLine(in float d, in uint styleIndex, out vec4 elementColor, out vec2 me
uint size = headUint % (1 << 15); uint size = headUint % (1 << 15);
bool gradual = (headUint & (1 << 15)) != 0; bool gradual = (headUint & (1 << 15)) != 0;
uint lastData = floatBitsToUint(elementData[styleIndex + 2 + 0 * 2]); uint lastData = floatBitsToUint(elementData[styleHeadIndex + 1 + 0 * 2]);
float lastLevel = 0; float lastLevel = 0;
vec2 lastMetallicRoughness = unpackUnorm4x8(lastData).rg; vec2 lastMetallicRoughness = unpackUnorm4x8(lastData).rg;
vec4 lastColor = vec4(unpackUnorm4x8(floatBitsToUint(elementData[styleIndex + 3 + 0 * 2])).rgb, 1); vec4 lastColor = vec4(unpackUnorm4x8(floatBitsToUint(elementData[styleHeadIndex + 2 + 0 * 2])).rgb, 1);
for (uint i = 0; i < size; i++) for (uint i = 0; i < size; i++)
{ {
uint data = floatBitsToUint(elementData[styleIndex + 2 + i * 2]); uint data = floatBitsToUint(elementData[styleHeadIndex + 1 + i * 2]);
float level = unpackUnorm2x16(data).y; float level = unpackUnorm2x16(data).y;
vec4 currentColor = vec4(unpackUnorm4x8(floatBitsToUint(elementData[styleIndex + 3 + i * 2])).rgb, 1); vec4 currentColor = vec4(unpackUnorm4x8(floatBitsToUint(elementData[styleHeadIndex + 2 + i * 2])).rgb, 1);
vec2 currentMetallicRoughness = unpackUnorm4x8(data).rg; vec2 currentMetallicRoughness = unpackUnorm4x8(data).rg;
if (d <= level) if (d <= level)
{ {
@ -1117,6 +1121,29 @@ bool fillElement(vec2 localUV, uint contourIndex, uint linesOffset, uint pointsO
return hitElement; return hitElement;
} }
vec2 getLineTangentEnd(uint contourIterator, uint linesOffset, uint pointsOffset)
{
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);
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]]);
if (p[0] == p[1] && p[2] == p[3])
{
p[1] = (p[0] + p[3]) / 2;
p[2] = p[1];
}
if (p[3] != p[2])
return normalize(p[3] - p[2]);
else
return normalize(p[3] - p[1]);
}
bool strokeElement(vec2 localUV, uint contourIndex, uint linesOffset, uint pointsOffset, uint styleIndex, bool strokeElement(vec2 localUV, uint contourIndex, uint linesOffset, uint pointsOffset, uint styleIndex,
inout vec4 elementColor, inout vec2 metallicRoughness) inout vec4 elementColor, inout vec2 metallicRoughness)
{ {
@ -1125,18 +1152,25 @@ bool strokeElement(vec2 localUV, uint contourIndex, uint linesOffset, uint point
float minDistance = 1e38; float minDistance = 1e38;
uint lineCount = elementIndexs[contourIndex]; uint lineCount = elementIndexs[contourIndex];
vec4 styleHead = unpackUnorm4x8(floatBitsToUint(elementData[styleIndex + 1])); uint widthMapSize = floatBitsToUint(elementData[styleIndex + 1]);
styleIndex += widthMapSize + 2;
vec4 styleHead = unpackUnorm4x8(floatBitsToUint(elementData[styleIndex]));
float lineType = floor(styleHead.b * 10); float lineType = floor(styleHead.b * 10);
int endType = int(round(styleHead.b * 100)) % 10; int endType = int(round(styleHead.b * 100)) % 10;
vec2 p3Last = vec2(1e38); vec2 p3Last = vec2(1e38);
vec2 p2Last = vec2(1e38); vec2 p2Last = vec2(1e38);
vec2 tangentEndLast; vec2 tangentFirstBegin;
vec2 tangentEndLast = getLineTangentEnd(contourIndex + lineCount, linesOffset, pointsOffset);
int debugBegin = 0; int debugBegin = 0;
bool onVeryBegin = false;
bool onVeryEnd = false;
if (endType != 4 /*StrokeEndType::kClosed*/)
onVeryBegin = true;
for (uint contourIterator_ = contourIndex + 1; contourIterator_ < contourIndex + 1 + lineCount; contourIterator_++) for (uint contourIterator_ = contourIndex + 1; contourIterator_ < contourIndex + 1 + lineCount; contourIterator_++)
{ {
uint contourIterator = contourIterator_; uint contourIterator = contourIterator_;
if (contourIterator_ == contourIndex + 1 + lineCount)
contourIterator = contourIndex + 1;
uint lineIndex = elementIndexs[contourIterator]; uint lineIndex = elementIndexs[contourIterator];
uint pLocation = linesOffset + 3 * lineIndex; uint pLocation = linesOffset + 3 * lineIndex;
vec2 percent = unpackUnorm2x16(elementIndexs[pLocation + 2]); vec2 percent = unpackUnorm2x16(elementIndexs[pLocation + 2]);
@ -1170,13 +1204,18 @@ bool strokeElement(vec2 localUV, uint contourIndex, uint linesOffset, uint point
pNext[2] = pNext[1]; pNext[2] = pNext[1];
} }
// if(pNext[0]!=p[3])
// break;
if (pNext[0] != pNext[1]) if (pNext[0] != pNext[1])
tangentBeginNext = normalize(pNext[0] - pNext[1]); tangentBeginNext = normalize(pNext[0] - pNext[1]);
else else
tangentBeginNext = normalize(pNext[0] - pNext[2]); tangentBeginNext = normalize(pNext[0] - pNext[2]);
} }
else
{
if (endType == 4 /*StrokeEndType::kClosed*/)
tangentBeginNext = tangentFirstBegin;
else
onVeryEnd = true;
}
if (p[0] == p[1] && p[2] == p[3]) if (p[0] == p[1] && p[2] == p[3])
{ {
@ -1212,20 +1251,13 @@ bool strokeElement(vec2 localUV, uint contourIndex, uint linesOffset, uint point
bool hit = d < minDistance; bool hit = d < minDistance;
if (onBegin) if (onBegin)
hit = hit && shouldFillBeginCap(localUV, contourIterator == contourIndex + 1, endType, p[0], hit = hit && shouldFillBeginCap(localUV, onVeryBegin, endType, p[0], tangentBegin, tangentEndLast);
tangentBegin, p3Last - p2Last);
if (onEnd) if (onEnd)
hit = hit && shouldFillEndCap(localUV, tangentBeginNext == vec2(0), endType, p[3], tangentEnd, hit = hit && shouldFillEndCap(localUV, onVeryEnd, endType, p[3], tangentEnd, tangentBeginNext);
tangentBeginNext);
if (hit) if (hit)
{ {
bool reverse = p[3].y - p[0].y < 0.; bool reverse = p[3].y - p[0].y < 0.;
// 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) + 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[0], tangentBegin, reverse) +
ray_int_test(localUV, p[3], tangentEnd, reverse); ray_int_test(localUV, p[3], tangentEnd, reverse);
@ -1234,21 +1266,16 @@ bool strokeElement(vec2 localUV, uint contourIndex, uint linesOffset, uint point
{ {
minDistance = min(minDistance, d); minDistance = min(minDistance, d);
hitElement = true; hitElement = true;
// elementColor = vec4(1, 1, 0, 1);
vec2 metallicRoughness;
drawLine(minDistance / strokeWidth, styleIndex, elementColor, metallicRoughness); drawLine(minDistance / strokeWidth, styleIndex, elementColor, metallicRoughness);
} }
// else if (p3Last == p[0])
// hitElement = false;
} }
tangentEndLast = tangentEnd; tangentEndLast = tangentEnd;
if (contourIterator == contourIndex + 1)
tangentFirstBegin = tangentBegin;
} }
p3Last = p[3]; p3Last = p[3];
p2Last = p[2]; p2Last = p[2];
} onVeryBegin = false;
if (hitElement && distance(localUV, p3Last) <= strokeWidth)
{
// hitElement = shouldFillEndCap(localUV, percent[1] > 1 - 1e-5, endType, p3Last, tangentEndLast, vec2(0));
} }
// if (minDistance <= 0.001) // if (minDistance <= 0.001)
@ -1349,8 +1376,11 @@ void main()
vec3 debugBVH = vec3(0); vec3 debugBVH = vec3(0);
// bool debugHit = false; // bool debugHit = false;
// vec4 color = vec4(0.76, 0.33, 0.15, -1); // vec4 color = vec4(0.76, 0.33, 0.15, -1);
vec4 color = vec4(backgroundColor, -1); vec4 color = vec4(backgroundRed, backgroundGreen, backgroundBlue, -1);
vec2 metallicRoughness = vec2(0, 0.8); vec2 metallicRoughness = vec2(backgroundMetallic, backgroundRoughness);
uint zIndex = 1 << 15;
// vec4 color = vec4(0.72f, 0.66f, 0.55f, -1);
// vec2 metallicRoughness = vec2(0,0.5);
stack.top = 0; stack.top = 0;
uint index = 0, visitTime = 0; uint index = 0, visitTime = 0;
// uint bvhLength = paintingOffsets[paintingIndex-1].y; // uint bvhLength = paintingOffsets[paintingIndex-1].y;
@ -1369,25 +1399,26 @@ void main()
debugBVH.g += 0.3; debugBVH.g += 0.3;
if (leftChild >= bvhLength) if (leftChild >= bvhLength)
{
uint elementZIndex = bvhChildren[index].y >> 18;
if (elementZIndex <= zIndex)
{ {
uint transformIndex = leftChild - 0x80000000; uint transformIndex = leftChild - 0x80000000;
uint zIndex = bvhChildren[index].y >> 18; uint elementIndex = bvhChildren[index].y & 0x3FFFF;
uint elementIndex = bvhChildren[index].y - zIndex;
mat3x2 transform = elementTranform[transformIndex]; mat3x2 transform = elementTranform[transformIndex];
vec2 localUV = vec2 localUV =
(mat3(vec3(transform[0], 0), vec3(transform[1], 0), vec3(transform[2], 1)) * vec3(uv, 1)).xy; (mat3(vec3(transform[0], 0), vec3(transform[1], 0), vec3(transform[2], 1)) * vec3(uv, 1))
.xy;
vec3 elementColor; vec3 elementColor;
vec2 elementMetallicRoughness; vec2 elementMetallicRoughness;
if (drawElement(elementIndex, localUV, elementColor, elementMetallicRoughness, if (drawElement(elementIndex, localUV, elementColor, elementMetallicRoughness, debugBVH))
debugBVH))
{ {
color = vec4(elementColor, zIndex); zIndex = elementZIndex;
color = vec4(elementColor, elementZIndex);
metallicRoughness = elementMetallicRoughness; metallicRoughness = elementMetallicRoughness;
} }
//if(elementIndex == 1 && transformIndex==1) }
// color = vec4(1,1,0,1);
index = bvhLength; index = bvhLength;
} }
else else

View File

@ -8,42 +8,25 @@
EditorWidget::EditorWidget(QWidget* parent) : QWidget(parent) EditorWidget::EditorWidget(QWidget* parent) : QWidget(parent)
{ {
ui.setupUi(this); ui.setupUi(this);
this->createButton = ui.createButton; this->fileMenuButton = ui.fileMenuButton;
this->closeButton = ui.closeButton; this->projectMenuButton = ui.projectMenuButton;
this->saveButton = ui.saveButton;
this->saveAsButton = ui.saveAsButton;
this->openButton = ui.openButton;
this->tabWidget = ui.tabWidget; this->tabWidget = ui.tabWidget;
while (this->tabWidget->count() > 0) while (this->tabWidget->count() > 0)
{ {
this->tabWidget->removeTab(0); this->tabWidget->removeTab(0);
} }
connect(this->createButton, &QPushButton::clicked, this, [this]() {
static int count = 0; connect(this, &EditorWidget::tabCountChanged, this, &EditorWidget::onTabCountChanged);
this->tabWidget->addTab(new EditorWidgetItem("../data.json",this), "untitled" + QString::number(count++));
}); initFileMenu();
connect(this->openButton, &QPushButton::clicked, this, [this]() { initProjectMenu();
QString fileName = QFileDialog::getOpenFileName(this->saveAsButton, QString::fromLocal8Bit("´ò¿ª"), "", QString::fromLocal8Bit("JSONÎļþ(*.json)"));
if(!fileName.isEmpty()) connect(this->tabWidget, &QTabWidget::tabCloseRequested, [this](int index) {
this->tabWidget->addTab(new EditorWidgetItem(fileName, this), fileName); const int prevCount = this->tabWidget->count();
}); this->tabWidget->removeTab(index);
connect(this->closeButton, &QPushButton::clicked, this, [this]() { const int nowCount = this->tabWidget->count();
this->tabWidget->removeTab(this->tabWidget->currentIndex());
}); emit tabCountChanged(prevCount, nowCount);
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);
}
}); });
} }
@ -55,3 +38,72 @@ void EditorWidget::renameTab(QWidget* target, QString name)
this->tabWidget->setTabText(index, name); this->tabWidget->setTabText(index, name);
} }
} }
void EditorWidget::onTabCountChanged(int prev, int now)
{
if (now != 0)
{
this->projectMenuButton->setDisabled(false);
}
else
{
this->projectMenuButton->setDisabled(true);
}
}
void EditorWidget::initFileMenu()
{
auto* actionCreate = new QAction(QStringLiteral("新建"), fileMenuButton);
auto* actionOpen = new QAction(QStringLiteral("打开"), fileMenuButton);
auto* actionSave = new QAction(QStringLiteral("保存"), fileMenuButton);
auto* actionSaveAs = new QAction(QStringLiteral("另存为"), fileMenuButton);
fileMenuButton->addMenuAction(actionCreate);
fileMenuButton->addMenuAction(actionOpen);
fileMenuButton->addMenuAction(actionSave);
fileMenuButton->addMenuAction(actionSaveAs);
connect(actionCreate, &QAction::triggered, [this] {
static int count = 0;
const int prevCount = this->tabWidget->count();
this->tabWidget->addTab(new EditorWidgetItem("../data.json", this), "untitled" + QString::number(count++));
const int nowCount = this->tabWidget->count();
emit tabCountChanged(prevCount, nowCount);
});
connect(actionOpen, &QAction::triggered, this, [this] {
const QString fileName = QFileDialog::getOpenFileName(this, QStringLiteral("打开"), "", QStringLiteral("JSON文件(*.json)"));
if (!fileName.isEmpty())
{
const int prevCount = this->tabWidget->count();
this->tabWidget->addTab(new EditorWidgetItem(fileName, this), fileName);
const int nowCount = this->tabWidget->count();
emit tabCountChanged(prevCount, nowCount);
}
});
connect(actionSave, &QAction::triggered, this, [this] {
auto* item = dynamic_cast<EditorWidgetItem*>(this->tabWidget->currentWidget());
if (item != nullptr)
{
item->save();
}
});
connect(actionSaveAs, &QAction::triggered, this, [this] {
const auto* item = dynamic_cast<EditorWidgetItem*>(this->tabWidget->currentWidget());
if (item != nullptr)
{
const QString fileName = QFileDialog::getSaveFileName(this, QStringLiteral("另存为"), "", QStringLiteral("JSON文件(*.json)"));
item->saveAs(fileName);
}
});
}
void EditorWidget::initProjectMenu()
{
if (tabWidget->count() == 0)
{
projectMenuButton->setDisabled(true);
}
auto* actionSettings = new QAction(QStringLiteral("项目设置"), projectMenuButton);
projectMenuButton->addMenuAction(actionSettings);
}

View File

@ -1,5 +1,6 @@
#pragma once #pragma once
#include <qwidget.h> #include <qwidget.h>
#include "../FluentMenuButton.h"
#include "ui_EditorWidget.h" #include "ui_EditorWidget.h"
class EditorWidget : class EditorWidget :
@ -9,15 +10,21 @@ class EditorWidget :
private: private:
Ui::EditorWidget ui; Ui::EditorWidget ui;
QTabWidget* tabWidget; QTabWidget* tabWidget;
QPushButton* createButton; FluentMenuButton* fileMenuButton;
QPushButton* closeButton; FluentMenuButton* projectMenuButton;
QPushButton* saveButton;
QPushButton* saveAsButton; void initFileMenu();
QPushButton* openButton; void initProjectMenu();
public: public:
EditorWidget(QWidget* parent = nullptr); EditorWidget(QWidget* parent = nullptr);
~EditorWidget()=default; ~EditorWidget()=default;
void renameTab(QWidget* target, QString name); void renameTab(QWidget* target, QString name);
protected slots:
void onTabCountChanged(int prev, int now);
signals:
void tabCountChanged(int prev, int now);
}; };

View File

@ -0,0 +1,191 @@
#include "LayerContainerListWidget.h"
#include "LayerStyleDialog.h"
#include "../ColorHelper.hpp"
#include <QLabel>
#include <ranges>
inline void initMaterialButton(QtMaterialRaisedButton* button)
{
button->setFixedHeight(25);
button->setBackgroundColor(ColorHelper::instance().getPrimary1());
}
LayerContainerListWidget::LayerContainerListWidget(QWidget* parent, const PLayerStyleContainer& styleContainer)
: QWidget(parent), headerWidget(new QWidget(this)), styleList(new QListWidget(this)), styleContainer(styleContainer)
{
connect(this, &LayerContainerListWidget::addLayerStyle,
this, &LayerContainerListWidget::onAddLayerStyle);
connect(this, &LayerContainerListWidget::removeLayerStyle,
this, &LayerContainerListWidget::onRemoveLayerStyle);
auto* widgetLayout = new QVBoxLayout(this);
initHeader();
widgetLayout->addWidget(this->headerWidget);
widgetLayout->addWidget(this->styleList);
setStyleContainer(styleContainer, true);
}
bool comparePStyleContainersEquality(const LayerStyleContainer* a, const LayerStyleContainer* b)
{
if (a == nullptr && b == nullptr)
{
return true;
}
if (a == nullptr || b == nullptr)
{
return false;
}
return a == b || *a == *b;
}
void LayerContainerListWidget::setStyleContainer(const PLayerStyleContainer& styleContainer, bool forceRefresh)
{
if (!forceRefresh && comparePStyleContainersEquality(this->styleContainer, styleContainer))
{
return;
}
styleList->clear();
if (!styleContainer)
{
return;
}
this->styleContainer = styleContainer;
for (const auto& style : *styleContainer | std::views::values)
{
auto* item = new QListWidgetItem(styleList);
item->setSizeHint(QSize(50, 40));
styleList->setItemWidget(item, buildStyleListWidget(style));
}
resetAddButton();
}
LayerContainerListWidget::PLayerStyleContainer LayerContainerListWidget::getStyleContainer() const
{
return styleContainer;
}
void LayerContainerListWidget::resetAddButton()
{
if (!styleContainer)
{
return;
}
qDebug() << "resetAddButton" << styleContainer->full();
addButton->setDisabled(styleContainer->full());
}
void LayerContainerListWidget::onAddLayerStyle(const std::shared_ptr<LayerStyle>& style)
{
if (const bool ok = styleContainer->useStyle(style))
{
styleContainer->computeNewHash();
resetAddButton();
auto* newItem = new QListWidgetItem(styleList);
styleItemMap[style->getDisplayName()] = newItem;
newItem->setSizeHint(QSize(50, 40));
styleList->setItemWidget(newItem, buildStyleListWidget(style));
}
}
void LayerContainerListWidget::onRemoveLayerStyle(const QString& styleName)
{
if (const bool ok = styleContainer->dropStyle(styleName))
{
styleContainer->computeNewHash();
auto* removedItem = styleItemMap.extract(styleName).mapped();
resetAddButton();
delete styleList->takeItem(styleList->row(removedItem));
}
}
void LayerContainerListWidget::initHeader()
{
auto* headerLayout = new QHBoxLayout;
auto* headerLabel = new QLabel(headerWidget);
headerLabel->setText(QStringLiteral("样式列表"));
headerLabel->setSizePolicy(QSizePolicy(QSizePolicy::Expanding, QSizePolicy::Minimum));
this->addButton = new QtMaterialRaisedButton("+", headerWidget);
initMaterialButton(addButton);
connect(addButton, &QPushButton::clicked, [this] {
auto* dialog = new LayerStyleDialog(this->styleContainer, nullptr, this);
dialog->exec();
if (dialog->layerStyle)
{
emit addLayerStyle(dialog->layerStyle);
}
});
this->copyButton = new QtMaterialRaisedButton(QStringLiteral("复制所有样式"), headerWidget);
initMaterialButton(copyButton);
// TODO: 实现复制
this->pasteButton = new QtMaterialRaisedButton(QStringLiteral("从剪贴板粘贴"), headerWidget);
initMaterialButton(pasteButton);
headerLayout->addWidget(headerLabel);
headerLayout->addWidget(addButton);
headerLayout->addWidget(copyButton);
headerLayout->addWidget(pasteButton);
headerLayout->setContentsMargins(5, 0, 5, 0);
headerWidget->setLayout(headerLayout);
/*auto* headerItem = new QListWidgetItem(this);
headerItem->setFlags(Qt::NoItemFlags);
this->addItem(headerItem);
this->setItemWidget(headerItem, headerWidget);*/
}
QWidget* LayerContainerListWidget::buildStyleListWidget(std::shared_ptr<LayerStyle> style)
{
auto* w = new QWidget(this);
auto* layout = new QHBoxLayout;
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(QStringLiteral("×"));
removeButton->setFixedSize(QSize(20, 20));
const QString styleDisplayName = style->getDisplayName();
connect(detailButton, &QPushButton::clicked,
[this, styleDisplayName]
{
const auto targetStyle = this->styleContainer->getStyle(styleDisplayName);
auto* dialog = new LayerStyleDialog(this->styleContainer, targetStyle, this);
dialog->exec();
if (dialog->layerStyle)
{
this->styleContainer->overrideStyle(dialog->layerStyle);
this->styleContainer->computeNewHash();
emit editLayerStyle(targetStyle);
}
});
connect(removeButton, &QPushButton::clicked,
[this, styleDisplayName]
{
emit removeLayerStyle(styleDisplayName);
});
QWidget* styleDisplayWidget = style->getListDisplayWidget();
styleDisplayWidget->setParent(w);
styleDisplayWidget->setSizePolicy(QSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding));
layout->addWidget(styleDisplayWidget);
layout->addWidget(detailButton);
layout->addWidget(removeButton);
w->setLayout(layout);
return w;
}

View File

@ -0,0 +1,38 @@
#pragma once
#include <map>
#include <QListWidget>
#include <qtmaterialraisedbutton.h>
#include "LayerStyle.h"
class LayerContainerListWidget : public QWidget
{
Q_OBJECT
using PLayerStyleContainer = LayerStyleContainer*;
private:
QWidget* headerWidget;
QtMaterialRaisedButton* addButton;
QtMaterialRaisedButton* copyButton;
QtMaterialRaisedButton* pasteButton;
QListWidget* styleList;
PLayerStyleContainer styleContainer;
std::map<QString, QListWidgetItem*> styleItemMap;
void initHeader();
void resetAddButton();
QWidget* buildStyleListWidget(std::shared_ptr<LayerStyle> style);
public:
LayerContainerListWidget(QWidget* parent = nullptr, const PLayerStyleContainer& styleContainer = nullptr);
void setStyleContainer(const PLayerStyleContainer& styleContainer, bool forceRefresh = false);
PLayerStyleContainer getStyleContainer() const;
protected slots:
void onAddLayerStyle(const std::shared_ptr<LayerStyle>& style);
void onRemoveLayerStyle(const QString& styleName);
signals:
void addLayerStyle(const std::shared_ptr<LayerStyle>& style);
void editLayerStyle(const std::shared_ptr<LayerStyle>& style);
void removeLayerStyle(const QString& styleName);
};

View File

@ -4,10 +4,10 @@
#include <QGridLayout> #include <QGridLayout>
LayerStyleDialog::LayerStyleDialog( LayerStyleDialog::LayerStyleDialog(
LayerStyleContainer& styles, LayerStyleContainer* pStyles,
const std::shared_ptr<LayerStyle>& existedStyle, const std::shared_ptr<LayerStyle>& existedStyle,
QWidget* parent QWidget* parent
) : QDialog(parent), styles(&styles) ) : QDialog(parent), styles(pStyles)
{ {
dialogLayout = new QGridLayout; dialogLayout = new QGridLayout;
dialogLayout->setAlignment(Qt::AlignmentFlag::AlignHCenter); dialogLayout->setAlignment(Qt::AlignmentFlag::AlignHCenter);
@ -23,13 +23,13 @@ LayerStyleDialog::LayerStyleDialog(
} }
else else
{ {
QStringList unusedStyleNames = styles.unusedStyleNames(); QStringList unusedStyleNames = styles->unusedStyleNames();
auto* typeSelector = new QComboBox(this); auto* typeSelector = new QComboBox(this);
if (!unusedStyleNames.empty()) if (!unusedStyleNames.empty())
{ {
typeSelector->addItems(unusedStyleNames); typeSelector->addItems(unusedStyleNames);
this->modifyingStyle = std::move(styles.makeUnusedStyle(unusedStyleNames[0])); this->modifyingStyle = std::move(styles->makeUnusedStyle(unusedStyleNames[0]));
dialogLayout->addWidget(typeSelector, 0, 0); dialogLayout->addWidget(typeSelector, 0, 0);

View File

@ -13,7 +13,7 @@ private:
std::unique_ptr<LayerStyle> modifyingStyle; std::unique_ptr<LayerStyle> modifyingStyle;
public: public:
LayerStyleDialog( LayerStyleDialog(
LayerStyleContainer& styles, LayerStyleContainer* pStyles,
const std::shared_ptr<LayerStyle>& existedStyle = nullptr, const std::shared_ptr<LayerStyle>& existedStyle = nullptr,
QWidget* parent = nullptr QWidget* parent = nullptr
); );

View File

@ -1,108 +0,0 @@
#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

@ -1,21 +0,0 @@
#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

@ -180,9 +180,9 @@ void StrokeStyleWidget::setTableRow(int row, float width, Renderer::Material& ma
removeButton->setBackgroundColor(ColorHelper::instance().getPrimary1()); removeButton->setBackgroundColor(ColorHelper::instance().getPrimary1());
removeButton->setFixedSize(20, 20); removeButton->setFixedSize(20, 20);
strokeTable->setCellWidget(row, COLUMN_OPERATIONS, removeButton); strokeTable->setCellWidget(row, COLUMN_OPERATIONS, removeButton);
connect(removeButton, &QtMaterialRaisedButton::clicked, [this, row] { connect(removeButton, &QtMaterialRaisedButton::clicked, [this, widthItem] {
radialStroke(this->stroke)->materialMap.erase(this->strokeTable->item(row, COLUMN_WIDTH)->text().toFloat()); radialStroke(this->stroke)->materialMap.erase(widthItem->data(Qt::EditRole).toFloat());
this->strokeTable->removeRow(row); this->strokeTable->removeRow(widthItem->row());
}); });
} }

View File

@ -17,17 +17,17 @@ EditorWidgetItem::EditorWidgetItem(QString filePath,QWidget *parent) : QWidget(p
treeWidget = ui.LayerTree; treeWidget = ui.LayerTree;
tabWidget = ui.DisplayTab; tabWidget = ui.DisplayTab;
this->filePath = filePath; this->filePath = filePath;
layerInfoDisplayWidget = dynamic_cast<InfoDisplayWidget *>(tabWidget->widget(0)); elementInfoDisplayWidget = ui.ElementDisplay;
elementInfoDisplayWidget = dynamic_cast<ElementPoolWidget *>(tabWidget->widget(1)); layerInfoDisplayWidget = dynamic_cast<InfoDisplayWidget*>(tabWidget->widget(0));
editorSettingWidget = dynamic_cast<EditorSettingWidget*>(tabWidget->widget(2)); editorSettingWidget = dynamic_cast<EditorSettingWidget*>(tabWidget->widget(1));
elementInfoDisplayWidget->enableEdit(); elementInfoDisplayWidget->enableEdit();
qDebug() << layerInfoDisplayWidget; qDebug() << layerInfoDisplayWidget;
qDebug() << elementInfoDisplayWidget; qDebug() << elementInfoDisplayWidget;
auto centralRefresh = [this]() { auto centralRefresh = [this]() {
layerInfoDisplayWidget->refresh(); layerInfoDisplayWidget->refresh();
elementInfoDisplayWidget->refresh();
treeWidget->refresh(); treeWidget->refresh();
previewWindow->refresh(); previewWindow->refresh();
elementInfoDisplayWidget->lazyRefresh();
}; };
connect(previewWindow, &PreviewWindow::triggerCentralRefresh, centralRefresh); connect(previewWindow, &PreviewWindow::triggerCentralRefresh, centralRefresh);
connect(layerInfoDisplayWidget, &InfoDisplayWidget::triggerCentralRefresh, centralRefresh); connect(layerInfoDisplayWidget, &InfoDisplayWidget::triggerCentralRefresh, centralRefresh);
@ -36,7 +36,6 @@ EditorWidgetItem::EditorWidgetItem(QString filePath,QWidget *parent) : QWidget(p
connect(editorSettingWidget, &EditorSettingWidget::backgroundColorChanged, this, &EditorWidgetItem::handleBackgroundColorChange); connect(editorSettingWidget, &EditorSettingWidget::backgroundColorChanged, this, &EditorWidgetItem::handleBackgroundColorChange);
connect(editorSettingWidget, &EditorSettingWidget::projectNameChanged, this, &EditorWidgetItem::handleProjectNameChange); connect(editorSettingWidget, &EditorSettingWidget::projectNameChanged, this, &EditorWidgetItem::handleProjectNameChange);
connect(previewWindow, &PreviewWindow::refreshElementPreviewByIndex, elementInfoDisplayWidget, &ElementPoolWidget::refreshPictureByIndex); connect(previewWindow, &PreviewWindow::refreshElementPreviewByIndex, elementInfoDisplayWidget, &ElementPoolWidget::refreshPictureByIndex);
connect(previewWindow, &PreviewWindow::layerInfoChanged, layerInfoDisplayWidget, &InfoDisplayWidget::triggerSelfRefresh);
connect(treeWidget, &LayerTreeWidget::displayLayerChange, previewWindow, &PreviewWindow::currentLayerChanged); connect(treeWidget, &LayerTreeWidget::displayLayerChange, previewWindow, &PreviewWindow::currentLayerChanged);
//connect(treeWidget, &LayerTreeWidget::requireRefreshElementWidget, elementInfoDisplayWidget, &ElementPoolWidget::refresh); //connect(treeWidget, &LayerTreeWidget::requireRefreshElementWidget, elementInfoDisplayWidget, &ElementPoolWidget::refresh);
// connect(layerInfoDisplayWidget, &InfoDisplayWidget::requireRefreshElementWidget, elementInfoDisplayWidget, &ElementPoolWidget::refresh); // connect(layerInfoDisplayWidget, &InfoDisplayWidget::requireRefreshElementWidget, elementInfoDisplayWidget, &ElementPoolWidget::refresh);
@ -86,6 +85,7 @@ EditorWidgetItem::EditorWidgetItem(QString filePath,QWidget *parent) : QWidget(p
this->projectName = source.value("project-name").toString(); this->projectName = source.value("project-name").toString();
qDebug() << this->backgroundColor; qDebug() << this->backgroundColor;
qDebug() << this->projectName; qDebug() << this->projectName;
elementInfoDisplayWidget->refresh();
QTimer::singleShot(300, this, [this, centralRefresh]() { QTimer::singleShot(300, this, [this, centralRefresh]() {
handleBackgroundColorChange(this->backgroundColor); handleBackgroundColorChange(this->backgroundColor);
handleProjectNameChange(this->projectName); handleProjectNameChange(this->projectName);

View File

@ -18,6 +18,14 @@ ElementManager::ElementManager(QJsonObject source, QString fileHome)
} }
for (auto element : elements) for (auto element : elements)
element->index = index++; element->index = index++;
timer = new QTimer();
timer->setInterval(30000);
connect(timer, &QTimer::timeout, this, [this]() {
for (auto element : elements)
if (typeid(*element) == typeid(SimpleElement))
((SimpleElement*)element)->forceRefresh = true;
});
timer->start();
} }
void ElementManager::addElement(GraphicElement *element) void ElementManager::addElement(GraphicElement *element)

View File

@ -4,17 +4,20 @@
#include <QJsonArray> #include <QJsonArray>
#include "../Renderer/Preview/ElementRenderer.h" #include "../Renderer/Preview/ElementRenderer.h"
#include <vector> #include <vector>
#include <QTimer>
using std::vector; using std::vector;
class LayerManager; class LayerManager;
class GraphicElement; class GraphicElement;
class Renderer::ElementRenderer; class Renderer::ElementRenderer;
class FolderLayerWrapper; class FolderLayerWrapper;
class ElementManager class ElementManager : public QObject
{ {
Q_OBJECT
public: public:
vector<GraphicElement *> elements; vector<GraphicElement *> elements;
QString fileHome; QString fileHome;
QTimer* timer;
public: public:
ElementManager(QJsonObject source, QString fileHome); ElementManager(QJsonObject source, QString fileHome);

View File

@ -3,12 +3,14 @@
#include <QInputDialog> #include <QInputDialog>
#include <QFileDialog> #include <QFileDialog>
#include <QDialogButtonBox> #include <QDialogButtonBox>
#include <QMessagebox>
#include <QTimer>
ElementPoolWidget::ElementPoolWidget(QWidget* parent) ElementPoolWidget::ElementPoolWidget(QWidget* parent)
: QWidget(parent) : QWidget(parent)
{ {
elementManager = nullptr; elementManager = nullptr;
iconWidth = 120, iconHeight = 90; iconWidth = 120, iconHeight = 120;
pictureList = new QListWidget(this); pictureList = new QListWidget(this);
pictureList->setContextMenuPolicy(Qt::CustomContextMenu); pictureList->setContextMenuPolicy(Qt::CustomContextMenu);
pictureList->setIconSize(QSize(iconWidth, iconHeight)); pictureList->setIconSize(QSize(iconWidth, iconHeight));
@ -37,9 +39,14 @@ void ElementPoolWidget::setElementList(std::vector<GraphicElement*> elements) {
pictureList->clear(); pictureList->clear();
this->elements = elements; this->elements = elements;
for (int index = 0; index < elements.size(); index++) { for (int index = 0; index < elements.size(); index++) {
QListWidgetItem* pItem = new QListWidgetItem( QListWidgetItem* pItem = new QListWidgetItem(elements[index]->name);
elements[index]->getPreview(QSize(iconWidth - 25, iconHeight - 25)), QPainter* painter = new QPainter();
elements[index]->name); QImage* image = new QImage(QSize(iconWidth - 25, iconHeight - 25), QImage::Format_ARGB32);
image->fill(Qt::transparent);
painter->begin(image);
elements[index]->paintPreview(painter);
painter->end();
pItem->setIcon(QIcon(QPixmap::fromImage(*image)));
pItem->setSizeHint(QSize(iconWidth, iconHeight)); pItem->setSizeHint(QSize(iconWidth, iconHeight));
pictureList->insertItem(index, pItem); pictureList->insertItem(index, pItem);
} }
@ -65,15 +72,51 @@ void ElementPoolWidget::setElementManager(ElementManager* element)
this->setElementList(this->elementManager->elements); this->setElementList(this->elementManager->elements);
} }
void ElementPoolWidget::lazyRefresh()
{
if (!refreshWait) {
refreshWait = true;
QTimer::singleShot(1000, this, SLOT(refresh()));
}
}
void ElementPoolWidget::refresh() { void ElementPoolWidget::refresh() {
this->setElementList(this->elementManager->elements); refreshWait = false;
// update(); if (elementManager != nullptr)
{
elements = elementManager->elements;
for (int i = elements.size(); i < pictureList->count(); i++)
pictureList->takeItem(i);
for (int i = 0; i < elements.size(); i++)
{
QListWidgetItem* pItem = pictureList->item(i);
if (pItem == nullptr)
{
pItem = new QListWidgetItem(elements[i]->name);
pictureList->insertItem(i, pItem);
}
QPainter* painter = new QPainter();
QImage* image = new QImage(QSize(iconWidth - 25, iconHeight - 25), QImage::Format_ARGB32);
image->fill(Qt::transparent);
painter->begin(image);
elements[i]->paintPreview(painter);
painter->end();
pItem->setIcon(QIcon(QPixmap::fromImage(*image)));
pItem->setData(Qt::UserRole, QVariant::fromValue(image));
pItem->setSizeHint(QSize(iconWidth, iconHeight));
pItem->setText(elements[i]->name);
pictureList->insertItem(i, pItem);
}
}
} }
void ElementPoolWidget::refreshPicture(GraphicElement* element) { void ElementPoolWidget::refreshPicture(GraphicElement* element) {
for (int i = 0; i < elements.size(); i++) { for (int i = 0; i < elements.size(); i++) {
if (element == elements[i]) { if (element == elements[i]) {
pictureList->item(i)->setIcon(elements[i]->getPreview(QSize(iconWidth - 25, iconHeight - 25))); QPainter* painter = new QPainter();
painter->begin(pictureList->item(i)->data(Qt::UserRole).value<QImage*>());
elements[i]->paintPreview(painter);
painter->end();
// update(); // update();
return; return;
} }
@ -83,7 +126,10 @@ void ElementPoolWidget::refreshPicture(GraphicElement* element) {
void ElementPoolWidget::refreshPictureByIndex(int index) { void ElementPoolWidget::refreshPictureByIndex(int index) {
if (index >= 0 && index < elements.size()) if (index >= 0 && index < elements.size())
{ {
pictureList->item(index)->setIcon(elements[index]->getPreview(QSize(iconWidth - 25, iconHeight - 25))); QPainter* painter = new QPainter();
painter->begin(pictureList->item(index)->data(Qt::UserRole).value<QImage*>());
elements[index]->paintPreview(painter);
painter->end();
// update(); // update();
} }
} }
@ -112,12 +158,12 @@ void ElementPoolWidget::popMenu(const QPoint& pos)
if (bOk && !sName.isEmpty()) if (bOk && !sName.isEmpty())
{ {
currentElement->name = sName; currentElement->name = sName;
emit triggerCentralRefresh(); this->refresh();
} }
}); });
menu->addAction(QString::fromLocal8Bit("ɾ³ý"), this, [this, currentElement]() { menu->addAction(QString::fromLocal8Bit("ɾ³ý"), this, [this, currentElement, currentIndex]() {
this->elementManager->removeElement(currentElement); this->elementManager->removeElement(currentElement);
emit triggerCentralRefresh(); this->refresh();
}); });
menu->actions().last()->setDisabled(currentElement->referencedCount > 0); menu->actions().last()->setDisabled(currentElement->referencedCount > 0);
} }
@ -125,10 +171,16 @@ void ElementPoolWidget::popMenu(const QPoint& pos)
{ {
menu->addAction(QString::fromLocal8Bit("添加元素从svg导入"), this, [this]() { menu->addAction(QString::fromLocal8Bit("添加元素从svg导入"), this, [this]() {
QString filePath = QFileDialog::getOpenFileName(this, QString::fromLocal8Bit("打开文件"), "", "SVG Files (*.svg)"); QString filePath = QFileDialog::getOpenFileName(this, QString::fromLocal8Bit("打开文件"), "", "SVG Files (*.svg)");
filePath = filePath.trimmed();
QFileInfo fileInfo(filePath); QFileInfo fileInfo(filePath);
if (!fileInfo.exists() || !fileInfo.isFile()) {
QMessageBox::warning(this, tr("Error"), QString::fromLocal8Bit("ÇëÑ¡ÔñSvgÎļþ"));
return;
}
QString fileName = fileInfo.fileName(); QString fileName = fileInfo.fileName();
qDebug() << fileName << " " << filePath; qDebug() << fileName << " " << filePath;
this->elementManager->createSimpleElement(fileName, filePath); this->elementManager->createSimpleElement(fileName, filePath);
this->refresh();
emit triggerCentralRefresh(); emit triggerCentralRefresh();
}); });
} }

View File

@ -14,6 +14,7 @@ private:
QListWidget* pictureList; QListWidget* pictureList;
int iconWidth, iconHeight; int iconWidth, iconHeight;
ElementManager* elementManager; ElementManager* elementManager;
bool refreshWait = false;
public: public:
int currentIndex = -1; int currentIndex = -1;
@ -22,6 +23,7 @@ public:
void setElementManager(ElementManager* element); void setElementManager(ElementManager* element);
~ElementPoolWidget(); ~ElementPoolWidget();
void enableEdit(); void enableEdit();
void lazyRefresh();
signals: signals:
void elementSelected(GraphicElement* element); void elementSelected(GraphicElement* element);

View File

@ -45,7 +45,6 @@ void GroupElement::setSourceLayer(FolderLayerWrapper* sourceLayer)
PixelPath GroupElement::getPaintObject() const PixelPath GroupElement::getPaintObject() const
{ {
if (sourceLayer != nullptr) { if (sourceLayer != nullptr) {
sourceLayer->refresh();
return sourceLayer->getCache(); return sourceLayer->getCache();
} }
else else
@ -107,17 +106,30 @@ void SimpleElement::paint(QPainter* painter, QTransform transform, const LayerSt
} }
else else
{ {
double angle = atan(transform.m12() / transform.m11()); float sx = sqrt(transform.m11() * transform.m11() + transform.m12() * transform.m12());
double maxScale; float sy = sqrt(transform.m21() * transform.m21() + transform.m22() * transform.m22());
if (fabs(cos(angle))>1e-5) double pixelRatio = std::max(sx, sy) * QGuiApplication::primaryScreen()->devicePixelRatio() * 2;
maxScale = std::max(fabs(transform.m11() / cos(angle)), fabs(transform.m22() / cos(angle))); //double pixelRatio = 5;
if (painterPath == painterPathPrev && styles.getHash() == stylesHashValue && pixelRatioPrev >= pixelRatio && !forceRefresh)
{
transform.translate(movPrev.x(), movPrev.y());
painter->setTransform(transform.scale(1 / pixelRatioPrev, 1 / pixelRatioPrev));
painter->drawImage(0, 0, imagePrev);
}
else 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); auto [img, mov] = Renderer::ElementRenderer::instance()->drawElement(painterPath, styles, pixelRatio);
transform.translate(mov.x(), mov.y()); transform.translate(mov.x(), mov.y());
painter->setTransform(transform.scale(1 / pixelRatio, 1 / pixelRatio)); painter->setTransform(transform.scale(1 / pixelRatio, 1 / pixelRatio));
painter->drawImage(0, 0, img); painter->drawImage(0, 0, img);
stylesHashValue = styles.getHash();
painterPathPrev = painterPath;
imagePrev = img;
movPrev = mov;
pixelRatioPrev = pixelRatio;
forceRefresh = false;
}
} }
painter->restore(); painter->restore();
} }
@ -129,7 +141,7 @@ bool SimpleElement::isClosed() const
void GroupElement::paint(QPainter* painter, QTransform transform, const LayerStyleContainer& styles) void GroupElement::paint(QPainter* painter, QTransform transform, const LayerStyleContainer& styles)
{ {
sourceLayer->paint(painter, transform, true); sourceLayer->paint(painter, transform, true, styles);
} }
bool GroupElement::isClosed() const bool GroupElement::isClosed() const
@ -138,31 +150,26 @@ bool GroupElement::isClosed() const
} }
QPixmap SimpleElement::getPreview(QSize size) void SimpleElement::paintPreview(QPainter* painter)
{ {
QPixmap result(size + QSize(5, 5)); painter->save();
QPainter painter(&result); painter->setWindow(painterPath.boundingRect().x() - 5, painterPath.boundingRect().y() - 5, (painterPath.boundingRect().width() + 10) * QGuiApplication::primaryScreen()->devicePixelRatio(), (painterPath.boundingRect().height() + 10) * QGuiApplication::primaryScreen()->devicePixelRatio());
painter.setRenderHint(QPainter::Antialiasing); painter->drawPath(painterPath);
painter.setRenderHint(QPainter::SmoothPixmapTransform); painter->restore();
painter.scale(size.width() / painterPath.boundingRect().width(), size.height() / painterPath.boundingRect().height());
painter.drawPath(painterPath);
return result;
} }
QPixmap GroupElement::getPreview(QSize size) void GroupElement::paintPreview(QPainter* painter)
{ {
painter->save();
auto cache = sourceLayer->getCache(); auto cache = sourceLayer->getCache();
QPixmap result(QSize(1024, 1024)); painter->setRenderHint(QPainter::Antialiasing, true);
QPainter painter(&result); painter->setRenderHint(QPainter::SmoothPixmapTransform, true);
painter.setRenderHint(QPainter::Antialiasing); painter->setWindow(cache.getBoundingRect().toRect().x() - 5, cache.getBoundingRect().toRect().y() - 5, (cache.getBoundingRect().toRect().width() + 10) * QGuiApplication::primaryScreen()->devicePixelRatio(), (cache.getBoundingRect().toRect().height() + 10) * QGuiApplication::primaryScreen()->devicePixelRatio());
painter.setRenderHint(QPainter::SmoothPixmapTransform); //painter->translate(-cache.getBoundingRect().toRect().x() - 5, -cache.getBoundingRect().toRect().y() - 5);
sourceLayer->paint(&painter, QTransform(), true); //double maxScale = std::max(cache.getBoundingRect().toRect().width() + 10, cache.getBoundingRect().toRect().height() + 10) * QGuiApplication::primaryScreen()->devicePixelRatio();
painter.end(); //painter->scale(1 / maxScale, 1/ maxScale);
QRect rect(cache.getBoundingRect().toRect()); sourceLayer->paint(painter, QTransform(), true);
rect.setTopLeft(rect.topLeft() - QPoint(5, 5)); painter->restore();
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 void GroupElement::collectReachable(std::set<LayerWrapper*>& set) const

View File

@ -20,9 +20,15 @@ class ComposedPainterPath;
class GraphicElement class GraphicElement
{ {
public: public:
bool needRefresh = true;
size_t referencedCount = 0; size_t referencedCount = 0;
Renderer::ElementRenderer *renderer; Renderer::ElementRenderer *renderer;
QString name = ""; QString name = "";
size_t stylesHashValue;
QPainterPath painterPathPrev;
QPointF movPrev;
QImage imagePrev;
double pixelRatioPrev;
int index; int index;
// TODO: ¸ÄΪBitmapPath // TODO: ¸ÄΪBitmapPath
virtual QJsonObject toJson() const = 0; virtual QJsonObject toJson() const = 0;
@ -30,7 +36,8 @@ public:
virtual PixelPath getPaintObject(const LayerStyleContainer& styles) const = 0; virtual PixelPath getPaintObject(const LayerStyleContainer& styles) const = 0;
virtual void paint(QPainter* painter, QTransform transform, const LayerStyleContainer& styles) = 0; virtual void paint(QPainter* painter, QTransform transform, const LayerStyleContainer& styles) = 0;
virtual bool isClosed() const = 0; virtual bool isClosed() const = 0;
virtual QPixmap getPreview(QSize size) = 0; virtual void paintPreview(QPainter* painter) = 0;
}; };
class SimpleElement : public GraphicElement class SimpleElement : public GraphicElement
@ -53,7 +60,8 @@ public:
PixelPath getPaintObject(const LayerStyleContainer& styles) const override; PixelPath getPaintObject(const LayerStyleContainer& styles) const override;
void paint(QPainter* painter, QTransform transform, const LayerStyleContainer& styles) override; void paint(QPainter* painter, QTransform transform, const LayerStyleContainer& styles) override;
bool isClosed() const override; bool isClosed() const override;
QPixmap getPreview(QSize size) override; void paintPreview(QPainter* painter) override;
bool forceRefresh = false;
}; };
class GroupElement : public GraphicElement class GroupElement : public GraphicElement
@ -71,10 +79,12 @@ public:
void setSourceLayer(FolderLayerWrapper* sourceLayer); void setSourceLayer(FolderLayerWrapper* sourceLayer);
void paint(QPainter* painter, QTransform transform, const LayerStyleContainer& styles) override; void paint(QPainter* painter, QTransform transform, const LayerStyleContainer& styles) override;
bool isClosed() const override; bool isClosed() const override;
QPixmap getPreview(QSize size) override; void paintPreview(QPainter* painter) override;
void collectReachable(std::set<LayerWrapper*>& set) const; void collectReachable(std::set<LayerWrapper*>& set) const;
}; };
Q_DECLARE_METATYPE(GraphicElement*)
//******************************** BitmapPath ********************************// //******************************** BitmapPath ********************************//
//using std::vector; //using std::vector;

View File

@ -14,6 +14,7 @@ LayerWrapper *LayerManager::getRoot() const
void LayerManager::paint(QPainter *painter, QSize size,LayerWrapper* selecetedLayer) const void LayerManager::paint(QPainter *painter, QSize size,LayerWrapper* selecetedLayer) const
{ {
painter->save(); painter->save();
root->markNeedRefresh();
root->getCache(); root->getCache();
root->paint(painter); root->paint(painter);
painter->restore(); painter->restore();
@ -60,10 +61,10 @@ bool LayerManager::combine() const
for (auto &layer : selectedLayers) for (auto &layer : selectedLayers)
if (layer->getParent() != prevCommonFather) if (layer->getParent() != prevCommonFather)
return false; return false;
auto newCommonFather = new FolderLayerWrapper(); //auto newCommonFather = new FolderLayerWrapper();
newCommonFather->setParent(static_cast<FolderLayerWrapper*>(prevCommonFather)); //newCommonFather->setParent(static_cast<FolderLayerWrapper*>(prevCommonFather));
for (auto &layer : selectedLayers) //for (auto &layer : selectedLayers)
layer->setParent(newCommonFather); // layer->setParent(newCommonFather);
return true; return true;
} }
bool LayerManager::changeParent(FolderLayerWrapper *newParent) const bool LayerManager::changeParent(FolderLayerWrapper *newParent) const

View File

@ -9,6 +9,7 @@
#include <QLabel> #include <QLabel>
#include <QObject> #include <QObject>
#include <QDebug> #include <QDebug>
#include <QListView>
#define _USE_JOIN_VIEW_INPUT_RANGE #define _USE_JOIN_VIEW_INPUT_RANGE
#include <QJsonArray> #include <QJsonArray>
#include <ranges> #include <ranges>
@ -91,9 +92,9 @@ void LayerStyleContainer::computeNewHash()
} }
} }
LayerStyleContainer LayerStyleContainer::fromJson(bool isClosedElement, const QJsonArray& jsonArray) LayerStyleContainer LayerStyleContainer::fromJson(ElementType elementType, const QJsonArray& jsonArray)
{ {
LayerStyleContainer container(isClosedElement); LayerStyleContainer container(elementType);
for (const auto& style : jsonArray) for (const auto& style : jsonArray)
{ {
container.useStyle(LayerStyle::fromJson(style.toObject())); container.useStyle(LayerStyle::fromJson(style.toObject()));
@ -101,20 +102,20 @@ LayerStyleContainer LayerStyleContainer::fromJson(bool isClosedElement, const QJ
return container; return container;
} }
LayerStyleContainer::LayerStyleContainer(bool isClosedElement) : hash(0) LayerStyleContainer::LayerStyleContainer(ElementType elementType) : hash(0)
{ {
for (const auto& style : commonStyles) for (const auto& style : commonStyles)
{ {
unusedStyles.insert(style); unusedStyles.insert(style);
} }
if (isClosedElement) if (elementType & LayerStyleContainer::TYPE_CLOSED)
{ {
for (const auto& style : closedOnlyStyles) for (const auto& style : closedOnlyStyles)
{ {
unusedStyles.insert(style); unusedStyles.insert(style);
} }
} }
else if (elementType & LayerStyleContainer::TYPE_UNCLOSED)
{ {
for (const auto& style : unclosedOnlyStyles) for (const auto& style : unclosedOnlyStyles)
{ {
@ -181,18 +182,44 @@ std::map<QString, std::shared_ptr<LayerStyle>>::iterator LayerStyleContainer::en
return styles.end(); return styles.end();
} }
bool LayerStyleContainer::useStyle(const std::shared_ptr<LayerStyle>& style) std::map<QString, std::shared_ptr<LayerStyle>>::const_iterator LayerStyleContainer::cbegin() const
{ {
auto styleNode = unusedStyles.extract(style->getDisplayName()); return styles.cbegin();
}
std::map<QString, std::shared_ptr<LayerStyle>>::const_iterator LayerStyleContainer::cend() const
{
return styles.cend();
}
bool LayerStyleContainer::useStyle(const std::shared_ptr<LayerStyle>& style, bool forceOverride)
{
const auto styleDisplayName = style->getDisplayName();
auto styleNode = unusedStyles.extract(styleDisplayName);
if (styleNode.empty()) if (styleNode.empty())
{
if (!forceOverride || !styles.contains(styleDisplayName))
{ {
return false; return false;
} }
styles[styleDisplayName] = style;
return true;
}
styles[styleNode.key()] = style; styles[styleNode.key()] = style;
usedStyles.insert(std::move(styleNode)); usedStyles.insert(std::move(styleNode));
return true; return true;
} }
bool LayerStyleContainer::overrideStyle(const std::shared_ptr<LayerStyle>& style)
{
if (!styles.contains(style->getDisplayName()))
{
return false;
}
styles[style->getDisplayName()] = style;
return true;
}
bool LayerStyleContainer::dropStyle(const QString& styleName) bool LayerStyleContainer::dropStyle(const QString& styleName)
{ {
auto styleNode = usedStyles.extract(styleName); auto styleNode = usedStyles.extract(styleName);
@ -205,6 +232,11 @@ bool LayerStyleContainer::dropStyle(const QString& styleName)
return true; return true;
} }
std::shared_ptr<LayerStyle> LayerStyleContainer::getStyle(const QString& styleName) const
{
return styles.at(styleName);
}
float LayerStyleContainer::boundingBoxAffectValue() const { float LayerStyleContainer::boundingBoxAffectValue() const {
float maxLineWidth = 0; float maxLineWidth = 0;
const auto strokeStyle = styles.find(StrokeElementLayerStyle::displayName()); const auto strokeStyle = styles.find(StrokeElementLayerStyle::displayName());
@ -234,6 +266,27 @@ size_t LayerStyleContainer::getHash() const
return hash; return hash;
} }
bool LayerStyleContainer::operator==(const LayerStyleContainer& other) const
{
if (getHash() != other.getHash() || unusedStyleNames() != other.unusedStyleNames())
{
return false;
}
return std::ranges::equal(styles | std::views::values, other.styles | std::views::values);
}
LayerStyleContainer LayerStyleContainer::operator|(const LayerStyleContainer& other) const
{
LayerStyleContainer result = other;
for (const auto& style : std::ranges::subrange(this->cbegin(), this->cend())
| std::views::values)
{
result.useStyle(style);
}
result.computeNewHash();
return result;
}
std::unique_ptr<StrokeElementLayerStyle> StrokeElementLayerStyle::fromJson(const QJsonObject& json) std::unique_ptr<StrokeElementLayerStyle> StrokeElementLayerStyle::fromJson(const QJsonObject& json)
{ {
auto ptr = std::make_unique<StrokeElementLayerStyle>( auto ptr = std::make_unique<StrokeElementLayerStyle>(
@ -298,6 +351,20 @@ std::unique_ptr<LayerStyle> StrokeElementLayerStyle::clone() const
return std::make_unique<StrokeElementLayerStyle>(StrokeElementLayerStyle(*this)); return std::make_unique<StrokeElementLayerStyle>(StrokeElementLayerStyle(*this));
} }
bool StrokeElementLayerStyle::operator==(const LayerStyle& other) const
{
if (!LayerStyle::operator==(other))
{
return false;
}
const auto otherStyle = dynamic_cast<const StrokeElementLayerStyle*>(&other);
if (!otherStyle)
{
return false;
}
return this->strokePair.first == otherStyle->strokePair.first && this->strokePair.second == otherStyle->strokePair.second;
}
std::unique_ptr<FillElementLayerStyle> FillElementLayerStyle::fromJson(const QJsonObject& json) std::unique_ptr<FillElementLayerStyle> FillElementLayerStyle::fromJson(const QJsonObject& json)
{ {
auto ptr = std::make_unique<FillElementLayerStyle>( auto ptr = std::make_unique<FillElementLayerStyle>(
@ -359,6 +426,20 @@ std::unique_ptr<LayerStyle> FillElementLayerStyle::clone() const
return std::make_unique<FillElementLayerStyle>(FillElementLayerStyle(*this)); return std::make_unique<FillElementLayerStyle>(FillElementLayerStyle(*this));
} }
bool FillElementLayerStyle::operator==(const LayerStyle& other) const
{
if (!LayerStyle::operator==(other))
{
return false;
}
const auto otherStyle = dynamic_cast<const FillElementLayerStyle*>(&other);
if (!otherStyle)
{
return false;
}
return this->fillMaterialStyle == otherStyle->fillMaterialStyle;
}
std::unique_ptr<LayerStyle> LayerStyle::fromJson(const QJsonObject& json) std::unique_ptr<LayerStyle> LayerStyle::fromJson(const QJsonObject& json)
{ {
QString type = json["type"].toString(); QString type = json["type"].toString();
@ -379,3 +460,8 @@ QJsonObject LayerStyle::toJson() const
json["type"] = this->getTypeName(); json["type"] = this->getTypeName();
return json; return json;
} }
bool LayerStyle::operator==(const LayerStyle& other) const
{
return this->getTypeName() == other.getTypeName();
}

View File

@ -3,7 +3,6 @@
#include <utility> #include <utility>
#include <set> #include <set>
#include <map> #include <map>
#include <QListWidget>
#include <QJsonObject> #include <QJsonObject>
#include <QJsonArray> #include <QJsonArray>
#include "../Renderer/Painting/ElementStyle.h" #include "../Renderer/Painting/ElementStyle.h"
@ -36,6 +35,8 @@ public:
virtual QJsonObject toJson() const; virtual QJsonObject toJson() const;
virtual std::unique_ptr<LayerStyle> clone() const = 0; virtual std::unique_ptr<LayerStyle> clone() const = 0;
virtual bool operator==(const LayerStyle& other) const;
}; };
class StrokeElementLayerStyle : public LayerStyle class StrokeElementLayerStyle : public LayerStyle
@ -57,6 +58,8 @@ public:
QJsonObject toJson() const override; QJsonObject toJson() const override;
std::unique_ptr<LayerStyle> clone() const override; std::unique_ptr<LayerStyle> clone() const override;
bool operator==(const LayerStyle& other) const override;
std::pair<PMaterialStyleStroke, PMaterialStyleStroke> strokePair; std::pair<PMaterialStyleStroke, PMaterialStyleStroke> strokePair;
bool enableEachSideIndependent = false; bool enableEachSideIndependent = false;
}; };
@ -79,6 +82,8 @@ public:
QJsonObject toJson() const override; QJsonObject toJson() const override;
std::unique_ptr<LayerStyle> clone() const override; std::unique_ptr<LayerStyle> clone() const override;
bool operator==(const LayerStyle& other) const override;
PMaterialStyleFill fillMaterialStyle; PMaterialStyleFill fillMaterialStyle;
}; };
@ -111,23 +116,38 @@ private:
std::map<QString, std::shared_ptr<LayerStyle>> styles; std::map<QString, std::shared_ptr<LayerStyle>> styles;
size_t hash; size_t hash;
public: public:
static LayerStyleContainer fromJson(bool isClosedElement, const QJsonArray& jsonArray); using ElementType = unsigned short;
constexpr static ElementType TYPE_ALL = 0xffff;
constexpr static ElementType TYPE_CLOSED = 0b01;
constexpr static ElementType TYPE_UNCLOSED = 0b10;
LayerStyleContainer(bool isClosedElement); static LayerStyleContainer fromJson(ElementType elementType, const QJsonArray& jsonArray);
std::vector<Renderer::BaseStyle> toBaseStyles() const override;
QJsonArray toJson() const;
bool empty() const; LayerStyleContainer(ElementType elementType);
bool full() const; [[nodiscard]] std::vector<Renderer::BaseStyle> toBaseStyles() const override;
std::map<QString, std::shared_ptr<LayerStyle>>::iterator begin(); [[nodiscard]] QJsonArray toJson() const;
std::map<QString, std::shared_ptr<LayerStyle>>::iterator end();
QStringList unusedStyleNames() const; [[nodiscard]] bool empty() const;
std::unique_ptr<LayerStyle> makeUnusedStyle(const QString& styleName) const; [[nodiscard]] bool full() const;
bool useStyle(const std::shared_ptr<LayerStyle>& style); [[nodiscard]] std::map<QString, std::shared_ptr<LayerStyle>>::iterator begin();
[[nodiscard]] std::map<QString, std::shared_ptr<LayerStyle>>::iterator end();
[[nodiscard]] std::map<QString, std::shared_ptr<LayerStyle>>::const_iterator cbegin() const;
[[nodiscard]] std::map<QString, std::shared_ptr<LayerStyle>>::const_iterator cend() const;
[[nodiscard]] QStringList unusedStyleNames() const;
[[nodiscard]] std::unique_ptr<LayerStyle> makeUnusedStyle(const QString& styleName) const;
bool useStyle(const std::shared_ptr<LayerStyle>& style, bool forceOverride = false);
bool overrideStyle(const std::shared_ptr<LayerStyle>& style);
bool dropStyle(const QString& styleName); bool dropStyle(const QString& styleName);
float boundingBoxAffectValue() const; [[nodiscard]] std::shared_ptr<LayerStyle> getStyle(const QString& styleName) const;
size_t getHash() const; [[nodiscard]] float boundingBoxAffectValue() const;
[[nodiscard]] size_t getHash() const;
[[nodiscard]] bool operator==(const LayerStyleContainer& other) const;
/**
* LayerStyleContainer
*/
[[nodiscard]] LayerStyleContainer operator|(const LayerStyleContainer& other) const;
/** /**
* *

View File

@ -25,10 +25,10 @@ FolderLayerWrapper*LayerWrapper::getParent() const
PixelPath LayerWrapper::getCache(LayerWrapper* selectedLayer) PixelPath LayerWrapper::getCache(LayerWrapper* selectedLayer)
{ {
this->refresh(selectedLayer); if (needRefresh)
if (selectedLayer == this)
{ {
this->cache.highLight(); this->refresh(selectedLayer);
needRefresh = false;
} }
return cache; return cache;
} }
@ -46,6 +46,9 @@ LayerWrapper::LayerWrapper(QJsonObject json, FolderLayerWrapper*parent, ElementM
property.scale = {transformJson.value("scale").toObject().value("x").toDouble(), property.scale = {transformJson.value("scale").toObject().value("x").toDouble(),
transformJson.value("scale").toObject().value("y").toDouble()}; transformJson.value("scale").toObject().value("y").toDouble()};
property.rotation = {transformJson.value("rotation").toDouble()}; property.rotation = {transformJson.value("rotation").toDouble()};
property.flipX = { transformJson.value("flipX").toBool() };
qDebug() << property.flipX;
property.flipY = { transformJson.value("flipY").toBool() };
selected = false; selected = false;
hidden = false; hidden = false;
} }
@ -56,6 +59,7 @@ FolderLayerWrapper::FolderLayerWrapper(QJsonObject json, ElementManager *element
qDebug() << json.value("name").toString() << " " << this; qDebug() << json.value("name").toString() << " " << this;
QJsonArray childrenJson = json.value("children").toArray(); QJsonArray childrenJson = json.value("children").toArray();
QJsonValue referencedJson = json.value("referenced-by"); QJsonValue referencedJson = json.value("referenced-by");
styles = new LayerStyleContainer(LayerStyleContainer::TYPE_ALL);
if (!referencedJson.isNull()) if (!referencedJson.isNull())
{ {
auto p = reinterpret_cast<GroupElement *>(elementManager->getElementById(referencedJson.toInt())); auto p = reinterpret_cast<GroupElement *>(elementManager->getElementById(referencedJson.toInt()));
@ -76,9 +80,11 @@ FolderLayerWrapper::FolderLayerWrapper(QJsonObject json, ElementManager *element
LeafLayerWrapper::LeafLayerWrapper(QJsonObject json, ElementManager* elementManager, FolderLayerWrapper* parent) LeafLayerWrapper::LeafLayerWrapper(QJsonObject json, ElementManager* elementManager, FolderLayerWrapper* parent)
: LayerWrapper(json, parent, elementManager), : LayerWrapper(json, parent, elementManager),
wrappedElement(elementManager->getElementById(json.value("element").toInt())), wrappedElement(elementManager->getElementById(json.value("element").toInt()))
styles(LayerStyleContainer::fromJson(wrappedElement->isClosed(), json.value("styles").toArray()))
{ {
styles = new LayerStyleContainer(LayerStyleContainer::fromJson(
wrappedElement->isClosed() ? LayerStyleContainer::TYPE_CLOSED : LayerStyleContainer::TYPE_UNCLOSED,
json.value("styles").toArray()));
qDebug() << json.value("name").toString() << " " << this; qDebug() << json.value("name").toString() << " " << this;
if(wrappedElement != nullptr) if(wrappedElement != nullptr)
wrappedElement->referencedCount++; wrappedElement->referencedCount++;
@ -90,6 +96,15 @@ LeafLayerWrapper::~LeafLayerWrapper()
wrappedElement->referencedCount--; wrappedElement->referencedCount--;
} }
void LayerWrapper::SimpleProperty::setRotation(double newRotation)
{
if (newRotation <= -360 || 360 <= newRotation)
{
newRotation -= static_cast<int>(std::round(newRotation)) / 360 * 360;
}
rotation = newRotation;
}
void LayerWrapper::SimpleProperty::apply(PixelPath&cache) void LayerWrapper::SimpleProperty::apply(PixelPath&cache)
{ {
transform.reset(); transform.reset();
@ -100,6 +115,10 @@ void LayerWrapper::SimpleProperty::apply(PixelPath&cache)
transform.translate(centerX, centerY); transform.translate(centerX, centerY);
transform.translate(offset.x(), offset.y()); transform.translate(offset.x(), offset.y());
transform.rotate(rotation); transform.rotate(rotation);
if (flipX)
transform.scale(-1, 1);
if (flipY)
transform.scale(1, -1);
transform.scale(scale.x(), scale.y()); transform.scale(scale.x(), scale.y());
transform.translate(-centerX, -centerY); transform.translate(-centerX, -centerY);
cache = cache.trans(transform); cache = cache.trans(transform);
@ -139,7 +158,7 @@ void LeafLayerWrapper::refresh(LayerWrapper* layer)
cache.clear(); cache.clear();
if (wrappedElement != nullptr) if (wrappedElement != nullptr)
{ {
cache.addPath(wrappedElement->getPaintObject(this->styles)); cache.addPath(wrappedElement->getPaintObject(*this->styles));
} }
LayerWrapper::refresh(); LayerWrapper::refresh();
} }
@ -245,6 +264,8 @@ QJsonObject LayerWrapper::toJson() const
QJsonObject transformJson; QJsonObject transformJson;
transformJson.insert("offset", QJsonObject({ {"x", property.offset.x()}, {"y", property.offset.y()} })); 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("scale", QJsonObject({ {"x", property.scale.x()}, {"y", property.scale.y()} }));
transformJson.insert("flipX", property.flipX);
transformJson.insert("flipY", property.flipY);
transformJson.insert("rotation", property.rotation); transformJson.insert("rotation", property.rotation);
json.insert("transform", transformJson); json.insert("transform", transformJson);
return json; return json;
@ -271,7 +292,7 @@ QJsonObject LeafLayerWrapper::toJson() const
QJsonObject json = LayerWrapper::toJson(); QJsonObject json = LayerWrapper::toJson();
json.insert("element", elementManager->getElementIndex(wrappedElement)); json.insert("element", elementManager->getElementIndex(wrappedElement));
json.insert("is-folder", false); json.insert("is-folder", false);
json.insert("styles", styles.toJson()); json.insert("styles", styles->toJson());
return json; return json;
} }
@ -283,7 +304,7 @@ int FolderLayerWrapper::getReferencedBy()const
return -1; return -1;
} }
void LayerWrapper::paint(QPainter* painter, QTransform transform, bool force) void LayerWrapper::paint(QPainter* painter, QTransform transform, bool force, LayerStyleContainer styles)
{ {
// if (this->selected) // if (this->selected)
// { // {
@ -295,27 +316,24 @@ void LayerWrapper::paint(QPainter* painter, QTransform transform, bool force)
// } // }
} }
void FolderLayerWrapper::paint(QPainter* painter, QTransform transform, bool force) void FolderLayerWrapper::paint(QPainter* painter, QTransform transform, bool force, LayerStyleContainer styles)
{ {
if (hidden && !force) if (hidden && !force)
return; return;
LayerWrapper::paint(painter, transform, force);
transform = property.transform * transform; transform = property.transform * transform;
//qDebug() << transform; for (auto it = children.rbegin(); it != children.rend(); ++it)
for (auto& child : children) (*it)->paint(painter, transform, force, styles | *this->styles);
child->paint(painter, transform, force);
} }
void LeafLayerWrapper::paint(QPainter* painter, QTransform transform, bool force) void LeafLayerWrapper::paint(QPainter* painter, QTransform transform, bool force, LayerStyleContainer styles)
{ {
if (hidden && !force) if (hidden && !force)
return; return;
LayerWrapper::paint(painter, transform, force);
transform = property.transform * transform; transform = property.transform * transform;
//qDebug() << transform; //qDebug() << transform;
if (wrappedElement != nullptr) if (wrappedElement != nullptr)
{ {
wrappedElement->paint(painter, transform, styles); wrappedElement->paint(painter, transform, styles | *this->styles);
} }
} }
@ -435,7 +453,8 @@ bool LeafLayerWrapper::referencingGroupElement() const
bool LayerWrapper::canApplyStyles() const bool LayerWrapper::canApplyStyles() const
{ {
return typeid(*this) == typeid(LeafLayerWrapper) && !referencingGroupElement(); return true;
//return typeid(*this) == typeid(LeafLayerWrapper) && !referencingGroupElement();
} }
void LayerWrapper::paintVisualBounding(QPainter* painter) const void LayerWrapper::paintVisualBounding(QPainter* painter) const
@ -450,7 +469,20 @@ void LayerWrapper::paintVisualBounding(QPainter* painter) const
layer = layer->parent; layer = layer->parent;
} }
painter->save(); painter->save();
painter->setTransform(transform); auto rect = this->cache.getBoundingRect();
painter->drawRect(cache.getBoundingRect()); rect = transform.mapRect(rect);
painter->drawRect(rect);
painter->restore(); painter->restore();
} }
void LayerWrapper::markNeedRefresh()
{
this->needRefresh = true;
}
void FolderLayerWrapper::markNeedRefresh()
{
LayerWrapper::markNeedRefresh();
for (auto& child : children)
child->markNeedRefresh();
}

View File

@ -35,19 +35,22 @@ class LayerWrapper
PixelPath cache; PixelPath cache;
public: public:
bool needRefresh = true;
QTreeWidgetItem* qTreeWidgetItem; QTreeWidgetItem* qTreeWidgetItem;
bool selected; bool selected;
bool hidden; bool hidden;
LayerStyleContainer* styles;
struct SimpleProperty struct SimpleProperty
{ {
QString name = ""; QString name = "";
QPointF scale = {1.0, 1.0}; QPointF scale = {1.0, 1.0};
QPointF offset = {0, 0}; QPointF offset = {0, 0};
double rotation = 0; double rotation = 0;
bool flipHorizontally = 0; bool flipX = 0;
bool flipVertically = 0; bool flipY = 0;
QTransform transform; QTransform transform;
// TODO: ½«QPainterPath¸ÄΪBitmapPath // TODO: ½«QPainterPath¸ÄΪBitmapPath
void setRotation(double newRotation);
void apply(PixelPath&cache); void apply(PixelPath&cache);
} property; } property;
virtual void setParent(FolderLayerWrapper*newParent); virtual void setParent(FolderLayerWrapper*newParent);
@ -59,7 +62,7 @@ class LayerWrapper
FolderLayerWrapper*getParent() const; // invoke by manager, then invoke parent's applyStyles FolderLayerWrapper*getParent() const; // invoke by manager, then invoke parent's applyStyles
LayerWrapper(QJsonObject json, FolderLayerWrapper*parent, ElementManager* elementManager=nullptr); LayerWrapper(QJsonObject json, FolderLayerWrapper*parent, ElementManager* elementManager=nullptr);
LayerWrapper() = default; LayerWrapper() = default;
virtual void paint(QPainter* painter, QTransform transform=QTransform(), bool force = false); virtual void paint(QPainter* painter, QTransform transform = QTransform(), bool force = false, LayerStyleContainer styles = LayerStyleContainer(LayerStyleContainer::TYPE_ALL));
// TODO : export Function // TODO : export Function
// virtual LayerWrapper *addChild() = 0; // Leaf Child Only // virtual LayerWrapper *addChild() = 0; // Leaf Child Only
// virtual LayerWrapper *addParent() = 0; // Folder Parent Only // virtual LayerWrapper *addParent() = 0; // Folder Parent Only
@ -77,6 +80,7 @@ class LayerWrapper
virtual bool referencingGroupElement() const; virtual bool referencingGroupElement() const;
virtual void paintVisualBounding(QPainter* painter) const; virtual void paintVisualBounding(QPainter* painter) const;
bool canApplyStyles() const; bool canApplyStyles() const;
virtual void markNeedRefresh();
}; };
class FolderLayerWrapper : public LayerWrapper class FolderLayerWrapper : public LayerWrapper
@ -99,25 +103,25 @@ class FolderLayerWrapper : public LayerWrapper
QTreeWidgetItem* getQTreeItem() override; QTreeWidgetItem* getQTreeItem() override;
QJsonObject toJson() const override; QJsonObject toJson() const override;
int getReferencedBy()const; int getReferencedBy()const;
void paint(QPainter* painter, QTransform transform = QTransform(), bool force = false) override; void paint(QPainter* painter, QTransform transform = QTransform(), bool force = false, LayerStyleContainer styles = LayerStyleContainer(LayerStyleContainer::TYPE_ALL)) override;
void collectDownReachable(std::set<LayerWrapper*>& reachable) override; void collectDownReachable(std::set<LayerWrapper*>& reachable) override;
void refreshTreeItem() override; void refreshTreeItem() override;
size_t referencedCount(bool excludeSelf = false) const override; size_t referencedCount(bool excludeSelf = false) const override;
bool deleteable(bool excludeSubTree = false) const override; bool deleteable(bool excludeSubTree = false) const override;
void markNeedRefresh() override;
}; };
class LeafLayerWrapper : public LayerWrapper class LeafLayerWrapper : public LayerWrapper
{ {
public: public:
GraphicElement *wrappedElement; GraphicElement *wrappedElement;
LayerStyleContainer styles;
public: public:
~LeafLayerWrapper(); ~LeafLayerWrapper();
void refresh(LayerWrapper* layer = nullptr) override; void refresh(LayerWrapper* layer = nullptr) override;
LeafLayerWrapper(QJsonObject json, ElementManager *elementManager, FolderLayerWrapper*parent); LeafLayerWrapper(QJsonObject json, ElementManager *elementManager, FolderLayerWrapper*parent);
QJsonObject toJson() const override; QJsonObject toJson() const override;
void paint(QPainter* painter, QTransform transform = QTransform(), bool force = false) override; void paint(QPainter* painter, QTransform transform = QTransform(), bool force = false, LayerStyleContainer styles = LayerStyleContainer(LayerStyleContainer::TYPE_ALL)) override;
void collectDownReachable(std::set<LayerWrapper*>& reachable) override; void collectDownReachable(std::set<LayerWrapper*>& reachable) override;
QTreeWidgetItem* getQTreeItem() override; QTreeWidgetItem* getQTreeItem() override;
void refreshTreeItem() override; void refreshTreeItem() override;

View File

@ -41,6 +41,7 @@ void PixelPath::addPath(const PixelPath& path)
QPainter painter(&pixmap); QPainter painter(&pixmap);
painter.drawPixmap(0, 0, path.getPixmap()); painter.drawPixmap(0, 0, path.getPixmap());
this->painterPath.addPath(path.getPainterPath()); this->painterPath.addPath(path.getPainterPath());
this->originPath.addPath(path.originPath);
boundingRect = boundingRect.united(path.getBoundingRect()); boundingRect = boundingRect.united(path.getBoundingRect());
} }
@ -52,6 +53,7 @@ void PixelPath::addPath(const QPainterPath& path)
painter.setPen(QPen(Qt::black,1)); painter.setPen(QPen(Qt::black,1));
painter.drawPath(path); painter.drawPath(path);
this->painterPath.addPath(path); this->painterPath.addPath(path);
this->originPath.addPath(path);
boundingRect = boundingRect.united(path.boundingRect()); boundingRect = boundingRect.united(path.boundingRect());
} }
@ -67,6 +69,7 @@ void PixelPath::clear()
pixmap.fill(Qt::transparent); pixmap.fill(Qt::transparent);
boundingRect = QRectF(0, 0, 0, 0); boundingRect = QRectF(0, 0, 0, 0);
painterPath.clear(); painterPath.clear();
originPath.clear();
} }
PixelPath PixelPath::trans(QTransform& mat)const PixelPath PixelPath::trans(QTransform& mat)const
@ -78,6 +81,7 @@ PixelPath PixelPath::trans(QTransform& mat)const
painter.setTransform(mat); painter.setTransform(mat);
painter.drawPixmap(0, 0, pixmap); painter.drawPixmap(0, 0, pixmap);
result.painterPath.addPath(this->painterPath); result.painterPath.addPath(this->painterPath);
result.originPath.addPath(this->painterPath);
result.painterPath = mat.map(result.painterPath); result.painterPath = mat.map(result.painterPath);
result.boundingRect = result.painterPath.boundingRect(); result.boundingRect = result.painterPath.boundingRect();
return result; return result;

View File

@ -10,7 +10,7 @@ class PixelPath
public: public:
QRectF boundingRect; QRectF boundingRect;
QPixmap pixmap; QPixmap pixmap;
QPainterPath painterPath; QPainterPath painterPath, originPath;
int w,h; int w,h;
public: public:
PixelPath(int w=16, int h= 16); PixelPath(int w=16, int h= 16);

View File

@ -1,7 +1,7 @@
#include "PreviewWindow.h" #include "PreviewWindow.h"
#include <QApplication> #include <QApplication>
PreviewWindow::PreviewWindow(QWidget *parent) : QOpenGLWidget(parent) PreviewWindow::PreviewWindow(QWidget* parent) : QOpenGLWidget(parent)
{ {
//this->setFixedSize(QSize(108, 108)); //this->setFixedSize(QSize(108, 108));
this->setStyleSheet("border: 1px solid black"); this->setStyleSheet("border: 1px solid black");
@ -18,7 +18,7 @@ PreviewWindow::PreviewWindow(QWidget *parent) : QOpenGLWidget(parent)
backgroundColor = QColor(255, 255, 255, 255); backgroundColor = QColor(255, 255, 255, 255);
} }
void PreviewWindow::initialize(LayerManager *layerManager,QSize windowSize) void PreviewWindow::initialize(LayerManager* layerManager, QSize windowSize)
{ {
this->logicalSize = windowSize; this->logicalSize = windowSize;
this->layerManager = layerManager; this->layerManager = layerManager;
@ -51,10 +51,10 @@ void PreviewWindow::paintGL()
glClearColor(backgroundColor.redF(), backgroundColor.greenF(), backgroundColor.blueF(), backgroundColor.alphaF()); glClearColor(backgroundColor.redF(), backgroundColor.greenF(), backgroundColor.blueF(), backgroundColor.alphaF());
glClear(GL_COLOR_BUFFER_BIT); glClear(GL_COLOR_BUFFER_BIT);
painter->begin(this); painter->begin(this);
painter->setWindow(0, 0, logicalSize.width(), logicalSize.height()); painter->setWindow(0, 0, logicalSize.width() * devicePixelRatioF(), logicalSize.height() * devicePixelRatioF());
painter->setRenderHint(QPainter::Antialiasing); painter->setRenderHint(QPainter::Antialiasing);
painter->setRenderHint(QPainter::HighQualityAntialiasing); painter->setRenderHint(QPainter::HighQualityAntialiasing);
layerManager->paint(painter,this->size(),currentLayer); layerManager->paint(painter, this->size(), currentLayer);
painter->end(); painter->end();
} }
@ -106,7 +106,7 @@ void PreviewWindow::mouseMoveEvent(QMouseEvent* event)
else if (event->buttons() & Qt::RightButton) { else if (event->buttons() & Qt::RightButton) {
// 如果按下的是右键,那么旋转图形 // 如果按下的是右键,那么旋转图形
qreal angle = dx; qreal angle = dx;
currentLayer->property.rotation += angle; currentLayer->property.setRotation(currentLayer->property.rotation + angle);
} }
auto layer = currentLayer; auto layer = currentLayer;
while (layer != nullptr) while (layer != nullptr)
@ -143,7 +143,7 @@ void PreviewWindow::wheelEvent(QWheelEvent* event)
zoomStep++; zoomStep++;
this->setFixedSize(logicalSize * (1 + zoomStep * ZOOM_RATE)); this->setFixedSize(logicalSize * (1 + zoomStep * ZOOM_RATE));
} }
else if(event->delta() < 0 && zoomStep > ZOOM_STEP_MIN) else if (event->delta() < 0 && zoomStep > ZOOM_STEP_MIN)
{ {
zoomStep--; zoomStep--;
this->setFixedSize(logicalSize * (1 + zoomStep * ZOOM_RATE)); this->setFixedSize(logicalSize * (1 + zoomStep * ZOOM_RATE));

View File

@ -1,187 +1,112 @@
#include "InfoDisplayWidget.h" #include "InfoDisplayWidget.h"
#include "./EditorWidgetComponent/LayerStyleDialog.h" #include "./EditorWidgetComponent/LayerStyleDialog.h"
#include "../ColorHelper.hpp"
#include <QLineEdit> #include <QLineEdit>
#include <QTextBlock> #include <QTextBlock>
#include <QListWidget>
#include <QDialog>
#include <QComboBox> #include <QComboBox>
#include <qtmaterialraisedbutton.h> #include <QCheckBox>
#include <qtmaterialflatbutton.h>
void InfoDisplayWidget::setLayer(LayerWrapper *layer) void InfoDisplayWidget::setLayer(LayerWrapper *layer)
{ {
this->displayLayer = layer; this->displayLayer = layer;
generateLayerForm(); this->refresh();
} }
void InfoDisplayWidget::generateLayerForm() InfoDisplayWidget::InfoDisplayWidget(QWidget* parent) :QWidget(parent)
{ {
QLayoutItem *item; ui.setupUi(this);
if (this->layout() != nullptr) this->displayLayer = nullptr;
{ ui.name->setDisabled(true);
while (this->layout()->count() > 0 && (item = this->layout()->takeAt(0)) != nullptr) ui.offsetX->setLabel(("水平偏移"));
{ ui.offsetY->setLabel(("垂直偏移"));
item->widget()->deleteLater(); ui.rotation->setLabel(("旋转角度"));
delete item; ui.scaleX->setLabel(("水平缩放"));
} ui.scaleY->setLabel(("垂直缩放"));
delete this->layout(); ui.rotation->setValidator(new QIntValidator(-360, 360, this));
} ui.styleList->setDisabled(true);
QFormLayout *layout = new QFormLayout(); connect(ui.rotation, &QLineEdit::textChanged, [=](const QString& content) {
layout->setRowWrapPolicy(QFormLayout::WrapAllRows); if (fabs(content.toDouble() - this->displayLayer->property.rotation) < 1e-6)
if (this->displayLayer == nullptr) return;
{ this->displayLayer->property.setRotation(content.toDouble());
layout->addRow("no selected layer", new QLabel());
}
else
{
QLineEdit *name = new QLineEdit(this->displayLayer->property.name, this);
QLineEdit *rotation = new QLineEdit(QString::number(this->displayLayer->property.rotation, 'f', 0), this);
QLineEdit *offsetX = new QLineEdit(QString::number(this->displayLayer->property.offset.x()), this);
QLineEdit *offsetY = new QLineEdit(QString::number(this->displayLayer->property.offset.y()), this);
QLineEdit *scaleX = new QLineEdit(QString::number(this->displayLayer->property.scale.x()), this);
QLineEdit *scaleY = new QLineEdit(QString::number(this->displayLayer->property.scale.y()), this);
name->setDisabled(true);
rotation->setValidator(new QIntValidator(-10000, 10000, this));
connect(rotation, &QLineEdit::textChanged, [=](QString content) {
this->displayLayer->property.rotation = content.toDouble();
emit triggerCentralRefresh(); emit triggerCentralRefresh();
}); });
offsetX->setValidator(new QIntValidator(-10000, 10000, this)); ui.offsetX->setValidator(new QDoubleValidator(-10000, 10000, 2, this));
connect(offsetX, &QLineEdit::textChanged, [=](QString content) { connect(ui.offsetX, &QLineEdit::textChanged, [=](const QString& content) {
this->displayLayer->property.offset = {content.toDouble(), this->displayLayer->property.offset.y()}; if (fabs(content.toDouble() - this->displayLayer->property.offset.x()) < 1e-6)
return;
this->displayLayer->property.offset = { content.toDouble(), this->displayLayer->property.offset.y() };
emit triggerCentralRefresh(); emit triggerCentralRefresh();
}); });
offsetY->setValidator(new QIntValidator(-10000, 10000, this)); ui.offsetY->setValidator(new QDoubleValidator(-10000, 10000, 2, this));
connect(offsetY, &QLineEdit::textChanged, [=](QString content) { connect(ui.offsetY, &QLineEdit::textChanged, [=](const QString& content) {
this->displayLayer->property.offset = {this->displayLayer->property.offset.x(), content.toDouble()}; if (fabs(content.toDouble() - this->displayLayer->property.offset.y()) < 1e-6)
return;
this->displayLayer->property.offset = { this->displayLayer->property.offset.x(), content.toDouble() };
emit triggerCentralRefresh(); emit triggerCentralRefresh();
}); });
scaleX->setValidator(new QDoubleValidator(-1000, 1000, 4, this)); ui.scaleX->setValidator(new QDoubleValidator(0, 10000, 6, this));
connect(scaleX, &QLineEdit::textChanged, [=](QString content) { connect(ui.scaleX, &QLineEdit::textChanged, [=](const QString& content) {
this->displayLayer->property.scale = {content.toDouble(), this->displayLayer->property.scale.y()}; if (fabs(content.toDouble() - this->displayLayer->property.scale.x()) < 1e-6)
return;
this->displayLayer->property.scale = { content.toDouble(), this->displayLayer->property.scale.y() };
emit triggerCentralRefresh(); emit triggerCentralRefresh();
}); });
scaleY->setValidator(new QDoubleValidator(-1000, 1000, 4, this)); ui.scaleY->setValidator(new QDoubleValidator(0, 10000, 6, this));
connect(scaleY, &QLineEdit::textChanged, [=](QString content) { connect(ui.scaleY, &QLineEdit::textChanged, [=](const QString& content) {
this->displayLayer->property.scale = {this->displayLayer->property.scale.x(), content.toDouble()}; if (fabs(content.toDouble() - this->displayLayer->property.scale.y()) < 1e-6)
return;
this->displayLayer->property.scale = { this->displayLayer->property.scale.x(), content.toDouble() };
emit triggerCentralRefresh(); emit triggerCentralRefresh();
}); });
connect(ui.flipX, &QtMaterialCheckBox::toggled, [=](bool state) {
layout->addRow("layer name:", name); if (state == this->displayLayer->property.flipX)
layout->addRow("rotation:", rotation); return;
layout->addRow("offset-X:", offsetX); this->displayLayer->property.flipX = state;
layout->addRow("offset-Y:", offsetY); emit triggerCentralRefresh();
layout->addRow("scale-X:", scaleX); });
layout->addRow("scale-Y:", scaleY); connect(ui.flipY, &QtMaterialCheckBox::toggled, [=](bool state) {
layout->setRowWrapPolicy(QFormLayout::DontWrapRows); if (state == this->displayLayer->property.flipY)
return;
if (auto* leafP = dynamic_cast<LeafLayerWrapper*>(this->displayLayer); leafP) { this->displayLayer->property.flipY = state;
auto* styleList = new QListWidget(this); emit triggerCentralRefresh();
});
auto* header = new QListWidgetItem; connect(ui.styleList, &LayerContainerListWidget::addLayerStyle, [this](const std::shared_ptr<LayerStyle>& style) {
auto* headerWidget = new QWidget(styleList); emit triggerCentralRefresh();
auto* headerLayout = new QHBoxLayout; });
connect(ui.styleList, &LayerContainerListWidget::editLayerStyle, [this](const std::shared_ptr<LayerStyle>& style) {
auto* headerLabel = new QLabel(headerWidget); emit triggerCentralRefresh();
headerLabel->setText("样式列表"); });
headerLabel->setSizePolicy(QSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding)); connect(ui.styleList, &LayerContainerListWidget::removeLayerStyle, [this](const QString& styleName) {
//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();
}
});
}
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();
}
});
connect(removeButton, &QPushButton::clicked, this,
[this, styleIterator, styles]
{
styles->dropStyle(styleIterator->first);
styles->computeNewHash();
emit triggerCentralRefresh(); 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(removeButton);
w->setLayout(layout);
styleList->addItem(item);
styleList->setItemWidget(item, w);
}
layout->addRow(styleList);
}
}
this->setLayout(layout);
} }
void InfoDisplayWidget::triggerSelfRefresh() void InfoDisplayWidget::setVisiable(bool visiable)
{ {
if (this->displayLayer != nullptr) this->setHidden(!visiable);
this->generateLayerForm();
} }
void InfoDisplayWidget::refresh() void InfoDisplayWidget::refresh()
{ {
if (this->displayLayer != nullptr) if (this->displayLayer != nullptr)
this->generateLayerForm(); {
//this->setVisiable(true);
ui.name->setText(this->displayLayer->property.name);
ui.offsetX->setText(QString::number(this->displayLayer->property.offset.x()));
ui.offsetY->setText(QString::number(this->displayLayer->property.offset.y()));
ui.rotation->setText(QString::number(this->displayLayer->property.rotation));
ui.scaleX->setText(QString::number(this->displayLayer->property.scale.x()));
ui.scaleY->setText(QString::number(this->displayLayer->property.scale.y()));
ui.flipX->setChecked(this->displayLayer->property.flipX);
ui.flipY->setChecked(this->displayLayer->property.flipY);
if (this->displayLayer->canApplyStyles())
{
ui.styleList->setDisabled(false);
ui.styleList->setStyleContainer(static_cast<LayerWrapper*>(this->displayLayer)->styles);
}
else
{
ui.styleList->setDisabled(true);
}
}
} }

View File

@ -1,30 +1,24 @@
#pragma once #pragma once
#include "GraphicElement.h" #include "GraphicElement.h"
#include "LayerWrapper.h"
#include <QFormLayout>
#include <QLabel>
#include <QWidget> #include <QWidget>
#include "ElementPoolWidget.h" #include "ElementPoolWidget.h"
#include "ui_EditorLayerInfoWidget.h"
class InfoDisplayWidget : public QWidget class InfoDisplayWidget : public QWidget
{ {
Q_OBJECT Q_OBJECT
private: private:
LayerWrapper *displayLayer; LayerWrapper *displayLayer;
Ui::EditorLayerInfoWidget ui;
public: public:
InfoDisplayWidget(QWidget* parent=nullptr);
void setLayer(LayerWrapper *layer); void setLayer(LayerWrapper *layer);
void generateLayerForm();
void refresh(); void refresh();
void setVisiable(bool visiable);
public slots:
void triggerSelfRefresh();
signals: signals:
void triggerCentralRefresh(); void triggerCentralRefresh();
void requireRefreshPreview();
void requireSelfRefresh();
void requireRefreshElementWidget();
}; };

View File

@ -3,6 +3,7 @@
#include <QMenu> #include <QMenu>
#include "./EditorWidgetComponent/LayerCreateWidget.h" #include "./EditorWidgetComponent/LayerCreateWidget.h"
#include <QTimer> #include <QTimer>
#include <QKeyEvent>
LayerTreeWidget::LayerTreeWidget(QWidget *parent) LayerTreeWidget::LayerTreeWidget(QWidget *parent)
{ {
@ -58,14 +59,19 @@ void LayerTreeWidget::popMenu(const QPoint &pos)
auto dialog = new LayerCreateWidget(elementManager, dynamic_cast<FolderLayerWrapper*>(layer), this); auto dialog = new LayerCreateWidget(elementManager, dynamic_cast<FolderLayerWrapper*>(layer), this);
connect(dialog, &LayerCreateWidget::LayerInfoReturned, this, [this, layer](QJsonObject jsonObj) { connect(dialog, &LayerCreateWidget::LayerInfoReturned, this, [this, layer](QJsonObject jsonObj) {
auto folderLayer = dynamic_cast<FolderLayerWrapper*>(layer); auto folderLayer = dynamic_cast<FolderLayerWrapper*>(layer);
LayerWrapper* newLayer; std::shared_ptr<LayerWrapper> newLayer;
qDebug() << this->elementManager; qDebug() << this->elementManager;
if(jsonObj.value("is-folder").toBool()) if (jsonObj.value("is-folder").toBool())
newLayer = new FolderLayerWrapper(jsonObj, this->elementManager, folderLayer); {
newLayer = std::make_shared<FolderLayerWrapper>(jsonObj, this->elementManager, folderLayer);
folderLayer->addChild(newLayer);
}
else else
newLayer = new LeafLayerWrapper(jsonObj, this->elementManager, folderLayer); {
newLayer = std::make_shared<LeafLayerWrapper>(jsonObj, this->elementManager, folderLayer);
folderLayer->addChild(newLayer);
}
folderLayer->addChild(std::shared_ptr<LayerWrapper>(newLayer));
folderLayer->qTreeWidgetItem->addChild(newLayer->getQTreeItem()); folderLayer->qTreeWidgetItem->addChild(newLayer->getQTreeItem());
qDebug() << jsonObj<<"----------------------"; qDebug() << jsonObj<<"----------------------";
emit triggerCentralRefresh(); emit triggerCentralRefresh();
@ -109,6 +115,8 @@ void LayerTreeWidget::popMenu(const QPoint &pos)
}); });
} }
} }
menu.addAction(getPromoteUpAction());
menu.addAction(getPromoteDownAction());
} }
menu.exec(mapToGlobal(pos)); menu.exec(mapToGlobal(pos));
} }
@ -132,3 +140,181 @@ void LayerTreeWidget::onRenameEvent()
void LayerTreeWidget::refresh() { void LayerTreeWidget::refresh() {
this->root->refreshTreeItem(); this->root->refreshTreeItem();
} }
void LayerTreeWidget::keyPressEvent(QKeyEvent* event)
{
if (QApplication::keyboardModifiers() == Qt::ALT)
{
if (event->key() == Qt::Key_Up)
{
pushUpLayer();
}
else if (event->key() == Qt::Key_Down)
{
pushDownLayer();
}
}
}
void LayerTreeWidget::pushUpLayer()
{
if (this->selectedItem == nullptr)
return;
auto layer = this->selectedItem->data(0, Qt::UserRole).value<LayerWrapper*>();
if (layer == nullptr)
return;
auto parent = layer->getParent();
int index = -1;
for (int i = 0; i < parent->children.size(); i++)
{
if (parent->children[i].get() == layer)
{
index = i;
break;
}
}
if (index == -1 || index == 0)
return;
auto temp = parent->children[index - 1];
parent->children[index - 1] = parent->children[index];
parent->children[index] = temp;
layer->qTreeWidgetItem->parent()->removeChild(layer->qTreeWidgetItem);
parent->qTreeWidgetItem->insertChild(index - 1, layer->qTreeWidgetItem);
this->setCurrentItem(layer->qTreeWidgetItem);
emit triggerCentralRefresh();
}
void LayerTreeWidget::pushDownLayer()
{
if (this->selectedItem == nullptr)
return;
auto layer = this->selectedItem->data(0, Qt::UserRole).value<LayerWrapper*>();
if (layer == nullptr)
return;
auto parent = layer->getParent();
int index = -1;
for (int i = 0; i < parent->children.size(); i++)
{
if (parent->children[i].get() == layer)
{
index = i;
break;
}
}
if (index == -1 || index == parent->children.size() - 1)
return;
auto temp = parent->children[index + 1];
parent->children[index + 1] = parent->children[index];
parent->children[index] = temp;
layer->qTreeWidgetItem->parent()->removeChild(layer->qTreeWidgetItem);
parent->qTreeWidgetItem->insertChild(index + 1, layer->qTreeWidgetItem);
this->setCurrentItem(layer->qTreeWidgetItem);
emit triggerCentralRefresh();
}
QAction* LayerTreeWidget::getPromoteUpAction()
{
QAction* action = new QAction(QString::fromLocal8Bit("提升 (experimental)"), this);
QMenu* optionMenu = new QMenu();
QList<QAction*> optionList;
std::vector<FolderLayerWrapper*> layers;
auto layer = this->selectedItem->data(0, Qt::UserRole).value<LayerWrapper*>();
layer = layer->getParent();
while (layer != nullptr)
{
if (layer->getParent() == nullptr)
break;
layer = layer->getParent();
layers.push_back(dynamic_cast<FolderLayerWrapper*>(layer));
}
reverse(layers.begin(), layers.end());
if (layers.size() == 0)
{
action->setEnabled(false);
return action;
}
for (auto layer : layers)
{
if (layer == nullptr)
continue;
QAction* option = new QAction(QString::fromLocal8Bit("提升至 ") + layer->property.name + QString::fromLocal8Bit(""), this);
option->setData(QVariant::fromValue(std::pair<LayerWrapper*, FolderLayerWrapper*>({this->selectedItem->data(0, Qt::UserRole).value<LayerWrapper*>(), layer})));
connect(option, &QAction::triggered, this, [this, option]() {
auto pair = option->data().value<std::pair<LayerWrapper*, FolderLayerWrapper*>>();
auto layer = pair.first;
auto parent = pair.second;
this->moveLayer(layer, parent);
this->setCurrentItem(layer->qTreeWidgetItem);
});
optionList.append(option);
}
optionMenu->addActions(optionList);
action->setMenu(optionMenu);
return action;
}
QAction* LayerTreeWidget::getPromoteDownAction()
{
QAction* action = new QAction(QString::fromLocal8Bit("下放 (experimental)"), this);
QMenu* optionMenu = new QMenu();
QList<QAction*> optionList;
std::vector<FolderLayerWrapper*> layers;
auto layer = this->selectedItem->data(0, Qt::UserRole).value<LayerWrapper*>();
auto parent = layer->getParent();
if (parent == nullptr)
{
action->setEnabled(false);
return action;
}
for (auto child : parent->children)
{
if (child.get() == layer || typeid(*child) != typeid(FolderLayerWrapper))
continue;
layers.push_back(dynamic_cast<FolderLayerWrapper*>(child.get()));
}
reverse(layers.begin(), layers.end());
if (layers.size() == 0)
{
action->setEnabled(false);
return action;
}
for (auto layer : layers)
{
if (layer == nullptr)
continue;
QAction* option = new QAction(QString::fromLocal8Bit("下放至 ") + layer->property.name + QString::fromLocal8Bit(""), this);
option->setData(QVariant::fromValue(std::pair<LayerWrapper*, FolderLayerWrapper*>({ this->selectedItem->data(0, Qt::UserRole).value<LayerWrapper*>(), layer })));
connect(option, &QAction::triggered, this, [this, option]() {
auto pair = option->data().value<std::pair<LayerWrapper*, FolderLayerWrapper*>>();
auto layer = pair.first;
auto parent = pair.second;
this->moveLayer(layer, parent);
this->setCurrentItem(layer->qTreeWidgetItem);
});
optionList.append(option);
}
optionMenu->addActions(optionList);
action->setMenu(optionMenu);
return action;
}
void LayerTreeWidget::moveLayer(LayerWrapper* layer, FolderLayerWrapper* parent)
{
auto oldParent = layer->getParent();
std::shared_ptr<LayerWrapper> layerPtr = nullptr;
for (int i = 0; i < oldParent->children.size(); i++)
{
if (oldParent->children[i].get() == layer)
{
layerPtr = oldParent->children[i];
oldParent->children.erase(oldParent->children.begin() + i);
break;
}
}
oldParent->qTreeWidgetItem->removeChild(layer->qTreeWidgetItem);
parent->qTreeWidgetItem->addChild(layer->qTreeWidgetItem);
parent->addChild(layerPtr);
layer->setParent(parent);
oldParent->removeChild(layer);
emit triggerCentralRefresh();
}

View File

@ -9,6 +9,11 @@ class LayerTreeWidget : public QTreeWidget
private: private:
QTreeWidgetItem *selectedItem; QTreeWidgetItem *selectedItem;
LayerWrapper *copiedItem; LayerWrapper *copiedItem;
void pushUpLayer();
void pushDownLayer();
QAction* getPromoteUpAction();
QAction* getPromoteDownAction();
void moveLayer(LayerWrapper* source, FolderLayerWrapper* parent);
public: public:
ElementManager* elementManager; ElementManager* elementManager;
@ -17,6 +22,7 @@ class LayerTreeWidget : public QTreeWidget
void onRenameEvent(); void onRenameEvent();
void popMenu(const QPoint &pos); void popMenu(const QPoint &pos);
void refresh(); void refresh();
void keyPressEvent(QKeyEvent* event) override;
// void mouseDoubleClickEvent(QMouseEvent *event) override; // void mouseDoubleClickEvent(QMouseEvent *event) override;
// void onItemDoubleClicked(QTreeWidgetItem *item, int column = 0); // void onItemDoubleClicked(QTreeWidgetItem *item, int column = 0);

View File

@ -3,6 +3,7 @@
#include <QJsondocument> #include <QJsondocument>
#include "PainterPathUtil.h" #include "PainterPathUtil.h"
#include <queue> #include <queue>
#include <ranges>
#include <QFileInfo> #include <QFileInfo>
using Renderer::Painting; using Renderer::Painting;
@ -13,12 +14,12 @@ using std::max;
using std::shared_ptr; using std::shared_ptr;
using std::make_shared; using std::make_shared;
using std::min; using std::min;
using std::queue;
struct LayerNode { int PaintingUtil::zIndexCount = 0;
LayerWrapper* nowLayer;
QTransform transfrom; void PaintingUtil::clear() {
}; zIndexCount = 0;
}
QJsonObject PaintingUtil::readJsonFile(QString jsonFilePath) { QJsonObject PaintingUtil::readJsonFile(QString jsonFilePath) {
QFile jsonFile(jsonFilePath); QFile jsonFile(jsonFilePath);
@ -32,26 +33,30 @@ QJsonObject PaintingUtil::readJsonFile(QString jsonFilePath) {
} }
Painting PaintingUtil::transfromToPainting(QString jsonFilePath) { Painting PaintingUtil::transfromToPainting(QString jsonFilePath) {
Painting painting;
glm::bvec2 flip(0, 0);
QJsonObject jsonObj = readJsonFile(jsonFilePath); QJsonObject jsonObj = readJsonFile(jsonFilePath);
qDebug() << jsonObj; //qDebug() << jsonObj;
std::unordered_map<LayerWrapper*, Contour> contourMap;
Painting painting(Renderer::Material(jsonObj.value("background-color").toVariant().value<QColor>()));
shared_ptr<ElementManager> elementManager = make_shared<ElementManager>(jsonObj, QFileInfo(jsonFilePath).absolutePath()); shared_ptr<ElementManager> elementManager = make_shared<ElementManager>(jsonObj, QFileInfo(jsonFilePath).absolutePath());
shared_ptr<LayerManager> layerManager = make_shared<LayerManager>(jsonObj, elementManager.get()); 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(); LayerWrapper* root = layerManager->getRoot();
root->getCache(); root->getCache();
handleLayerWrapper(root, QTransform(), painting, contourMap);
clear();
return painting;
//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;
//double maxLineWidth = getMaxLineWidth(root); //double maxLineWidth = getMaxLineWidth(root);
layerQueue.push({ root, root->property.transform }); //layerQueue.push({ root, QTransform()});
while (!layerQueue.empty()) { /*while (!layerQueue.empty()) {
auto layerNode = layerQueue.front(); auto layerNode = layerQueue.front();
layerQueue.pop(); layerQueue.pop();
FolderLayerWrapper* nowLayer = handleLayerWrapper(layerNode.nowLayer, layerNode.transfrom, painting); FolderLayerWrapper* nowLayer = handleLayerWrapper(layerNode.nowLayer, layerNode.transfrom, painting);
@ -60,13 +65,14 @@ Painting PaintingUtil::transfromToPainting(QString jsonFilePath) {
layerQueue.push({ sonLayer.get(), layerNode.transfrom }); layerQueue.push({ sonLayer.get(), layerNode.transfrom });
} }
} }
} }*/
return painting;
} }
FolderLayerWrapper* PaintingUtil::handleLayerWrapper(LayerWrapper* nowLayer, QTransform& transform, Painting& painting) { void PaintingUtil::handleLayerWrapper(LayerWrapper* nowLayer, QTransform transform, Painting& painting, std::unordered_map<LayerWrapper*, Contour>& contourMap) {
LeafLayerWrapper* leafLayer = dynamic_cast<LeafLayerWrapper*>(nowLayer); LeafLayerWrapper* leafLayer = dynamic_cast<LeafLayerWrapper*>(nowLayer);
qDebug() << nowLayer;
transform = nowLayer->property.transform * transform; transform = nowLayer->property.transform * transform;
@ -74,62 +80,86 @@ FolderLayerWrapper* PaintingUtil::handleLayerWrapper(LayerWrapper* nowLayer, QTr
GroupElement* wrapperElement = dynamic_cast<GroupElement*>(leafLayer->wrappedElement); GroupElement* wrapperElement = dynamic_cast<GroupElement*>(leafLayer->wrappedElement);
if (wrapperElement != nullptr) { if (wrapperElement != nullptr) {
transform = wrapperElement->sourceLayer->property.transform * transform; handleLayerWrapper(wrapperElement->sourceLayer, transform, painting, contourMap);
return wrapperElement->sourceLayer; return;
} }
PixelPath pixelPath = nowLayer->getCache(); PixelPath pixelPath = nowLayer->getCache();
QPainterPath painterPath = pixelPath.getPainterPath(); QPainterPath painterPath = pixelPath.originPath;
QRectF bound = painterPath.boundingRect(); QRectF bound = painterPath.boundingRect();
//qDebug() << leafLayer<<"------" << painterPath; //qDebug() << leafLayer<<"------" << painterPath;
// transform to -1£¬ 1 // transform to -1£¬ 1
QTransform trans; QTransform trans;
double maxLen = std::max(bound.width(), bound.height()); double maxLen = std::max(bound.width(), bound.height()) * 1.00001;
qDebug() << maxLen << bound; //qDebug() << maxLen << bound;
trans.scale(1 / maxLen, 1 / maxLen); trans.scale(1 / maxLen, 1 / maxLen);
trans.translate(-bound.center().x(), -bound.center().y()); trans.translate(-bound.center().x(), -bound.center().y());
painterPath = trans.map(painterPath); painterPath = trans.map(painterPath);
shared_ptr<vector<vector<Renderer::Point> >> contour = std::make_shared<vector<vector<Renderer::Point> >>(PainterPathUtil::transformToLines(painterPath)); //std::shared_ptr<LayerWrapper> keyLayerPtr(nowLayer);
QSize screenSize = QSize(1024, 1024); auto iterContour = contourMap.find(nowLayer);
PaintingUtil::Contour contour = nullptr;
if (iterContour != contourMap.end()) {
contour = iterContour->second;
}
else {
contour = std::make_shared<vector<vector<Renderer::Point> >>(PainterPathUtil::transformToLines(painterPath));
contourMap.insert({ nowLayer, contour });
;
}
QSize screenSize = QSize(1080, 1080);
ElementTransform elementTransform; ElementTransform elementTransform;
transform = trans.inverted() * transform * QTransform::fromScale(2. / screenSize.width(), 2. / screenSize.height()) * QTransform::fromTranslate(-1, -1) * QTransform::fromScale(1, -1); QTransform leafTransform = trans.inverted() * transform * QTransform::fromScale(2. / screenSize.width(), 2. / screenSize.height()) * QTransform::fromTranslate(-1, -1) * QTransform::fromScale(1, -1);
QTransform transformInverted = leafTransform.inverted();
auto baseStyles = leafLayer->styles.toBaseStyles(); //++zIndexCount;
Renderer::BaseElement element; auto baseStyles = leafLayer->styles->toBaseStyles();
element.contour = contour; qDebug() << baseStyles.size();
for (auto& baseStyle : baseStyles) { for (auto& baseStyle : std::views::reverse(baseStyles)) {
double lineWidth = 0; double lineWidth = 0;
QPainterPath path;
std::shared_ptr<MaterialStyle> material;
if (baseStyle.material->type() == Renderer::MaterialStyleType::kStroke) { if (baseStyle.material->type() == Renderer::MaterialStyleType::kStroke) {
auto material = std::static_pointer_cast<MaterialStyleStroke>(baseStyle.material); qDebug() << "MaterialStyleType::kStroke";
material->halfWidth /= maxLen; std::shared_ptr copy = baseStyle.material->clone();
lineWidth = material->halfWidth; std::static_pointer_cast<MaterialStyleStroke>(copy)->halfWidth /= maxLen;
qDebug() << material->halfWidth; lineWidth = std::static_pointer_cast<MaterialStyleStroke>(copy)->halfWidth;
} material = copy;
QPainterPathStroker stroker; QPainterPathStroker stroker;
stroker.setWidth(lineWidth * 2); stroker.setWidth(lineWidth * 2);
stroker.setCapStyle(Qt::RoundCap); stroker.setCapStyle(Qt::RoundCap);
stroker.setJoinStyle(Qt::RoundJoin); stroker.setJoinStyle(Qt::RoundJoin);
QPainterPath strokePath = stroker.createStroke(painterPath); path = stroker.createStroke(painterPath);
auto rect = transform.map(strokePath).boundingRect(); }
else
{
qDebug() << "MaterialStyleType::kFill";
material = baseStyle.material;
path = painterPath;
}
auto rect = leafTransform.map(path).boundingRect();
elementTransform.bound = glm::vec4(rect.x(), rect.y(), rect.x() + rect.width(), rect.y() + rect.height()); 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; //elementTransform.bound = glm::vec4(-1,-1,1,1);
transform = transform.inverted(); //qDebug() << elementTransform.bound.x << elementTransform.bound.y << elementTransform.bound.z << elementTransform.bound.z;
elementTransform.transform = glm::mat3x2( elementTransform.transform = glm::mat3x2(
transform.m11(), transform.m12(), transform.m21(), transformInverted.m11(), transformInverted.m12(), transformInverted.m21(),
transform.m22(), transform.m31(), transform.m32() transformInverted.m22(), transformInverted.m31(), transformInverted.m32()
); );
//qDebug() << transform; elementTransform.zIndex = ++zIndexCount;
elementTransform.zIndex = 0; painting.addElement(BaseElement{ contour, material }, elementTransform);
element.style = baseStyle.material;
painting.addElement(element, elementTransform);
} }
return nullptr; return;
} }
FolderLayerWrapper* folderLayer = dynamic_cast<FolderLayerWrapper*>(nowLayer); FolderLayerWrapper* folderLayer = dynamic_cast<FolderLayerWrapper*>(nowLayer);
return folderLayer; if (folderLayer != nullptr) {
for (auto sonLayer : folderLayer->children) {
handleLayerWrapper(sonLayer.get(), transform, painting, contourMap);
}
}
return;
} }

View File

@ -2,12 +2,17 @@
#include "../../Renderer/Painting/Painting.h" #include "../../Renderer/Painting/Painting.h"
#include <QJsonObject> #include <QJsonObject>
#include <ElementManager.h> #include <ElementManager.h>
#include <map>
class PaintingUtil class PaintingUtil
{ {
using Contour = std::shared_ptr<vector<vector<Renderer::Point> > >;
private: private:
static int zIndexCount;
static void clear();
//static const double pi; //static const double pi;
static QJsonObject readJsonFile(QString jsonFilePath); static QJsonObject readJsonFile(QString jsonFilePath);
static FolderLayerWrapper* handleLayerWrapper(LayerWrapper* nowLayer, QTransform& transform, Renderer::Painting& painting); static void handleLayerWrapper(LayerWrapper* nowLayer, QTransform transform, Renderer::Painting& painting, std::unordered_map<LayerWrapper*, Contour>& contourMap);
//static double getMaxLineWidth(LayerWrapper* root); //static double getMaxLineWidth(LayerWrapper* root);
public: public:
static Renderer::Painting transfromToPainting(QString jsonFilePath); static Renderer::Painting transfromToPainting(QString jsonFilePath);

View File

@ -0,0 +1,30 @@
#include "FluentMenuButton.h"
FluentMenuButton::FluentMenuButton(QWidget* parent)
: FluentMenuButton("Menu", parent)
{
}
FluentMenuButton::FluentMenuButton(const QString& text, QWidget* parent)
: QtMaterialFlatButton(text, parent)
{
menu = new FluentMenu(this);
setHaloVisible(false);
setOverlayStyle(::Material::TintedOverlay);
setCheckable(true);
QObject::connect(menu, &QMenu::aboutToShow, [&] {
setChecked(true);
});
QObject::connect(menu, &QMenu::aboutToHide, [&] {
setChecked(false);
});
QObject::connect(this, &QPushButton::clicked, [&]() {
menu->exec(mapToGlobal(QPoint(0, height())));
});
}
void FluentMenuButton::addMenuAction(QAction* action)
{
menu->addAction(action);
}

View File

@ -0,0 +1,12 @@
#pragma once
#include <qtmaterialflatbutton.h>
#include "FluentMenu.h"
class FluentMenuButton : public QtMaterialFlatButton
{
public:
explicit FluentMenuButton(QWidget* parent = nullptr);
explicit FluentMenuButton(const QString& text, QWidget* parent = nullptr);
void addMenuAction(QAction* action);
FluentMenu* menu = nullptr;
};

View File

@ -244,8 +244,7 @@ GLuint Renderer::Model::loadPainting(std::string path)
return iter->second; return iter->second;
Painting painting; Painting painting;
path = "../test.json"; if (auto file = QFileInfo(directory.filePath(path.c_str())); file.isFile())
if (auto file = QFileInfo(QString(path.c_str())); file.isFile())
painting = PaintingUtil::transfromToPainting(file.filePath()); painting = PaintingUtil::transfromToPainting(file.filePath());
else else
{ {
@ -280,13 +279,13 @@ GLuint Renderer::Model::loadPainting(std::string path)
std::make_shared<Element>(Element{ contours[2].first, style[0], contours[2].second}), std::make_shared<Element>(Element{ contours[2].first, style[0], contours[2].second}),
}; };
if (path == "0.json") if (path == "0.json" && false)
{ {
//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[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[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[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") else if (path == "1.json" || true)
{ {
//painting.backgroundColor = QColor(196, 81, 35); //painting.backgroundColor = QColor(196, 81, 35);
float widths[] = { 0.22, 0.22 * 0.25 / 0.15, 0.22 * 0.25 / 0.15 }; float widths[] = { 0.22, 0.22 * 0.25 / 0.15, 0.22 * 0.25 / 0.15 };
@ -352,7 +351,7 @@ GLuint Renderer::Model::loadPainting(std::string path)
trans.m13(), trans.m23(), trans.m33()).c_str(); trans.m13(), trans.m23(), trans.m33()).c_str();
//painting.addElement(*element[0], ElementTransform{ glm::vec4(-1,-1,1,1), mat, 0}); //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[1], ElementTransform{ glm::vec4(-1,-1,0,1), mat, 1 });
painting.addElement(*element[2], ElementTransform{ glm::vec4(0,-1,1,1), mat2, 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[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[1], ElementTransform{ glm::vec2(-0.535,0.33), glm::vec2(0.15), 0, glm::bvec2(false), 0 });

View File

@ -413,6 +413,8 @@ int CubicBezierSignedDistance::solve_quadric(dvec2 coeffs, dvec2& roots) {
return 2; return 2;
} }
else
return 0;
} }
void CubicBezierSignedDistance::sort_roots3(dvec3& roots) { void CubicBezierSignedDistance::sort_roots3(dvec3& roots) {

View File

@ -77,8 +77,8 @@ bool Renderer::StrokeRadialGradient::operator==(const MaterialStroke& m) const
&& materialMap == static_cast<const StrokeRadialGradient&>(m).materialMap; && materialMap == static_cast<const StrokeRadialGradient&>(m).materialMap;
} }
Renderer::MaterialStyleStroke::MaterialStyleStroke(float halfWidth, StrokeType strokeType, StrokeEndType endType, std::shared_ptr<MaterialStroke> materialStroke) Renderer::MaterialStyleStroke::MaterialStyleStroke(float halfWidth, StrokeType strokeType, StrokeEndType endType, std::shared_ptr<MaterialStroke> materialStroke, std::map<float, float> widthMap)
: halfWidth(halfWidth), strokeType(strokeType), endType(endType), materialStroke(materialStroke) : halfWidth(halfWidth), strokeType(strokeType), endType(endType), materialStroke(materialStroke), widthMap(widthMap)
{ {
} }
@ -89,7 +89,9 @@ MaterialStyleType Renderer::MaterialStyleStroke::type() const
std::vector<GLfloat> Renderer::MaterialStyleStroke::encoded() const std::vector<GLfloat> Renderer::MaterialStyleStroke::encoded() const
{ {
std::vector<GLfloat> v = { halfWidth }; std::vector<GLfloat> v = { halfWidth, glm::uintBitsToFloat(widthMap.size()) };
for(auto& [lengthRate, widthRate] : widthMap)
v.emplace_back(glm::uintBitsToFloat(glm::packUnorm2x16(glm::vec2(lengthRate, widthRate))));
auto encoded = materialStroke->encoded(); auto encoded = materialStroke->encoded();
glm::vec4 head = glm::unpackUnorm4x8(glm::floatBitsToUint(encoded[0])); glm::vec4 head = glm::unpackUnorm4x8(glm::floatBitsToUint(encoded[0]));
head.b = (float)strokeType / 10. + (float)endType / 100.; head.b = (float)strokeType / 10. + (float)endType / 100.;

View File

@ -48,7 +48,7 @@ namespace Renderer
class MaterialStyleStroke : public MaterialStyle class MaterialStyleStroke : public MaterialStyle
{ {
public: public:
MaterialStyleStroke(float width, StrokeType strokeType, StrokeEndType endType, std::shared_ptr<MaterialStroke> materialStroke); MaterialStyleStroke(float width, StrokeType strokeType, StrokeEndType endType, std::shared_ptr<MaterialStroke> materialStroke, std::map<float, float> widthMap = {});
virtual MaterialStyleType type() const override; virtual MaterialStyleType type() const override;
virtual std::vector<GLfloat> encoded() const override; virtual std::vector<GLfloat> encoded() const override;
virtual std::unique_ptr<MaterialStyle> clone() const override; virtual std::unique_ptr<MaterialStyle> clone() const override;
@ -59,6 +59,7 @@ namespace Renderer
StrokeType strokeType; StrokeType strokeType;
StrokeEndType endType; StrokeEndType endType;
std::shared_ptr<MaterialStroke> materialStroke; std::shared_ptr<MaterialStroke> materialStroke;
std::map<float, float> widthMap;
static const std::array<std::pair<QString, StrokeEndType>, 4> strokeEndTypeNames; static const std::array<std::pair<QString, StrokeEndType>, 4> strokeEndTypeNames;
}; };
} }

View File

@ -9,7 +9,7 @@ using namespace Renderer;
constexpr int kMaxLineCount = 20; constexpr int kMaxLineCount = 20;
Painting::Painting(QColor backgroundColor) : backgroundColor(backgroundColor) Painting::Painting(Material background) : background(background)
{ {
} }
@ -115,7 +115,7 @@ void Painting::generateBuffers(QOpenGLFunctions_4_5_Core* glFunc)
elementData.insert(elementData.end(), encodedStyle.begin(), encodedStyle.end()); elementData.insert(elementData.end(), encodedStyle.begin(), encodedStyle.end());
} }
for (auto & i : elementTransformPool) for (auto& i : elementTransformPool)
elementTransform.emplace_back(i.first); elementTransform.emplace_back(i.first);
for (auto& i : elementPool) for (auto& i : elementPool)
@ -142,8 +142,8 @@ void Painting::generateBuffers(QOpenGLFunctions_4_5_Core* glFunc)
glFunc->glNamedBufferData(buffers[3], elementOffsets.size() * sizeof(elementOffsets[0]), elementOffsets.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[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); glFunc->glNamedBufferData(buffers[5], elementData.size() * sizeof(elementData[0]), elementData.data(), GL_STATIC_READ);
glm::vec3 color(backgroundColor.redF(), backgroundColor.greenF(), backgroundColor.blueF()); GLfloat backgroundBuffer[] = { (GLfloat)background.color.redF(), (GLfloat)background.color.greenF(), (GLfloat)background.color.blueF(), background.metallicF(), background.roughnessF() };
glFunc->glNamedBufferData(buffers[6], sizeof(glm::vec3), &color, GL_STATIC_READ); glFunc->glNamedBufferData(buffers[6], 5 * sizeof(GLfloat), backgroundBuffer, GL_STATIC_READ);
} }
GLuint Renderer::Painting::getElementCount() GLuint Renderer::Painting::getElementCount()

View File

@ -54,9 +54,10 @@ namespace Renderer
std::vector<GLfloat> elementData; std::vector<GLfloat> elementData;
int paintingId = 0; int paintingId = 0;
std::array<GLuint, 7> buffers; std::array<GLuint, 7> buffers;
QColor backgroundColor; //QColor backgroundColor;
Material background;
Painting(QColor backgroundColor = Qt::white); Painting(Material background = Material(Qt::white));
void addElement(const BaseElement& element, const ElementTransform& transform); void addElement(const BaseElement& element, const ElementTransform& transform);
void generateBuffers(QOpenGLFunctions_4_5_Core* glFunc); void generateBuffers(QOpenGLFunctions_4_5_Core* glFunc);
GLuint getElementCount(); GLuint getElementCount();

View File

@ -15,6 +15,8 @@ using namespace Renderer;
std::vector<glm::vec2> generatePathBuffer(const QPainterPath& path) std::vector<glm::vec2> generatePathBuffer(const QPainterPath& path)
{ {
float lastLength = 0;
glm::vec2 lastPoint;
std::vector<glm::vec2> pathBuffer; std::vector<glm::vec2> pathBuffer;
for (int i = 0; i < path.elementCount(); i++) for (int i = 0; i < path.elementCount(); i++)
{ {
@ -24,18 +26,24 @@ std::vector<glm::vec2> generatePathBuffer(const QPainterPath& path)
case QPainterPath::MoveToElement: case QPainterPath::MoveToElement:
//qDebug() << "MoveToElement"; //qDebug() << "MoveToElement";
//qDebug() << element; //qDebug() << element;
pathBuffer.push_back(glm::vec2(std::numeric_limits<float>::infinity())); pathBuffer.emplace_back(std::numeric_limits<float>::infinity());
pathBuffer.push_back(glm::vec2(element.x, element.y)); pathBuffer.emplace_back(element.x, element.y);
lastLength = 0;
lastPoint = glm::vec2(element.x, element.y);
break; break;
case QPainterPath::LineToElement: case QPainterPath::LineToElement:
{ {
//qDebug() << "LineToElement"; //qDebug() << "LineToElement";
//qDebug() << element; //qDebug() << element;
glm::vec2 end = glm::vec2(element.x, element.y); glm::vec2 end = glm::vec2(element.x, element.y);
glm::vec2 mid = (pathBuffer.back() + end) / 2.f; glm::vec2 mid = (lastPoint + end) / 2.f;
pathBuffer.push_back(mid); pathBuffer.push_back(mid);
pathBuffer.push_back(mid); pathBuffer.push_back(mid);
pathBuffer.push_back(end); pathBuffer.push_back(end);
float length = (i + 1.) / path.elementCount();
pathBuffer.emplace_back(lastLength, length);
lastLength = length;
lastPoint = end;
break; break;
} }
case QPainterPath::CurveToElement: case QPainterPath::CurveToElement:
@ -49,16 +57,20 @@ std::vector<glm::vec2> generatePathBuffer(const QPainterPath& path)
element = path.elementAt(++i); element = path.elementAt(++i);
//qDebug() << element; //qDebug() << element;
glm::vec2 p3 = glm::vec2(element.x, element.y); glm::vec2 p3 = glm::vec2(element.x, element.y);
if (p3 != pathBuffer.back()) if (p3 != lastPoint)
{ {
pathBuffer.push_back(p1); pathBuffer.push_back(p1);
pathBuffer.push_back(p2); pathBuffer.push_back(p2);
pathBuffer.push_back(p3); pathBuffer.push_back(p3);
float length = (i + 1.) / path.elementCount();
pathBuffer.emplace_back(lastLength, length);
lastLength = length;
lastPoint = p3;
} }
break; break;
} }
case QPainterPath::CurveToDataElement: case QPainterPath::CurveToDataElement:
pathBuffer.push_back(glm::vec2(element.x, element.y)); qCritical() << "Read QPainterPath Error";
break; break;
} }
} }

View File

@ -8,29 +8,14 @@ Renderer::RendererWidget::RendererWidget(QWidget* parent)
{ {
ui.setupUi(this); ui.setupUi(this);
FluentMenu* menu = new FluentMenu(this); auto openAction = new QAction(QStringLiteral("´ò¿ª"), ui.openButton);
auto openAction = new QAction(QStringLiteral("´ò¿ª"), menu); auto saveAction = new QAction(QStringLiteral("±£´æ"), ui.openButton);
auto saveAction = new QAction(QStringLiteral("±£´æ"), menu); auto testAction = new QAction(QStringLiteral("²âÊÔ"), ui.openButton);
auto testAction = new QAction(QStringLiteral("²âÊÔ"), menu); auto test2Action = new QAction(QStringLiteral("²âÊÔ2"), ui.openButton);
auto test2Action = new QAction(QStringLiteral("²âÊÔ2"), menu); ui.openButton->addMenuAction(openAction);
menu->addAction(openAction); ui.openButton->addMenuAction(saveAction);
menu->addAction(saveAction); ui.openButton->addMenuAction(testAction);
menu->addAction(testAction); ui.openButton->addMenuAction(test2Action);
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, QObject::connect(ui.horizontalSlider, &QSlider::valueChanged,
ui.openGLWidget, &Renderer::RendererGLWidget::setMainLightPitch); ui.openGLWidget, &Renderer::RendererGLWidget::setMainLightPitch);

View File

@ -206,9 +206,9 @@ namespace UnitTest
} }
TEST_METHOD(TestBothSidesClosed) TEST_METHOD(TestBothSidesClosed)
{ {
QPainterPath closedPath; QPainterPath testPath;
SvgFileLoader().loadSvgFile("../../svg/4_L0.svg", closedPath); SvgFileLoader().loadSvgFile("../../svg/4_L0.svg", testPath);
closedPath = QTransform::fromScale(5, 5).map(closedPath); testPath = QTransform::fromScale(5, 5).map(testPath);
QApplication a(argc, argv); QApplication a(argc, argv);
class StyleStrokeRadialGradient : public Renderer::ElementStyle class StyleStrokeRadialGradient : public Renderer::ElementStyle
{ {
@ -220,11 +220,60 @@ namespace UnitTest
{1.00, Material{QColor(58,64,151)}} {1.00, Material{QColor(58,64,151)}}
}; };
return { BaseStyle(std::make_shared<TransformStyle>(), return { BaseStyle(std::make_shared<TransformStyle>(),
std::make_shared<MaterialStyleStroke>(20, StrokeType::kLeftSide, StrokeEndType::kClosed, std::make_shared<MaterialStyleStroke>(10, StrokeType::kBothSides, StrokeEndType::kClosed,
std::make_shared<StrokeRadialGradient>(materialMap, false))) }; std::make_shared<StrokeRadialGradient>(materialMap, false))) };
} }
} style; } style;
TestGLWidget w(style, closedPath); TestGLWidget w(style, testPath);
w.show();
a.exec();
}
TEST_METHOD(TestAcuteAngle)
{
QPainterPath testPath;
testPath.moveTo(50, 50);
testPath.lineTo(300, 300);
testPath.lineTo(300, 50);
QApplication a(argc, argv);
class StyleStrokeRadialGradient : public Renderer::ElementStyle
{
virtual std::vector<Renderer::BaseStyle> toBaseStyles() const override
{
std::map<float, Material> materialMap = {
{0.20, Material{QColor(255,255,255)}},
{0.60, Material{QColor(165,176,207)}},
{1.00, Material{QColor(58,64,151)}}
};
return { BaseStyle(std::make_shared<TransformStyle>(),
std::make_shared<MaterialStyleStroke>(20, StrokeType::kRightSide, StrokeEndType::kRound,
std::make_shared<StrokeRadialGradient>(materialMap, false))) };
}
} style;
TestGLWidget w(style, testPath);
w.show();
a.exec();
}
TEST_METHOD(TestBothSidesGradientWidth)
{
QPainterPath testPath;
testPath.moveTo(50, 50);
testPath.cubicTo(50, 200, 200, 300, 300, 300);
QApplication a(argc, argv);
class StyleStrokeRadialGradient : public Renderer::ElementStyle
{
virtual std::vector<Renderer::BaseStyle> toBaseStyles() const override
{
std::map<float, Material> materialMap = {
{0.20, Material{QColor(255,255,255)}},
{0.60, Material{QColor(165,176,207)}},
{1.00, Material{QColor(58,64,151)}}
};
return { BaseStyle(std::make_shared<TransformStyle>(),
std::make_shared<MaterialStyleStroke>(20, StrokeType::kLeftSide, StrokeEndType::kRound,
std::make_shared<StrokeRadialGradient>(materialMap, false))) };
}
} style;
TestGLWidget w(style, testPath);
w.show(); w.show();
a.exec(); a.exec();
} }

View File

@ -0,0 +1,49 @@
#include "CppUnitTest.h"
#include "Editor/LayerStyle.h"
#include <QGuiApplication>
#include <QtWidgets/QApplication>
using namespace Microsoft::VisualStudio::CppUnitTestFramework;
namespace UnitTest
{
TEST_CLASS(LayerStyleContainerTest)
{
private:
char* argv[1];
int argc;
LayerStyleContainer containerParent, containerChild;
public:
LayerStyleContainerTest() :
argv{ const_cast<char*>("") }, argc(1),
containerParent(LayerStyleContainer::TYPE_ALL),
containerChild(LayerStyleContainer::TYPE_UNCLOSED)
{
QApplication app(argc, argv);
Assert::IsTrue(containerParent.empty());
Assert::IsTrue(containerChild.empty());
auto fillStyle = containerParent.makeUnusedStyle(FillElementLayerStyle::displayName());
Assert::IsTrue(containerParent.useStyle(std::shared_ptr(std::move(fillStyle))));
Assert::IsFalse(containerParent.empty());
Assert::IsFalse(containerParent.full());
auto strokeStyle = containerParent.makeUnusedStyle(StrokeElementLayerStyle::displayName());
containerParent.useStyle(std::shared_ptr(std::move(strokeStyle)));
auto childStrokeStyle = containerChild.makeUnusedStyle(StrokeElementLayerStyle::displayName());
containerChild.useStyle(std::shared_ptr(std::move(childStrokeStyle)));
Assert::IsTrue(containerChild.full());
containerParent.computeNewHash();
containerChild.computeNewHash();
}
TEST_METHOD(ContainerCoverTest)
{
const auto& newContainer = containerParent | containerChild;
Assert::IsTrue(newContainer.full());
Assert::AreEqual(newContainer.getHash(), containerChild.getHash());
}
};
}

View File

@ -111,6 +111,7 @@
<DynamicSource Condition="'$(Configuration)|$(Platform)'=='Release|x64'">input</DynamicSource> <DynamicSource Condition="'$(Configuration)|$(Platform)'=='Release|x64'">input</DynamicSource>
<QtMocFileName Condition="'$(Configuration)|$(Platform)'=='Release|x64'">%(Filename).moc</QtMocFileName> <QtMocFileName Condition="'$(Configuration)|$(Platform)'=='Release|x64'">%(Filename).moc</QtMocFileName>
</ClCompile> </ClCompile>
<ClCompile Include="LayerStyleTest.cpp" />
<ClCompile Include="PaintingTest.cpp"> <ClCompile Include="PaintingTest.cpp">
<DynamicSource Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">input</DynamicSource> <DynamicSource Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">input</DynamicSource>
<QtMocFileName Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">%(Filename).moc</QtMocFileName> <QtMocFileName Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">%(Filename).moc</QtMocFileName>

View File

@ -51,5 +51,8 @@
<ClCompile Include="StyleTest.cpp"> <ClCompile Include="StyleTest.cpp">
<Filter>Source Files</Filter> <Filter>Source Files</Filter>
</ClCompile> </ClCompile>
<ClCompile Include="LayerStyleTest.cpp">
<Filter>Source Files</Filter>
</ClCompile>
</ItemGroup> </ItemGroup>
</Project> </Project>

View File

@ -343,24 +343,6 @@
</QtMoc> </QtMoc>
<ClInclude Include="qtmaterialtoggle_p.h" /> <ClInclude Include="qtmaterialtoggle_p.h" />
</ItemGroup> </ItemGroup>
<ItemGroup>
<CustomBuild Include="debug\moc_predefs.h.cbt">
<FileType>Document</FileType>
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Release|x64'">true</ExcludedFromBuild>
<AdditionalInputs Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">$(QTDIR)\mkspecs\features\data\dummy.cpp;%(AdditionalInputs)</AdditionalInputs>
<Command Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">cl -Bx"$(QTDIR)\bin\qmake.exe" -nologo -Zc:wchar_t -FS -Zc:rvalueCast -Zc:inline -Zc:strictStrings -Zc:throwingNew -Zc:referenceBinding -Zc:__cplusplus -Zi -MDd -W3 -w34100 -w34189 -w44996 -w44456 -w44457 -w44458 -wd4577 -wd4467 -E $(QTDIR)\mkspecs\features\data\dummy.cpp 2&gt;NUL &gt;debug\moc_predefs.h</Command>
<Message Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">Generate moc_predefs.h</Message>
<Outputs Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">debug\moc_predefs.h;%(Outputs)</Outputs>
</CustomBuild>
<CustomBuild Include="release\moc_predefs.h.cbt">
<FileType>Document</FileType>
<AdditionalInputs Condition="'$(Configuration)|$(Platform)'=='Release|x64'">$(QTDIR)\mkspecs\features\data\dummy.cpp;%(AdditionalInputs)</AdditionalInputs>
<Command Condition="'$(Configuration)|$(Platform)'=='Release|x64'">cl -Bx"$(QTDIR)\bin\qmake.exe" -nologo -Zc:wchar_t -FS -Zc:rvalueCast -Zc:inline -Zc:strictStrings -Zc:throwingNew -Zc:referenceBinding -Zc:__cplusplus -O2 -MD -W3 -w34100 -w34189 -w44996 -w44456 -w44457 -w44458 -wd4577 -wd4467 -E $(QTDIR)\mkspecs\features\data\dummy.cpp 2&gt;NUL &gt;release\moc_predefs.h</Command>
<Message Condition="'$(Configuration)|$(Platform)'=='Release|x64'">Generate moc_predefs.h</Message>
<Outputs Condition="'$(Configuration)|$(Platform)'=='Release|x64'">release\moc_predefs.h;%(Outputs)</Outputs>
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">true</ExcludedFromBuild>
</CustomBuild>
</ItemGroup>
<ItemGroup> <ItemGroup>
<None Include="..\fonts\Roboto\Roboto-Black.ttf" /> <None Include="..\fonts\Roboto\Roboto-Black.ttf" />
<None Include="..\fonts\Roboto\Roboto-Bold.ttf" /> <None Include="..\fonts\Roboto\Roboto-Bold.ttf" />

View File

@ -1,4 +1,11 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 141 171"> <?xml version="1.0" encoding="utf-8"?>
<title>4_L0的副本</title> <!-- Generator: Adobe Illustrator 25.0.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
<polygon points="0.5 0.5 140.5 0.5 140.5 10.5 115.5 10.5 115.5 35.5 105.5 35.5 105.5 10.5 75.5 10.5 75.5 160.5 105.5 160.5 105.5 135.5 115.5 135.5 115.5 160.5 140.5 160.5 140.5 170.5 0.5 170.5 0.5 160.5 25.5 160.5 25.5 135.5 35.5 135.5 35.5 160.5 65.5 160.5 65.5 10.5 35.5 10.5 35.5 35.5 25.5 35.5 25.5 10.5 0.5 10.5 0.5 0.5" fill="none" stroke="#000" stroke-miterlimit="10"/> <svg version="1.1" id="图层_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
viewBox="0 0 141 171" style="enable-background:new 0 0 141 171;" xml:space="preserve">
<style type="text/css">
.st0{fill:none;stroke:#000000;stroke-miterlimit:10;}
</style>
<polygon class="st0" points="0.5,0.5 140.5,0.5 140.5,10.5 115.5,10.5 115.5,31.5 105.5,31.5 105.5,10.5 75.5,10.5 75.5,165.8
105.5,165.8 105.5,144.7 115.5,144.7 115.5,165.8 140.5,165.8 140.5,175.8 0.5,175.8 0.5,165.8 25.5,165.8 25.5,144.7 35.5,144.7
35.5,165.8 65.5,165.8 65.5,10.5 35.5,10.5 35.5,31.5 25.5,31.5 25.5,10.5 0.5,10.5 "/>
</svg> </svg>

Before

Width:  |  Height:  |  Size: 480 B

After

Width:  |  Height:  |  Size: 781 B

1
svg/正方形.svg Normal file
View File

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 116.45 116.45"><defs><style>.cls-1{fill:#fff;stroke:#231815;stroke-miterlimit:10;}</style></defs><g id="图层_2" data-name="图层 2"><g id="图层_1-2" data-name="图层 1"><rect class="cls-1" x="0.5" y="0.5" width="115.45" height="115.45"/></g></g></svg>

After

Width:  |  Height:  |  Size: 310 B

136
test.json
View File

@ -3,7 +3,7 @@
"elements": [ "elements": [
{ {
"data": { "data": {
"include": "../svg/2.svg" "include": "/svg/2.svg"
}, },
"name": "ababa", "name": "ababa",
"type": "svg-file" "type": "svg-file"
@ -17,16 +17,16 @@
}, },
{ {
"data": { "data": {
"include": "../svg/0.svg" "include": "/svg/0.svg"
}, },
"name": "ababa2", "name": "ababa2",
"type": "svg-file" "type": "svg-file"
}, },
{ {
"data": { "data": {
"include": "D:/BigC2022/temp/ArchitectureColoredPainting/svg/3.svg" "include": "/svg/4_L0.svg"
}, },
"name": "3.svg", "name": "4_L0.svg",
"type": "svg-file" "type": "svg-file"
} }
], ],
@ -36,46 +36,22 @@
"children": [ "children": [
{ {
"children": [ "children": [
{
"element": 0,
"is-folder": false,
"name": "Leaf1",
"styles": [
{
"enableEachSideIndependent": false,
"left": "AAAAQAEAIZwAf///qqr//w==",
"right": "AADgQAAACJw=",
"type": "stroke"
}
],
"transform": {
"offset": {
"x": 0,
"y": 0
},
"rotation": 0,
"scale": {
"x": 1,
"y": 1
}
}
},
{ {
"element": 0, "element": 0,
"is-folder": false, "is-folder": false,
"name": "Leaf2", "name": "Leaf2",
"styles": [ "styles": [
{ {
"enableEachSideIndependent": false, "enableEachSideIndependent": true,
"left": "AAAAQAEAIZwAf////1UA/w==", "left": "AABAQAEAIZwAf///AFqe/w==",
"right": "AADgQAEACJwAf///AFqe/w==", "right": "AABAQAAACJw=",
"type": "stroke" "type": "stroke"
} }
], ],
"transform": { "transform": {
"offset": { "offset": {
"x": 150, "x": 501,
"y": 0 "y": -3
}, },
"rotation": 0, "rotation": 0,
"scale": { "scale": {
@ -83,30 +59,6 @@
"y": 1.5 "y": 1.5
} }
} }
},
{
"element": -842150451,
"is-folder": false,
"name": "子图层-3",
"styles": [
{
"enableEachSideIndependent": false,
"left": "AAAAQAEAIZwAf///AP8A/w==",
"right": "AADgQAAACJw=",
"type": "stroke"
}
],
"transform": {
"offset": {
"x": 0,
"y": 0
},
"rotation": 0,
"scale": {
"x": 1,
"y": 1
}
}
} }
], ],
"is-folder": true, "is-folder": true,
@ -114,8 +66,8 @@
"referenced-by": 1, "referenced-by": 1,
"transform": { "transform": {
"offset": { "offset": {
"x": 50, "x": 503,
"y": 50 "y": 36
}, },
"rotation": 0, "rotation": 0,
"scale": { "scale": {
@ -127,15 +79,73 @@
{ {
"element": 1, "element": 1,
"is-folder": false, "is-folder": false,
"name": "ReferencingGroupLayer", "name": "子图层-2",
"styles": [ "styles": [
], ],
"transform": { "transform": {
"offset": { "offset": {
"x": 100, "x": 1,
"y": 0 "y": 986
}, },
"rotation": 45, "rotation": 0,
"scale": {
"x": 1,
"y": 1
}
}
},
{
"element": 1,
"is-folder": false,
"name": "子图层-3",
"styles": [
],
"transform": {
"offset": {
"x": -959,
"y": -5
},
"rotation": 0,
"scale": {
"x": 1,
"y": 1
}
}
},
{
"element": 1,
"is-folder": false,
"name": "子图层-4",
"styles": [
],
"transform": {
"offset": {
"x": -958,
"y": 980
},
"rotation": 0,
"scale": {
"x": 1,
"y": 1
}
}
},
{
"element": 3,
"is-folder": false,
"name": "子图层-5",
"styles": [
{
"material": "AH8A/wBanv8=",
"type": "fill"
}
],
"transform": {
"offset": {
"x": 473,
"y": 419
},
"rotation": 0,
"scale": { "scale": {
"x": 1, "x": 1,
"y": 1 "y": 1

View File

@ -0,0 +1,60 @@
# 图层删除
当删除一个节点时,该节点一定满足且只满足以下条件中的一条:
1. 是一个Leaf节点引用了其他的Element
2. 是一个Folder节点没有GroupElement引用没有引用任何其他节点因为是Folder
3. 是一个Folder节点存在对应的GroupElement引用。
其中对于第1条由于Leaf节点被删除只影响其父亲Folder的属性所以显然在删除Leaf节点时是绝对安全的。
对于第2条对于其父亲Folder其被删除只影响其父亲Folder的属性所以不会影响其父亲对于其孩子有可能存在以下情况
1. 孩子中不存在任何被引用的情况:删除安全;
2. 孩子中存在Folder节点被GroupElement引用不安全需要手动将情况转换为第1条后才可以删除。
对于第3条必须首先手动解除其本身的引用然后将转变为第2条。
# 图元删除
**显然**无论是什么种类的图元删除一个图元会影响到的元素只有引用它的图层所以我们将SimpleElement与GroupElement合并成一种情况讨论。
当删除一个图元时,该图元一定满足且只满足以下条件中的一条:
1. 是一个GraphicElement未被其他图层引用
2. 是一个GraphicElement被其他图层引用。
对于第1条删除绝对安全。
对于第2条将引用关系手动解除后删除安全。
# 图层移动
## 条件分析
图层移动时,显然,被移动的节点只影响它本身及它的子图层,
移动的目标节点只影响它本身及它的父图层,那么:
(1) 被移动的图层一定满足且只满足以下条件中的一条:
1. 是一个Folder节点无对应的GroupElement引用子节点无引用GroupElement
2. 是一个Folder节点无对应的GroupElement引用子节点存在引用GroupElement
3. 是一个Folder节点存在对应的GroupElement引用
4. 是一个Leaf节点引用了GroupElement
5. 是一个Leaf节点未引用GroupElement。
(2) 目标FolderLayer一定满足且只满足以下条件中的一条
1. 自身无对应的GroupElement引用无父节点有GroupElement引用
2. 自身无对应的GroupElement引用存在父节点有GroupElement引用
3. 自身存在对应的GroupElement引用。
显然,以上条件的全组合可以覆盖所有情况。
由于:
- `(1) 1``(2) 中的任一条件`进行组合显然是绝对安全的;
- `(1) 3` 中自身提供的GroupElement并不会影响移动
- `(1) 5``(2) 中的任一条件`进行组合显然是绝对安全的;
- `(1) 中的任一条件``(2) 1` 进行组合显然是绝对安全的。
所以接下来对于除去以上情况的所有组合情况进行讨论。
## 讨论证明
### `(1) 2` - `(2) 2`
当且仅当被移动的图层的子节点引用的GroupElement 与 目标图层父节点提供的GroupElement 相同时,移动不安全。
### `(1) 4` - `(2) 2`
当且仅当被移动的图层引用的GroupElement 与 目标图层父节点提供的 GroupElement 相同时,移动不安全。
### `(2) 3` 的特殊讨论
当且仅当自身提供的GroupElement 被 被移动图层或其子节点引用时需要处理其自身情况否则无视其自身提供的GroupElement`(2) 1``(2) 2` 视为同情况处理。
## 结论
当且仅当被移动的图层或其子节点所引用的GroupElement 与 目标图层或其父节点所提供的GroupElement 相同时,移动不安全;其余情况均安全。