Compare commits
40 Commits
a84c8cb752
...
b2e49dae17
Author | SHA1 | Date |
---|---|---|
wuyize | b2e49dae17 | |
karlis | a3d39b32e5 | |
wuyize | a55a38385e | |
karlis | 5d5ea2307f | |
karlis | aa9743d4e7 | |
ArgonarioD | 214fa0f82f | |
karlis | 483afb9fb4 | |
ArgonarioD | 2f9b988dac | |
karlis | ba4be72918 | |
karlis | b739f4506b | |
karlis | 578fd5da8e | |
karlis | f175e50b7b | |
karlis | beee94ebe0 | |
ArgonarioD | 62d887aa07 | |
wuyize | 512181f6a3 | |
wuyize | 60c7695545 | |
karlis | 7e0e07292f | |
karlis | b2f37cc4eb | |
karlis | 0b63e8e737 | |
yang.yongquan | 56777c1f00 | |
ArgonarioD | 2e7d20d0d7 | |
karlis | 3edca0287b | |
karlis | d1aeb28b97 | |
wuyize | 492f0a12d7 | |
wuyize | 88dc039fe8 | |
karlis | c77114b7f3 | |
karlis | 2b5598a81e | |
wuyize | 3a693de9fe | |
karlis | 291d112d11 | |
karlis | fff1ce3424 | |
karlis | 9057500da8 | |
karlis | 4d16b47edd | |
karlis | 5fbf46faf2 | |
karlis | 16671ae71f | |
yang.yongquan | 182284c60f | |
yang.yongquan | 93537954a9 | |
wuyize | d3cf84f479 | |
ArgonarioD | 2581624388 | |
wuyize | 35e7648206 | |
yang.yongquan | 793402e3c5 |
581
4_L0.json
581
4_L0.json
|
@ -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,41 +44,47 @@
|
|||
{
|
||||
"children": [
|
||||
{
|
||||
"element": 3,
|
||||
"children": [
|
||||
{
|
||||
"element": 2,
|
||||
"is-folder": false,
|
||||
"name": "4_L0",
|
||||
"styles": [
|
||||
{
|
||||
"material": "AH8A/2pkiv8=",
|
||||
"material": "AH8A/1JOff8=",
|
||||
"type": "fill"
|
||||
},
|
||||
{
|
||||
"enableEachSideIndependent": true,
|
||||
"left": "AADAQAEAJJwAf///9dKG/w==",
|
||||
"left": "AAAAQAEAJJwAf///9c19/w==",
|
||||
"right": "AADgQAAACpw=",
|
||||
"type": "stroke"
|
||||
}
|
||||
],
|
||||
"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
|
||||
}
|
||||
}
|
||||
}
|
||||
],
|
||||
"is-folder": true,
|
||||
"name": "GroupFolderExample",
|
||||
"referenced-by": 1,
|
||||
"name": "工",
|
||||
"referenced-by": 3,
|
||||
"transform": {
|
||||
"filpX": false,
|
||||
"filpY": false,
|
||||
"offset": {
|
||||
"x": 50,
|
||||
"y": 50
|
||||
"x": 0,
|
||||
"y": 0
|
||||
},
|
||||
"rotation": 0,
|
||||
"scale": {
|
||||
|
@ -81,87 +94,17 @@
|
|||
}
|
||||
},
|
||||
{
|
||||
"element": 1,
|
||||
"element": 3,
|
||||
"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",
|
||||
"name": "1",
|
||||
"styles": [
|
||||
],
|
||||
"transform": {
|
||||
"filpX": false,
|
||||
"filpY": false,
|
||||
"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
|
||||
"y": -1
|
||||
},
|
||||
"rotation": 90,
|
||||
"scale": {
|
||||
|
@ -171,51 +114,17 @@
|
|||
}
|
||||
},
|
||||
{
|
||||
"element": 1,
|
||||
"element": 3,
|
||||
"is-folder": false,
|
||||
"name": "bbbb3",
|
||||
"name": "2",
|
||||
"styles": [
|
||||
],
|
||||
"transform": {
|
||||
"filpX": false,
|
||||
"filpY": false,
|
||||
"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
|
||||
"x": 104,
|
||||
"y": 100
|
||||
},
|
||||
"rotation": 0,
|
||||
"scale": {
|
||||
|
@ -225,15 +134,17 @@
|
|||
}
|
||||
},
|
||||
{
|
||||
"element": 1,
|
||||
"element": 3,
|
||||
"is-folder": false,
|
||||
"name": "cccc2",
|
||||
"name": "3",
|
||||
"styles": [
|
||||
],
|
||||
"transform": {
|
||||
"filpX": false,
|
||||
"filpY": false,
|
||||
"offset": {
|
||||
"x": 361,
|
||||
"y": -70
|
||||
"x": 3,
|
||||
"y": 102
|
||||
},
|
||||
"rotation": 90,
|
||||
"scale": {
|
||||
|
@ -241,104 +152,14 @@
|
|||
"y": 1
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"element": 1,
|
||||
"is-folder": false,
|
||||
"name": "cccc3",
|
||||
"styles": [
|
||||
],
|
||||
"transform": {
|
||||
"offset": {
|
||||
"x": 273,
|
||||
"y": -211
|
||||
},
|
||||
"rotation": 180,
|
||||
"scale": {
|
||||
"x": 1,
|
||||
"y": 1
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"element": 1,
|
||||
"is-folder": false,
|
||||
"name": "cccc4",
|
||||
"styles": [
|
||||
],
|
||||
"transform": {
|
||||
"offset": {
|
||||
"x": 574,
|
||||
"y": 198
|
||||
},
|
||||
"rotation": 270,
|
||||
"scale": {
|
||||
"x": 1,
|
||||
"y": 1
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"element": 1,
|
||||
"is-folder": false,
|
||||
"name": "dddd1",
|
||||
"styles": [
|
||||
],
|
||||
"transform": {
|
||||
"offset": {
|
||||
"x": 33,
|
||||
"y": -231
|
||||
},
|
||||
"rotation": 0,
|
||||
"scale": {
|
||||
"x": 1,
|
||||
"y": 1
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"element": 1,
|
||||
"is-folder": false,
|
||||
"name": "dddd2",
|
||||
"styles": [
|
||||
],
|
||||
"transform": {
|
||||
"offset": {
|
||||
"x": 55,
|
||||
"y": 352
|
||||
},
|
||||
"rotation": 90,
|
||||
"scale": {
|
||||
"x": 1,
|
||||
"y": 1
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"element": 1,
|
||||
"is-folder": false,
|
||||
"name": "dddd3",
|
||||
"styles": [
|
||||
],
|
||||
"transform": {
|
||||
"offset": {
|
||||
"x": -40,
|
||||
"y": 220
|
||||
},
|
||||
"rotation": 180,
|
||||
"scale": {
|
||||
"x": 1,
|
||||
"y": 1
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"element": 1,
|
||||
"is-folder": false,
|
||||
"name": "dddd4",
|
||||
"styles": [
|
||||
}
|
||||
],
|
||||
"is-folder": true,
|
||||
"name": "子图层-1",
|
||||
"referenced-by": 4,
|
||||
"transform": {
|
||||
"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
|
||||
}
|
||||
}
|
||||
},
|
||||
|
|
|
@ -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" />
|
||||
|
|
|
@ -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">
|
||||
|
|
|
@ -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/>
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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>
|
||||
|
@ -88,35 +88,11 @@
|
|||
</size>
|
||||
</property>
|
||||
<property name="maximumSize">
|
||||
<size>
|
||||
<width>10241024</width>
|
||||
<height>10241024</height>
|
||||
</size>
|
||||
</property>
|
||||
<layout class="QHBoxLayout" name="horizontalLayout_2">
|
||||
<item>
|
||||
<widget class="PreviewWindow" name="Preview">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Fixed" vsizetype="Fixed">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="minimumSize">
|
||||
<size>
|
||||
<width>1024</width>
|
||||
<height>1024</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="maximumSize">
|
||||
<size>
|
||||
<width>1024</width>
|
||||
<height>1024</height>
|
||||
</size>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
</widget>
|
||||
</item>
|
||||
|
@ -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>
|
||||
|
|
|
@ -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/>
|
||||
|
|
|
@ -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: {
|
||||
|
@ -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];
|
||||
|
@ -1077,44 +1082,39 @@ void main()
|
|||
{
|
||||
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];
|
||||
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,7 +1146,7 @@ void main()
|
|||
}
|
||||
else
|
||||
{
|
||||
if(endType == 4)
|
||||
if (endType == 4 /*StrokeEndType::kClosed*/)
|
||||
{
|
||||
// onVeryEnd = false;
|
||||
tangentBeginNext = tangentFirstBegin;
|
||||
|
@ -1155,23 +1155,27 @@ void main()
|
|||
onVeryEnd = true;
|
||||
}
|
||||
|
||||
|
||||
float d = cubic_bezier_dis(localUV, p[0], p[1], p[2], p[3], true);
|
||||
if (d <= strokeWidth)
|
||||
{
|
||||
bool onBegin = distance(localUV, p[0]) <= strokeWidth;
|
||||
bool onEnd = distance(localUV, p[3]) <= strokeWidth;
|
||||
vec2 tangentBegin;
|
||||
vec2 tangentEnd;
|
||||
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]);
|
||||
|
||||
float t;
|
||||
float d = cubic_bezier_dis(localUV, p[0], p[1], p[2], p[3], true, t);
|
||||
float localWidth = strokeWidth; // * mix(lengthRate.x, lengthRate.y, t);
|
||||
if (d <= localWidth)
|
||||
{
|
||||
bool onBegin = t<1e-5;
|
||||
bool onEnd = t>1-1e-5;
|
||||
//bool onBegin = true;
|
||||
//bool onEnd = true;
|
||||
vec2 tangentBegin;
|
||||
if (p[0] != p[1])
|
||||
tangentBegin = normalize(p[0] - p[1]);
|
||||
else
|
||||
tangentBegin = normalize(p[0] - p[2]);
|
||||
|
||||
bool hit = d < minDistance;
|
||||
if (onBegin)
|
||||
hit = hit &&
|
||||
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
|
@ -1369,25 +1399,26 @@ void main()
|
|||
debugBVH.g += 0.3;
|
||||
|
||||
if (leftChild >= bvhLength)
|
||||
{
|
||||
uint elementZIndex = bvhChildren[index].y >> 18;
|
||||
if (elementZIndex <= zIndex)
|
||||
{
|
||||
uint transformIndex = leftChild - 0x80000000;
|
||||
uint zIndex = bvhChildren[index].y >> 18;
|
||||
uint elementIndex = bvhChildren[index].y - zIndex;
|
||||
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;
|
||||
(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))
|
||||
if (drawElement(elementIndex, localUV, elementColor, elementMetallicRoughness, debugBVH))
|
||||
{
|
||||
color = vec4(elementColor, zIndex);
|
||||
zIndex = elementZIndex;
|
||||
color = vec4(elementColor, elementZIndex);
|
||||
metallicRoughness = elementMetallicRoughness;
|
||||
}
|
||||
//if(elementIndex == 1 && transformIndex==1)
|
||||
// color = vec4(1,1,0,1);
|
||||
|
||||
}
|
||||
index = bvhLength;
|
||||
}
|
||||
else
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
};
|
||||
|
||||
|
|
|
@ -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;
|
||||
}
|
|
@ -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);
|
||||
};
|
||||
|
|
@ -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);
|
||||
|
||||
|
|
|
@ -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
|
||||
);
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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);
|
||||
};
|
||||
|
|
@ -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());
|
||||
});
|
||||
}
|
||||
|
||||
|
|
|
@ -17,17 +17,17 @@ EditorWidgetItem::EditorWidgetItem(QString filePath,QWidget *parent) : QWidget(p
|
|||
treeWidget = ui.LayerTree;
|
||||
tabWidget = ui.DisplayTab;
|
||||
this->filePath = filePath;
|
||||
elementInfoDisplayWidget = ui.ElementDisplay;
|
||||
layerInfoDisplayWidget = dynamic_cast<InfoDisplayWidget*>(tabWidget->widget(0));
|
||||
elementInfoDisplayWidget = dynamic_cast<ElementPoolWidget *>(tabWidget->widget(1));
|
||||
editorSettingWidget = dynamic_cast<EditorSettingWidget*>(tabWidget->widget(2));
|
||||
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);
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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();
|
||||
});
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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);
|
||||
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
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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())
|
||||
{
|
||||
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();
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
|
||||
/**
|
||||
* 需要在每次更改后手动调用
|
||||
|
|
|
@ -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();
|
||||
}
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -51,7 +51,7 @@ 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->setWindow(0, 0, logicalSize.width() * devicePixelRatioF(), logicalSize.height() * devicePixelRatioF());
|
||||
painter->setRenderHint(QPainter::Antialiasing);
|
||||
painter->setRenderHint(QPainter::HighQualityAntialiasing);
|
||||
layerManager->paint(painter, this->size(), currentLayer);
|
||||
|
@ -106,7 +106,7 @@ void PreviewWindow::mouseMoveEvent(QMouseEvent* event)
|
|||
else if (event->buttons() & Qt::RightButton) {
|
||||
// 如果按下的是右键,那么旋转图形
|
||||
qreal angle = dx;
|
||||
currentLayer->property.rotation += angle;
|
||||
currentLayer->property.setRotation(currentLayer->property.rotation + angle);
|
||||
}
|
||||
auto layer = currentLayer;
|
||||
while (layer != nullptr)
|
||||
|
|
|
@ -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();
|
||||
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) {
|
||||
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) {
|
||||
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) {
|
||||
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) {
|
||||
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();
|
||||
});
|
||||
|
||||
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();
|
||||
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();
|
||||
});
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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();
|
||||
|
||||
};
|
||||
|
|
|
@ -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);
|
||||
{
|
||||
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();
|
||||
}
|
|
@ -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);
|
||||
|
||||
|
|
|
@ -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;
|
||||
QPainterPath path;
|
||||
|
||||
std::shared_ptr<MaterialStyle> material;
|
||||
|
||||
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;
|
||||
}
|
||||
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);
|
||||
QPainterPath strokePath = stroker.createStroke(painterPath);
|
||||
auto rect = transform.map(strokePath).boundingRect();
|
||||
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());
|
||||
qDebug() << elementTransform.bound.x << elementTransform.bound.y << elementTransform.bound.z << elementTransform.bound.z;
|
||||
transform = transform.inverted();
|
||||
//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(
|
||||
transform.m11(), transform.m12(), transform.m21(),
|
||||
transform.m22(), transform.m31(), transform.m32()
|
||||
transformInverted.m11(), transformInverted.m12(), transformInverted.m21(),
|
||||
transformInverted.m22(), transformInverted.m31(), transformInverted.m32()
|
||||
);
|
||||
//qDebug() << transform;
|
||||
elementTransform.zIndex = 0;
|
||||
|
||||
element.style = baseStyle.material;
|
||||
painting.addElement(element, elementTransform);
|
||||
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;
|
||||
}
|
|
@ -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);
|
||||
|
|
|
@ -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);
|
||||
}
|
|
@ -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;
|
||||
};
|
|
@ -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 });
|
||||
|
|
|
@ -413,6 +413,8 @@ int CubicBezierSignedDistance::solve_quadric(dvec2 coeffs, dvec2& roots) {
|
|||
|
||||
return 2;
|
||||
}
|
||||
else
|
||||
return 0;
|
||||
}
|
||||
|
||||
void CubicBezierSignedDistance::sort_roots3(dvec3& roots) {
|
||||
|
|
|
@ -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.;
|
||||
|
|
|
@ -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;
|
||||
};
|
||||
}
|
||||
|
|
|
@ -9,7 +9,7 @@ using namespace Renderer;
|
|||
|
||||
constexpr int kMaxLineCount = 20;
|
||||
|
||||
Painting::Painting(QColor backgroundColor) : backgroundColor(backgroundColor)
|
||||
Painting::Painting(Material background) : background(background)
|
||||
{
|
||||
}
|
||||
|
||||
|
@ -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()
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
|
|
|
@ -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());
|
||||
}
|
||||
};
|
||||
}
|
|
@ -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>
|
||||
|
|
|
@ -51,5 +51,8 @@
|
|||
<ClCompile Include="StyleTest.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="LayerStyleTest.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
</ItemGroup>
|
||||
</Project>
|
|
@ -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>NUL >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>NUL >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" />
|
||||
|
|
13
svg/4_L0.svg
13
svg/4_L0.svg
|
@ -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 |
|
@ -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
136
test.json
|
@ -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
|
||||
|
|
|
@ -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 相同时,移动不安全;其余情况均安全。
|
Loading…
Reference in New Issue