Compare commits

..

No commits in common. "b2e49dae17b34cca0401db33f3182c949751fb92" and "a84c8cb7522fe4770521d8c2d12ed7876c578ce7" have entirely different histories.

58 changed files with 1256 additions and 2052 deletions

705
4_L0.json
View File

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

View File

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

View File

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

View File

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

View File

@ -41,7 +41,7 @@
<number>0</number> <number>0</number>
</property> </property>
<item> <item>
<layout class="QHBoxLayout" name="horizontalLayout_2" stretch="1,1,0"> <layout class="QHBoxLayout" name="horizontalLayout_2" stretch="1,1,1,1,1,0">
<property name="spacing"> <property name="spacing">
<number>0</number> <number>0</number>
</property> </property>
@ -49,7 +49,7 @@
<number>0</number> <number>0</number>
</property> </property>
<item> <item>
<widget class="FluentMenuButton" name="fileMenuButton"> <widget class="QPushButton" name="createButton">
<property name="minimumSize"> <property name="minimumSize">
<size> <size>
<width>80</width> <width>80</width>
@ -63,12 +63,12 @@
</size> </size>
</property> </property>
<property name="text"> <property name="text">
<string>文件</string> <string>新建</string>
</property> </property>
</widget> </widget>
</item> </item>
<item> <item>
<widget class="FluentMenuButton" name="projectMenuButton"> <widget class="QPushButton" name="openButton">
<property name="minimumSize"> <property name="minimumSize">
<size> <size>
<width>80</width> <width>80</width>
@ -82,7 +82,70 @@
</size> </size>
</property> </property>
<property name="text"> <property name="text">
<string>项目</string> <string>打开</string>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="saveButton">
<property name="minimumSize">
<size>
<width>80</width>
<height>40</height>
</size>
</property>
<property name="maximumSize">
<size>
<width>80</width>
<height>16777215</height>
</size>
</property>
<property name="text">
<string>保存</string>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="saveAsButton">
<property name="minimumSize">
<size>
<width>80</width>
<height>40</height>
</size>
</property>
<property name="maximumSize">
<size>
<width>80</width>
<height>16777215</height>
</size>
</property>
<property name="text">
<string>另存为</string>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="closeButton">
<property name="sizePolicy">
<sizepolicy hsizetype="Fixed" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="minimumSize">
<size>
<width>80</width>
<height>40</height>
</size>
</property>
<property name="maximumSize">
<size>
<width>80</width>
<height>16777215</height>
</size>
</property>
<property name="text">
<string>关闭</string>
</property> </property>
</widget> </widget>
</item> </item>
@ -115,9 +178,6 @@
<property name="currentIndex"> <property name="currentIndex">
<number>0</number> <number>0</number>
</property> </property>
<property name="tabsClosable">
<bool>true</bool>
</property>
<widget class="QWidget" name="tab"> <widget class="QWidget" name="tab">
<attribute name="title"> <attribute name="title">
<string>Tab 1</string> <string>Tab 1</string>
@ -134,13 +194,6 @@
</item> </item>
</layout> </layout>
</widget> </widget>
<customwidgets>
<customwidget>
<class>FluentMenuButton</class>
<extends>QPushButton</extends>
<header location="global">../FluentMenuButton.h</header>
</customwidget>
</customwidgets>
<resources/> <resources/>
<connections/> <connections/>
</ui> </ui>

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

Before

Width:  |  Height:  |  Size: 781 B

After

Width:  |  Height:  |  Size: 480 B

View File

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

Before

Width:  |  Height:  |  Size: 310 B

138
test.json
View File

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

View File

@ -1,60 +0,0 @@
# 图层删除
当删除一个节点时,该节点一定满足且只满足以下条件中的一条:
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 相同时,移动不安全;其余情况均安全。