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

705
4_L0.json
View File

@ -1,33 +1,40 @@
{
"background-color": "#b7a386",
"background-color": "#ae9f86",
"elements": [
{
"data": {
"include": "../svg/2.svg"
"include": "/svg/2.svg"
},
"name": "ababa",
"type": "svg-file"
},
{
"data": {
"reference-layer": "0.0"
},
"name": "ababa-group",
"type": "group"
},
{
"data": {
"include": "../svg/0.svg"
"include": "/svg/0.svg"
},
"name": "ababa2",
"type": "svg-file"
},
{
"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"
},
{
"data": {
"reference-layer": "0.0"
},
"name": "图源工",
"type": "group"
},
{
"data": {
"reference-layer": "0.0"
},
"name": "图源万",
"type": "group"
}
],
"height": 1080,
@ -37,308 +44,122 @@
{
"children": [
{
"element": 3,
"is-folder": false,
"name": "4_L0",
"styles": [
"children": [
{
"material": "AH8A/2pkiv8=",
"type": "fill"
},
{
"enableEachSideIndependent": true,
"left": "AADAQAEAJJwAf///9dKG/w==",
"right": "AADgQAAACpw=",
"type": "stroke"
"element": 2,
"is-folder": false,
"name": "4_L0",
"styles": [
{
"material": "AH8A/1JOff8=",
"type": "fill"
},
{
"enableEachSideIndependent": true,
"left": "AAAAQAEAJJwAf///9c19/w==",
"right": "AADgQAAACpw=",
"type": "stroke"
}
],
"transform": {
"filpX": false,
"filpY": false,
"offset": {
"x": 0,
"y": 0
},
"rotation": 0,
"scale": {
"x": 1,
"y": 1
}
}
}
],
"is-folder": true,
"name": "工",
"referenced-by": 3,
"transform": {
"filpX": false,
"filpY": false,
"offset": {
"x": -230,
"y": -533
"x": 0,
"y": 0
},
"rotation": 0,
"scale": {
"x": 0.32341007644113307,
"y": 0.32341007644113307
"x": 1,
"y": 1
}
}
},
{
"element": 3,
"is-folder": false,
"name": "1",
"styles": [
],
"transform": {
"filpX": false,
"filpY": false,
"offset": {
"x": 103,
"y": -1
},
"rotation": 90,
"scale": {
"x": 1,
"y": 1
}
}
},
{
"element": 3,
"is-folder": false,
"name": "2",
"styles": [
],
"transform": {
"filpX": false,
"filpY": false,
"offset": {
"x": 104,
"y": 100
},
"rotation": 0,
"scale": {
"x": 1,
"y": 1
}
}
},
{
"element": 3,
"is-folder": false,
"name": "3",
"styles": [
],
"transform": {
"filpX": false,
"filpY": false,
"offset": {
"x": 3,
"y": 102
},
"rotation": 90,
"scale": {
"x": 1,
"y": 1
}
}
}
],
"is-folder": true,
"name": "GroupFolderExample",
"referenced-by": 1,
"transform": {
"offset": {
"x": 50,
"y": 50
},
"rotation": 0,
"scale": {
"x": 1,
"y": 1
}
}
},
{
"element": 1,
"is-folder": false,
"name": "aaaa2",
"styles": [
],
"transform": {
"offset": {
"x": 127,
"y": -109
},
"rotation": 90,
"scale": {
"x": 1,
"y": 1
}
}
},
{
"element": 1,
"is-folder": false,
"name": "aaaa3",
"styles": [
],
"transform": {
"offset": {
"x": 232,
"y": 17
},
"rotation": 180,
"scale": {
"x": 1,
"y": 1
}
}
},
{
"element": 1,
"is-folder": false,
"name": "aaaa4",
"styles": [
],
"transform": {
"offset": {
"x": 103,
"y": 130
},
"rotation": 270,
"scale": {
"x": 1,
"y": 1
}
}
},
{
"element": 1,
"is-folder": false,
"name": "bbbb1",
"styles": [
],
"transform": {
"offset": {
"x": 197,
"y": 265
},
"rotation": 0,
"scale": {
"x": 1,
"y": 1
}
}
},
{
"element": 1,
"is-folder": false,
"name": "bbbb2",
"styles": [
],
"transform": {
"offset": {
"x": 323,
"y": 156
},
"rotation": 90,
"scale": {
"x": 1,
"y": 1
}
}
},
{
"element": 1,
"is-folder": false,
"name": "bbbb3",
"styles": [
],
"transform": {
"offset": {
"x": 432,
"y": 285
},
"rotation": 180,
"scale": {
"x": 1,
"y": 1
}
}
},
{
"element": 1,
"is-folder": false,
"name": "bbbb4",
"styles": [
],
"transform": {
"offset": {
"x": 302,
"y": 399
},
"rotation": 270,
"scale": {
"x": 1,
"y": 1
}
}
},
{
"element": 1,
"is-folder": false,
"name": "cccc1",
"styles": [
],
"transform": {
"offset": {
"x": 467,
"y": 71
},
"rotation": 0,
"scale": {
"x": 1,
"y": 1
}
}
},
{
"element": 1,
"is-folder": false,
"name": "cccc2",
"styles": [
],
"transform": {
"offset": {
"x": 361,
"y": -70
},
"rotation": 90,
"scale": {
"x": 1,
"y": 1
}
}
},
{
"element": 1,
"is-folder": false,
"name": "cccc3",
"styles": [
],
"transform": {
"offset": {
"x": 273,
"y": -211
},
"rotation": 180,
"scale": {
"x": 1,
"y": 1
}
}
},
{
"element": 1,
"is-folder": false,
"name": "cccc4",
"styles": [
],
"transform": {
"offset": {
"x": 574,
"y": 198
},
"rotation": 270,
"scale": {
"x": 1,
"y": 1
}
}
},
{
"element": 1,
"is-folder": false,
"name": "dddd1",
"styles": [
],
"transform": {
"offset": {
"x": 33,
"y": -231
},
"rotation": 0,
"scale": {
"x": 1,
"y": 1
}
}
},
{
"element": 1,
"is-folder": false,
"name": "dddd2",
"styles": [
],
"transform": {
"offset": {
"x": 55,
"y": 352
},
"rotation": 90,
"scale": {
"x": 1,
"y": 1
}
}
},
{
"element": 1,
"is-folder": false,
"name": "dddd3",
"styles": [
],
"transform": {
"offset": {
"x": -40,
"y": 220
},
"rotation": 180,
"scale": {
"x": 1,
"y": 1
}
}
},
{
"element": 1,
"is-folder": false,
"name": "dddd4",
"styles": [
],
"name": "子图层-1",
"referenced-by": 4,
"transform": {
"filpX": false,
"filpY": false,
"offset": {
"x": 0,
"y": 0
@ -351,14 +172,16 @@
}
},
{
"element": 1,
"element": 4,
"is-folder": false,
"name": "eeee1",
"name": "子图层-2",
"styles": [
],
"transform": {
"filpX": false,
"filpY": false,
"offset": {
"x": 0,
"x": 204,
"y": 0
},
"rotation": 0,
@ -369,17 +192,19 @@
}
},
{
"element": 1,
"element": 4,
"is-folder": false,
"name": "eeee2",
"name": "子图层-3",
"styles": [
],
"transform": {
"filpX": false,
"filpY": false,
"offset": {
"x": -149,
"y": 85
"x": 3,
"y": 205
},
"rotation": 90,
"rotation": 0,
"scale": {
"x": 1,
"y": 1
@ -387,17 +212,259 @@
}
},
{
"element": 1,
"element": 4,
"is-folder": false,
"name": "eeee3",
"name": "子图层-4",
"styles": [
],
"transform": {
"filpX": false,
"filpY": false,
"offset": {
"x": 166,
"y": 482
"x": 207,
"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": {
"x": 1,
"y": 1
@ -409,14 +476,16 @@
"name": "root",
"referenced-by": null,
"transform": {
"filpX": false,
"filpY": false,
"offset": {
"x": 8,
"y": 20
"x": 116,
"y": 288
},
"rotation": 0,
"rotation": 60,
"scale": {
"x": 1.7159367435419115,
"y": 1.7159367435419115
"x": 1.85202,
"y": 1.85202
}
}
},

View File

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

View File

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

View File

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

View File

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

View File

@ -39,7 +39,7 @@
<widget class="QWidget" name="MainWindow" native="true">
<layout class="QVBoxLayout" name="verticalLayout_2" stretch="20,1">
<item>
<layout class="QHBoxLayout" name="horizontalLayout" stretch="0,12,5">
<layout class="QHBoxLayout" name="horizontalLayout" stretch="0,7,4,3">
<item>
<widget class="QWidget" name="LeftBar" native="true"/>
</item>
@ -66,11 +66,11 @@
<property name="widgetResizable">
<bool>true</bool>
</property>
<widget class="QWidget" name="scrollAreaWidgetContents">
<widget class="PreviewWindow" name="Preview">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<y>-17</y>
<width>1024</width>
<height>1024</height>
</rect>
@ -89,34 +89,10 @@
</property>
<property name="maximumSize">
<size>
<width>10241024</width>
<height>10241024</height>
<width>1024</width>
<height>1024</height>
</size>
</property>
<layout class="QHBoxLayout" name="horizontalLayout_2">
<item>
<widget class="PreviewWindow" name="Preview">
<property name="sizePolicy">
<sizepolicy hsizetype="Fixed" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="minimumSize">
<size>
<width>1024</width>
<height>1024</height>
</size>
</property>
<property name="maximumSize">
<size>
<width>1024</width>
<height>1024</height>
</size>
</property>
</widget>
</item>
</layout>
</widget>
</widget>
</item>
@ -147,18 +123,13 @@
</size>
</property>
<property name="currentIndex">
<number>2</number>
<number>0</number>
</property>
<widget class="InfoDisplayWidget" name="LayerDisplay">
<attribute name="title">
<string>图层信息</string>
</attribute>
</widget>
<widget class="ElementPoolWidget" name="ElementDisplay">
<attribute name="title">
<string>图元池</string>
</attribute>
</widget>
<widget class="EditorSettingWidget" name="EditorSetting">
<attribute name="title">
<string>设置</string>
@ -196,16 +167,28 @@
<string>关联图元</string>
</property>
</column>
<column>
<property name="text">
<string>可见</string>
</property>
</column>
</widget>
</item>
</layout>
</widget>
</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>
</item>
<item>

View File

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

View File

@ -557,7 +557,7 @@ int solve_quartic(vec4 coeffs, inout vec4 s)
return num;
}
float cubic_bezier_dis(vec2 uv, vec2 p0, vec2 p1, vec2 p2, vec2 p3, bool roundEnd)
float cubic_bezier_dis(vec2 uv, vec2 p0, vec2 p1, vec2 p2, vec2 p3, bool roundEnd, out float t)
{
// 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.);
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
{
@ -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);
metallicRoughness = vec2(0.8);
uint headUint = floatBitsToUint(style[styleIndex + 1]);
uint headUint = floatBitsToUint(style[styleHeadIndex]);
vec4 head = unpackUnorm4x8(headUint);
switch (int(head.a * 100) % 10)
// switch (2)
@ -905,7 +911,7 @@ void drawLine(in float d, uint styleIndex, out vec4 elementColor, out vec2 metal
/// Plain
case 0: {
metallicRoughness = head.rg;
elementColor = vec4(unpackUnorm4x8(floatBitsToUint(style[styleIndex + 2])).rgb, 1);
elementColor = vec4(unpackUnorm4x8(floatBitsToUint(style[styleHeadIndex + 1])).rgb, 1);
break;
}
/// RadialGradient
@ -913,16 +919,16 @@ void drawLine(in float d, uint styleIndex, out vec4 elementColor, out vec2 metal
uint size = headUint % (1 << 15);
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;
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++)
{
uint data = floatBitsToUint(style[styleIndex + 2 + i * 2]);
uint data = floatBitsToUint(style[styleHeadIndex + 1 + i * 2]);
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;
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)
{
uint headUint = floatBitsToUint(style[styleIndex + 1]);
uint headUint = floatBitsToUint(style[styleIndex]);
vec4 head = unpackUnorm4x8(headUint);
switch (int(head.a * 100) % 10)
{
/// Plain
case 0: {
styleIndex += 3;
styleIndex += 2;
break;
}
/// RadialGradient
case 1: {
uint size = headUint % (1 << 15);
styleIndex += 2 + size * 2;
styleIndex += 1 + size * 2;
break;
}
case 2: {
@ -998,9 +1007,9 @@ bool shouldFillBeginCap(vec2 localUV, bool onVeryBegin, int endType, vec2 p0, ve
vec2 normal;
if (onVeryBegin)
{
if (endType%2 == 0)
if (endType % 2 == 0)
return true;
else if (endType%2 == 1)
else if (endType % 2 == 1)
normal = normalize(mat2(0, 1, -1, 0) * (-tangentBegin));
}
else
@ -1017,9 +1026,9 @@ bool shouldFillEndCap(vec2 localUV, bool onVeryEnd, int endType, vec2 p3, vec2 t
vec2 normal;
if (onVeryEnd)
{
if ((endType/2)%2 == 0)
if ((endType / 2) % 2 == 0)
return true;
else if ((endType/2)%2 == 1)
else if ((endType / 2) % 2 == 1)
normal = normalize(mat2(0, 1, -1, 0) * tangentEnd);
}
else
@ -1034,11 +1043,7 @@ bool shouldFillEndCap(vec2 localUV, bool onVeryEnd, int endType, vec2 p3, vec2 t
void main()
{
uvec2 pixelLocation = gl_GlobalInvocationID.xy;
// imageStore(gBaseColor, ivec2(pixelLocation), vec4(vec2(pixelLocation)/vec2(imageSize(gBaseColor)), 1,1));
vec4 color = vec4(0);
// if(isinf(path[0].x)) imageStore(gBaseColor, ivec2(pixelLocation),
// vec4(vec2(pixelLocation)/vec2(imageSize(gBaseColor)), 1,1)); return;
for (uint styleIndex = 0; styleIndex < styleSize;)
{
styleIndex += 6;
@ -1063,7 +1068,7 @@ void main()
continue;
}
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]);
p3Last = p[3];
p2Last = p[2];
@ -1075,46 +1080,41 @@ void main()
vec4 head = unpackUnorm4x8(floatBitsToUint(style[styleIndex]));
if (head.z == 0)
{
elementColor = vec4(unpackUnorm4x8(floatBitsToUint(style[styleIndex+1])).rgb, 1);
elementColor = vec4(unpackUnorm4x8(floatBitsToUint(style[styleIndex + 1])).rgb, 1);
}
}
styleIndex += 2;
}
else // Stroke
{
uint widthMapSize = floatBitsToUint(style[styleIndex + 1]);
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 = 2;
int endType = int(round(styleHead.b * 100)) % 10;
// endType = 1;
int debugBegin = 0;
bool onVeryBegin = false;
bool onVeryEnd = false;
vec2 tangentEndLast;
vec2 tangentEndLast = vec2(0);
vec2 tangentFirstBegin;
uint lastHitIndex = 0;
bool lastHitElement = false;
hitElement = false;
for (uint pathIndex = 0; pathIndex < pathSize; pathIndex++)
//for (uint pathIndex = 0; pathIndex < 46; pathIndex++)
{
vec2 pTemp = path[pathIndex];
if (isinf(pTemp.x))
{
// TODO: ¼ì²âÊÇ·ñ·â±Õ²¢´¦Àí
pBegin = path[++pathIndex];
p3Last = pBegin;
p2Last = pBegin;
if(endType == 4)
if (endType == 4 /*StrokeEndType::kClosed*/)
{
//onVeryBegin = false;
vec2 lastP1 = path[pathSize-3];
vec2 lastP2 = path[pathSize-2];
vec2 lastP3 = path[pathSize-1];
// onVeryBegin = false;
vec2 lastP1 = path[pathSize - 4];
vec2 lastP2 = path[pathSize - 3];
vec2 lastP3 = path[pathSize - 2];
if (lastP3 != lastP2)
tangentEndLast = normalize(lastP3 - lastP2);
else
@ -1125,7 +1125,7 @@ void main()
continue;
}
mat4x2 p = mat4x2(p3Last, pTemp, path[++pathIndex], path[++pathIndex]);
vec2 lengthRate = path[++pathIndex];
vec2 tangentBeginNext;
if (pathIndex + 1 < pathSize)
{
@ -1146,31 +1146,35 @@ void main()
}
else
{
if(endType == 4)
if (endType == 4 /*StrokeEndType::kClosed*/)
{
//onVeryEnd = false;
// onVeryEnd = false;
tangentBeginNext = tangentFirstBegin;
}
else
onVeryEnd = true;
}
vec2 tangentEnd;
if (p[3] != p[2])
tangentEnd = normalize(p[3] - p[2]);
else
tangentEnd = normalize(p[3] - p[1]);
float d = cubic_bezier_dis(localUV, p[0], p[1], p[2], p[3], true);
if (d <= strokeWidth)
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 = distance(localUV, p[0]) <= strokeWidth;
bool onEnd = distance(localUV, p[3]) <= strokeWidth;
bool onBegin = t<1e-5;
bool onEnd = t>1-1e-5;
//bool onBegin = true;
//bool onEnd = true;
vec2 tangentBegin;
vec2 tangentEnd;
if (p[0] != p[1])
tangentBegin = normalize(p[0] - p[1]);
else
tangentBegin = normalize(p[0] - p[2]);
if (p[3] != p[2])
tangentEnd = normalize(p[3] - p[2]);
else
tangentEnd = normalize(p[3] - p[1]);
bool hit = d < minDistance;
if (onBegin)
@ -1198,19 +1202,19 @@ void main()
hitElement = true;
// elementColor = vec4(1, 1, 0, 1);
vec2 metallicRoughness;
drawLine(minDistance / strokeWidth, styleIndex, elementColor, metallicRoughness);
drawLine(minDistance / localWidth, styleIndex, elementColor, metallicRoughness);
}
}
tangentEndLast = tangentEnd;
if(pathIndex == 4)
if (pathIndex == 5)
tangentFirstBegin = tangentBegin;
}
tangentEndLast = tangentEnd;
p3Last = p[3];
p2Last = p[2];
onVeryBegin = false;
}
nextStyleIndex(styleIndex);
}
if (hitElement)
color = elementColor;

View File

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

View File

@ -8,42 +8,25 @@
EditorWidget::EditorWidget(QWidget* parent) : QWidget(parent)
{
ui.setupUi(this);
this->createButton = ui.createButton;
this->closeButton = ui.closeButton;
this->saveButton = ui.saveButton;
this->saveAsButton = ui.saveAsButton;
this->openButton = ui.openButton;
this->fileMenuButton = ui.fileMenuButton;
this->projectMenuButton = ui.projectMenuButton;
this->tabWidget = ui.tabWidget;
while (this->tabWidget->count() > 0)
{
this->tabWidget->removeTab(0);
}
connect(this->createButton, &QPushButton::clicked, this, [this]() {
static int count = 0;
this->tabWidget->addTab(new EditorWidgetItem("../data.json",this), "untitled" + QString::number(count++));
});
connect(this->openButton, &QPushButton::clicked, this, [this]() {
QString fileName = QFileDialog::getOpenFileName(this->saveAsButton, QString::fromLocal8Bit("´ò¿ª"), "", QString::fromLocal8Bit("JSONÎļþ(*.json)"));
if(!fileName.isEmpty())
this->tabWidget->addTab(new EditorWidgetItem(fileName, this), fileName);
});
connect(this->closeButton, &QPushButton::clicked, this, [this]() {
this->tabWidget->removeTab(this->tabWidget->currentIndex());
});
connect(this->saveButton, &QPushButton::clicked, this, [this]() {
EditorWidgetItem* item = dynamic_cast<EditorWidgetItem*>(this->tabWidget->currentWidget());
if (item != nullptr)
{
//item->save();
}
});
connect(this->saveAsButton, &QPushButton::clicked, this, [this]() {
EditorWidgetItem* item = dynamic_cast<EditorWidgetItem*>(this->tabWidget->currentWidget());
if (item != nullptr)
{
QString fileName = QFileDialog::getSaveFileName(this->saveAsButton, QString::fromLocal8Bit("Áí´æΪ"), "", QString::fromLocal8Bit("JSONÎļþ(*.json)"));
item->saveAs(fileName);
}
connect(this, &EditorWidget::tabCountChanged, this, &EditorWidget::onTabCountChanged);
initFileMenu();
initProjectMenu();
connect(this->tabWidget, &QTabWidget::tabCloseRequested, [this](int index) {
const int prevCount = this->tabWidget->count();
this->tabWidget->removeTab(index);
const int nowCount = this->tabWidget->count();
emit tabCountChanged(prevCount, nowCount);
});
}
@ -55,3 +38,72 @@ void EditorWidget::renameTab(QWidget* target, QString 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
#include <qwidget.h>
#include "../FluentMenuButton.h"
#include "ui_EditorWidget.h"
class EditorWidget :
@ -9,15 +10,21 @@ class EditorWidget :
private:
Ui::EditorWidget ui;
QTabWidget* tabWidget;
QPushButton* createButton;
QPushButton* closeButton;
QPushButton* saveButton;
QPushButton* saveAsButton;
QPushButton* openButton;
FluentMenuButton* fileMenuButton;
FluentMenuButton* projectMenuButton;
void initFileMenu();
void initProjectMenu();
public:
EditorWidget(QWidget* parent = nullptr);
~EditorWidget()=default;
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>
LayerStyleDialog::LayerStyleDialog(
LayerStyleContainer& styles,
LayerStyleContainer* pStyles,
const std::shared_ptr<LayerStyle>& existedStyle,
QWidget* parent
) : QDialog(parent), styles(&styles)
) : QDialog(parent), styles(pStyles)
{
dialogLayout = new QGridLayout;
dialogLayout->setAlignment(Qt::AlignmentFlag::AlignHCenter);
@ -23,13 +23,13 @@ LayerStyleDialog::LayerStyleDialog(
}
else
{
QStringList unusedStyleNames = styles.unusedStyleNames();
QStringList unusedStyleNames = styles->unusedStyleNames();
auto* typeSelector = new QComboBox(this);
if (!unusedStyleNames.empty())
{
typeSelector->addItems(unusedStyleNames);
this->modifyingStyle = std::move(styles.makeUnusedStyle(unusedStyleNames[0]));
this->modifyingStyle = std::move(styles->makeUnusedStyle(unusedStyleNames[0]));
dialogLayout->addWidget(typeSelector, 0, 0);

View File

@ -13,7 +13,7 @@ private:
std::unique_ptr<LayerStyle> modifyingStyle;
public:
LayerStyleDialog(
LayerStyleContainer& styles,
LayerStyleContainer* pStyles,
const std::shared_ptr<LayerStyle>& existedStyle = 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->setFixedSize(20, 20);
strokeTable->setCellWidget(row, COLUMN_OPERATIONS, removeButton);
connect(removeButton, &QtMaterialRaisedButton::clicked, [this, row] {
radialStroke(this->stroke)->materialMap.erase(this->strokeTable->item(row, COLUMN_WIDTH)->text().toFloat());
this->strokeTable->removeRow(row);
connect(removeButton, &QtMaterialRaisedButton::clicked, [this, widthItem] {
radialStroke(this->stroke)->materialMap.erase(widthItem->data(Qt::EditRole).toFloat());
this->strokeTable->removeRow(widthItem->row());
});
}

View File

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

View File

@ -18,6 +18,14 @@ ElementManager::ElementManager(QJsonObject source, QString fileHome)
}
for (auto element : elements)
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)

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -3,7 +3,6 @@
#include <utility>
#include <set>
#include <map>
#include <QListWidget>
#include <QJsonObject>
#include <QJsonArray>
#include "../Renderer/Painting/ElementStyle.h"
@ -36,6 +35,8 @@ public:
virtual QJsonObject toJson() const;
virtual std::unique_ptr<LayerStyle> clone() const = 0;
virtual bool operator==(const LayerStyle& other) const;
};
class StrokeElementLayerStyle : public LayerStyle
@ -57,6 +58,8 @@ public:
QJsonObject toJson() const override;
std::unique_ptr<LayerStyle> clone() const override;
bool operator==(const LayerStyle& other) const override;
std::pair<PMaterialStyleStroke, PMaterialStyleStroke> strokePair;
bool enableEachSideIndependent = false;
};
@ -79,6 +82,8 @@ public:
QJsonObject toJson() const override;
std::unique_ptr<LayerStyle> clone() const override;
bool operator==(const LayerStyle& other) const override;
PMaterialStyleFill fillMaterialStyle;
};
@ -111,23 +116,38 @@ private:
std::map<QString, std::shared_ptr<LayerStyle>> styles;
size_t hash;
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);
std::vector<Renderer::BaseStyle> toBaseStyles() const override;
QJsonArray toJson() const;
static LayerStyleContainer fromJson(ElementType elementType, const QJsonArray& jsonArray);
bool empty() const;
bool full() const;
std::map<QString, std::shared_ptr<LayerStyle>>::iterator begin();
std::map<QString, std::shared_ptr<LayerStyle>>::iterator end();
LayerStyleContainer(ElementType elementType);
[[nodiscard]] std::vector<Renderer::BaseStyle> toBaseStyles() const override;
[[nodiscard]] QJsonArray toJson() const;
QStringList unusedStyleNames() const;
std::unique_ptr<LayerStyle> makeUnusedStyle(const QString& styleName) const;
bool useStyle(const std::shared_ptr<LayerStyle>& style);
[[nodiscard]] bool empty() const;
[[nodiscard]] bool full() const;
[[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);
float boundingBoxAffectValue() const;
size_t getHash() const;
[[nodiscard]] std::shared_ptr<LayerStyle> getStyle(const QString& styleName) 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)
{
this->refresh(selectedLayer);
if (selectedLayer == this)
if (needRefresh)
{
this->cache.highLight();
this->refresh(selectedLayer);
needRefresh = false;
}
return cache;
}
@ -46,6 +46,9 @@ LayerWrapper::LayerWrapper(QJsonObject json, FolderLayerWrapper*parent, ElementM
property.scale = {transformJson.value("scale").toObject().value("x").toDouble(),
transformJson.value("scale").toObject().value("y").toDouble()};
property.rotation = {transformJson.value("rotation").toDouble()};
property.flipX = { transformJson.value("flipX").toBool() };
qDebug() << property.flipX;
property.flipY = { transformJson.value("flipY").toBool() };
selected = false;
hidden = false;
}
@ -56,6 +59,7 @@ FolderLayerWrapper::FolderLayerWrapper(QJsonObject json, ElementManager *element
qDebug() << json.value("name").toString() << " " << this;
QJsonArray childrenJson = json.value("children").toArray();
QJsonValue referencedJson = json.value("referenced-by");
styles = new LayerStyleContainer(LayerStyleContainer::TYPE_ALL);
if (!referencedJson.isNull())
{
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)
: LayerWrapper(json, parent, elementManager),
wrappedElement(elementManager->getElementById(json.value("element").toInt())),
styles(LayerStyleContainer::fromJson(wrappedElement->isClosed(), json.value("styles").toArray()))
wrappedElement(elementManager->getElementById(json.value("element").toInt()))
{
styles = new LayerStyleContainer(LayerStyleContainer::fromJson(
wrappedElement->isClosed() ? LayerStyleContainer::TYPE_CLOSED : LayerStyleContainer::TYPE_UNCLOSED,
json.value("styles").toArray()));
qDebug() << json.value("name").toString() << " " << this;
if(wrappedElement != nullptr)
wrappedElement->referencedCount++;
@ -90,6 +96,15 @@ LeafLayerWrapper::~LeafLayerWrapper()
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)
{
transform.reset();
@ -100,6 +115,10 @@ void LayerWrapper::SimpleProperty::apply(PixelPath&cache)
transform.translate(centerX, centerY);
transform.translate(offset.x(), offset.y());
transform.rotate(rotation);
if (flipX)
transform.scale(-1, 1);
if (flipY)
transform.scale(1, -1);
transform.scale(scale.x(), scale.y());
transform.translate(-centerX, -centerY);
cache = cache.trans(transform);
@ -139,7 +158,7 @@ void LeafLayerWrapper::refresh(LayerWrapper* layer)
cache.clear();
if (wrappedElement != nullptr)
{
cache.addPath(wrappedElement->getPaintObject(this->styles));
cache.addPath(wrappedElement->getPaintObject(*this->styles));
}
LayerWrapper::refresh();
}
@ -245,6 +264,8 @@ QJsonObject LayerWrapper::toJson() const
QJsonObject transformJson;
transformJson.insert("offset", QJsonObject({ {"x", property.offset.x()}, {"y", property.offset.y()} }));
transformJson.insert("scale", QJsonObject({ {"x", property.scale.x()}, {"y", property.scale.y()} }));
transformJson.insert("flipX", property.flipX);
transformJson.insert("flipY", property.flipY);
transformJson.insert("rotation", property.rotation);
json.insert("transform", transformJson);
return json;
@ -271,7 +292,7 @@ QJsonObject LeafLayerWrapper::toJson() const
QJsonObject json = LayerWrapper::toJson();
json.insert("element", elementManager->getElementIndex(wrappedElement));
json.insert("is-folder", false);
json.insert("styles", styles.toJson());
json.insert("styles", styles->toJson());
return json;
}
@ -283,7 +304,7 @@ int FolderLayerWrapper::getReferencedBy()const
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)
// {
@ -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)
return;
LayerWrapper::paint(painter, transform, force);
transform = property.transform * transform;
//qDebug() << transform;
for (auto& child : children)
child->paint(painter, transform, force);
for (auto it = children.rbegin(); it != children.rend(); ++it)
(*it)->paint(painter, transform, force, styles | *this->styles);
}
void LeafLayerWrapper::paint(QPainter* painter, QTransform transform, bool force)
void LeafLayerWrapper::paint(QPainter* painter, QTransform transform, bool force, LayerStyleContainer styles)
{
if (hidden && !force)
return;
LayerWrapper::paint(painter, transform, force);
transform = property.transform * transform;
//qDebug() << transform;
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
{
return typeid(*this) == typeid(LeafLayerWrapper) && !referencingGroupElement();
return true;
//return typeid(*this) == typeid(LeafLayerWrapper) && !referencingGroupElement();
}
void LayerWrapper::paintVisualBounding(QPainter* painter) const
@ -450,7 +469,20 @@ void LayerWrapper::paintVisualBounding(QPainter* painter) const
layer = layer->parent;
}
painter->save();
painter->setTransform(transform);
painter->drawRect(cache.getBoundingRect());
auto rect = this->cache.getBoundingRect();
rect = transform.mapRect(rect);
painter->drawRect(rect);
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;
public:
bool needRefresh = true;
QTreeWidgetItem* qTreeWidgetItem;
bool selected;
bool hidden;
LayerStyleContainer* styles;
struct SimpleProperty
{
QString name = "";
QPointF scale = {1.0, 1.0};
QPointF offset = {0, 0};
double rotation = 0;
bool flipHorizontally = 0;
bool flipVertically = 0;
bool flipX = 0;
bool flipY = 0;
QTransform transform;
// TODO: ½«QPainterPath¸ÄΪBitmapPath
void setRotation(double newRotation);
void apply(PixelPath&cache);
} property;
virtual void setParent(FolderLayerWrapper*newParent);
@ -59,7 +62,7 @@ class LayerWrapper
FolderLayerWrapper*getParent() const; // invoke by manager, then invoke parent's applyStyles
LayerWrapper(QJsonObject json, FolderLayerWrapper*parent, ElementManager* elementManager=nullptr);
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
// virtual LayerWrapper *addChild() = 0; // Leaf Child Only
// virtual LayerWrapper *addParent() = 0; // Folder Parent Only
@ -77,6 +80,7 @@ class LayerWrapper
virtual bool referencingGroupElement() const;
virtual void paintVisualBounding(QPainter* painter) const;
bool canApplyStyles() const;
virtual void markNeedRefresh();
};
class FolderLayerWrapper : public LayerWrapper
@ -99,25 +103,25 @@ class FolderLayerWrapper : public LayerWrapper
QTreeWidgetItem* getQTreeItem() override;
QJsonObject toJson() const override;
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 refreshTreeItem() override;
size_t referencedCount(bool excludeSelf = false) const override;
bool deleteable(bool excludeSubTree = false) const override;
void markNeedRefresh() override;
};
class LeafLayerWrapper : public LayerWrapper
{
public:
GraphicElement *wrappedElement;
LayerStyleContainer styles;
public:
~LeafLayerWrapper();
void refresh(LayerWrapper* layer = nullptr) override;
LeafLayerWrapper(QJsonObject json, ElementManager *elementManager, FolderLayerWrapper*parent);
QJsonObject toJson() const override;
void paint(QPainter* painter, QTransform transform = QTransform(), bool force = false) override;
void paint(QPainter* painter, QTransform transform = QTransform(), bool force = false, LayerStyleContainer styles = LayerStyleContainer(LayerStyleContainer::TYPE_ALL)) override;
void collectDownReachable(std::set<LayerWrapper*>& reachable) override;
QTreeWidgetItem* getQTreeItem() override;
void refreshTreeItem() override;

View File

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

View File

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

View File

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

View File

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

View File

@ -3,6 +3,7 @@
#include <QMenu>
#include "./EditorWidgetComponent/LayerCreateWidget.h"
#include <QTimer>
#include <QKeyEvent>
LayerTreeWidget::LayerTreeWidget(QWidget *parent)
{
@ -58,14 +59,19 @@ void LayerTreeWidget::popMenu(const QPoint &pos)
auto dialog = new LayerCreateWidget(elementManager, dynamic_cast<FolderLayerWrapper*>(layer), this);
connect(dialog, &LayerCreateWidget::LayerInfoReturned, this, [this, layer](QJsonObject jsonObj) {
auto folderLayer = dynamic_cast<FolderLayerWrapper*>(layer);
LayerWrapper* newLayer;
std::shared_ptr<LayerWrapper> newLayer;
qDebug() << this->elementManager;
if(jsonObj.value("is-folder").toBool())
newLayer = new FolderLayerWrapper(jsonObj, this->elementManager, folderLayer);
if (jsonObj.value("is-folder").toBool())
{
newLayer = std::make_shared<FolderLayerWrapper>(jsonObj, this->elementManager, folderLayer);
folderLayer->addChild(newLayer);
}
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());
qDebug() << jsonObj<<"----------------------";
emit triggerCentralRefresh();
@ -109,6 +115,8 @@ void LayerTreeWidget::popMenu(const QPoint &pos)
});
}
}
menu.addAction(getPromoteUpAction());
menu.addAction(getPromoteDownAction());
}
menu.exec(mapToGlobal(pos));
}
@ -132,3 +140,181 @@ void LayerTreeWidget::onRenameEvent()
void LayerTreeWidget::refresh() {
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:
QTreeWidgetItem *selectedItem;
LayerWrapper *copiedItem;
void pushUpLayer();
void pushDownLayer();
QAction* getPromoteUpAction();
QAction* getPromoteDownAction();
void moveLayer(LayerWrapper* source, FolderLayerWrapper* parent);
public:
ElementManager* elementManager;
@ -17,6 +22,7 @@ class LayerTreeWidget : public QTreeWidget
void onRenameEvent();
void popMenu(const QPoint &pos);
void refresh();
void keyPressEvent(QKeyEvent* event) override;
// void mouseDoubleClickEvent(QMouseEvent *event) override;
// void onItemDoubleClicked(QTreeWidgetItem *item, int column = 0);

View File

@ -3,6 +3,7 @@
#include <QJsondocument>
#include "PainterPathUtil.h"
#include <queue>
#include <ranges>
#include <QFileInfo>
using Renderer::Painting;
@ -13,12 +14,12 @@ using std::max;
using std::shared_ptr;
using std::make_shared;
using std::min;
using std::queue;
struct LayerNode {
LayerWrapper* nowLayer;
QTransform transfrom;
};
int PaintingUtil::zIndexCount = 0;
void PaintingUtil::clear() {
zIndexCount = 0;
}
QJsonObject PaintingUtil::readJsonFile(QString jsonFilePath) {
QFile jsonFile(jsonFilePath);
@ -32,26 +33,30 @@ QJsonObject PaintingUtil::readJsonFile(QString jsonFilePath) {
}
Painting PaintingUtil::transfromToPainting(QString jsonFilePath) {
Painting painting;
glm::bvec2 flip(0, 0);
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<LayerManager> layerManager = make_shared<LayerManager>(jsonObj, elementManager.get());
//qDebug() << elementManager->toJson();
//qDebug() << layerManager->toJson();
//qDebug() << ((SimpleElement*)((LeafLayerWrapper*)((FolderLayerWrapper*)((FolderLayerWrapper*)layerManager->getRoot())->children[0].get())->children[0].get())->wrappedElement)->painterPath;
//qDebug() << ((LeafLayerWrapper*)((FolderLayerWrapper*)((FolderLayerWrapper*)layerManager->getRoot())->children[0].get())->children[0].get())->getCache().painterPath;
//qDebug() << elementManager->toJson();
//qDebug() << layerManager->toJson();
//qDebug() << ((SimpleElement*)((LeafLayerWrapper*)((FolderLayerWrapper*)((FolderLayerWrapper*)layerManager->getRoot())->children[0].get())->children[0].get())->wrappedElement)->painterPath;
//qDebug() << ((LeafLayerWrapper*)((FolderLayerWrapper*)((FolderLayerWrapper*)layerManager->getRoot())->children[0].get())->children[0].get())->getCache().painterPath;
queue<LayerNode> layerQueue;
LayerWrapper* root = layerManager->getRoot();
root->getCache();
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);
layerQueue.push({ root, root->property.transform });
while (!layerQueue.empty()) {
//layerQueue.push({ root, QTransform()});
/*while (!layerQueue.empty()) {
auto layerNode = layerQueue.front();
layerQueue.pop();
FolderLayerWrapper* nowLayer = handleLayerWrapper(layerNode.nowLayer, layerNode.transfrom, painting);
@ -60,13 +65,14 @@ Painting PaintingUtil::transfromToPainting(QString jsonFilePath) {
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);
qDebug() << nowLayer;
transform = nowLayer->property.transform * transform;
@ -74,62 +80,86 @@ FolderLayerWrapper* PaintingUtil::handleLayerWrapper(LayerWrapper* nowLayer, QTr
GroupElement* wrapperElement = dynamic_cast<GroupElement*>(leafLayer->wrappedElement);
if (wrapperElement != nullptr) {
transform = wrapperElement->sourceLayer->property.transform * transform;
return wrapperElement->sourceLayer;
handleLayerWrapper(wrapperElement->sourceLayer, transform, painting, contourMap);
return;
}
PixelPath pixelPath = nowLayer->getCache();
QPainterPath painterPath = pixelPath.getPainterPath();
QPainterPath painterPath = pixelPath.originPath;
QRectF bound = painterPath.boundingRect();
//qDebug() << leafLayer<<"------" << painterPath;
// transform to -1£¬ 1
QTransform trans;
double maxLen = std::max(bound.width(), bound.height());
qDebug() << maxLen << bound;
double maxLen = std::max(bound.width(), bound.height()) * 1.00001;
//qDebug() << maxLen << bound;
trans.scale(1 / maxLen, 1 / maxLen);
trans.translate(-bound.center().x(), -bound.center().y());
painterPath = trans.map(painterPath);
shared_ptr<vector<vector<Renderer::Point> >> contour = std::make_shared<vector<vector<Renderer::Point> >>(PainterPathUtil::transformToLines(painterPath));
QSize screenSize = QSize(1024, 1024);
//std::shared_ptr<LayerWrapper> keyLayerPtr(nowLayer);
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;
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();
Renderer::BaseElement element;
element.contour = contour;
for (auto& baseStyle : baseStyles) {
//++zIndexCount;
auto baseStyles = leafLayer->styles->toBaseStyles();
qDebug() << baseStyles.size();
for (auto& baseStyle : std::views::reverse(baseStyles)) {
double lineWidth = 0;
if (baseStyle.material->type() == Renderer::MaterialStyleType::kStroke) {
auto material = std::static_pointer_cast<MaterialStyleStroke>(baseStyle.material);
material->halfWidth /= maxLen;
lineWidth = material->halfWidth;
qDebug() << material->halfWidth;
}
QPainterPathStroker stroker;
stroker.setWidth(lineWidth * 2);
stroker.setCapStyle(Qt::RoundCap);
stroker.setJoinStyle(Qt::RoundJoin);
QPainterPath strokePath = stroker.createStroke(painterPath);
auto rect = transform.map(strokePath).boundingRect();
elementTransform.bound = glm::vec4(rect.x(), rect.y(), rect.x() + rect.width(), rect.y() + rect.height());
qDebug() << elementTransform.bound.x << elementTransform.bound.y << elementTransform.bound.z << elementTransform.bound.z;
transform = transform.inverted();
elementTransform.transform = glm::mat3x2(
transform.m11(), transform.m12(), transform.m21(),
transform.m22(), transform.m31(), transform.m32()
);
//qDebug() << transform;
elementTransform.zIndex = 0;
QPainterPath path;
element.style = baseStyle.material;
painting.addElement(element, elementTransform);
std::shared_ptr<MaterialStyle> material;
if (baseStyle.material->type() == Renderer::MaterialStyleType::kStroke) {
qDebug() << "MaterialStyleType::kStroke";
std::shared_ptr copy = baseStyle.material->clone();
std::static_pointer_cast<MaterialStyleStroke>(copy)->halfWidth /= maxLen;
lineWidth = std::static_pointer_cast<MaterialStyleStroke>(copy)->halfWidth;
material = copy;
QPainterPathStroker stroker;
stroker.setWidth(lineWidth * 2);
stroker.setCapStyle(Qt::RoundCap);
stroker.setJoinStyle(Qt::RoundJoin);
path = stroker.createStroke(painterPath);
}
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(-1,-1,1,1);
//qDebug() << elementTransform.bound.x << elementTransform.bound.y << elementTransform.bound.z << elementTransform.bound.z;
elementTransform.transform = glm::mat3x2(
transformInverted.m11(), transformInverted.m12(), transformInverted.m21(),
transformInverted.m22(), transformInverted.m31(), transformInverted.m32()
);
elementTransform.zIndex = ++zIndexCount;
painting.addElement(BaseElement{ contour, material }, elementTransform);
}
return nullptr;
return;
}
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 <QJsonObject>
#include <ElementManager.h>
#include <map>
class PaintingUtil
{
using Contour = std::shared_ptr<vector<vector<Renderer::Point> > >;
private:
static int zIndexCount;
static void clear();
//static const double pi;
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);
public:
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;
Painting painting;
path = "../test.json";
if (auto file = QFileInfo(QString(path.c_str())); file.isFile())
if (auto file = QFileInfo(directory.filePath(path.c_str())); file.isFile())
painting = PaintingUtil::transfromToPainting(file.filePath());
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}),
};
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[1], ElementTransform{ glm::vec2(-0.45, 0.45), glm::vec2(0.5,0.5) / 2.f, 0, glm::bvec2(false), 0 });
//painting.addElement(*element[2], ElementTransform{ glm::vec2(0.50,-0.45), glm::vec2(0.6,0.7) / 2.f, 0, glm::bvec2(false), 0 });
}
else if (path == "1.json")
else if (path == "1.json" || true)
{
//painting.backgroundColor = QColor(196, 81, 35);
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();
//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[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 });

View File

@ -413,6 +413,8 @@ int CubicBezierSignedDistance::solve_quadric(dvec2 coeffs, dvec2& roots) {
return 2;
}
else
return 0;
}
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;
}
Renderer::MaterialStyleStroke::MaterialStyleStroke(float halfWidth, StrokeType strokeType, StrokeEndType endType, std::shared_ptr<MaterialStroke> materialStroke)
: halfWidth(halfWidth), strokeType(strokeType), endType(endType), 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), widthMap(widthMap)
{
}
@ -89,7 +89,9 @@ MaterialStyleType Renderer::MaterialStyleStroke::type() 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();
glm::vec4 head = glm::unpackUnorm4x8(glm::floatBitsToUint(encoded[0]));
head.b = (float)strokeType / 10. + (float)endType / 100.;

View File

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

View File

@ -9,7 +9,7 @@ using namespace Renderer;
constexpr int kMaxLineCount = 20;
Painting::Painting(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());
}
for (auto & i : elementTransformPool)
for (auto& i : elementTransformPool)
elementTransform.emplace_back(i.first);
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[4], elementIndex.size() * sizeof(elementIndex[0]), elementIndex.data(), GL_STATIC_READ);
glFunc->glNamedBufferData(buffers[5], elementData.size() * sizeof(elementData[0]), elementData.data(), GL_STATIC_READ);
glm::vec3 color(backgroundColor.redF(), backgroundColor.greenF(), backgroundColor.blueF());
glFunc->glNamedBufferData(buffers[6], sizeof(glm::vec3), &color, GL_STATIC_READ);
GLfloat backgroundBuffer[] = { (GLfloat)background.color.redF(), (GLfloat)background.color.greenF(), (GLfloat)background.color.blueF(), background.metallicF(), background.roughnessF() };
glFunc->glNamedBufferData(buffers[6], 5 * sizeof(GLfloat), backgroundBuffer, GL_STATIC_READ);
}
GLuint Renderer::Painting::getElementCount()

View File

@ -54,9 +54,10 @@ namespace Renderer
std::vector<GLfloat> elementData;
int paintingId = 0;
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 generateBuffers(QOpenGLFunctions_4_5_Core* glFunc);
GLuint getElementCount();

View File

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

View File

@ -8,29 +8,14 @@ Renderer::RendererWidget::RendererWidget(QWidget* parent)
{
ui.setupUi(this);
FluentMenu* menu = new FluentMenu(this);
auto openAction = new QAction(QStringLiteral("´ò¿ª"), menu);
auto saveAction = new QAction(QStringLiteral("±£´æ"), menu);
auto testAction = new QAction(QStringLiteral("²âÊÔ"), menu);
auto test2Action = new QAction(QStringLiteral("²âÊÔ2"), menu);
menu->addAction(openAction);
menu->addAction(saveAction);
menu->addAction(testAction);
menu->addAction(test2Action);
ui.openButton->setHaloVisible(false);
ui.openButton->setOverlayStyle(::Material::TintedOverlay);
ui.openButton->setCheckable(true);
QObject::connect(menu, &QMenu::aboutToShow, [&]() {
ui.openButton->setChecked(true);
});
QObject::connect(menu, &QMenu::aboutToHide, [&]() {
ui.openButton->setChecked(false);
});
QObject::connect(ui.openButton, &QPushButton::clicked, [&, menu]() {
menu->exec(ui.openButton->mapToGlobal(QPoint(0, ui.openButton->height())));
});
auto openAction = new QAction(QStringLiteral("´ò¿ª"), ui.openButton);
auto saveAction = new QAction(QStringLiteral("±£´æ"), ui.openButton);
auto testAction = new QAction(QStringLiteral("²âÊÔ"), ui.openButton);
auto test2Action = new QAction(QStringLiteral("²âÊÔ2"), ui.openButton);
ui.openButton->addMenuAction(openAction);
ui.openButton->addMenuAction(saveAction);
ui.openButton->addMenuAction(testAction);
ui.openButton->addMenuAction(test2Action);
QObject::connect(ui.horizontalSlider, &QSlider::valueChanged,
ui.openGLWidget, &Renderer::RendererGLWidget::setMainLightPitch);

View File

@ -206,9 +206,9 @@ namespace UnitTest
}
TEST_METHOD(TestBothSidesClosed)
{
QPainterPath closedPath;
SvgFileLoader().loadSvgFile("../../svg/4_L0.svg", closedPath);
closedPath = QTransform::fromScale(5, 5).map(closedPath);
QPainterPath testPath;
SvgFileLoader().loadSvgFile("../../svg/4_L0.svg", testPath);
testPath = QTransform::fromScale(5, 5).map(testPath);
QApplication a(argc, argv);
class StyleStrokeRadialGradient : public Renderer::ElementStyle
{
@ -220,11 +220,60 @@ namespace UnitTest
{1.00, Material{QColor(58,64,151)}}
};
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))) };
}
} 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();
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>
<QtMocFileName Condition="'$(Configuration)|$(Platform)'=='Release|x64'">%(Filename).moc</QtMocFileName>
</ClCompile>
<ClCompile Include="LayerStyleTest.cpp" />
<ClCompile Include="PaintingTest.cpp">
<DynamicSource Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">input</DynamicSource>
<QtMocFileName Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">%(Filename).moc</QtMocFileName>

View File

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

View File

@ -343,24 +343,6 @@
</QtMoc>
<ClInclude Include="qtmaterialtoggle_p.h" />
</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>
<None Include="..\fonts\Roboto\Roboto-Black.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">
<title>4_L0的副本</title>
<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"/>
<?xml version="1.0" encoding="utf-8"?>
<!-- Generator: Adobe Illustrator 25.0.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
<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>

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": [
{
"data": {
"include": "../svg/2.svg"
"include": "/svg/2.svg"
},
"name": "ababa",
"type": "svg-file"
@ -17,16 +17,16 @@
},
{
"data": {
"include": "../svg/0.svg"
"include": "/svg/0.svg"
},
"name": "ababa2",
"type": "svg-file"
},
{
"data": {
"include": "D:/BigC2022/temp/ArchitectureColoredPainting/svg/3.svg"
"include": "/svg/4_L0.svg"
},
"name": "3.svg",
"name": "4_L0.svg",
"type": "svg-file"
}
],
@ -36,46 +36,22 @@
"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,
"is-folder": false,
"name": "Leaf2",
"styles": [
{
"enableEachSideIndependent": false,
"left": "AAAAQAEAIZwAf////1UA/w==",
"right": "AADgQAEACJwAf///AFqe/w==",
"enableEachSideIndependent": true,
"left": "AABAQAEAIZwAf///AFqe/w==",
"right": "AABAQAAACJw=",
"type": "stroke"
}
],
"transform": {
"offset": {
"x": 150,
"y": 0
"x": 501,
"y": -3
},
"rotation": 0,
"scale": {
@ -83,30 +59,6 @@
"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,
@ -114,8 +66,8 @@
"referenced-by": 1,
"transform": {
"offset": {
"x": 50,
"y": 50
"x": 503,
"y": 36
},
"rotation": 0,
"scale": {
@ -127,15 +79,73 @@
{
"element": 1,
"is-folder": false,
"name": "ReferencingGroupLayer",
"name": "子图层-2",
"styles": [
],
"transform": {
"offset": {
"x": 100,
"y": 0
"x": 1,
"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": {
"x": 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 相同时,移动不安全;其余情况均安全。