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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -25,10 +25,10 @@ FolderLayerWrapper*LayerWrapper::getParent() const
PixelPath LayerWrapper::getCache(LayerWrapper* selectedLayer)
{
if (needRefresh)
this->refresh(selectedLayer);
if (selectedLayer == this)
{
this->refresh(selectedLayer);
needRefresh = false;
this->cache.highLight();
}
return cache;
}
@ -46,9 +46,6 @@ LayerWrapper::LayerWrapper(QJsonObject json, FolderLayerWrapper*parent, ElementM
property.scale = {transformJson.value("scale").toObject().value("x").toDouble(),
transformJson.value("scale").toObject().value("y").toDouble()};
property.rotation = {transformJson.value("rotation").toDouble()};
property.flipX = { transformJson.value("flipX").toBool() };
qDebug() << property.flipX;
property.flipY = { transformJson.value("flipY").toBool() };
selected = false;
hidden = false;
}
@ -59,7 +56,6 @@ FolderLayerWrapper::FolderLayerWrapper(QJsonObject json, ElementManager *element
qDebug() << json.value("name").toString() << " " << this;
QJsonArray childrenJson = json.value("children").toArray();
QJsonValue referencedJson = json.value("referenced-by");
styles = new LayerStyleContainer(LayerStyleContainer::TYPE_ALL);
if (!referencedJson.isNull())
{
auto p = reinterpret_cast<GroupElement *>(elementManager->getElementById(referencedJson.toInt()));
@ -80,11 +76,9 @@ FolderLayerWrapper::FolderLayerWrapper(QJsonObject json, ElementManager *element
LeafLayerWrapper::LeafLayerWrapper(QJsonObject json, ElementManager* elementManager, FolderLayerWrapper* parent)
: LayerWrapper(json, parent, elementManager),
wrappedElement(elementManager->getElementById(json.value("element").toInt()))
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;
if(wrappedElement != nullptr)
wrappedElement->referencedCount++;
@ -96,15 +90,6 @@ LeafLayerWrapper::~LeafLayerWrapper()
wrappedElement->referencedCount--;
}
void LayerWrapper::SimpleProperty::setRotation(double newRotation)
{
if (newRotation <= -360 || 360 <= newRotation)
{
newRotation -= static_cast<int>(std::round(newRotation)) / 360 * 360;
}
rotation = newRotation;
}
void LayerWrapper::SimpleProperty::apply(PixelPath&cache)
{
transform.reset();
@ -115,10 +100,6 @@ void LayerWrapper::SimpleProperty::apply(PixelPath&cache)
transform.translate(centerX, centerY);
transform.translate(offset.x(), offset.y());
transform.rotate(rotation);
if (flipX)
transform.scale(-1, 1);
if (flipY)
transform.scale(1, -1);
transform.scale(scale.x(), scale.y());
transform.translate(-centerX, -centerY);
cache = cache.trans(transform);
@ -158,7 +139,7 @@ void LeafLayerWrapper::refresh(LayerWrapper* layer)
cache.clear();
if (wrappedElement != nullptr)
{
cache.addPath(wrappedElement->getPaintObject(*this->styles));
cache.addPath(wrappedElement->getPaintObject(this->styles));
}
LayerWrapper::refresh();
}
@ -264,8 +245,6 @@ QJsonObject LayerWrapper::toJson() const
QJsonObject transformJson;
transformJson.insert("offset", QJsonObject({ {"x", property.offset.x()}, {"y", property.offset.y()} }));
transformJson.insert("scale", QJsonObject({ {"x", property.scale.x()}, {"y", property.scale.y()} }));
transformJson.insert("flipX", property.flipX);
transformJson.insert("flipY", property.flipY);
transformJson.insert("rotation", property.rotation);
json.insert("transform", transformJson);
return json;
@ -292,7 +271,7 @@ QJsonObject LeafLayerWrapper::toJson() const
QJsonObject json = LayerWrapper::toJson();
json.insert("element", elementManager->getElementIndex(wrappedElement));
json.insert("is-folder", false);
json.insert("styles", styles->toJson());
json.insert("styles", styles.toJson());
return json;
}
@ -304,7 +283,7 @@ int FolderLayerWrapper::getReferencedBy()const
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)
// {
@ -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)
return;
LayerWrapper::paint(painter, transform, force);
transform = property.transform * transform;
for (auto it = children.rbegin(); it != children.rend(); ++it)
(*it)->paint(painter, transform, force, styles | *this->styles);
//qDebug() << transform;
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)
return;
LayerWrapper::paint(painter, transform, force);
transform = property.transform * transform;
//qDebug() << transform;
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
{
return true;
//return typeid(*this) == typeid(LeafLayerWrapper) && !referencingGroupElement();
return typeid(*this) == typeid(LeafLayerWrapper) && !referencingGroupElement();
}
void LayerWrapper::paintVisualBounding(QPainter* painter) const
@ -469,20 +450,7 @@ void LayerWrapper::paintVisualBounding(QPainter* painter) const
layer = layer->parent;
}
painter->save();
auto rect = this->cache.getBoundingRect();
rect = transform.mapRect(rect);
painter->drawRect(rect);
painter->setTransform(transform);
painter->drawRect(cache.getBoundingRect());
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;
public:
bool needRefresh = true;
QTreeWidgetItem* qTreeWidgetItem;
bool selected;
bool hidden;
LayerStyleContainer* styles;
struct SimpleProperty
{
QString name = "";
QPointF scale = {1.0, 1.0};
QPointF offset = {0, 0};
double rotation = 0;
bool flipX = 0;
bool flipY = 0;
bool flipHorizontally = 0;
bool flipVertically = 0;
QTransform transform;
// TODO: ½«QPainterPath¸ÄΪBitmapPath
void setRotation(double newRotation);
void apply(PixelPath&cache);
} property;
virtual void setParent(FolderLayerWrapper*newParent);
@ -62,7 +59,7 @@ class LayerWrapper
FolderLayerWrapper*getParent() const; // invoke by manager, then invoke parent's applyStyles
LayerWrapper(QJsonObject json, FolderLayerWrapper*parent, ElementManager* elementManager=nullptr);
LayerWrapper() = default;
virtual void paint(QPainter* painter, QTransform transform = QTransform(), bool force = false, LayerStyleContainer styles = LayerStyleContainer(LayerStyleContainer::TYPE_ALL));
virtual void paint(QPainter* painter, QTransform transform=QTransform(), bool force = false);
// TODO : export Function
// virtual LayerWrapper *addChild() = 0; // Leaf Child Only
// virtual LayerWrapper *addParent() = 0; // Folder Parent Only
@ -80,7 +77,6 @@ class LayerWrapper
virtual bool referencingGroupElement() const;
virtual void paintVisualBounding(QPainter* painter) const;
bool canApplyStyles() const;
virtual void markNeedRefresh();
};
class FolderLayerWrapper : public LayerWrapper
@ -103,25 +99,25 @@ class FolderLayerWrapper : public LayerWrapper
QTreeWidgetItem* getQTreeItem() override;
QJsonObject toJson() const override;
int getReferencedBy()const;
void paint(QPainter* painter, QTransform transform = QTransform(), bool force = false, 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 refreshTreeItem() override;
size_t referencedCount(bool excludeSelf = false) const override;
bool deleteable(bool excludeSubTree = false) const override;
void markNeedRefresh() override;
};
class LeafLayerWrapper : public LayerWrapper
{
public:
GraphicElement *wrappedElement;
LayerStyleContainer styles;
public:
~LeafLayerWrapper();
void refresh(LayerWrapper* layer = nullptr) override;
LeafLayerWrapper(QJsonObject json, ElementManager *elementManager, FolderLayerWrapper*parent);
QJsonObject toJson() const override;
void paint(QPainter* painter, QTransform transform = QTransform(), bool force = false, 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;
QTreeWidgetItem* getQTreeItem() override;
void refreshTreeItem() override;

View File

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

View File

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

View File

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

View File

@ -1,112 +1,187 @@
#include "InfoDisplayWidget.h"
#include "./EditorWidgetComponent/LayerStyleDialog.h"
#include "../ColorHelper.hpp"
#include <QLineEdit>
#include <QTextBlock>
#include <QListWidget>
#include <QDialog>
#include <QComboBox>
#include <QCheckBox>
#include <qtmaterialraisedbutton.h>
#include <qtmaterialflatbutton.h>
void InfoDisplayWidget::setLayer(LayerWrapper *layer)
{
this->displayLayer = layer;
this->refresh();
generateLayerForm();
}
InfoDisplayWidget::InfoDisplayWidget(QWidget* parent) :QWidget(parent)
void InfoDisplayWidget::generateLayerForm()
{
ui.setupUi(this);
this->displayLayer = nullptr;
ui.name->setDisabled(true);
ui.offsetX->setLabel(("水平偏移"));
ui.offsetY->setLabel(("垂直偏移"));
ui.rotation->setLabel(("旋转角度"));
ui.scaleX->setLabel(("水平缩放"));
ui.scaleY->setLabel(("垂直缩放"));
ui.rotation->setValidator(new QIntValidator(-360, 360, this));
ui.styleList->setDisabled(true);
connect(ui.rotation, &QLineEdit::textChanged, [=](const QString& content) {
if (fabs(content.toDouble() - this->displayLayer->property.rotation) < 1e-6)
return;
this->displayLayer->property.setRotation(content.toDouble());
emit triggerCentralRefresh();
QLayoutItem *item;
if (this->layout() != nullptr)
{
while (this->layout()->count() > 0 && (item = this->layout()->takeAt(0)) != nullptr)
{
item->widget()->deleteLater();
delete item;
}
delete this->layout();
}
QFormLayout *layout = new QFormLayout();
layout->setRowWrapPolicy(QFormLayout::WrapAllRows);
if (this->displayLayer == nullptr)
{
layout->addRow("no selected layer", new QLabel());
}
else
{
QLineEdit *name = new QLineEdit(this->displayLayer->property.name, this);
QLineEdit *rotation = new QLineEdit(QString::number(this->displayLayer->property.rotation, 'f', 0), this);
QLineEdit *offsetX = new QLineEdit(QString::number(this->displayLayer->property.offset.x()), this);
QLineEdit *offsetY = new QLineEdit(QString::number(this->displayLayer->property.offset.y()), this);
QLineEdit *scaleX = new QLineEdit(QString::number(this->displayLayer->property.scale.x()), this);
QLineEdit *scaleY = new QLineEdit(QString::number(this->displayLayer->property.scale.y()), this);
name->setDisabled(true);
rotation->setValidator(new QIntValidator(-10000, 10000, this));
connect(rotation, &QLineEdit::textChanged, [=](QString content) {
this->displayLayer->property.rotation = content.toDouble();
emit triggerCentralRefresh();
});
ui.offsetX->setValidator(new QDoubleValidator(-10000, 10000, 2, this));
connect(ui.offsetX, &QLineEdit::textChanged, [=](const QString& content) {
if (fabs(content.toDouble() - this->displayLayer->property.offset.x()) < 1e-6)
return;
this->displayLayer->property.offset = { content.toDouble(), this->displayLayer->property.offset.y() };
emit triggerCentralRefresh();
offsetX->setValidator(new QIntValidator(-10000, 10000, this));
connect(offsetX, &QLineEdit::textChanged, [=](QString content) {
this->displayLayer->property.offset = {content.toDouble(), this->displayLayer->property.offset.y()};
emit triggerCentralRefresh();
});
ui.offsetY->setValidator(new QDoubleValidator(-10000, 10000, 2, this));
connect(ui.offsetY, &QLineEdit::textChanged, [=](const QString& content) {
if (fabs(content.toDouble() - this->displayLayer->property.offset.y()) < 1e-6)
return;
this->displayLayer->property.offset = { this->displayLayer->property.offset.x(), content.toDouble() };
emit triggerCentralRefresh();
offsetY->setValidator(new QIntValidator(-10000, 10000, this));
connect(offsetY, &QLineEdit::textChanged, [=](QString content) {
this->displayLayer->property.offset = {this->displayLayer->property.offset.x(), content.toDouble()};
emit triggerCentralRefresh();
});
ui.scaleX->setValidator(new QDoubleValidator(0, 10000, 6, this));
connect(ui.scaleX, &QLineEdit::textChanged, [=](const QString& content) {
if (fabs(content.toDouble() - this->displayLayer->property.scale.x()) < 1e-6)
return;
this->displayLayer->property.scale = { content.toDouble(), this->displayLayer->property.scale.y() };
emit triggerCentralRefresh();
scaleX->setValidator(new QDoubleValidator(-1000, 1000, 4, this));
connect(scaleX, &QLineEdit::textChanged, [=](QString content) {
this->displayLayer->property.scale = {content.toDouble(), this->displayLayer->property.scale.y()};
emit triggerCentralRefresh();
});
ui.scaleY->setValidator(new QDoubleValidator(0, 10000, 6, this));
connect(ui.scaleY, &QLineEdit::textChanged, [=](const QString& content) {
if (fabs(content.toDouble() - this->displayLayer->property.scale.y()) < 1e-6)
return;
this->displayLayer->property.scale = { this->displayLayer->property.scale.x(), content.toDouble() };
emit triggerCentralRefresh();
});
connect(ui.flipX, &QtMaterialCheckBox::toggled, [=](bool state) {
if (state == this->displayLayer->property.flipX)
return;
this->displayLayer->property.flipX = state;
emit triggerCentralRefresh();
});
connect(ui.flipY, &QtMaterialCheckBox::toggled, [=](bool state) {
if (state == this->displayLayer->property.flipY)
return;
this->displayLayer->property.flipY = state;
emit triggerCentralRefresh();
});
connect(ui.styleList, &LayerContainerListWidget::addLayerStyle, [this](const std::shared_ptr<LayerStyle>& style) {
emit triggerCentralRefresh();
});
connect(ui.styleList, &LayerContainerListWidget::editLayerStyle, [this](const std::shared_ptr<LayerStyle>& style) {
emit triggerCentralRefresh();
});
connect(ui.styleList, &LayerContainerListWidget::removeLayerStyle, [this](const QString& styleName) {
emit triggerCentralRefresh();
scaleY->setValidator(new QDoubleValidator(-1000, 1000, 4, this));
connect(scaleY, &QLineEdit::textChanged, [=](QString content) {
this->displayLayer->property.scale = {this->displayLayer->property.scale.x(), content.toDouble()};
emit triggerCentralRefresh();
});
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()
{
if (this->displayLayer != nullptr)
{
//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);
}
}
this->generateLayerForm();
}

View File

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

View File

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

View File

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

View File

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

View File

@ -2,17 +2,12 @@
#include "../../Renderer/Painting/Painting.h"
#include <QJsonObject>
#include <ElementManager.h>
#include <map>
class PaintingUtil
{
using Contour = std::shared_ptr<vector<vector<Renderer::Point> > >;
private:
static int zIndexCount;
static void clear();
//static const double pi;
static QJsonObject readJsonFile(QString jsonFilePath);
static 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);
public:
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;
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());
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}),
};
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[1], ElementTransform{ glm::vec2(-0.45, 0.45), glm::vec2(0.5,0.5) / 2.f, 0, glm::bvec2(false), 0 });
//painting.addElement(*element[2], ElementTransform{ glm::vec2(0.50,-0.45), glm::vec2(0.6,0.7) / 2.f, 0, glm::bvec2(false), 0 });
}
else if (path == "1.json" || true)
else if (path == "1.json")
{
//painting.backgroundColor = QColor(196, 81, 35);
float widths[] = { 0.22, 0.22 * 0.25 / 0.15, 0.22 * 0.25 / 0.15 };
@ -351,7 +352,7 @@ GLuint Renderer::Model::loadPainting(std::string path)
trans.m13(), trans.m23(), trans.m33()).c_str();
//painting.addElement(*element[0], ElementTransform{ glm::vec4(-1,-1,1,1), mat, 0});
painting.addElement(*element[1], ElementTransform{ glm::vec4(-1,-1,0,1), mat, 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[0], ElementTransform{ glm::vec2(-0.45,0.45), glm::vec2(0.25), 0, glm::bvec2(false), 0 });
//painting.addElement(*element[1], ElementTransform{ glm::vec2(-0.535,0.33), glm::vec2(0.15), 0, glm::bvec2(false), 0 });

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -206,9 +206,9 @@ namespace UnitTest
}
TEST_METHOD(TestBothSidesClosed)
{
QPainterPath testPath;
SvgFileLoader().loadSvgFile("../../svg/4_L0.svg", testPath);
testPath = QTransform::fromScale(5, 5).map(testPath);
QPainterPath closedPath;
SvgFileLoader().loadSvgFile("../../svg/4_L0.svg", closedPath);
closedPath = QTransform::fromScale(5, 5).map(closedPath);
QApplication a(argc, argv);
class StyleStrokeRadialGradient : public Renderer::ElementStyle
{
@ -220,60 +220,11 @@ namespace UnitTest
{1.00, Material{QColor(58,64,151)}}
};
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))) };
}
} style;
TestGLWidget w(style, testPath);
w.show();
a.exec();
}
TEST_METHOD(TestAcuteAngle)
{
QPainterPath testPath;
testPath.moveTo(50, 50);
testPath.lineTo(300, 300);
testPath.lineTo(300, 50);
QApplication a(argc, argv);
class StyleStrokeRadialGradient : public Renderer::ElementStyle
{
virtual std::vector<Renderer::BaseStyle> toBaseStyles() const override
{
std::map<float, Material> materialMap = {
{0.20, Material{QColor(255,255,255)}},
{0.60, Material{QColor(165,176,207)}},
{1.00, Material{QColor(58,64,151)}}
};
return { BaseStyle(std::make_shared<TransformStyle>(),
std::make_shared<MaterialStyleStroke>(20, StrokeType::kRightSide, StrokeEndType::kRound,
std::make_shared<StrokeRadialGradient>(materialMap, false))) };
}
} style;
TestGLWidget w(style, testPath);
w.show();
a.exec();
}
TEST_METHOD(TestBothSidesGradientWidth)
{
QPainterPath testPath;
testPath.moveTo(50, 50);
testPath.cubicTo(50, 200, 200, 300, 300, 300);
QApplication a(argc, argv);
class StyleStrokeRadialGradient : public Renderer::ElementStyle
{
virtual std::vector<Renderer::BaseStyle> toBaseStyles() const override
{
std::map<float, Material> materialMap = {
{0.20, Material{QColor(255,255,255)}},
{0.60, Material{QColor(165,176,207)}},
{1.00, Material{QColor(58,64,151)}}
};
return { BaseStyle(std::make_shared<TransformStyle>(),
std::make_shared<MaterialStyleStroke>(20, StrokeType::kLeftSide, StrokeEndType::kRound,
std::make_shared<StrokeRadialGradient>(materialMap, false))) };
}
} style;
TestGLWidget w(style, testPath);
TestGLWidget w(style, closedPath);
w.show();
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>
<QtMocFileName Condition="'$(Configuration)|$(Platform)'=='Release|x64'">%(Filename).moc</QtMocFileName>
</ClCompile>
<ClCompile Include="LayerStyleTest.cpp" />
<ClCompile Include="PaintingTest.cpp">
<DynamicSource Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">input</DynamicSource>
<QtMocFileName Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">%(Filename).moc</QtMocFileName>

View File

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

View File

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

View File

@ -1,11 +1,4 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Generator: Adobe Illustrator 25.0.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
<svg version="1.1" id="图层_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
viewBox="0 0 141 171" style="enable-background:new 0 0 141 171;" xml:space="preserve">
<style type="text/css">
.st0{fill:none;stroke:#000000;stroke-miterlimit:10;}
</style>
<polygon class="st0" points="0.5,0.5 140.5,0.5 140.5,10.5 115.5,10.5 115.5,31.5 105.5,31.5 105.5,10.5 75.5,10.5 75.5,165.8
105.5,165.8 105.5,144.7 115.5,144.7 115.5,165.8 140.5,165.8 140.5,175.8 0.5,175.8 0.5,165.8 25.5,165.8 25.5,144.7 35.5,144.7
35.5,165.8 65.5,165.8 65.5,10.5 35.5,10.5 35.5,31.5 25.5,31.5 25.5,10.5 0.5,10.5 "/>
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 141 171">
<title>4_L0的副本</title>
<polygon points="0.5 0.5 140.5 0.5 140.5 10.5 115.5 10.5 115.5 35.5 105.5 35.5 105.5 10.5 75.5 10.5 75.5 160.5 105.5 160.5 105.5 135.5 115.5 135.5 115.5 160.5 140.5 160.5 140.5 170.5 0.5 170.5 0.5 160.5 25.5 160.5 25.5 135.5 35.5 135.5 35.5 160.5 65.5 160.5 65.5 10.5 35.5 10.5 35.5 35.5 25.5 35.5 25.5 10.5 0.5 10.5 0.5 0.5" fill="none" stroke="#000" stroke-miterlimit="10"/>
</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": [
{
"data": {
"include": "/svg/2.svg"
"include": "../svg/2.svg"
},
"name": "ababa",
"type": "svg-file"
@ -17,16 +17,16 @@
},
{
"data": {
"include": "/svg/0.svg"
"include": "../svg/0.svg"
},
"name": "ababa2",
"type": "svg-file"
},
{
"data": {
"include": "/svg/4_L0.svg"
"include": "D:/BigC2022/temp/ArchitectureColoredPainting/svg/3.svg"
},
"name": "4_L0.svg",
"name": "3.svg",
"type": "svg-file"
}
],
@ -39,19 +39,43 @@
{
"element": 0,
"is-folder": false,
"name": "Leaf2",
"name": "Leaf1",
"styles": [
{
"enableEachSideIndependent": true,
"left": "AABAQAEAIZwAf///AFqe/w==",
"right": "AABAQAAACJw=",
"enableEachSideIndependent": false,
"left": "AAAAQAEAIZwAf///qqr//w==",
"right": "AADgQAAACJw=",
"type": "stroke"
}
],
"transform": {
"offset": {
"x": 501,
"y": -3
"x": 0,
"y": 0
},
"rotation": 0,
"scale": {
"x": 1,
"y": 1
}
}
},
{
"element": 0,
"is-folder": false,
"name": "Leaf2",
"styles": [
{
"enableEachSideIndependent": false,
"left": "AAAAQAEAIZwAf////1UA/w==",
"right": "AADgQAEACJwAf///AFqe/w==",
"type": "stroke"
}
],
"transform": {
"offset": {
"x": 150,
"y": 0
},
"rotation": 0,
"scale": {
@ -59,6 +83,30 @@
"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,
@ -66,8 +114,8 @@
"referenced-by": 1,
"transform": {
"offset": {
"x": 503,
"y": 36
"x": 50,
"y": 50
},
"rotation": 0,
"scale": {
@ -79,73 +127,15 @@
{
"element": 1,
"is-folder": false,
"name": "子图层-2",
"name": "ReferencingGroupLayer",
"styles": [
],
"transform": {
"offset": {
"x": 1,
"y": 986
"x": 100,
"y": 0
},
"rotation": 0,
"scale": {
"x": 1,
"y": 1
}
}
},
{
"element": 1,
"is-folder": false,
"name": "子图层-3",
"styles": [
],
"transform": {
"offset": {
"x": -959,
"y": -5
},
"rotation": 0,
"scale": {
"x": 1,
"y": 1
}
}
},
{
"element": 1,
"is-folder": false,
"name": "子图层-4",
"styles": [
],
"transform": {
"offset": {
"x": -958,
"y": 980
},
"rotation": 0,
"scale": {
"x": 1,
"y": 1
}
}
},
{
"element": 3,
"is-folder": false,
"name": "子图层-5",
"styles": [
{
"material": "AH8A/wBanv8=",
"type": "fill"
}
],
"transform": {
"offset": {
"x": 473,
"y": 419
},
"rotation": 0,
"rotation": 45,
"scale": {
"x": 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 相同时,移动不安全;其余情况均安全。