dev-yyq
yang.yongquan 2023-03-19 21:32:03 +08:00
commit c4281a444e
78 changed files with 3892 additions and 1693 deletions

View File

@ -104,6 +104,9 @@
</Link> </Link>
</ItemDefinitionGroup> </ItemDefinitionGroup>
<ItemGroup> <ItemGroup>
<ClCompile Include="src\Editor\EditorWidgetComponent\FillStyleWidget.cpp" />
<ClCompile Include="src\Editor\RightBar\EditorSettingWidget.cpp" />
<ClCompile Include="src\Editor\EditorWidgetComponent\ColorPicker.cpp" />
<ClCompile Include="src\Editor\EditorWidgetComponent\LayerCreateWidget.cpp" /> <ClCompile Include="src\Editor\EditorWidgetComponent\LayerCreateWidget.cpp" />
<ClCompile Include="src\Editor\EditorWidgetComponent\LayerStyleDialog.cpp" /> <ClCompile Include="src\Editor\EditorWidgetComponent\LayerStyleDialog.cpp" />
<ClCompile Include="src\Editor\EditorWidget.cpp" /> <ClCompile Include="src\Editor\EditorWidget.cpp" />
@ -152,6 +155,8 @@
<ClCompile Include="src\Renderer\Painting\StraightLine.cpp" /> <ClCompile Include="src\Renderer\Painting\StraightLine.cpp" />
<ClCompile Include="src\Renderer\VirtualTextureManager.cpp" /> <ClCompile Include="src\Renderer\VirtualTextureManager.cpp" />
<ClCompile Include="src\SvgParser.cpp" /> <ClCompile Include="src\SvgParser.cpp" />
<ClCompile Include="src\Editor\EditorWidgetComponent\StrokeStyleWidget.cpp" />
<QtUic Include="EditorSettingWidget.ui" />
<QtUic Include="EditorWidget.ui" /> <QtUic Include="EditorWidget.ui" />
<QtUic Include="EditorWidgetItem.ui" /> <QtUic Include="EditorWidgetItem.ui" />
<QtUic Include="MainWindow.ui" /> <QtUic Include="MainWindow.ui" />
@ -187,12 +192,19 @@
<None Include="res\Shaders\ssgi.comp" /> <None Include="res\Shaders\ssgi.comp" />
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<QtMoc Include="src\Editor\EditorWidgetComponent\StrokeStyleWidget.h" />
<QtMoc Include="src\Editor\RightBar\LayerTreeWidget.h" /> <QtMoc Include="src\Editor\RightBar\LayerTreeWidget.h" />
<QtMoc Include="src\Editor\RightBar\InfoDisplayWidget.h" /> <QtMoc Include="src\Editor\RightBar\InfoDisplayWidget.h" />
<QtMoc Include="src\MainWindow.h" /> <QtMoc Include="src\MainWindow.h" />
<QtMoc Include="src\Editor\EditorWidget.h" /> <QtMoc Include="src\Editor\EditorWidget.h" />
<QtMoc Include="src\Editor\EditorWidgetComponent\LayerCreateWidget.h" /> <QtMoc Include="src\Editor\EditorWidgetComponent\LayerCreateWidget.h" />
<QtMoc Include="src\Editor\EditorWidgetComponent\LayerStyleDialog.h" /> <QtMoc Include="src\Editor\EditorWidgetComponent\LayerStyleDialog.h" />
<QtMoc Include="src\Editor\EditorWidgetComponent\ColorPicker.h" />
<QtMoc Include="src\Editor\RightBar\EditorSettingWidget.h" />
<QtMoc Include="src\Editor\EditorWidgetComponent\FillStyleWidget.h" />
<ClInclude Include="src\Editor\LayerWrapper.h" />
<ClInclude Include="src\Editor\util\EncodeUtil.hpp" />
<ClInclude Include="src\Editor\util\JsonUtil.hpp" />
<ClInclude Include="src\Editor\ElementManager.h" /> <ClInclude Include="src\Editor\ElementManager.h" />
<QtMoc Include="src\Editor\ElementPoolWidget.h" /> <QtMoc Include="src\Editor\ElementPoolWidget.h" />
<ClInclude Include="src\Editor\GraphicElement.h" /> <ClInclude Include="src\Editor\GraphicElement.h" />

View File

@ -65,6 +65,24 @@
<Filter Include="Header Files\Renderer\Preview"> <Filter Include="Header Files\Renderer\Preview">
<UniqueIdentifier>{7ead1a66-586a-4584-ae80-9e7a4e667364}</UniqueIdentifier> <UniqueIdentifier>{7ead1a66-586a-4584-ae80-9e7a4e667364}</UniqueIdentifier>
</Filter> </Filter>
<Filter Include="Header Files\Editor\Style">
<UniqueIdentifier>{be3f4585-c8ba-410f-8619-2adcd4349f02}</UniqueIdentifier>
</Filter>
<Filter Include="Source Files\Editor\Style">
<UniqueIdentifier>{b9732a33-aa2e-4f8d-886f-1b1730c66519}</UniqueIdentifier>
</Filter>
<Filter Include="Header Files\Editor\Element">
<UniqueIdentifier>{8d846557-8fd5-47d5-8edf-eb3eb77c226b}</UniqueIdentifier>
</Filter>
<Filter Include="Source Files\Editor\Element">
<UniqueIdentifier>{22d7f3ef-8185-476e-8fe1-aea24c4faacc}</UniqueIdentifier>
</Filter>
<Filter Include="Header Files\Editor\Layer">
<UniqueIdentifier>{6fc32493-d5a2-44c3-a283-d2d3181330fb}</UniqueIdentifier>
</Filter>
<Filter Include="Source Files\Editor\Layer">
<UniqueIdentifier>{e6de889e-8313-4846-8bdf-125b766eef59}</UniqueIdentifier>
</Filter>
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<QtUic Include="MainWindow.ui"> <QtUic Include="MainWindow.ui">
@ -76,6 +94,9 @@
<QtUic Include="src\Editor\EditorWidgetComponent\LayerCreateWidget.ui"> <QtUic Include="src\Editor\EditorWidgetComponent\LayerCreateWidget.ui">
<Filter>Form Files</Filter> <Filter>Form Files</Filter>
</QtUic> </QtUic>
<QtUic Include="EditorSettingWidget.ui">
<Filter>Form Files</Filter>
</QtUic>
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<ClCompile Include="src\Editor\EditorWidgetItem.cpp"> <ClCompile Include="src\Editor\EditorWidgetItem.cpp">
@ -111,9 +132,6 @@
<ClCompile Include="src\Renderer\RendererWidget.cpp"> <ClCompile Include="src\Renderer\RendererWidget.cpp">
<Filter>Source Files\Renderer</Filter> <Filter>Source Files\Renderer</Filter>
</ClCompile> </ClCompile>
<ClCompile Include="src\NavigationBarWidget.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="src\Renderer\Painting\BvhTree.cpp"> <ClCompile Include="src\Renderer\Painting\BvhTree.cpp">
<Filter>Source Files\Renderer\Painting</Filter> <Filter>Source Files\Renderer\Painting</Filter>
</ClCompile> </ClCompile>
@ -135,18 +153,6 @@
<ClCompile Include="src\Renderer\Painting\Element.cpp"> <ClCompile Include="src\Renderer\Painting\Element.cpp">
<Filter>Source Files\Renderer\Painting</Filter> <Filter>Source Files\Renderer\Painting</Filter>
</ClCompile> </ClCompile>
<ClCompile Include="src\Editor\GraphicElement.cpp">
<Filter>Source Files\Editor</Filter>
</ClCompile>
<ClCompile Include="src\Editor\ElementManager.cpp">
<Filter>Source Files\Editor</Filter>
</ClCompile>
<ClCompile Include="src\Editor\LayerManager.cpp">
<Filter>Source Files\Editor</Filter>
</ClCompile>
<ClCompile Include="src\Editor\LayerWrapper.cpp">
<Filter>Source Files\Editor</Filter>
</ClCompile>
<ClCompile Include="src\Editor\PreviewWindow.cpp"> <ClCompile Include="src\Editor\PreviewWindow.cpp">
<Filter>Source Files\Editor</Filter> <Filter>Source Files\Editor</Filter>
</ClCompile> </ClCompile>
@ -156,12 +162,6 @@
<ClCompile Include="src\Renderer\Painting\CubicBezierSignedDistance.cpp"> <ClCompile Include="src\Renderer\Painting\CubicBezierSignedDistance.cpp">
<Filter>Source Files\Renderer\Painting</Filter> <Filter>Source Files\Renderer\Painting</Filter>
</ClCompile> </ClCompile>
<ClCompile Include="src\Editor\RightBar\LayerTreeWidget.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="src\Editor\RightBar\InfoDisplayWidget.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="src\Renderer\Painting\ElementStyle.cpp"> <ClCompile Include="src\Renderer\Painting\ElementStyle.cpp">
<Filter>Source Files\Renderer\Painting</Filter> <Filter>Source Files\Renderer\Painting</Filter>
</ClCompile> </ClCompile>
@ -189,9 +189,6 @@
<ClCompile Include="src\Renderer\Preview\ElementRenderer.cpp"> <ClCompile Include="src\Renderer\Preview\ElementRenderer.cpp">
<Filter>Source Files\Renderer\Preview</Filter> <Filter>Source Files\Renderer\Preview</Filter>
</ClCompile> </ClCompile>
<ClCompile Include="src\Editor\LayerStyle.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="src\Renderer\IblUtils.cpp"> <ClCompile Include="src\Renderer\IblUtils.cpp">
<Filter>Source Files\Renderer</Filter> <Filter>Source Files\Renderer</Filter>
</ClCompile> </ClCompile>
@ -204,22 +201,61 @@
<ClCompile Include="src\Editor\PixelPath.cpp"> <ClCompile Include="src\Editor\PixelPath.cpp">
<Filter>Source Files\Editor</Filter> <Filter>Source Files\Editor</Filter>
</ClCompile> </ClCompile>
<ClCompile Include="src\Editor\EditorWidget.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="src\Editor\ElementPoolWidget.cpp">
<Filter>Source Files\Editor</Filter>
</ClCompile>
<ClCompile Include="src\FluentMenu.cpp"> <ClCompile Include="src\FluentMenu.cpp">
<Filter>Source Files</Filter> <Filter>Source Files</Filter>
</ClCompile> </ClCompile>
<ClCompile Include="src\Editor\util\PaintingUtil.cpp"> <ClCompile Include="src\Editor\util\PaintingUtil.cpp">
<Filter>Source Files\Editor\util</Filter> <Filter>Source Files\Editor\util</Filter>
</ClCompile> </ClCompile>
<ClCompile Include="src\Editor\EditorWidgetComponent\LayerCreateWidget.cpp"> <ClCompile Include="src\Editor\LayerStyle.cpp">
<Filter>Source Files</Filter> <Filter>Source Files\Editor\Style</Filter>
</ClCompile> </ClCompile>
<ClCompile Include="src\Editor\EditorWidgetComponent\LayerStyleDialog.cpp"> <ClCompile Include="src\Editor\EditorWidgetComponent\LayerStyleDialog.cpp">
<Filter>Source Files\Editor\Style</Filter>
</ClCompile>
<ClCompile Include="src\Editor\EditorWidgetComponent\StrokeStyleWidget.cpp">
<Filter>Source Files\Editor\Style</Filter>
</ClCompile>
<ClCompile Include="src\Editor\RightBar\InfoDisplayWidget.cpp">
<Filter>Source Files\Editor</Filter>
</ClCompile>
<ClCompile Include="src\NavigationBarWidget.cpp">
<Filter>Source Files\Editor</Filter>
</ClCompile>
<ClCompile Include="src\Editor\EditorWidgetComponent\ColorPicker.cpp">
<Filter>Source Files\Editor</Filter>
</ClCompile>
<ClCompile Include="src\Editor\EditorWidget.cpp">
<Filter>Source Files\Editor</Filter>
</ClCompile>
<ClCompile Include="src\Editor\RightBar\EditorSettingWidget.cpp">
<Filter>Source Files\Editor</Filter>
</ClCompile>
<ClCompile Include="src\Editor\EditorWidgetComponent\FillStyleWidget.cpp">
<Filter>Source Files\Editor\Style</Filter>
</ClCompile>
<ClCompile Include="src\Editor\ElementManager.cpp">
<Filter>Source Files\Editor\Element</Filter>
</ClCompile>
<ClCompile Include="src\Editor\ElementPoolWidget.cpp">
<Filter>Source Files\Editor\Element</Filter>
</ClCompile>
<ClCompile Include="src\Editor\GraphicElement.cpp">
<Filter>Source Files\Editor\Element</Filter>
</ClCompile>
<ClCompile Include="src\Editor\LayerManager.cpp">
<Filter>Source Files\Editor\Layer</Filter>
</ClCompile>
<ClCompile Include="src\Editor\RightBar\LayerTreeWidget.cpp">
<Filter>Source Files\Editor\Layer</Filter>
</ClCompile>
<ClCompile Include="src\Editor\EditorWidgetComponent\LayerCreateWidget.cpp">
<Filter>Source Files\Editor\Layer</Filter>
</ClCompile>
<ClCompile Include="src\Editor\LayerWrapper.cpp">
<Filter>Source Files\Editor\Layer</Filter>
</ClCompile>
<ClCompile Include="src\Editor\EditorWidgetComponent\FillStyleWidget.cpp">
<Filter>Source Files</Filter> <Filter>Source Files</Filter>
</ClCompile> </ClCompile>
</ItemGroup> </ItemGroup>
@ -233,34 +269,49 @@
<QtMoc Include="src\Renderer\RendererWidget.h"> <QtMoc Include="src\Renderer\RendererWidget.h">
<Filter>Header Files\Renderer</Filter> <Filter>Header Files\Renderer</Filter>
</QtMoc> </QtMoc>
<QtMoc Include="src\NavigationBarWidget.h">
<Filter>Header Files</Filter>
</QtMoc>
<QtMoc Include="src\MainWindow.h">
<Filter>Header Files</Filter>
</QtMoc>
<QtMoc Include="src\Editor\PreviewWindow.h"> <QtMoc Include="src\Editor\PreviewWindow.h">
<Filter>Header Files\Editor</Filter> <Filter>Header Files\Editor</Filter>
</QtMoc> </QtMoc>
<QtMoc Include="src\Editor\RightBar\LayerTreeWidget.h">
<Filter>Header Files</Filter>
</QtMoc>
<QtMoc Include="src\Editor\RightBar\InfoDisplayWidget.h">
<Filter>Header Files</Filter>
</QtMoc>
<QtMoc Include="src\Editor\EditorWidget.h">
<Filter>Header Files</Filter>
</QtMoc>
<QtMoc Include="src\Editor\ElementPoolWidget.h">
<Filter>Header Files\Editor</Filter>
</QtMoc>
<QtMoc Include="src\FluentMenu.h"> <QtMoc Include="src\FluentMenu.h">
<Filter>Header Files</Filter> <Filter>Header Files</Filter>
</QtMoc> </QtMoc>
<QtMoc Include="src\Editor\EditorWidgetComponent\LayerCreateWidget.h"> <QtMoc Include="src\Editor\EditorWidgetComponent\StrokeStyleWidget.h">
<Filter>Header Files</Filter> <Filter>Header Files\Editor\Style</Filter>
</QtMoc> </QtMoc>
<QtMoc Include="src\Editor\EditorWidgetComponent\LayerStyleDialog.h"> <QtMoc Include="src\Editor\EditorWidgetComponent\LayerStyleDialog.h">
<Filter>Header Files\Editor\Style</Filter>
</QtMoc>
<QtMoc Include="src\Editor\RightBar\InfoDisplayWidget.h">
<Filter>Header Files\Editor</Filter>
</QtMoc>
<QtMoc Include="src\Editor\EditorWidget.h">
<Filter>Header Files\Editor</Filter>
</QtMoc>
<QtMoc Include="src\MainWindow.h">
<Filter>Header Files\Editor</Filter>
</QtMoc>
<QtMoc Include="src\NavigationBarWidget.h">
<Filter>Header Files\Editor</Filter>
</QtMoc>
<QtMoc Include="src\Editor\EditorWidgetComponent\ColorPicker.h">
<Filter>Header Files\Editor</Filter>
</QtMoc>
<QtMoc Include="src\Editor\RightBar\EditorSettingWidget.h">
<Filter>Header Files\Editor</Filter>
</QtMoc>
<QtMoc Include="src\Editor\EditorWidgetComponent\FillStyleWidget.h">
<Filter>Header Files\Editor\Style</Filter>
</QtMoc>
<QtMoc Include="src\Editor\ElementPoolWidget.h">
<Filter>Header Files\Editor\Element</Filter>
</QtMoc>
<QtMoc Include="src\Editor\EditorWidgetComponent\LayerCreateWidget.h">
<Filter>Header Files\Editor\Layer</Filter>
</QtMoc>
<QtMoc Include="src\Editor\RightBar\LayerTreeWidget.h">
<Filter>Header Files\Editor\Layer</Filter>
</QtMoc>
<QtMoc Include="src\Editor\EditorWidgetComponent\FillStyleWidget.h">
<Filter>Header Files</Filter> <Filter>Header Files</Filter>
</QtMoc> </QtMoc>
</ItemGroup> </ItemGroup>
@ -393,15 +444,6 @@
<ClInclude Include="src\Renderer\Painting\Element.h"> <ClInclude Include="src\Renderer\Painting\Element.h">
<Filter>Header Files\Renderer\Painting</Filter> <Filter>Header Files\Renderer\Painting</Filter>
</ClInclude> </ClInclude>
<ClInclude Include="src\Editor\GraphicElement.h">
<Filter>Header Files\Editor</Filter>
</ClInclude>
<ClInclude Include="src\Editor\LayerManager.h">
<Filter>Header Files\Editor</Filter>
</ClInclude>
<ClInclude Include="src\Editor\ElementManager.h">
<Filter>Header Files\Editor</Filter>
</ClInclude>
<ClInclude Include="src\Renderer\Painting\LineTree.h"> <ClInclude Include="src\Renderer\Painting\LineTree.h">
<Filter>Header Files\Renderer\Painting</Filter> <Filter>Header Files\Renderer\Painting</Filter>
</ClInclude> </ClInclude>
@ -441,9 +483,6 @@
<ClInclude Include="src\Renderer\Preview\ElementRenderer.h"> <ClInclude Include="src\Renderer\Preview\ElementRenderer.h">
<Filter>Header Files\Renderer\Preview</Filter> <Filter>Header Files\Renderer\Preview</Filter>
</ClInclude> </ClInclude>
<ClInclude Include="src\Editor\PixelPath.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="src\Renderer\IblUtils.h"> <ClInclude Include="src\Renderer\IblUtils.h">
<Filter>Header Files\Renderer</Filter> <Filter>Header Files\Renderer</Filter>
</ClInclude> </ClInclude>
@ -457,8 +496,29 @@
<Filter>Header Files\Editor\util</Filter> <Filter>Header Files\Editor\util</Filter>
</ClInclude> </ClInclude>
<ClInclude Include="src\Editor\LayerStyle.h"> <ClInclude Include="src\Editor\LayerStyle.h">
<Filter>Header Files\Editor\Style</Filter>
</ClInclude>
<ClInclude Include="src\Editor\util\JsonUtil.hpp">
<Filter>Header Files\Editor\util</Filter>
</ClInclude>
<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>
<ClInclude Include="src\Editor\LayerManager.h">
<Filter>Header Files\Editor\Layer</Filter>
</ClInclude>
<ClInclude Include="src\Editor\PixelPath.h">
<Filter>Header Files\Editor</Filter> <Filter>Header Files\Editor</Filter>
</ClInclude> </ClInclude>
<ClInclude Include="src\Editor\LayerWrapper.h">
<Filter>Header Files\Editor\Layer</Filter>
</ClInclude>
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<QtRcc Include="res\MainWindow.qrc"> <QtRcc Include="res\MainWindow.qrc">

View File

@ -0,0 +1,32 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>EditorSettingWidget</class>
<widget class="QWidget" name="EditorSettingWidget">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>400</width>
<height>300</height>
</rect>
</property>
<property name="windowTitle">
<string>Form</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout_2">
<item>
<layout class="QVBoxLayout" name="verticalLayout">
<item>
<widget class="QPushButton" name="backgroundColorButton">
<property name="text">
<string>设置背景颜色</string>
</property>
</widget>
</item>
</layout>
</item>
</layout>
</widget>
<resources/>
<connections/>
</ui>

View File

@ -0,0 +1,63 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>EditorSettingWidget</class>
<widget class="QWidget" name="EditorSettingWidget">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>400</width>
<height>300</height>
</rect>
</property>
<property name="windowTitle">
<string>Form</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout_2">
<item>
<layout class="QVBoxLayout" name="verticalLayout">
<item alignment="Qt::AlignHCenter">
<widget class="QPushButton" name="backgroundColorButton">
<property name="sizePolicy">
<sizepolicy hsizetype="Fixed" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="maximumSize">
<size>
<width>120</width>
<height>16777215</height>
</size>
</property>
<property name="text">
<string>设置背景颜色</string>
</property>
</widget>
</item>
<item alignment="Qt::AlignHCenter">
<widget class="QPushButton" name="renameButton">
<property name="sizePolicy">
<sizepolicy hsizetype="Minimum" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="maximumSize">
<size>
<width>120</width>
<height>16777215</height>
</size>
</property>
<property name="text">
<string>设置项目名称</string>
</property>
</widget>
</item>
</layout>
</item>
</layout>
</widget>
<resources/>
<connections/>
</ui>

View File

@ -37,38 +37,87 @@
</property> </property>
<item> <item>
<widget class="QWidget" name="MainWindow" native="true"> <widget class="QWidget" name="MainWindow" native="true">
<layout class="QVBoxLayout" name="verticalLayout_2" stretch="30"> <layout class="QVBoxLayout" name="verticalLayout_2" stretch="20,1">
<property name="leftMargin">
<number>0</number>
</property>
<property name="topMargin">
<number>0</number>
</property>
<property name="rightMargin">
<number>0</number>
</property>
<property name="bottomMargin">
<number>0</number>
</property>
<item> <item>
<layout class="QHBoxLayout" name="horizontalLayout" stretch="0,12,5"> <layout class="QHBoxLayout" name="horizontalLayout" stretch="0,12,5">
<item> <item>
<widget class="QWidget" name="LeftBar" native="true"/> <widget class="QWidget" name="LeftBar" native="true"/>
</item> </item>
<item> <item>
<widget class="PreviewWindow" name="Preview"> <widget class="QScrollArea" name="scrollArea">
<property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="Preferred">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="minimumSize"> <property name="minimumSize">
<size> <size>
<width>1080</width> <width>0</width>
<height>1080</height> <height>0</height>
</size> </size>
</property> </property>
<property name="maximumSize"> <property name="maximumSize">
<size> <size>
<width>1080</width> <width>10801080</width>
<height>1080</height> <height>10801080</height>
</size> </size>
</property> </property>
<property name="widgetResizable">
<bool>true</bool>
</property>
<widget class="QWidget" name="scrollAreaWidgetContents">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>1024</width>
<height>1024</height>
</rect>
</property>
<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>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> </widget>
</item> </item>
<item> <item>
@ -98,7 +147,7 @@
</size> </size>
</property> </property>
<property name="currentIndex"> <property name="currentIndex">
<number>1</number> <number>2</number>
</property> </property>
<widget class="InfoDisplayWidget" name="LayerDisplay"> <widget class="InfoDisplayWidget" name="LayerDisplay">
<attribute name="title"> <attribute name="title">
@ -110,6 +159,11 @@
<string>图元池</string> <string>图元池</string>
</attribute> </attribute>
</widget> </widget>
<widget class="EditorSettingWidget" name="EditorSetting">
<attribute name="title">
<string>设置</string>
</attribute>
</widget>
</widget> </widget>
</item> </item>
<item> <item>
@ -128,7 +182,12 @@
</property> </property>
<column> <column>
<property name="text"> <property name="text">
<string>图层树</string> <string>图层名</string>
</property>
</column>
<column>
<property name="text">
<string>关联图元</string>
</property> </property>
</column> </column>
</widget> </widget>
@ -138,6 +197,19 @@
</item> </item>
</layout> </layout>
</item> </item>
<item>
<spacer name="horizontalSpacer">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>40</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
</layout> </layout>
</widget> </widget>
</item> </item>
@ -167,6 +239,12 @@
<header>ElementPoolWidget.h</header> <header>ElementPoolWidget.h</header>
<container>1</container> <container>1</container>
</customwidget> </customwidget>
<customwidget>
<class>EditorSettingWidget</class>
<extends>QWidget</extends>
<header location="global">EditorSettingWidget.h</header>
<container>1</container>
</customwidget>
</customwidgets> </customwidgets>
<resources/> <resources/>
<connections/> <connections/>

View File

@ -84,10 +84,26 @@
</layout> </layout>
</item> </item>
<item> <item>
<layout class="QHBoxLayout" name="horizontalLayout" stretch="1,0"> <layout class="QHBoxLayout" name="horizontalLayout" stretch="0,1,0">
<property name="sizeConstraint"> <property name="sizeConstraint">
<enum>QLayout::SetDefaultConstraint</enum> <enum>QLayout::SetDefaultConstraint</enum>
</property> </property>
<item>
<widget class="QListWidget" name="textureListWidget">
<property name="sizePolicy">
<sizepolicy hsizetype="Fixed" vsizetype="Expanding">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="maximumSize">
<size>
<width>0</width>
<height>16777215</height>
</size>
</property>
</widget>
</item>
<item> <item>
<widget class="Renderer::RendererGLWidget" name="openGLWidget"> <widget class="Renderer::RendererGLWidget" name="openGLWidget">
<property name="sizePolicy"> <property name="sizePolicy">

View File

@ -893,7 +893,7 @@ bool angleLargeThanPi(vec2 a, vec2 b)
/************************************************************************************/ /************************************************************************************/
void drawLine(in float d, inout uint styleIndex, out vec4 elementColor, out vec2 metallicRoughness) void drawLine(in float d, uint styleIndex, out vec4 elementColor, out vec2 metallicRoughness)
{ {
elementColor = vec4(1); elementColor = vec4(1);
metallicRoughness = vec2(0.8); metallicRoughness = vec2(0.8);
@ -969,14 +969,38 @@ void drawLine(in float d, inout uint styleIndex, out vec4 elementColor, out vec2
} }
} }
bool shouldFillBeginCap(vec2 localUV, bool onVeryBegin, int endType, vec2 p3, vec2 tangentBegin, vec2 tangentEndLast) void nextStyleIndex(inout uint styleIndex)
{
uint headUint = floatBitsToUint(style[styleIndex + 1]);
vec4 head = unpackUnorm4x8(headUint);
switch (int(head.a * 100) % 10)
{
/// Plain
case 0: {
styleIndex += 3;
break;
}
/// RadialGradient
case 1: {
uint size = headUint % (1 << 15);
styleIndex += 2 + size * 2;
break;
}
case 2: {
break;
}
}
}
bool shouldFillBeginCap(vec2 localUV, bool onVeryBegin, int endType, vec2 p0, vec2 tangentBegin, vec2 tangentEndLast)
{ {
vec2 normal; vec2 normal;
if (onVeryBegin) if (onVeryBegin)
{ {
if (endType == 0) if (endType%2 == 0)
return true; return true;
else if (endType == 1) else if (endType%2 == 1)
normal = normalize(mat2(0, 1, -1, 0) * (-tangentBegin)); normal = normalize(mat2(0, 1, -1, 0) * (-tangentBegin));
} }
else else
@ -985,17 +1009,26 @@ bool shouldFillBeginCap(vec2 localUV, bool onVeryBegin, int endType, vec2 p3, ve
vec2 normalNow = normalize(mat2(0, 1, -1, 0) * (-tangentBegin)); vec2 normalNow = normalize(mat2(0, 1, -1, 0) * (-tangentBegin));
normal = normalLast + normalNow; normal = normalLast + normalNow;
} }
return angleLargeThanPi(normal, localUV - p3); return angleLargeThanPi(normal, localUV - p0);
} }
bool shouldFillEndCap(vec2 localUV, int endType, vec2 p0, vec2 tangentEnd) bool shouldFillEndCap(vec2 localUV, bool onVeryEnd, int endType, vec2 p3, vec2 tangentEnd, vec2 tangentBeginNext)
{ {
vec2 normal; vec2 normal;
if (endType == 0) if (onVeryEnd)
{
if ((endType/2)%2 == 0)
return true; return true;
else if (endType == 1) else if ((endType/2)%2 == 1)
normal = normalize(mat2(0, 1, -1, 0) * tangentEnd); normal = normalize(mat2(0, 1, -1, 0) * tangentEnd);
return angleLargeThanPi(localUV - p0, normal); }
else
{
vec2 normalLast = normalize(mat2(0, 1, -1, 0) * tangentEnd);
vec2 normalNow = normalize(mat2(0, 1, -1, 0) * (-tangentBeginNext));
normal = normalLast + normalNow;
}
return angleLargeThanPi(localUV - p3, normal);
} }
void main() void main()
@ -1006,9 +1039,8 @@ void main()
vec4 color = vec4(0); vec4 color = vec4(0);
// if(isinf(path[0].x)) imageStore(gBaseColor, ivec2(pixelLocation), // if(isinf(path[0].x)) imageStore(gBaseColor, ivec2(pixelLocation),
// vec4(vec2(pixelLocation)/vec2(imageSize(gBaseColor)), 1,1)); return; // vec4(vec2(pixelLocation)/vec2(imageSize(gBaseColor)), 1,1)); return;
for (uint styleIndex = 0; styleIndex < styleSize; styleIndex++) for (uint styleIndex = 0; styleIndex < styleSize;)
{ {
styleIndex += 6; styleIndex += 6;
vec2 localUV = vec2(pixelLocation) / pixelRatio + leftTop; vec2 localUV = vec2(pixelLocation) / pixelRatio + leftTop;
@ -1039,14 +1071,15 @@ void main()
if (num_its % 2 == 1) if (num_its % 2 == 1)
{ {
hitElement = true; hitElement = true;
elementColor = vec4(1, 1, 0, 0);
vec4 head = unpackUnorm4x8(floatBitsToUint(style[styleIndex])); vec4 head = unpackUnorm4x8(floatBitsToUint(style[styleIndex]));
if (head.z == 0) if (head.z == 0)
{ {
elementColor = vec4(unpackUnorm4x8(floatBitsToUint(style[++styleIndex])).rgb, 1); elementColor = vec4(unpackUnorm4x8(floatBitsToUint(style[styleIndex+1])).rgb, 1);
} }
} }
styleIndex += 2;
} }
else // Stroke else // Stroke
{ {
@ -1058,18 +1091,19 @@ void main()
// endType = 1; // endType = 1;
int debugBegin = 0; int debugBegin = 0;
bool onVeryBegin = false; bool onVeryBegin = false;
bool onVeryEnd = false;
vec2 tangentEndLast; vec2 tangentEndLast;
uint lastHitIndex = 0;
bool lastHitElement = false;
hitElement = false;
for (uint pathIndex = 0; pathIndex < pathSize; pathIndex++) for (uint pathIndex = 0; pathIndex < pathSize; pathIndex++)
// for (uint pathIndex = 0; pathIndex < 4; pathIndex++) //for (uint pathIndex = 0; pathIndex < 46; pathIndex++)
{ {
vec2 pTemp = path[pathIndex]; vec2 pTemp = path[pathIndex];
if (isinf(pTemp.x)) if (isinf(pTemp.x))
{ {
// TODO: ¼ì²âÊÇ·ñ·â±Õ²¢´¦Àí // TODO: ¼ì²âÊÇ·ñ·â±Õ²¢´¦Àí
if (hitElement && distance(localUV, p3Last) <= strokeWidth)
{
hitElement = shouldFillEndCap(localUV, endType, p3Last, tangentEndLast);
}
pBegin = path[++pathIndex]; pBegin = path[++pathIndex];
p3Last = pBegin; p3Last = pBegin;
@ -1079,10 +1113,32 @@ void main()
} }
mat4x2 p = mat4x2(p3Last, pTemp, path[++pathIndex], path[++pathIndex]); mat4x2 p = mat4x2(p3Last, pTemp, path[++pathIndex], path[++pathIndex]);
vec2 tangentBeginNext;
if (pathIndex + 1 < pathSize)
{
vec2 pTemp = path[pathIndex + 1];
if (isinf(pTemp.x))
{
onVeryEnd = true;
}
else
{
onVeryEnd = false;
vec2 pNext[3] = {p[3], pTemp, path[pathIndex + 2]};
if (pNext[0] != pNext[1])
tangentBeginNext = normalize(pNext[0] - pNext[1]);
else
tangentBeginNext = normalize(pNext[0] - pNext[2]);
}
}
else
onVeryEnd = true;
float d = cubic_bezier_dis(localUV, p[0], p[1], p[2], p[3], true); float d = cubic_bezier_dis(localUV, p[0], p[1], p[2], p[3], true);
if (d <= strokeWidth) if (d <= strokeWidth)
{ {
bool onBegin = distance(localUV, p[0]) <= strokeWidth && p3Last == p[0]; bool onBegin = distance(localUV, p[0]) <= strokeWidth;
bool onEnd = distance(localUV, p[3]) <= strokeWidth;
vec2 tangentBegin; vec2 tangentBegin;
vec2 tangentEnd; vec2 tangentEnd;
if (p[0] != p[1]) if (p[0] != p[1])
@ -1094,11 +1150,14 @@ void main()
else else
tangentEnd = normalize(p[3] - p[1]); tangentEnd = normalize(p[3] - p[1]);
if (onBegin ? shouldFillBeginCap(localUV, onVeryBegin, endType, p[0], tangentBegin, p3Last - p2Last) bool hit = d < minDistance;
: d < minDistance) if (onBegin)
hit = hit &&
shouldFillBeginCap(localUV, onVeryBegin, endType, p[0], tangentBegin, tangentEndLast);
if (onEnd)
hit = hit && shouldFillEndCap(localUV, onVeryEnd, endType, p[3], tangentEnd, tangentBeginNext);
if (hit)
{ {
minDistance = min(minDistance, d);
bool reverse = p[3].y - p[0].y < 0.; bool reverse = p[3].y - p[0].y < 0.;
if (tangentBegin.y == 0.) if (tangentBegin.y == 0.)
@ -1111,13 +1170,14 @@ void main()
if (lineType == 2 || (intTest % 2 == int(lineType))) if (lineType == 2 || (intTest % 2 == int(lineType)))
{ {
minDistance = min(minDistance, d);
lastHitElement = hitElement;
lastHitIndex = pathIndex;
hitElement = true; hitElement = true;
// elementColor = vec4(1, 1, 0, 1); // elementColor = vec4(1, 1, 0, 1);
vec2 metallicRoughness; vec2 metallicRoughness;
drawLine(minDistance / strokeWidth, styleIndex, elementColor, metallicRoughness); drawLine(minDistance / strokeWidth, styleIndex, elementColor, metallicRoughness);
} }
else if (p3Last == p[0])
hitElement = false;
} }
tangentEndLast = tangentEnd; tangentEndLast = tangentEnd;
} }
@ -1125,14 +1185,11 @@ void main()
p2Last = p[2]; p2Last = p[2];
onVeryBegin = false; onVeryBegin = false;
} }
if (hitElement && distance(localUV, p3Last) <= strokeWidth) nextStyleIndex(styleIndex);
{
hitElement = shouldFillEndCap(localUV, endType, p3Last, tangentEndLast);
}
} }
if (hitElement) if (hitElement)
color = elementColor; color = elementColor;
styleIndex += 100;
} }
if (color.a != 0) if (color.a != 0)
imageStore(gBaseColor, ivec2(pixelLocation), color); imageStore(gBaseColor, ivec2(pixelLocation), color);

View File

@ -4,6 +4,11 @@ layout (local_size_x = 8, local_size_y = 8) in;
layout(location = 0) uniform ivec2 pixelOffset; layout(location = 0) uniform ivec2 pixelOffset;
layout(std140, binding = 1) uniform ubo
{
vec3 backgroundColor;
};
layout(rgba8, binding = 0) uniform image2D gBaseColor; layout(rgba8, binding = 0) uniform image2D gBaseColor;
layout(rg8, binding = 1) uniform image2D gMetallicRoughness; layout(rg8, binding = 1) uniform image2D gMetallicRoughness;
@ -15,29 +20,31 @@ layout(std430, binding = 1) buffer bvhBoundBuffer
{ {
vec4 bvhBound[]; vec4 bvhBound[];
}; };
layout(std430, binding = 2) buffer elementOffsetBuffer layout(std430, binding = 2) buffer elementTranformBuffer
{
mat3x2 elementTranform[];
};
layout(std430, binding = 3) buffer elementOffsetBuffer
{ {
/** /**
* @[0] elementBvhRoot * @[0] elementBvhRoot
* @[1] styleOffset * @[1] styleOffset
* @[2] pointsOffset * @[2] pointsOffset
* @[3] linesOffset * @[3] linesOffset
*/ */
uint elementOffset[][5]; uint elementOffset[][4];
}; };
layout(std430, binding = 3) buffer elementIndexBuffer layout(std430, binding = 4) buffer elementIndexBuffer
{ {
uint elementIndexs[]; // ÏߺÍÃæ uint elementIndexs[]; // ÏߺÍÃæ
}; };
layout(std430, binding = 4) buffer elementDataBuffer layout(std430, binding = 5) buffer elementDataBuffer
{ {
float elementData[]; // µãºÍStyle float elementData[]; // µãºÍStyle
}; };
const float PI = 3.14159265358979; const float PI = 3.14159265358979;
const uint STACK_SIZE = 10; const uint STACK_SIZE = 10;
struct Stack struct Stack
@ -77,7 +84,6 @@ struct Stack
} }
} stack, elementStack; } stack, elementStack;
// Modified from http://tog.acm.org/resources/GraphicsGems/gems/Roots3And4.c // Modified from http://tog.acm.org/resources/GraphicsGems/gems/Roots3And4.c
// Credits to Doublefresh for hinting there // Credits to Doublefresh for hinting there
int solve_quadric(vec2 coeffs, inout vec2 roots) int solve_quadric(vec2 coeffs, inout vec2 roots)
@ -151,7 +157,6 @@ int solve_cubic(vec3 coeffs, inout vec3 r)
return 3; return 3;
} }
int segment_int_test(vec2 uv, vec2 p0, vec2 p1) int segment_int_test(vec2 uv, vec2 p0, vec2 p1)
{ {
p0 -= uv; p0 -= uv;
@ -299,15 +304,19 @@ bvec3 cubic_bezier_sign_test(vec2 uv, vec2 p0, vec2 p1, vec2 p2, vec2 p3)
// int n_ints = 0; // int n_ints = 0;
bvec3 result = bvec3(false); bvec3 result = bvec3(false);
for(int i=0;i<3;i++){ for (int i = 0; i < 3; i++)
if(i < n_roots){ {
if(roots[i] >= 0. && roots[i] <= 1.){ if (i < n_roots)
{
if (roots[i] >= 0. && roots[i] <= 1.)
{
float x_pos = -p0.x + 3. * p1.x - 3. * p2.x + p3.x; float x_pos = -p0.x + 3. * p1.x - 3. * p2.x + p3.x;
x_pos = x_pos * roots[i] + 3. * p0.x - 6. * p1.x + 3. * p2.x; x_pos = x_pos * roots[i] + 3. * p0.x - 6. * p1.x + 3. * p2.x;
x_pos = x_pos * roots[i] + -3. * p0.x + 3. * p1.x; x_pos = x_pos * roots[i] + -3. * p0.x + 3. * p1.x;
x_pos = x_pos * roots[i] + p0.x; x_pos = x_pos * roots[i] + p0.x;
if(x_pos > uv.x){ if (x_pos > uv.x)
{
result[1] = !result[1]; result[1] = !result[1];
} }
} }
@ -320,24 +329,32 @@ bvec3 cubic_bezier_sign_test(vec2 uv, vec2 p0, vec2 p1, vec2 p2, vec2 p3)
vec2 nor1 = vec2(tang1.y, -tang1.x); vec2 nor1 = vec2(tang1.y, -tang1.x);
vec2 nor2 = vec2(tang2.y, -tang2.x); vec2 nor2 = vec2(tang2.y, -tang2.x);
if(p0.y < p1.y){ if (p0.y < p1.y)
if((uv.y<=p0.y) && (dot(uv-p0.xy,nor1)>0.)){ {
if ((uv.y <= p0.y) && (dot(uv - p0.xy, nor1) > 0.))
{
result[0] = !result[0]; result[0] = !result[0];
} }
} }
else{ else
if(!(uv.y<=p0.y) && !(dot(uv-p0.xy,nor1)>0.)){ {
if (!(uv.y <= p0.y) && !(dot(uv - p0.xy, nor1) > 0.))
{
result[0] = !result[0]; result[0] = !result[0];
} }
} }
if(p2.y<p3.y){ if (p2.y < p3.y)
if(!(uv.y<=p3.y) && dot(uv-p3.xy,nor2)>0.){ {
if (!(uv.y <= p3.y) && dot(uv - p3.xy, nor2) > 0.)
{
result[2] = !result[2]; result[2] = !result[2];
} }
} }
else{ else
if((uv.y<=p3.y) && !(dot(uv-p3.xy,nor2)>0.)){ {
if ((uv.y <= p3.y) && !(dot(uv - p3.xy, nor2) > 0.))
{
result[2] = !result[2]; result[2] = !result[2];
} }
} }
@ -355,7 +372,8 @@ const float eps = .000005;
const int halley_iterations = 8; const int halley_iterations = 8;
// lagrange positive real root upper bound // lagrange positive real root upper bound
// see for example: https://doi.org/10.1016/j.jsc.2014.09.038 // see for example: https://doi.org/10.1016/j.jsc.2014.09.038
float upper_bound_lagrange5(float a0, float a1, float a2, float a3, float a4){ float upper_bound_lagrange5(float a0, float a1, float a2, float a3, float a4)
{
vec4 coeffs1 = vec4(a0, a1, a2, a3); vec4 coeffs1 = vec4(a0, a1, a2, a3);
@ -383,7 +401,8 @@ float upper_bound_lagrange5(float a0, float a1, float a2, float a3, float a4){
} }
// lagrange upper bound applied to f(-x) to get lower bound // lagrange upper bound applied to f(-x) to get lower bound
float lower_bound_lagrange5(float a0, float a1, float a2, float a3, float a4){ float lower_bound_lagrange5(float a0, float a1, float a2, float a3, float a4)
{
vec4 coeffs1 = vec4(-a0, a1, -a2, a3); vec4 coeffs1 = vec4(-a0, a1, -a2, a3);
@ -410,7 +429,8 @@ float lower_bound_lagrange5(float a0, float a1, float a2, float a3, float a4){
return -max_max - max_max2; return -max_max - max_max2;
} }
vec2 parametric_cub_bezier(float t, vec2 p0, vec2 p1, vec2 p2, vec2 p3){ vec2 parametric_cub_bezier(float t, vec2 p0, vec2 p1, vec2 p2, vec2 p3)
{
vec2 a0 = (-p0 + 3. * p1 - 3. * p2 + p3); vec2 a0 = (-p0 + 3. * p1 - 3. * p2 + p3);
vec2 a1 = (3. * p0 - 6. * p1 + 3. * p2); vec2 a1 = (3. * p0 - 6. * p1 + 3. * p2);
vec2 a2 = (-3. * p0 + 3. * p1); vec2 a2 = (-3. * p0 + 3. * p1);
@ -419,7 +439,8 @@ vec2 parametric_cub_bezier(float t, vec2 p0, vec2 p1, vec2 p2, vec2 p3){
return (((a0 * t) + a1) * t + a2) * t + a3; return (((a0 * t) + a1) * t + a2) * t + a3;
} }
void sort_roots3(inout vec3 roots){ void sort_roots3(inout vec3 roots)
{
vec3 tmp; vec3 tmp;
tmp[0] = min(roots[0], min(roots[1], roots[2])); tmp[0] = min(roots[0], min(roots[1], roots[2]));
@ -429,7 +450,8 @@ void sort_roots3(inout vec3 roots){
roots = tmp; roots = tmp;
} }
void sort_roots4(inout vec4 roots){ void sort_roots4(inout vec4 roots)
{
vec4 tmp; vec4 tmp;
vec2 min1_2 = min(roots.xz, roots.yw); vec2 min1_2 = min(roots.xz, roots.yw);
@ -446,7 +468,8 @@ void sort_roots4(inout vec4 roots){
roots = tmp; roots = tmp;
} }
float eval_poly5(float a0, float a1, float a2, float a3, float a4, float x){ float eval_poly5(float a0, float a1, float a2, float a3, float a4, float x)
{
float f = ((((x + a4) * x + a3) * x + a2) * x + a1) * x + a0; float f = ((((x + a4) * x + a3) * x + a2) * x + a1) * x + a0;
@ -457,7 +480,8 @@ float eval_poly5(float a0, float a1, float a2, float a3, float a4, float x){
// basically a variant of newton raphson which converges quicker and has bigger basins of convergence // basically a variant of newton raphson which converges quicker and has bigger basins of convergence
// see http://mathworld.wolfram.com/HalleysMethod.html // see http://mathworld.wolfram.com/HalleysMethod.html
// or https://en.wikipedia.org/wiki/Halley%27s_method // or https://en.wikipedia.org/wiki/Halley%27s_method
float halley_iteration5(float a0, float a1, float a2, float a3, float a4, float x){ float halley_iteration5(float a0, float a1, float a2, float a3, float a4, float x)
{
float f = ((((x + a4) * x + a3) * x + a2) * x + a1) * x + a0; float f = ((((x + a4) * x + a3) * x + a2) * x + a1) * x + a0;
float f1 = (((5. * x + 4. * a4) * x + 3. * a3) * x + 2. * a2) * x + a1; float f1 = (((5. * x + 4. * a4) * x + 3. * a3) * x + 2. * a2) * x + a1;
@ -466,7 +490,8 @@ float halley_iteration5(float a0, float a1, float a2, float a3, float a4, float
return x - (2. * f * f1) / (2. * f1 * f1 - f * f2); return x - (2. * f * f1) / (2. * f1 * f1 - f * f2);
} }
float halley_iteration4(vec4 coeffs, float x){ float halley_iteration4(vec4 coeffs, float x)
{
float f = (((x + coeffs[3]) * x + coeffs[2]) * x + coeffs[1]) * x + coeffs[0]; float f = (((x + coeffs[3]) * x + coeffs[2]) * x + coeffs[1]) * x + coeffs[0];
float f1 = ((4. * x + 3. * coeffs[3]) * x + 2. * coeffs[2]) * x + coeffs[1]; float f1 = ((4. * x + 3. * coeffs[3]) * x + 2. * coeffs[2]) * x + coeffs[1];
@ -477,7 +502,8 @@ float halley_iteration4(vec4 coeffs, float x){
// Modified from http://tog.acm.org/resources/GraphicsGems/gems/Roots3And4.c // Modified from http://tog.acm.org/resources/GraphicsGems/gems/Roots3And4.c
// Credits to Doublefresh for hinting there // Credits to Doublefresh for hinting there
int solve_quartic(vec4 coeffs, inout vec4 s){ int solve_quartic(vec4 coeffs, inout vec4 s)
{
float a = coeffs[3]; float a = coeffs[3];
float b = coeffs[2]; float b = coeffs[2];
@ -529,17 +555,21 @@ int solve_quartic(vec4 coeffs, inout vec4 s){
float u = z * z - r; float u = z * z - r;
float v = 2. * z - p; float v = 2. * z - p;
if(u > -eps){ if (u > -eps)
{
u = sqrt(abs(u)); u = sqrt(abs(u));
} }
else{ else
{
return 0; return 0;
} }
if(v > -eps){ if (v > -eps)
{
v = sqrt(abs(v)); v = sqrt(abs(v));
} }
else{ else
{
return 0; return 0;
} }
@ -557,12 +587,15 @@ int solve_quartic(vec4 coeffs, inout vec4 s){
int old_num = num; int old_num = num;
num += solve_quadric(quad_coeffs, tmp); num += solve_quadric(quad_coeffs, tmp);
if(old_num!=num){ if (old_num != num)
if(old_num == 0){ {
if (old_num == 0)
{
s[0] = tmp[0]; s[0] = tmp[0];
s[1] = tmp[1]; s[1] = tmp[1];
} }
else{//old_num == 2 else
{ // old_num == 2
s[2] = tmp[0]; s[2] = tmp[0];
s[3] = tmp[1]; s[3] = tmp[1];
} }
@ -574,8 +607,10 @@ int solve_quartic(vec4 coeffs, inout vec4 s){
float sub = 1. / 4. * a; float sub = 1. / 4. * a;
/* single halley iteration to fix cancellation */ /* single halley iteration to fix cancellation */
for(int i=0;i<4;i+=2){ for (int i = 0; i < 4; i += 2)
if(i < num){ {
if (i < num)
{
s[i] -= sub; s[i] -= sub;
s[i] = halley_iteration4(coeffs, s[i]); s[i] = halley_iteration4(coeffs, s[i]);
@ -586,7 +621,8 @@ int solve_quartic(vec4 coeffs, inout vec4 s){
return num; return num;
} }
float cubic_bezier_dis(vec2 uv, vec2 p0, vec2 p1, vec2 p2, vec2 p3, bool roundEnd){ float cubic_bezier_dis(vec2 uv, vec2 p0, vec2 p1, vec2 p2, vec2 p3, bool roundEnd)
{
// switch points when near to end point to minimize numerical error // switch points when near to end point to minimize numerical error
// only needed when control point(s) very far away // only needed when control point(s) very far away
@ -659,57 +695,71 @@ float cubic_bezier_dis(vec2 uv, vec2 p0, vec2 p1, vec2 p2, vec2 p3, bool roundEn
// compute root isolating intervals by roots of derivative and outer root bounds // compute root isolating intervals by roots of derivative and outer root bounds
// only roots going form - to + considered, because only those result in a minimum // only roots going form - to + considered, because only those result in a minimum
if(num_roots_drv==4){ if (num_roots_drv == 4)
if(eval_poly5(b0,b1,b2,b3,b4,roots_drv[0]) > 0.){ {
if (eval_poly5(b0, b1, b2, b3, b4, roots_drv[0]) > 0.)
{
a[0] = lb; a[0] = lb;
b[0] = roots_drv[0]; b[0] = roots_drv[0];
num_roots = 1; num_roots = 1;
} }
if(sign(eval_poly5(b0,b1,b2,b3,b4,roots_drv[1])) != sign(eval_poly5(b0,b1,b2,b3,b4,roots_drv[2]))){ if (sign(eval_poly5(b0, b1, b2, b3, b4, roots_drv[1])) != sign(eval_poly5(b0, b1, b2, b3, b4, roots_drv[2])))
if(num_roots == 0){ {
if (num_roots == 0)
{
a[0] = roots_drv[1]; a[0] = roots_drv[1];
b[0] = roots_drv[2]; b[0] = roots_drv[2];
num_roots = 1; num_roots = 1;
} }
else{ else
{
a[1] = roots_drv[1]; a[1] = roots_drv[1];
b[1] = roots_drv[2]; b[1] = roots_drv[2];
num_roots = 2; num_roots = 2;
} }
} }
if(eval_poly5(b0,b1,b2,b3,b4,roots_drv[3]) < 0.){ if (eval_poly5(b0, b1, b2, b3, b4, roots_drv[3]) < 0.)
if(num_roots == 0){ {
if (num_roots == 0)
{
a[0] = roots_drv[3]; a[0] = roots_drv[3];
b[0] = ub; b[0] = ub;
num_roots = 1; num_roots = 1;
} }
else if(num_roots == 1){ else if (num_roots == 1)
{
a[1] = roots_drv[3]; a[1] = roots_drv[3];
b[1] = ub; b[1] = ub;
num_roots = 2; num_roots = 2;
} }
else{ else
{
a[2] = roots_drv[3]; a[2] = roots_drv[3];
b[2] = ub; b[2] = ub;
num_roots = 3; num_roots = 3;
} }
} }
} }
else{ else
if(num_roots_drv==2){ {
if(eval_poly5(b0,b1,b2,b3,b4,roots_drv[0]) < 0.){ if (num_roots_drv == 2)
{
if (eval_poly5(b0, b1, b2, b3, b4, roots_drv[0]) < 0.)
{
num_roots = 1; num_roots = 1;
a[0] = roots_drv[1]; a[0] = roots_drv[1];
b[0] = ub; b[0] = ub;
} }
else if(eval_poly5(b0,b1,b2,b3,b4,roots_drv[1]) > 0.){ else if (eval_poly5(b0, b1, b2, b3, b4, roots_drv[1]) > 0.)
{
num_roots = 1; num_roots = 1;
a[0] = lb; a[0] = lb;
b[0] = roots_drv[0]; b[0] = roots_drv[0];
} }
else{ else
{
num_roots = 2; num_roots = 2;
a[0] = lb; a[0] = lb;
@ -718,9 +768,9 @@ float cubic_bezier_dis(vec2 uv, vec2 p0, vec2 p1, vec2 p2, vec2 p3, bool roundEn
a[1] = roots_drv[1]; a[1] = roots_drv[1];
b[1] = ub; b[1] = ub;
} }
} }
else{//num_roots_drv==0 else
{ // num_roots_drv==0
vec3 roots_snd_drv = vec3(1e38); vec3 roots_snd_drv = vec3(1e38);
int num_roots_snd_drv = solve_cubic(c2, roots_snd_drv); int num_roots_snd_drv = solve_cubic(c2, roots_snd_drv);
@ -741,31 +791,44 @@ float cubic_bezier_dis(vec2 uv, vec2 p0, vec2 p1, vec2 p2, vec2 p3, bool roundEn
int num_roots_trd_drv = 0; int num_roots_trd_drv = 0;
vec2 roots_trd_drv = vec2(1e38); vec2 roots_trd_drv = vec2(1e38);
if(num_roots_snd_drv!=3){ if (num_roots_snd_drv != 3)
{
num_roots_trd_drv = solve_quadric(c3, roots_trd_drv); num_roots_trd_drv = solve_quadric(c3, roots_trd_drv);
} }
for(int i=0;i<3;i++){ for (int i = 0; i < 3; i++)
if(i < num_roots){ {
for(int j=0;j<3;j+=2){ if (i < num_roots)
if(j < num_roots_snd_drv){ {
if(a[i] < roots_snd_drv[j] && b[i] > roots_snd_drv[j]){ for (int j = 0; j < 3; j += 2)
if(eval_poly5(b0,b1,b2,b3,b4,roots_snd_drv[j]) > 0.){ {
if (j < num_roots_snd_drv)
{
if (a[i] < roots_snd_drv[j] && b[i] > roots_snd_drv[j])
{
if (eval_poly5(b0, b1, b2, b3, b4, roots_snd_drv[j]) > 0.)
{
b[i] = roots_snd_drv[j]; b[i] = roots_snd_drv[j];
} }
else{ else
{
a[i] = roots_snd_drv[j]; a[i] = roots_snd_drv[j];
} }
} }
} }
} }
for(int j=0;j<2;j++){ for (int j = 0; j < 2; j++)
if(j < num_roots_trd_drv){ {
if(a[i] < roots_trd_drv[j] && b[i] > roots_trd_drv[j]){ if (j < num_roots_trd_drv)
if(eval_poly5(b0,b1,b2,b3,b4,roots_trd_drv[j]) > 0.){ {
if (a[i] < roots_trd_drv[j] && b[i] > roots_trd_drv[j])
{
if (eval_poly5(b0, b1, b2, b3, b4, roots_trd_drv[j]) > 0.)
{
b[i] = roots_trd_drv[j]; b[i] = roots_trd_drv[j];
} }
else{ else
{
a[i] = roots_trd_drv[j]; a[i] = roots_trd_drv[j];
} }
} }
@ -779,15 +842,17 @@ float cubic_bezier_dis(vec2 uv, vec2 p0, vec2 p1, vec2 p2, vec2 p3, bool roundEn
// compute roots with halley's method // compute roots with halley's method
for(int i=0;i<3;i++){ for (int i = 0; i < 3; i++)
if(i < num_roots){ {
if (i < num_roots)
{
roots[i] = .5 * (a[i] + b[i]); roots[i] = .5 * (a[i] + b[i]);
for(int j=0;j<halley_iterations;j++){ for (int j = 0; j < halley_iterations; j++)
{
roots[i] = halley_iteration5(b0, b1, b2, b3, b4, roots[i]); roots[i] = halley_iteration5(b0, b1, b2, b3, b4, roots[i]);
} }
// compute squared distance to nearest point on curve // compute squared distance to nearest point on curve
if (roundEnd) if (roundEnd)
{ {
@ -797,21 +862,20 @@ float cubic_bezier_dis(vec2 uv, vec2 p0, vec2 p1, vec2 p2, vec2 p3, bool roundEn
} }
else else
{ {
if(roots[i]<0.||roots[i]>1.) d0=min(d0,1e38); if (roots[i] < 0. || roots[i] > 1.)
d0 = min(d0, 1e38);
else else
{ {
vec2 to_curve = uv - parametric_cub_bezier(roots[i], p0, p1, p2, p3); vec2 to_curve = uv - parametric_cub_bezier(roots[i], p0, p1, p2, p3);
d0 = min(d0, dot(to_curve, to_curve)); d0 = min(d0, dot(to_curve, to_curve));
} }
} }
} }
} }
return sqrt(d0); return sqrt(d0);
} }
int cubic_bezier_int_test2(vec2 uv, vec2 p0, vec2 p1, vec2 p2, vec2 p3, bool reverse) int cubic_bezier_int_test2(vec2 uv, vec2 p0, vec2 p1, vec2 p2, vec2 p3, bool reverse)
{ {
float cu = (-p0.y + 3. * p1.y - 3. * p2.y + p3.y); float cu = (-p0.y + 3. * p1.y - 3. * p2.y + p3.y);
@ -831,8 +895,10 @@ int cubic_bezier_int_test2(vec2 uv, vec2 p0, vec2 p1, vec2 p2, vec2 p3, bool rev
} }
else else
{ {
if (abs(cu) < .0001) n_roots = solve_quadric(vec2(co / qu, li / qu), roots.xy); if (abs(cu) < .0001)
else n_roots = solve_cubic(vec3(co / cu, li / cu, qu / cu), roots); n_roots = solve_quadric(vec2(co / qu, li / qu), roots.xy);
else
n_roots = solve_cubic(vec3(co / cu, li / cu, qu / cu), roots);
for (int i = 0; i < n_roots; i++) for (int i = 0; i < n_roots; i++)
{ {
@ -843,7 +909,8 @@ int cubic_bezier_int_test2(vec2 uv, vec2 p0, vec2 p1, vec2 p2, vec2 p3, bool rev
x_pos = x_pos * roots[i] + -3. * p0.x + 3. * p1.x; x_pos = x_pos * roots[i] + -3. * p0.x + 3. * p1.x;
x_pos = x_pos * roots[i] + p0.x; x_pos = x_pos * roots[i] + p0.x;
if (reverse? x_pos < uv.x: x_pos > uv.x) n_ints++; if (reverse ? x_pos < uv.x : x_pos > uv.x)
n_ints++;
} }
} }
} }
@ -858,10 +925,12 @@ int ray_int_test(vec2 uv, vec2 p0, vec2 direction, bool reverse)
vec2 nor = -direction; vec2 nor = -direction;
nor = vec2(nor.y, -nor.x); nor = vec2(nor.y, -nor.x);
float sgn = p0.y > direction.y ? 1. : -1.; float sgn = p0.y > direction.y ? 1. : -1.;
if(reverse) sgn = -sgn; if (reverse)
sgn = -sgn;
return dot(nor, p0) * sgn < 0. ? 0 : 1; return dot(nor, p0) * sgn < 0. ? 0 : 1;
} }
else return 0; else
return 0;
} }
vec2 bezierTangent(float t, vec2 p0, vec2 p1, vec2 p2, vec2 p3) vec2 bezierTangent(float t, vec2 p0, vec2 p1, vec2 p2, vec2 p3)
@ -962,65 +1031,63 @@ void drawLine(in float d, in uint styleIndex, out vec4 elementColor, out vec2 me
} }
} }
bool drawElement(uint elementIndex, vec2 localUV, vec2 scale, out vec3 color, out vec2 metallicRoughness, inout vec3 debugBVH = vec3(0)) bool shouldFillBeginCap(vec2 localUV, bool onVeryBegin, int endType, vec2 p0, vec2 tangentBegin, vec2 tangentEndLast)
{
vec2 normal;
if (onVeryBegin)
{
if (endType % 2 == 0)
return true;
else if (endType % 2 == 1)
normal = normalize(mat2(0, 1, -1, 0) * (-tangentBegin));
}
else
{
vec2 normalLast = normalize(mat2(0, 1, -1, 0) * tangentEndLast);
vec2 normalNow = normalize(mat2(0, 1, -1, 0) * (-tangentBegin));
normal = normalLast + normalNow;
}
return angleLargeThanPi(normal, localUV - p0);
}
bool shouldFillEndCap(vec2 localUV, bool onVeryEnd, int endType, vec2 p3, vec2 tangentEnd, vec2 tangentBeginNext)
{
vec2 normal;
if (onVeryEnd)
{
if ((endType / 2) % 2 == 0)
return true;
else if ((endType / 2) % 2 == 1)
normal = normalize(mat2(0, 1, -1, 0) * tangentEnd);
}
else
{
vec2 normalLast = normalize(mat2(0, 1, -1, 0) * tangentEnd);
vec2 normalNow = normalize(mat2(0, 1, -1, 0) * (-tangentBeginNext));
normal = normalLast + normalNow;
}
return angleLargeThanPi(localUV - p3, normal);
}
bool fillElement(vec2 localUV, uint contourIndex, uint linesOffset, uint pointsOffset, uint styleIndex,
inout vec4 elementColor, inout vec2 metallicRoughness)
{ {
bool hitElement = false; bool hitElement = false;
vec4 elementColor = vec4(-1);
metallicRoughness = vec2(0, 0.8);
uint currentOffset[] = elementOffset[elementIndex];
uint elementBvhRoot = currentOffset[0];
uint styleIndex = currentOffset[1];
uint elementBvhLength = 0x80000000;
uint pointsOffset = currentOffset[2];
uint linesOffset = currentOffset[3];
elementStack.top = 0;
uint elementBvhIndex = 0;
while (elementBvhIndex < elementBvhLength || !elementStack.empty())
{
while (elementBvhIndex < elementBvhLength)
{
vec4 bound = bvhBound[elementBvhRoot + elementBvhIndex];
uint leftChild = bvhChildren[elementBvhRoot + elementBvhIndex].x;
if (all(lessThan(bound.xy, localUV)) && all(lessThan(localUV, bound.zw)))
{
if (leftChild >= elementBvhLength)
{
if (any(greaterThan(bound.xy+vec2(0.003), localUV)) || any(greaterThan(localUV, bound.zw-vec2(0.003))))
{
debugBVH.g = 0;
debugBVH.r += 1;
}
//uint styleIndex = bvhChildren[elementBvhRoot + elementBvhIndex].y;
//uint elementType = bvhChildren[elementBvhRoot + elementBvhIndex].y;
//float elementType = elementData[styleIndex];
bool isFillStyle = elementData[styleIndex]<=0;
// for(int i = 0; i<200;i++)
if (isFillStyle) //Ãæ
{
uint contourIndex = linesOffset + leftChild - 0x80000000;
uint num_its = 0; uint num_its = 0;
uint lineCount = elementIndexs[contourIndex]; uint lineCount = elementIndexs[contourIndex];
for (uint contourIterator = contourIndex + 1; contourIterator < contourIndex + 1 + lineCount; contourIterator++) for (uint contourIterator = contourIndex + 1; contourIterator < contourIndex + 1 + lineCount; contourIterator++)
{ {
uint lineIndex = elementIndexs[contourIterator]; uint lineIndex = elementIndexs[contourIterator];
uint pLocation = linesOffset + 2 * lineIndex; uint pLocation = linesOffset + 2 * lineIndex;
uvec4 pxIndex = uvec4(pointsOffset)+2*uvec4(elementIndexs[pLocation]>>16, elementIndexs[pLocation]&0xFFFF, elementIndexs[pLocation+1]>>16, elementIndexs[pLocation+1]&0xFFFF); 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; uvec4 pyIndex = uvec4(1) + pxIndex;
mat4x2 p = mat4x2(elementData[pxIndex[0]], elementData[pyIndex[0]], mat4x2 p =
elementData[pxIndex[1]], elementData[pyIndex[1]], mat4x2(elementData[pxIndex[0]], elementData[pyIndex[0]], elementData[pxIndex[1]], elementData[pyIndex[1]],
elementData[pxIndex[2]],elementData[pyIndex[2]], elementData[pxIndex[2]], elementData[pyIndex[2]], elementData[pxIndex[3]], elementData[pyIndex[3]]);
elementData[pxIndex[3]], elementData[pyIndex[3]]);
// vec2 p[4] = {vec2(elementData[pxIndex[0]], elementData[pyIndex[0]]),
// vec2(elementData[pxIndex[1]], elementData[pyIndex[1]]),
// vec2(elementData[pxIndex[2]],elementData[pyIndex[2]]),
// vec2(elementData[pxIndex[3]], elementData[pyIndex[3]])};
// if (bound.z == p[0].x && distance(localUV, p[3]) < 0.01) // if (bound.z == p[0].x && distance(localUV, p[3]) < 0.01)
// { // {
// debugBVH = vec3(0, 0, 1); // debugBVH = vec3(0, 0, 1);
@ -1031,7 +1098,6 @@ bool drawElement(uint elementIndex, vec2 localUV, vec2 scale, out vec3 color, ou
// { // {
// num_its += segment_int_test(localUV, p0, p3); // num_its += segment_int_test(localUV, p0, p3);
// } // }
//
// else // else
num_its += cubic_bezier_int_test(localUV, p[0], p[1], p[2], p[3]); num_its += cubic_bezier_int_test(localUV, p[0], p[1], p[2], p[3]);
} }
@ -1047,48 +1113,70 @@ bool drawElement(uint elementIndex, vec2 localUV, vec2 scale, out vec3 color, ou
elementColor = vec4(unpackUnorm4x8(floatBitsToUint(elementData[styleIndex + 1])).rgb, 0); elementColor = vec4(unpackUnorm4x8(floatBitsToUint(elementData[styleIndex + 1])).rgb, 0);
metallicRoughness = head.xy; metallicRoughness = head.xy;
} }
} }
return hitElement;
} }
else //Ïß
bool strokeElement(vec2 localUV, uint contourIndex, uint linesOffset, uint pointsOffset, uint styleIndex,
inout vec4 elementColor, inout vec2 metallicRoughness)
{ {
bool hitElement = false;
float strokeWidth = elementData[styleIndex]; float strokeWidth = elementData[styleIndex];
float widthHeightRatio = uintBitsToFloat(currentOffset[4]);
vec2 size = normalize(vec2(widthHeightRatio, 1)) + vec2(2 * strokeWidth);
vec2 ratio = widthHeightRatio < 1 ? vec2(widthHeightRatio, 1) : vec2(1, 1 / widthHeightRatio);
localUV *= ratio;
uint contourIndex = linesOffset + leftChild - 0x80000000;
float minDistance = 1e38; float minDistance = 1e38;
uint lineCount = elementIndexs[contourIndex]; uint lineCount = elementIndexs[contourIndex];
vec4 styleHead = unpackUnorm4x8(floatBitsToUint(elementData[styleIndex + 1])); vec4 styleHead = unpackUnorm4x8(floatBitsToUint(elementData[styleIndex + 1]));
float lineType = floor(styleHead.b * 10); float lineType = floor(styleHead.b * 10);
//float lineType = 2; int endType = int(round(styleHead.b * 100)) % 10;
vec2 p3Last = vec2(1e38); vec2 p3Last = vec2(1e38);
vec2 p2Last = vec2(1e38); vec2 p2Last = vec2(1e38);
vec2 tangentEndLast;
int debugBegin = 0; int debugBegin = 0;
for ( uint contourIterator_ = contourIndex + 1;contourIterator_ <= contourIndex + 1 + lineCount; contourIterator_++) for (uint contourIterator_ = contourIndex + 1; contourIterator_ < contourIndex + 1 + lineCount; contourIterator_++)
{ {
uint contourIterator = contourIterator_; uint contourIterator = contourIterator_;
if (contourIterator_ == contourIndex + 1 + lineCount) if (contourIterator_ == contourIndex + 1 + lineCount)
contourIterator = contourIndex + 1; contourIterator = contourIndex + 1;
uint lineIndex = elementIndexs[contourIterator]; uint lineIndex = elementIndexs[contourIterator];
uint pLocation = linesOffset + 3 * lineIndex; 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); vec2 percent = unpackUnorm2x16(elementIndexs[pLocation + 2]);
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; uvec4 pyIndex = uvec4(1) + pxIndex;
mat4x2 p = mat4x2(elementData[pxIndex[0]], elementData[pyIndex[0]], mat4x2 p =
elementData[pxIndex[1]], elementData[pyIndex[1]], mat4x2(elementData[pxIndex[0]], elementData[pyIndex[0]], elementData[pxIndex[1]], elementData[pyIndex[1]],
elementData[pxIndex[2]], elementData[pyIndex[2]], elementData[pxIndex[2]], elementData[pyIndex[2]], elementData[pxIndex[3]], elementData[pyIndex[3]]);
vec2 tangentBeginNext = vec2(0);
if (contourIterator + 1 < contourIndex + 1 + lineCount)
{
uint lineIndex = elementIndexs[contourIterator + 1];
uint pLocation = linesOffset + 3 * lineIndex;
// vec2 percent = unpackUnorm2x16(elementIndexs[pLocation + 2]);
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 pNext = 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]]); elementData[pxIndex[3]], elementData[pyIndex[3]]);
p[0] *= ratio; if (pNext[0] == pNext[1] && pNext[2] == pNext[3])
p[1] *= ratio; {
p[2] *= ratio; pNext[1] = (pNext[0] + pNext[3]) / 2;
p[3] *= ratio; 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]);
}
if (p[0] == p[1] && p[2] == p[3]) if (p[0] == p[1] && p[2] == p[3])
{ {
@ -1098,63 +1186,129 @@ bool drawElement(uint elementIndex, vec2 localUV, vec2 scale, out vec3 color, ou
if (distance(localUV, p[0]) <= 0.001) if (distance(localUV, p[0]) <= 0.001)
{ {
if(p3Last==p[0]) debugBegin = 2; if (p3Last == p[0])
else debugBegin = 1; debugBegin = 2;
else
debugBegin = 1;
} }
float d = cubic_bezier_dis(localUV, p[0], p[1], p[2], p[3], true); float d = cubic_bezier_dis(localUV, p[0], p[1], p[2], p[3], true);
if (d <= strokeWidth) if (d <= strokeWidth)
{ {
bool onBegin = distance(localUV,p[0])<=strokeWidth&&p3Last==p[0]; bool onBegin =
bool fill = true; distance(localUV, p[0]) <= strokeWidth; //&& (p3Last == p[0] || contourIterator == contourIndex + 1);
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) if (onBegin)
hit = hit && shouldFillBeginCap(localUV, contourIterator == contourIndex + 1, endType, p[0],
tangentBegin, p3Last - p2Last);
if (onEnd)
hit = hit && shouldFillEndCap(localUV, tangentBeginNext == vec2(0), endType, p[3], tangentEnd,
tangentBeginNext);
if (hit)
{ {
vec2 normalLast = normalize(mat2(0,1,-1,0)*(p3Last-p2Last));
vec2 normalNow = normalize(mat2(0,1,-1,0)*(p[1]-p[0]));
vec2 normal = normalLast+normalNow;
fill = angleLargeThanPi(normal, localUV-p[0]);
}
if(onBegin?fill:d<minDistance)
{
minDistance = min(minDistance, d);
bool reverse = p[3].y - p[0].y < 0.; bool reverse = p[3].y - p[0].y < 0.;
vec2 tangentBegin = normalize(p[0]-p[1]); // if (tangentBegin.y == 0.)
vec2 tangentEnd = normalize(p[3]-p[2]); // tangentBegin.y = reverse ? eps : -eps;
if(tangentBegin.y==0.) tangentBegin.y=reverse?eps:-eps; // if (tangentEnd.y == 0.)
if(tangentEnd.y==0.) tangentEnd.y=reverse?-eps:eps; // tangentEnd.y = reverse ? -eps : eps;
int intTest = cubic_bezier_int_test2(localUV, p[0], p[1], p[2], p[3], reverse) int intTest = cubic_bezier_int_test2(localUV, p[0], p[1], p[2], p[3], reverse) +
+ ray_int_test(localUV, p[0], tangentBegin, reverse) ray_int_test(localUV, p[0], tangentBegin, reverse) +
+ ray_int_test(localUV, p[3], tangentEnd, reverse); ray_int_test(localUV, p[3], tangentEnd, reverse);
if(lineType==2 || intTest%2==int(lineType))
if (lineType == 2 || (intTest % 2 == int(lineType)))
{ {
minDistance = min(minDistance, d);
hitElement = true; hitElement = true;
// elementColor = vec4(1, 1, 0, 1);
vec2 metallicRoughness;
drawLine(minDistance / strokeWidth, styleIndex, elementColor, metallicRoughness); drawLine(minDistance / strokeWidth, styleIndex, elementColor, metallicRoughness);
} }
else if(p3Last==p[0]) hitElement = false; // else if (p3Last == p[0])
// if(distance(localUV,p[0])<=strokeWidth&&p3Last==p[0]&&fill) // hitElement = false;
// {
// hitElement = true;
// elementColor = vec4(0,0,1,1);
// }
} }
tangentEndLast = tangentEnd;
} }
p3Last = p[3]; p3Last = p[3];
p2Last = p[2]; p2Last = p[2];
} }
if (hitElement && distance(localUV, p3Last) <= strokeWidth)
if(minDistance<=0.001)
{ {
hitElement = true; // hitElement = shouldFillEndCap(localUV, percent[1] > 1 - 1e-5, endType, p3Last, tangentEndLast, vec2(0));
elementColor = vec4(0,0,0,1);
if(debugBegin==1)
elementColor = vec4(1,1,0,1);
else if(debugBegin==2)
elementColor = vec4(0,1,0,1);
} }
// if (minDistance <= 0.001)
// {
// hitElement = true;
// elementColor = vec4(0, 0, 0, 1);
// if (debugBegin == 1)
// elementColor = vec4(1, 1, 0, 1);
// else if (debugBegin == 2)
// elementColor = vec4(0, 1, 0, 1);
// }
return hitElement;
}
bool drawElement(uint elementIndex, vec2 localUV, out vec3 color, out vec2 metallicRoughness,
inout vec3 debugBVH = vec3(0))
{
bool hitElement = false;
vec4 elementColor = vec4(-1);
metallicRoughness = vec2(0, 0.8);
uint currentOffset[] = elementOffset[elementIndex];
uint elementBvhRoot = currentOffset[0];
uint styleIndex = currentOffset[1];
uint pointsOffset = currentOffset[2];
uint linesOffset = currentOffset[3];
// float widthHeightRatio = uintBitsToFloat(currentOffset[4]);
elementStack.top = 0;
uint elementBvhIndex = 0;
uint elementBvhLength = 0x80000000;
while (elementBvhIndex < elementBvhLength || !elementStack.empty())
{
while (elementBvhIndex < elementBvhLength)
{
vec4 bound = bvhBound[elementBvhRoot + elementBvhIndex];
uint leftChild = bvhChildren[elementBvhRoot + elementBvhIndex].x;
if (all(lessThan(bound.xy, localUV)) && all(lessThan(localUV, bound.zw)))
{
if (leftChild >= elementBvhLength)
{
if (any(greaterThan(bound.xy + vec2(0.003), localUV)) ||
any(greaterThan(localUV, bound.zw - vec2(0.003))))
{
debugBVH.g = 0;
debugBVH.r += 1;
}
uint contourIndex = linesOffset + leftChild - 0x80000000;
bool isFillStyle = elementData[styleIndex] <= 0;
if (isFillStyle) // Ãæ
{
hitElement = fillElement(localUV, contourIndex, linesOffset, pointsOffset, styleIndex,
elementColor, metallicRoughness);
}
else // Ïß
{
hitElement = strokeElement(localUV, contourIndex, linesOffset, pointsOffset, styleIndex,
elementColor, metallicRoughness);
} }
elementBvhIndex = elementBvhLength; elementBvhIndex = elementBvhLength;
@ -1182,7 +1336,6 @@ bool drawElement(uint elementIndex, vec2 localUV, vec2 scale, out vec3 color, ou
return hitElement; return hitElement;
} }
void main() void main()
{ {
ivec2 pixelLocation = ivec2(pixelOffset + gl_GlobalInvocationID.xy); ivec2 pixelLocation = ivec2(pixelOffset + gl_GlobalInvocationID.xy);
@ -1195,7 +1348,8 @@ void main()
vec3 debugBVH = vec3(0); vec3 debugBVH = vec3(0);
// bool debugHit = false; // bool debugHit = false;
vec4 color = vec4(1,1,1,-1); // vec4 color = vec4(0.76, 0.33, 0.15, -1);
vec4 color = vec4(backgroundColor, -1);
vec2 metallicRoughness = vec2(0, 0.8); vec2 metallicRoughness = vec2(0, 0.8);
stack.top = 0; stack.top = 0;
uint index = 0, visitTime = 0; uint index = 0, visitTime = 0;
@ -1208,47 +1362,40 @@ void main()
visitTime++; visitTime++;
vec4 bound = bvhBound[index]; vec4 bound = bvhBound[index];
uint leftChild = bvhChildren[index].x; uint leftChild = bvhChildren[index].x;
if (all(lessThan(bound.xy, uv)) && all(lessThan(uv, bound.zw)))
{
if (any(greaterThan(bound.xy + vec2(0.005), uv)) || any(greaterThan(uv, bound.zw - vec2(0.005))))
debugBVH.g += 0.3;
if (leftChild >= bvhLength) if (leftChild >= bvhLength)
{ {
uint transformIndex = leftChild - 0x80000000;
uint zIndex = bvhChildren[index].y >> 18; uint zIndex = bvhChildren[index].y >> 18;
bvec2 flip = bvec2(bvhChildren[index].y & (1<<16), bvhChildren[index].y & (1<<17)); uint elementIndex = bvhChildren[index].y - zIndex;
float angle = (float(bvhChildren[index].y&((1<<16)-1)) / 65535.0) * 2 * PI; mat3x2 transform = elementTranform[transformIndex];
mat2 rotation = {{cos(angle), -sin(angle)}, {sin(angle), cos(angle)}}; vec2 localUV =
vec2 localUV = uv - (bound.xy + bound.zw) / 2; (mat3(vec3(transform[0], 0), vec3(transform[1], 0), vec3(transform[2], 1)) * vec3(uv, 1)).xy;
localUV = rotation * localUV;
vec2 scale = (bound.zw - bound.xy) / 2;
localUV /= scale;
if (all(lessThan(vec2(-1), localUV)) && all(lessThan(localUV, vec2(1))) && zIndex>color.w)
{
//if (any(greaterThan(bound.xy+vec2(0.005), uv)) || any(greaterThan(uv, bound.zw-vec2(0.005))))
if (any(greaterThan(vec2(-1)+vec2(0.005), localUV)) || any(greaterThan(localUV, vec2(1)-vec2(0.005))))
debugBVH.g += 0.3;
//uint elementIndex = leftChild - bvhLength;
//debugBVH.bg += 0.5 * (localUV + vec2(1));
//debugBVH = vec3(0);
if(flip.x) localUV.x = -localUV.x;
if(flip.y) localUV.y = -localUV.y;
vec3 elementColor; vec3 elementColor;
vec2 elementMetallicRoughness; vec2 elementMetallicRoughness;
if(drawElement(leftChild - 0x80000000, localUV, scale, elementColor, elementMetallicRoughness, debugBVH)) if (drawElement(elementIndex, localUV, elementColor, elementMetallicRoughness,
debugBVH))
{ {
color = vec4(elementColor, zIndex); color = vec4(elementColor, zIndex);
metallicRoughness = elementMetallicRoughness; metallicRoughness = elementMetallicRoughness;
} }
//if(elementIndex == 1 && transformIndex==1)
} // color = vec4(1,1,0,1);
index = bvhLength; index = bvhLength;
} }
else if (all(lessThan(bound.xy, uv)) && all(lessThan(uv, bound.zw))) else
{ {
if (any(greaterThan(bound.xy+vec2(0.005), uv)) || any(greaterThan(uv, bound.zw-vec2(0.005))))
debugBVH.g += 0.3;
//debugBVH.r += 0.02;
stack.push(index); stack.push(index);
index = leftChild; index = leftChild;
} }
}
else else
index = bvhLength; index = bvhLength;
} }
@ -1262,7 +1409,7 @@ void main()
imageStore(gBaseColor, pixelLocation, vec4(color.rgb, 1)); imageStore(gBaseColor, pixelLocation, vec4(color.rgb, 1));
imageStore(gMetallicRoughness, pixelLocation, vec4(metallicRoughness, 0, 1)); imageStore(gMetallicRoughness, pixelLocation, vec4(metallicRoughness, 0, 1));
return; //return;
if (/*color.a!=-1&&*/ debugBVH == vec3(0)) if (/*color.a!=-1&&*/ debugBVH == vec3(0))
{ {
// imageStore(gBaseColor, pixelLocation, vec4(vec3(1, 1, 0),1)); // imageStore(gBaseColor, pixelLocation, vec4(vec3(1, 1, 0),1));

View File

@ -36,11 +36,6 @@ void main()
lod++; lod++;
gMetallicRoughness = mt.rg; gMetallicRoughness = mt.rg;
// int pageSize = textureSize(texture_basecolor, levels-1).x;
// uint pageId = 0;
// for(uint i = 0; i < lodExpect; i++)
// pageId += 1<<(2*(levels-1-i));
uint w = 1<<(levels-1-lodExpect); uint w = 1<<(levels-1-lodExpect);
ivec2 page = ivec2(TexCoords * vec2(w)); ivec2 page = ivec2(TexCoords * vec2(w));
page = clamp(page, ivec2(0), ivec2(w-1)); page = clamp(page, ivec2(0), ivec2(w-1));
@ -49,7 +44,6 @@ void main()
gPaintingIndex = uvec2(0); gPaintingIndex = uvec2(0);
else else
gPaintingIndex = uvec2((paintingId<<4)+lodExpect, (page.y<<8)+page.x); gPaintingIndex = uvec2((paintingId<<4)+lodExpect, (page.y<<8)+page.x);
gPaintingTexCoord = vec2(1., 1.) - TexCoords * 2;
return; return;
} }

View File

@ -24,6 +24,7 @@ EditorWidget::EditorWidget(QWidget* parent) : QWidget(parent)
}); });
connect(this->openButton, &QPushButton::clicked, this, [this]() { connect(this->openButton, &QPushButton::clicked, this, [this]() {
QString fileName = QFileDialog::getOpenFileName(this->saveAsButton, QString::fromLocal8Bit("´ò¿ª"), "", QString::fromLocal8Bit("JSONÎļþ(*.json)")); QString fileName = QFileDialog::getOpenFileName(this->saveAsButton, QString::fromLocal8Bit("´ò¿ª"), "", QString::fromLocal8Bit("JSONÎļþ(*.json)"));
if(!fileName.isEmpty())
this->tabWidget->addTab(new EditorWidgetItem(fileName, this), fileName); this->tabWidget->addTab(new EditorWidgetItem(fileName, this), fileName);
}); });
connect(this->closeButton, &QPushButton::clicked, this, [this]() { connect(this->closeButton, &QPushButton::clicked, this, [this]() {
@ -46,3 +47,11 @@ EditorWidget::EditorWidget(QWidget* parent) : QWidget(parent)
}); });
} }
void EditorWidget::renameTab(QWidget* target, QString name)
{
int index = this->tabWidget->indexOf(target);
if (index != -1)
{
this->tabWidget->setTabText(index, name);
}
}

View File

@ -18,6 +18,6 @@ private:
public: public:
EditorWidget(QWidget* parent = nullptr); EditorWidget(QWidget* parent = nullptr);
~EditorWidget()=default; ~EditorWidget()=default;
void renameTab(QWidget* target, QString name);
}; };

View File

@ -0,0 +1,38 @@
#include "ColorPicker.h"
#include <QColorDialog>
QString getStyleSheet(const QColor& color)
{
return
"QPushButton#colorPicker {"
" border-style: solid;"
" border-width: 1px;"
" border-color: black;"
" background-color: " + color.name() + ";"
"}";
}
ColorPicker::ColorPicker(const QColor& color, QWidget* parent) : QPushButton(parent), color(color)
{
this->setObjectName("colorPicker");
this->setStyleSheet(getStyleSheet(color));
connect(this, &QPushButton::clicked, this, &ColorPicker::onClicked);
}
QColor ColorPicker::getColor() const
{
return color;
}
void ColorPicker::onClicked()
{
QColorDialog dialog(this->color, this);
dialog.exec();
QColor newColor = dialog.selectedColor();
if (newColor.isValid() && this->color != newColor)
{
this->color = newColor;
this->setStyleSheet(getStyleSheet(newColor));
emit colorChanged(newColor);
}
}

View File

@ -0,0 +1,16 @@
#pragma once
#include <QPushButton>
class ColorPicker : public QPushButton
{
Q_OBJECT
private:
QColor color;
public:
ColorPicker(const QColor& color = QColor::fromRgb(0, 0, 0), QWidget* parent = nullptr);
QColor getColor() const;
public slots:
void onClicked();
signals:
void colorChanged(QColor newColor);
};

View File

@ -0,0 +1,48 @@
#include "FillStyleWidget.h"
#include "ColorPicker.h"
#include <QGridLayout>
#include <QLabel>
#include <qtmaterialtextfield.h>
FillStyleWidget::FillStyleWidget(std::shared_ptr<Renderer::MaterialStyleFill> fill, QWidget* parent)
: QWidget(parent), fill(fill)
{
auto* layout = new QGridLayout(this);
this->setLayout(layout);
// ÑÕÉ«
auto* material = &plainFill(fill)->material;
auto* colorLabel = new QLabel(QStringLiteral("ÑÕÉ«"), this);
layout->addWidget(colorLabel, 0, 0);
auto* colorPicker = new ColorPicker(material->color);
connect(colorPicker, &ColorPicker::colorChanged,
[material](QColor color)
{
material->color = color;
});
layout->addWidget(colorPicker, 0, 1);
// ½ðÊô¶È
auto* metallicLabel = new QLabel(QStringLiteral("½ðÊô¶È"), this);
layout->addWidget(metallicLabel, 1, 0);
auto* metallicInput = new QtMaterialTextField(this);
metallicInput->setText(QString::number(material->metallicF(), 'g', 3));
connect(metallicInput, &QtMaterialTextField::textChanged,
[material](const QString& text)
{
material->setMetallicF(text.toFloat());
});
layout->addWidget(metallicInput, 1, 1);
// ´Ö²Ú¶È
auto* roughnessLabel = new QLabel(QStringLiteral("´Ö²Ú¶È"), this);
layout->addWidget(roughnessLabel, 2, 0);
auto* roughnessInput = new QtMaterialTextField(this);
roughnessInput->setText(QString::number(material->roughnessF(), 'g', 3));
connect(roughnessInput, &QtMaterialTextField::textChanged,
[material](const QString& text)
{
material->setRoughnessF(text.toFloat());
});
layout->addWidget(roughnessInput, 2, 1);
}

View File

@ -0,0 +1,12 @@
#pragma once
#include "../Renderer/Painting/MaterialStyleFill.h"
#include "LayerStyle.h"
#include <QWidget>
class FillStyleWidget : public QWidget
{
Q_OBJECT
public:
FillStyleWidget(std::shared_ptr<Renderer::MaterialStyleFill> fill, QWidget* parent = nullptr);
std::shared_ptr<Renderer::MaterialStyleFill> fill;
};

View File

@ -1,15 +1,46 @@
#include "LayerCreateWidget.h" #include "LayerCreateWidget.h"
#include <QComboBox> #include <QComboBox>
LayerCreateWidget::LayerCreateWidget(ElementManager* elementManager, QWidget* parent) : LayerCreateWidget::LayerCreateWidget(ElementManager* elementManager, FolderLayerWrapper* folderLayer, QWidget* parent) :
QDialog(parent) QDialog(parent)
{ {
this->elementManager = elementManager;
ui.setupUi(this); ui.setupUi(this);
connect(ui.buttonBox, SIGNAL(accepted()), this, SLOT(accept())); connect(ui.buttonBox, SIGNAL(accepted()), this, SLOT(accept()));
connect(ui.buttonBox, SIGNAL(rejected()), this, SLOT(reject())); connect(ui.buttonBox, SIGNAL(rejected()), this, SLOT(reject()));
connect(ui.comboBox, SIGNAL(currentIndexChanged(int)), this, SLOT(onCurrentIndexChanged(int))); connect(ui.comboBox, SIGNAL(currentIndexChanged(int)), this, SLOT(onCurrentIndexChanged(int)));
elementPool = new ElementPoolWidget(ui.elementPool); elementPool = new ElementPoolWidget(ui.elementPool);
elementPool->setElementList(elementManager->elements); elements = elementManager->elements;
elementsCheck(folderLayer);
elementPool->setElementList(elements);
}
void LayerCreateWidget::elementsCheck(FolderLayerWrapper* parent)
{
std::set<LayerWrapper*> upSet, downSet;
parent->collectUpReachable(upSet);
for (auto it = elements.begin(); it != elements.end();)
{
bool valid = true;
auto ele = dynamic_cast<GroupElement*>(*it);
if (ele != nullptr)
{
downSet.clear();
ele->collectReachable(downSet);
for (auto& layer : downSet)
{
if (upSet.find(layer) != upSet.end())
{
valid = false;
break;
}
}
}
if (valid)
++it;
else
it = elements.erase(it);
}
} }
LayerCreateWidget::~LayerCreateWidget() LayerCreateWidget::~LayerCreateWidget()
@ -21,8 +52,20 @@ void LayerCreateWidget::accept()
QJsonObject jsonObj; QJsonObject jsonObj;
jsonObj.insert("name", ui.name->text()); jsonObj.insert("name", ui.name->text());
if (ui.comboBox->currentIndex() == 0) { if (ui.comboBox->currentIndex() == 0) {
auto currentEle = elements[elementPool->currentIndex];
int index = -1;
for (int i = 0; i < elementManager->elements.size(); i++)
{
if (elementManager->elements[i] == currentEle)
{
index = i;
break;
}
}
if (index == -1)
return;
jsonObj.insert("is-folder", false); jsonObj.insert("is-folder", false);
jsonObj.insert("element", elementPool->currentIndex); jsonObj.insert("element", index);
} }
else { else {
jsonObj.insert("is-folder", true); jsonObj.insert("is-folder", true);

View File

@ -11,12 +11,15 @@ class LayerCreateWidget :
Q_OBJECT Q_OBJECT
private: private:
ElementManager* elementManager;
std::vector<GraphicElement*> elements;
Ui::LayerCreateWidget ui; Ui::LayerCreateWidget ui;
ElementPoolWidget* elementPool; ElementPoolWidget* elementPool;
void elementsCheck(FolderLayerWrapper*);
public: public:
LayerCreateWidget(ElementManager* elementManager,QWidget* parent = nullptr); LayerCreateWidget(ElementManager* elementManager, FolderLayerWrapper* folderLayer, QWidget* parent = nullptr);
~LayerCreateWidget(); ~LayerCreateWidget();
void accept() override; void accept() override;

View File

@ -3,22 +3,20 @@
#include <QDialogButtonBox> #include <QDialogButtonBox>
#include <QHBoxLayout> #include <QHBoxLayout>
#include <QGridLayout> #include <QGridLayout>
#include <QDebug>
#include <unordered_set>
LayerStyleDialog::LayerStyleDialog( LayerStyleDialog::LayerStyleDialog(
QWidget* parent, LayerStyleContainer& styles,
std::shared_ptr<LayerStyle> existedStyle, std::shared_ptr<LayerStyle> existedStyle,
std::vector<std::shared_ptr<LayerStyle>>* excludeStyles QWidget* parent
) : QDialog(parent) ) : QDialog(parent), styles(&styles)
{ {
QVBoxLayout* dialogLayout = new QVBoxLayout(this); auto* dialogLayout = new QVBoxLayout(this);
dialogLayout->setAlignment(Qt::AlignmentFlag::AlignHCenter); dialogLayout->setAlignment(Qt::AlignmentFlag::AlignHCenter);
this->setLayout(dialogLayout); this->setLayout(dialogLayout);
if (existedStyle) if (existedStyle)
{ {
this->modifyingStyle = existedStyle->clonePtr(); this->modifyingStyle = existedStyle->clone();
this->styleContainer = nullptr; this->styleContainer = nullptr;
this->styleWidget = modifyingStyle->getInputWidget(); this->styleWidget = modifyingStyle->getInputWidget();
@ -28,28 +26,14 @@ LayerStyleDialog::LayerStyleDialog(
} }
else else
{ {
std::unordered_set<QString> excludeStyleNames; QStringList unusedStyleNames = styles.unusedStyleNames();
for(auto &style : *excludeStyles) auto* typeSelector = new QComboBox(this);
{
excludeStyleNames.insert(style->getStyleName());
}
QComboBox* typeSelector = new QComboBox(this); if (!unusedStyleNames.empty())
{
typeSelector->addItems(unusedStyleNames);
this->modifyingStyle = std::move(styles.makeUnusedStyle(unusedStyleNames[0]));
for (auto& pair : LayerStyle::types)
{
if (!excludeStyleNames.contains(pair.first))
{
typeSelector->addItem(pair.first);
if (!this->modifyingStyle)
{
this->modifyingStyle = std::move(pair.second());
}
}
}
if (typeSelector->count() > 0)
{
dialogLayout->addWidget(typeSelector); dialogLayout->addWidget(typeSelector);
this->styleContainer = new QGridLayout(this); this->styleContainer = new QGridLayout(this);
dialogLayout->addLayout(styleContainer); dialogLayout->addLayout(styleContainer);
@ -57,11 +41,11 @@ LayerStyleDialog::LayerStyleDialog(
this->styleWidget = this->modifyingStyle->getInputWidget(); this->styleWidget = this->modifyingStyle->getInputWidget();
this->styleWidget->setParent(this); this->styleWidget->setParent(this);
this->styleContainer->addWidget(styleWidget); this->styleContainer->addWidget(styleWidget);
connect(typeSelector, QOverload<int>::of(&QComboBox::currentIndexChanged), connect(typeSelector, &QComboBox::currentTextChanged,
this, &LayerStyleDialog::onStyleTypeSelectorChanged); this, &LayerStyleDialog::onStyleTypeSelectorChanged);
} }
} }
QDialogButtonBox* buttonBox = new QDialogButtonBox(QDialogButtonBox::Ok | QDialogButtonBox::Cancel, this); auto* buttonBox = new QDialogButtonBox(QDialogButtonBox::Ok | QDialogButtonBox::Cancel, this);
connect(buttonBox, &QDialogButtonBox::accepted, this, &LayerStyleDialog::accept); connect(buttonBox, &QDialogButtonBox::accepted, this, &LayerStyleDialog::accept);
connect(buttonBox, &QDialogButtonBox::rejected, this, &LayerStyleDialog::reject); connect(buttonBox, &QDialogButtonBox::rejected, this, &LayerStyleDialog::reject);
dialogLayout->addWidget(buttonBox); dialogLayout->addWidget(buttonBox);
@ -73,7 +57,7 @@ void LayerStyleDialog::accept()
QDialog::accept(); QDialog::accept();
} }
void LayerStyleDialog::onStyleTypeSelectorChanged(int index) void LayerStyleDialog::onStyleTypeSelectorChanged(const QString& current)
{ {
if (this->styleWidget) if (this->styleWidget)
{ {
@ -81,8 +65,10 @@ void LayerStyleDialog::onStyleTypeSelectorChanged(int index)
this->styleWidget->setParent(nullptr); this->styleWidget->setParent(nullptr);
delete styleWidget; delete styleWidget;
} }
this->modifyingStyle = std::move(LayerStyle::types[index].second()); this->modifyingStyle = std::move(styles->makeUnusedStyle(current));
this->styleWidget = this->modifyingStyle->getInputWidget(); this->styleWidget = this->modifyingStyle->getInputWidget();
this->styleWidget->setParent(this); this->styleWidget->setParent(this);
this->styleContainer->addWidget(styleWidget, 0, 0, 1, 1); this->styleContainer->addWidget(styleWidget, 0, 0, 1, 1);
this->styleWidget->adjustSize();
this->adjustSize();
} }

View File

@ -9,15 +9,17 @@ class LayerStyleDialog : public QDialog
private: private:
QWidget* styleWidget; QWidget* styleWidget;
QGridLayout* styleContainer; QGridLayout* styleContainer;
LayerStyleContainer* styles;
std::unique_ptr<LayerStyle> modifyingStyle; std::unique_ptr<LayerStyle> modifyingStyle;
public: public:
LayerStyleDialog( LayerStyleDialog(
QWidget* parent = nullptr, LayerStyleContainer& styles,
std::shared_ptr<LayerStyle> existedStyle = nullptr, std::shared_ptr<LayerStyle> existedStyle = nullptr,
std::vector<std::shared_ptr<LayerStyle>>* excludeStyles = nullptr); QWidget* parent = nullptr
);
std::shared_ptr<LayerStyle> layerStyle; std::shared_ptr<LayerStyle> layerStyle;
private slots: private slots:
void onStyleTypeSelectorChanged(int index); void onStyleTypeSelectorChanged(const QString& current);
void accept() override; void accept() override;
}; };

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

@ -0,0 +1,218 @@
#include "StrokeStyleWidget.h"
#include "ColorPicker.h"
#include <qtmaterialraisedbutton.h>
#include <limits>
#include <lib/qtmaterialstyle.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;
StrokeStyleWidget::StrokeStyleWidget(
std::shared_ptr<MaterialStyleStroke> stroke,
QWidget* parent
) : QWidget(parent), stroke(stroke)
{
auto* viewLayout = new QVBoxLayout(this);
this->setLayout(viewLayout);
initStrokeSettings();
auto* strokeProperties = new QWidget(this);
auto* strokePropertiesLayout = new QHBoxLayout(strokeProperties);
strokePropertiesLayout->setMargin(0);
strokeProperties->setLayout(strokePropertiesLayout);
strokePropertiesLayout->addWidget(enableGradual);
strokePropertiesLayout->addWidget(endTypeBox);
viewLayout->addWidget(strokeProperties);
viewLayout->addWidget(widthField);
initTable(std::dynamic_pointer_cast<Renderer::StrokeRadialGradient>(stroke->materialStroke));
viewLayout->addWidget(strokeTable);
this->adjustSize();
}
void StrokeStyleWidget::initStrokeSettings()
{
this->enableGradual = new QtMaterialCheckBox(this);
enableGradual->setText(QStringLiteral("渐变"));
enableGradual->setChecked(radialStroke(stroke)->gradual);
connect(enableGradual, &QtMaterialCheckBox::toggled, [this](bool checked) {
radialStroke(this->stroke)->gradual = checked;
});
#define endTypeBoxLabel(start, end) QStringLiteral(start##" -> "##end)
this->endTypeBox = new QComboBox(this);
endTypeBox->addItem(endTypeBoxLabel("圆头", "圆头")); // kRound
endTypeBox->addItem(endTypeBoxLabel("平头", "圆头")); // kFlatRound
endTypeBox->addItem(endTypeBoxLabel("圆头", "平头")); // kRoundFlat
endTypeBox->addItem(endTypeBoxLabel("平头", "平头")); // kFlat
endTypeBox->setCurrentIndex(static_cast<int>(this->stroke->endType));
connect(endTypeBox, QOverload<int>::of(&QComboBox::currentIndexChanged), [this](int index) {
switch (index)
{
case 0:
this->stroke->endType = Renderer::StrokeEndType::kRound;
break;
case 1:
this->stroke->endType = Renderer::StrokeEndType::kFlatRound;
break;
case 2:
this->stroke->endType = Renderer::StrokeEndType::kRoundFlat;
break;
case 3:
this->stroke->endType = Renderer::StrokeEndType::kFlat;
break;
}
});
this->widthField = new QtMaterialTextField(this);
widthField->setLabel(QStringLiteral("本侧描边宽度"));
widthField->setText(QString::number(stroke->halfWidth));
auto* widthValidator = new QDoubleValidator(0.1, std::numeric_limits<float>::max(), 3, widthField);
widthValidator->setNotation(QDoubleValidator::StandardNotation);
widthField->setValidator(widthValidator);
connect(widthField, &QtMaterialTextField::textChanged, [this](const QString& changed) {
if (this->widthField->hasAcceptableInput())
{
this->stroke->halfWidth = changed.toFloat();
}
});
}
// TODO: 新增时参数校验
void StrokeStyleWidget::initTable(std::shared_ptr<Renderer::StrokeRadialGradient> materialStroke)
{
this->strokeTable = new QTableWidget(this);
strokeTable->setSizeAdjustPolicy(QAbstractScrollArea::AdjustToContentsOnFirstShow);
strokeTable->setColumnCount(5);
strokeTable->setRowCount(materialStroke->materialMap.size() + 1);
QStringList headers;
headers << QStringLiteral("离心距离占比")
<< QStringLiteral("颜色")
<< QStringLiteral("金属度")
<< QStringLiteral("粗糙度")
<< QStringLiteral("其他操作");
strokeTable->setHorizontalHeaderLabels(headers);
int row = 0;
// 内容
for (auto & strokePair : materialStroke->materialMap)
{
setTableRow(row, strokePair.first, strokePair.second);
row++;
}
// 新增按钮
QtMaterialRaisedButton* addButton = new QtMaterialRaisedButton("+", strokeTable);
addButton->setBackgroundColor(QtMaterialStyle::instance().themeColor("primary1"));
strokeTable->setSpan(row, 0, 1, 5);
strokeTable->setCellWidget(row, 0, addButton);
strokeTable->setMinimumHeight(strokeTable->rowHeight(row) * 5);
strokeTable->setMinimumWidth(strokeTable->sizeHint().width());
addButton->setFixedHeight(strokeTable->rowHeight(row));
connect(addButton, &QtMaterialRaisedButton::clicked, [this]() {
handlingRowInsert = true;
auto materialMap = &(radialStroke(this->stroke)->materialMap);
float newWidth = 0;
if (materialMap->size() == 0)
{
newWidth = 0.1;
}
else
{
auto lastPair = materialMap->rbegin();
newWidth = lastPair->first + 0.01;
}
Renderer::Material newMaterial(QColor::fromRgb(0, 0, 0));
(*materialMap)[newWidth] = newMaterial;
int newRow = this->strokeTable->rowCount() - 1;
this->strokeTable->insertRow(newRow);
setTableRow(newRow, newWidth, (*materialMap)[newWidth]);
this->strokeTable->update();
handlingRowInsert = false;
});
connect(strokeTable, &QTableWidget::currentItemChanged, this, &StrokeStyleWidget::onCurrentItemChanged);
connect(strokeTable, &QTableWidget::cellChanged, this, &StrokeStyleWidget::onCellChanged);
}
void StrokeStyleWidget::setTableRow(int row, float width, Renderer::Material& material)
{
auto* widthItem = new QTableWidgetItem;
widthItem->setData(Qt::EditRole, width);
strokeTable->setItem(row, COLUMN_WIDTH, widthItem);
QColor* colorPtr = &(material.color);
auto* colorItem = new QTableWidgetItem;
colorItem->setData(Qt::DisplayRole, *colorPtr);
strokeTable->setItem(row, COLUMN_COLOR, colorItem);
auto* colorPicker = new ColorPicker(*colorPtr, strokeTable);
strokeTable->setCellWidget(row, COLUMN_COLOR, colorPicker);
connect(colorPicker, &ColorPicker::colorChanged, [this, colorPtr](QColor color) {
*colorPtr = color;
this->strokeTable->update();
});
auto* metallicItem = new QTableWidgetItem;
metallicItem->setData(Qt::EditRole, material.metallicF());
strokeTable->setItem(row, COLUMN_METALLIC, metallicItem);
auto* roughnessItem = new QTableWidgetItem;
roughnessItem->setData(Qt::EditRole, material.roughnessF());
strokeTable->setItem(row, COLUMN_ROUGHNESS, roughnessItem);
auto* removeButton = new QtMaterialRaisedButton("-", strokeTable);
removeButton->setBackgroundColor(QtMaterialStyle::instance().themeColor("primary1"));
removeButton->setFixedSize(20, 20);
strokeTable->setCellWidget(row, COLUMN_OPERATIONS, removeButton);
connect(removeButton, &QtMaterialRaisedButton::clicked, [this, row]() {
radialStroke(this->stroke)->materialMap.erase(this->strokeTable->item(row, COLUMN_WIDTH)->text().toFloat());
this->strokeTable->removeRow(row);
});
}
void StrokeStyleWidget::onCurrentItemChanged(QTableWidgetItem* current, QTableWidgetItem* previous)
{
if (!current) return;
int column = current->column();
if (column != COLUMN_COLOR && column != COLUMN_OPERATIONS)
{
this->currentItemValue = current->data(Qt::EditRole);
}
}
void StrokeStyleWidget::onCellChanged(int row, int column)
{
if (handlingRowInsert) return;
auto changedItem = strokeTable->item(row, column);
auto changedItemValue = changedItem->text().toFloat();
if (changedItemValue < 0 || 1 < changedItemValue)
{
changedItem->setData(Qt::EditRole, this->currentItemValue.toFloat());
return;
}
auto changedWidth = strokeTable->item(row, COLUMN_WIDTH)->data(Qt::EditRole).toFloat();
switch (column)
{
case COLUMN_WIDTH:
{
float oldWidth = this->currentItemValue.toFloat();
auto node = radialStroke(stroke)->materialMap.extract(oldWidth);
node.key() = changedWidth;
radialStroke(stroke)->materialMap.insert(std::move(node));
strokeTable->sortItems(COLUMN_WIDTH);
break;
}
case COLUMN_METALLIC:
{
radialStroke(stroke)->materialMap[changedWidth].setMetallicF(changedItemValue);
break;
}
case COLUMN_ROUGHNESS:
{
radialStroke(stroke)->materialMap[changedWidth].setRoughnessF(changedItemValue);
break;
}
}
}

View File

@ -0,0 +1,33 @@
#pragma once
#include "LayerStyle.h"
#include "../Renderer/Painting/MaterialStyleStroke.h"
#include <QTableWidget>
#include <QVBoxLayout>
#include <QComboBox>
#include <qtmaterialtextfield.h>
#include <qtmaterialcheckbox.h>
class StrokeStyleWidget : public QWidget
{
Q_OBJECT
private:
QVariant currentItemValue;
QtMaterialCheckBox* enableGradual;
QComboBox* endTypeBox;
QtMaterialTextField* widthField;
QTableWidget* strokeTable;
bool handlingRowInsert = false;
void initStrokeSettings();
void initTable(std::shared_ptr<Renderer::StrokeRadialGradient> materialStroke);
void setTableRow(int row, float width, Renderer::Material& material);
public:
StrokeStyleWidget(std::shared_ptr<Renderer::MaterialStyleStroke> stroke, QWidget* parent = nullptr);
std::shared_ptr<Renderer::MaterialStyleStroke> stroke;
protected slots:
void onCurrentItemChanged(QTableWidgetItem* current, QTableWidgetItem* previous);
void onCellChanged(int row, int column);
};

View File

@ -1,8 +1,11 @@
#include "EditorWidgetItem.h" #include "EditorWidgetItem.h"
#include "EditorWidget.h"
#include <QTimer>
EditorWidgetItem::EditorWidgetItem(QString filePath,QWidget *parent) : QWidget(parent) EditorWidgetItem::EditorWidgetItem(QString filePath,QWidget *parent) : QWidget(parent)
{ {
QImage x; QImage x;
this->parent = parent;
displayLayer = nullptr; displayLayer = nullptr;
displayElement = nullptr; displayElement = nullptr;
ui.setupUi(this); ui.setupUi(this);
@ -12,8 +15,22 @@ EditorWidgetItem::EditorWidgetItem(QString filePath,QWidget *parent) : QWidget(p
this->filePath = filePath; this->filePath = filePath;
layerInfoDisplayWidget = dynamic_cast<InfoDisplayWidget *>(tabWidget->widget(0)); layerInfoDisplayWidget = dynamic_cast<InfoDisplayWidget *>(tabWidget->widget(0));
elementInfoDisplayWidget = dynamic_cast<ElementPoolWidget *>(tabWidget->widget(1)); elementInfoDisplayWidget = dynamic_cast<ElementPoolWidget *>(tabWidget->widget(1));
editorSettingWidget = dynamic_cast<EditorSettingWidget*>(tabWidget->widget(2));
elementInfoDisplayWidget->enableEdit();
qDebug() << layerInfoDisplayWidget; qDebug() << layerInfoDisplayWidget;
qDebug() << elementInfoDisplayWidget; qDebug() << elementInfoDisplayWidget;
auto centralRefresh = [this]() {
layerInfoDisplayWidget->refresh();
elementInfoDisplayWidget->refresh();
treeWidget->refresh();
previewWindow->refresh();
};
connect(layerInfoDisplayWidget, &InfoDisplayWidget::triggerCentralRefresh, centralRefresh);
connect(elementInfoDisplayWidget, &ElementPoolWidget::triggerCentralRefresh, centralRefresh);
connect(treeWidget, &LayerTreeWidget::triggerCentralRefresh, centralRefresh);
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(previewWindow, &PreviewWindow::layerInfoChanged, layerInfoDisplayWidget, &InfoDisplayWidget::triggerSelfRefresh);
connect(treeWidget, &LayerTreeWidget::displayLayerChange, previewWindow, &PreviewWindow::currentLayerChanged); connect(treeWidget, &LayerTreeWidget::displayLayerChange, previewWindow, &PreviewWindow::currentLayerChanged);
connect(treeWidget, &LayerTreeWidget::requireRefreshElementWidget, elementInfoDisplayWidget, &ElementPoolWidget::refresh); connect(treeWidget, &LayerTreeWidget::requireRefreshElementWidget, elementInfoDisplayWidget, &ElementPoolWidget::refresh);
@ -24,6 +41,7 @@ EditorWidgetItem::EditorWidgetItem(QString filePath,QWidget *parent) : QWidget(p
connect(treeWidget, &LayerTreeWidget::requireRefreshPreview, this, connect(treeWidget, &LayerTreeWidget::requireRefreshPreview, this,
&EditorWidgetItem::triggerRefreshPreview); &EditorWidgetItem::triggerRefreshPreview);
connect(layerInfoDisplayWidget, &InfoDisplayWidget::requireSelfRefresh, layerInfoDisplayWidget, &InfoDisplayWidget::triggerSelfRefresh); connect(layerInfoDisplayWidget, &InfoDisplayWidget::requireSelfRefresh, layerInfoDisplayWidget, &InfoDisplayWidget::triggerSelfRefresh);
connect(elementInfoDisplayWidget, &ElementPoolWidget::refreshLayerTree, treeWidget, &LayerTreeWidget::refresh);
// &EditorWidget::triggerRefreshPreview); // &EditorWidget::triggerRefreshPreview);
// test // test
QFile settingFile; QFile settingFile;
@ -37,11 +55,11 @@ EditorWidgetItem::EditorWidgetItem(QString filePath,QWidget *parent) : QWidget(p
qDebug() << jError.errorString(); qDebug() << jError.errorString();
// end test // end test
QJsonObject source = jsonDoc.object(); QJsonObject source = jsonDoc.object();
elementManager = new ElementManager(source,previewWindow->getRenderer()); elementManager = new ElementManager(source,Renderer::ElementRenderer::instance());
layerManager = new LayerManager(source, elementManager); layerManager = new LayerManager(source, elementManager);
elementInfoDisplayWidget->setElementManager(elementManager); elementInfoDisplayWidget->setElementManager(elementManager);
treeWidget->elementManager = elementManager; treeWidget->elementManager = elementManager;
qDebug() << layerManager->toJson(); //qDebug() << layerManager->toJson();
previewWindow->initialize(layerManager,QSize(jsonDoc.object().value("width").toDouble(),jsonDoc.object().value("height").toDouble())); previewWindow->initialize(layerManager,QSize(jsonDoc.object().value("width").toDouble(),jsonDoc.object().value("height").toDouble()));
@ -51,6 +69,15 @@ EditorWidgetItem::EditorWidgetItem(QString filePath,QWidget *parent) : QWidget(p
treeWidget->refresh(); treeWidget->refresh();
treeWidget->addTopLevelItem(treeWidget->root->getQTreeItem()); treeWidget->addTopLevelItem(treeWidget->root->getQTreeItem());
} }
this->backgroundColor = source.value("background-color").toVariant().value<QColor>();
this->projectName = source.value("project-name").toString();
qDebug() << this->backgroundColor;
qDebug() << this->projectName;
QTimer::singleShot(300, this, [this]() {
handleBackgroundColorChange(this->backgroundColor);
handleProjectNameChange(this->projectName);
});
} }
EditorWidgetItem::~EditorWidgetItem() EditorWidgetItem::~EditorWidgetItem()
@ -98,9 +125,28 @@ void EditorWidgetItem::saveImpl(QString filePath) const
json.insert("height", 1080); json.insert("height", 1080);
json.insert("root-layer", source1.value("root-layer")); json.insert("root-layer", source1.value("root-layer"));
json.insert("elements", source2.value("elements")); json.insert("elements", source2.value("elements"));
json.insert("project-name", this->projectName);
json.insert("background-color", QJsonValue::fromVariant(QVariant::fromValue(this->backgroundColor)));
QJsonDocument doc(json); QJsonDocument doc(json);
QFile file(filePath); QFile file(filePath);
file.open(QIODevice::WriteOnly); file.open(QIODevice::WriteOnly);
file.write(doc.toJson()); file.write(doc.toJson());
file.close(); file.close();
} }
void EditorWidgetItem::handleBackgroundColorChange(QColor color)
{
this->backgroundColor = color;
previewWindow->setBackgroundColor(color);
}
void EditorWidgetItem::handleProjectNameChange(QString name)
{
this->projectName = name;
auto parent = dynamic_cast<EditorWidget*>(this->parent);
qDebug() << name << " " << parent<<" "<<this;
if (parent != nullptr)
{
parent->renameTab(this, name);
}
}

View File

@ -6,6 +6,7 @@
#include "LayerManager.h" #include "LayerManager.h"
#include "LayerTreeWidget.h" #include "LayerTreeWidget.h"
#include "PreviewWindow.h" #include "PreviewWindow.h"
#include "EditorSettingWidget.h"
#include "ui_EditorWidgetItem.h" #include "ui_EditorWidgetItem.h"
#include <QPainter> #include <QPainter>
#include <QTreeWidget> #include <QTreeWidget>
@ -26,11 +27,17 @@ class EditorWidgetItem : public QWidget
QTabWidget *tabWidget; QTabWidget *tabWidget;
InfoDisplayWidget* layerInfoDisplayWidget; InfoDisplayWidget* layerInfoDisplayWidget;
ElementPoolWidget* elementInfoDisplayWidget; ElementPoolWidget* elementInfoDisplayWidget;
EditorSettingWidget* editorSettingWidget;
// QT DATA PART // QT DATA PART
LayerWrapper *displayLayer; LayerWrapper *displayLayer;
GraphicElement *displayElement; GraphicElement *displayElement;
QString filePath; QWidget* parent;
void saveImpl(QString filePath)const; void saveImpl(QString filePath)const;
public:
// PROJECT INFO
QString filePath;
QString projectName;
QColor backgroundColor;
public: public:
EditorWidgetItem(QString filePath, QWidget *parent = nullptr); EditorWidgetItem(QString filePath, QWidget *parent = nullptr);
@ -38,6 +45,8 @@ class EditorWidgetItem : public QWidget
void paintEvent(QPaintEvent *event) override; void paintEvent(QPaintEvent *event) override;
void save() const; void save() const;
void saveAs(QString filePath)const; void saveAs(QString filePath)const;
void handleBackgroundColorChange(QColor color);
void handleProjectNameChange(QString name);
private slots: private slots:
void onLayerChange(LayerWrapper *layer); void onLayerChange(LayerWrapper *layer);

View File

@ -2,7 +2,6 @@
ElementManager::ElementManager(QJsonObject source,Renderer::ElementRenderer* renderer) ElementManager::ElementManager(QJsonObject source,Renderer::ElementRenderer* renderer)
{ {
auto elementsJson = source.value("elements").toArray(); auto elementsJson = source.value("elements").toArray();
qDebug() << elementsJson.size();
int index = 0; int index = 0;
for (auto elementJson : elementsJson) for (auto elementJson : elementsJson)
{ {
@ -68,9 +67,6 @@ int ElementManager::getLayerReferencedBy(const FolderLayerWrapper* layer)
{ {
for (int i = 0; i < elements.size(); i++) for (int i = 0; i < elements.size(); i++)
if (typeid(*elements[i]) == typeid(GroupElement)) { if (typeid(*elements[i]) == typeid(GroupElement)) {
qDebug() << ((GroupElement*)elements[i])->sourceLayer;
qDebug() << layer;
qDebug() << "------------";
if (((GroupElement*)elements[i])->sourceLayer == layer) if (((GroupElement*)elements[i])->sourceLayer == layer)
return i; return i;
} }

View File

@ -1,11 +1,16 @@
#include "ElementPoolWidget.h" #include "ElementPoolWidget.h"
#include <QMenu>
#include <QInputDialog>
#include <QFileDialog>
#include <QDialogButtonBox>
ElementPoolWidget::ElementPoolWidget(QWidget* parent) ElementPoolWidget::ElementPoolWidget(QWidget* parent)
: QWidget(parent) : QWidget(parent)
{ {
elementManager = nullptr; elementManager = nullptr;
iconWidth = 120, iconHeight = 90; iconWidth = 120, iconHeight = 90;
pictureList = new QListWidget(); pictureList = new QListWidget(this);
pictureList->setContextMenuPolicy(Qt::CustomContextMenu);
pictureList->setIconSize(QSize(iconWidth, iconHeight)); pictureList->setIconSize(QSize(iconWidth, iconHeight));
pictureList->setWindowFlags(Qt::FramelessWindowHint); pictureList->setWindowFlags(Qt::FramelessWindowHint);
pictureList->setResizeMode(QListWidget::Adjust); pictureList->setResizeMode(QListWidget::Adjust);
@ -32,23 +37,8 @@ void ElementPoolWidget::setElementList(std::vector<GraphicElement*> elements) {
pictureList->clear(); pictureList->clear();
this->elements = elements; this->elements = elements;
for (int index = 0; index < elements.size(); index++) { for (int index = 0; index < elements.size(); index++) {
//
//QString strPath = QString("C:\\Users\\86177\\Pictures\\Screenshots\\test.png");
//QPixmap itemPixmap(strPath);
//QPixmap itemPixmap(QSize(200, 200));
//itemPixmap.fill(Qt::red);
QPixmap itemPixmap = elements[index]->getPaintObject().getDetail();
qDebug() << this->parentWidget()->size();
//auto p = new QWidget();
//auto lb = new QLabel(p);
//lb->setPixmap(itemPixmap);
//lb->setFixedSize(1920, 1080);
//p->setFixedSize(1920, 1080);
//lb->show();
//p->show();
QListWidgetItem* pItem = new QListWidgetItem( QListWidgetItem* pItem = new QListWidgetItem(
itemPixmap.scaled(QSize(iconWidth - 25, iconHeight - 25)), elements[index]->getPreview(QSize(iconWidth - 25, iconHeight - 25)),
elements[index]->name); elements[index]->name);
pItem->setSizeHint(QSize(iconWidth, iconHeight)); pItem->setSizeHint(QSize(iconWidth, iconHeight));
pictureList->insertItem(index, pItem); pictureList->insertItem(index, pItem);
@ -79,3 +69,68 @@ void ElementPoolWidget::refresh() {
this->setElementList(this->elementManager->elements); this->setElementList(this->elementManager->elements);
// update(); // update();
} }
void ElementPoolWidget::refreshPicture(GraphicElement* element) {
for (int i = 0; i < elements.size(); i++) {
if (element == elements[i]) {
pictureList->item(i)->setIcon(elements[i]->getPreview(QSize(iconWidth - 25, iconHeight - 25)));
// update();
return;
}
}
}
void ElementPoolWidget::refreshPictureByIndex(int index) {
if (index >= 0 && index < elements.size())
{
pictureList->item(index)->setIcon(elements[index]->getPreview(QSize(iconWidth - 25, iconHeight - 25)));
// update();
}
}
void ElementPoolWidget::enableEdit()
{
connect(this->pictureList, &QListWidget::customContextMenuRequested, this, &ElementPoolWidget::popMenu);
}
void ElementPoolWidget::popMenu(const QPoint& pos)
{
QListWidgetItem* item = pictureList->itemAt(pos);
int currentIndex = -1;
if (item != nullptr)
{
currentIndex = pictureList->row(item);
}
QMenu* menu = new QMenu(this);
if (currentIndex >= 0 && currentIndex < elementManager->elements.size())
{
auto currentElement = this->elementManager->elements[currentIndex];
menu->addAction(QString::fromLocal8Bit("重命名"), this, [this, currentElement]() {
bool bOk = false;
QString sName =
QInputDialog::getText(this, QString::fromLocal8Bit("重命名"), QString::fromLocal8Bit("新名称:"), QLineEdit::Normal, currentElement->name, &bOk);
if (bOk && !sName.isEmpty())
{
currentElement->name = sName;
emit triggerCentralRefresh();
}
});
menu->addAction(QString::fromLocal8Bit("删除"), this, [this, currentElement]() {
this->elementManager->removeElement(currentElement);
emit triggerCentralRefresh();
});
menu->actions().last()->setDisabled(currentElement->referencedCount > 0);
}
else
{
menu->addAction(QString::fromLocal8Bit("添加元素从svg导入"), this, [this]() {
QString filePath = QFileDialog::getOpenFileName(this, QString::fromLocal8Bit("打开文件"), "", "SVG Files (*.svg)");
QFileInfo fileInfo(filePath);
QString fileName = fileInfo.fileName();
qDebug() << fileName << " " << filePath;
this->elementManager->createSimpleElement(fileName, filePath);
emit triggerCentralRefresh();
});
}
menu->popup(mapToGlobal(pos));
}

View File

@ -21,12 +21,18 @@ public:
void setElementList(std::vector<GraphicElement*> elementList); void setElementList(std::vector<GraphicElement*> elementList);
void setElementManager(ElementManager* element); void setElementManager(ElementManager* element);
~ElementPoolWidget(); ~ElementPoolWidget();
void enableEdit();
signals: signals:
void elementSelected(GraphicElement* element); void elementSelected(GraphicElement* element);
void refreshLayerTree();
void triggerCentralRefresh();
public slots: public slots:
int pictureItemClicked(QListWidgetItem* item); int pictureItemClicked(QListWidgetItem* item);
void refresh(); void refresh();
void refreshPicture(GraphicElement* element);
void refreshPictureByIndex(int index);
void popMenu(const QPoint& pos);
}; };

View File

@ -1,11 +1,14 @@
#include "GraphicElement.h" #include "GraphicElement.h"
#include "util/SvgFileLoader.h" #include "util/SvgFileLoader.h"
#include <QGuiApplication>
#include <QScreen>
using namespace std; using namespace std;
PixelPath SimpleElement::getPaintObject() const PixelPath SimpleElement::getPaintObject() const
{ {
PixelPath result; PixelPath result;
result.addPath(painterPath); result.addPath(painterPath);
//qDebug() << result.getPainterPath();
return result; return result;
} }
@ -14,14 +17,19 @@ void SimpleElement::loadSvgFile(const QString& filePath)
// TODO ÑùʽÎÊÌâ // TODO ÑùʽÎÊÌâ
SvgFileLoader loader; SvgFileLoader loader;
loader.loadSvgFile(filePath, painterPath); loader.loadSvgFile(filePath, painterPath);
qDebug() << "load svg file success " << painterPath.elementCount(); auto startPoint = static_cast<QPointF>(painterPath.elementAt(0));
auto endPoint = static_cast<QPointF>(painterPath.elementAt(painterPath.elementCount() - 1));
this->closed = startPoint == endPoint;
qDebug() << "load svg file success " << painterPath.elementCount() << (isClosed() ? "is" : "not") << "closed";
} }
SimpleElement::SimpleElement(QJsonObject jsonSource) : jsonSource(jsonSource) SimpleElement::SimpleElement(QJsonObject jsonSource) : jsonSource(jsonSource)
{ {
painterPath.clear(); painterPath.clear();
filePath = jsonSource["data"].toObject()["include"].toString();
//loadSvgFile("D:\\Projects\\BigC\\svg\\3.svg"); //loadSvgFile("D:\\Projects\\BigC\\svg\\3.svg");
loadSvgFile("../"/*TODO: 改成json文件所在文件夹路径*/ + jsonSource.value("data").toObject().value("include").toString()); QFileInfo info(filePath);
loadSvgFile(filePath);
} }
GroupElement::GroupElement(FolderLayerWrapper* sourceLayer) GroupElement::GroupElement(FolderLayerWrapper* sourceLayer)
@ -43,24 +51,11 @@ PixelPath GroupElement::getPaintObject() const
} }
//TODO: apply styles and send back //TODO: apply styles and send back
PixelPath SimpleElement::getPaintObject(std::vector<std::shared_ptr<LayerStyle>>* styles) const { PixelPath SimpleElement::getPaintObject(const LayerStyleContainer& styles) const {
PixelPath result; return this->getPaintObject();
Renderer::ElementStyleStrokeDemo demo(2);
auto [img, mov] = renderer->drawElement(painterPath, demo, 1.0);
//qDebug() << mov;
//qDebug() << img << " ------";
result.addImage(img, mov);
//result.addPath(painterPath);
// QImage img(80,80,QImage::Format_ARGB32);
// QPainter pt(&img);
//pt.setPen(QPen(Qt::red, 2));
//pt.drawLine(0, 0, 80, 80);
//pt.end();
//result.addImage(img, QPoint(0, 0));
return result;
} }
PixelPath GroupElement::getPaintObject(std::vector<std::shared_ptr<LayerStyle>>* styles) const { PixelPath GroupElement::getPaintObject(const LayerStyleContainer& styles) const {
return getPaintObject(); return getPaintObject();
} }
@ -78,9 +73,100 @@ PixelPath GroupElement::getPaintObject(std::vector<std::shared_ptr<LayerStyle>>*
// } // }
//} //}
//TODO : Ìí¼Óϸ½Ú //TODO : Ìí¼Óϸ½Ú
QJsonObject GraphicElement::toJson() const QJsonObject SimpleElement::toJson() const
{ {
QJsonObject result; QJsonObject result;
result.insert("name", name); QJsonObject data;
data["include"] = filePath;
result["type"] = "svg-file";
result["data"] = data;
result["name"] = name;
return result; return result;
} }
QJsonObject GroupElement::toJson() const
{
QJsonObject result;
QJsonObject data;
data["reference-layer"] = "0.0";
result["type"] = "group";
result["data"] = data;
result["name"] = name;
return result;
}
void SimpleElement::paint(QPainter* painter, QTransform transform, const LayerStyleContainer& styles)
{
painter->save();
painter->setTransform(transform);
if (styles.empty())
{
painter->drawPath(painterPath);
}
else
{
double angle = atan(transform.m12() / transform.m11());
double maxScale;
if (fabs(cos(angle))>1e-5)
maxScale = std::max(fabs(transform.m11() / cos(angle)), fabs(transform.m22() / cos(angle)));
else
maxScale = std::max(fabs(transform.m12() / sin(angle)), fabs(transform.m21() / sin(angle)));
double pixelRatio = maxScale * QGuiApplication::primaryScreen()->devicePixelRatio();
auto [img, mov] = Renderer::ElementRenderer::instance()->drawElement(painterPath, styles, pixelRatio);
transform.translate(mov.x(), mov.y());
painter->setTransform(transform.scale(1 / pixelRatio, 1 / pixelRatio));
painter->drawImage(0, 0, img);
}
painter->restore();
}
bool SimpleElement::isClosed() const
{
return closed;
}
void GroupElement::paint(QPainter* painter, QTransform transform, const LayerStyleContainer& styles)
{
sourceLayer->paint(painter, transform);
}
bool GroupElement::isClosed() const
{
return false;
}
QPixmap SimpleElement::getPreview(QSize size)
{
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;
}
QPixmap GroupElement::getPreview(QSize size)
{
auto cache = sourceLayer->getCache();
QPixmap result(QSize(1024, 1024));
QPainter painter(&result);
painter.setRenderHint(QPainter::Antialiasing);
painter.setRenderHint(QPainter::SmoothPixmapTransform);
sourceLayer->paint(&painter, QTransform(), true);
painter.end();
QRect rect(cache.getBoundingRect().toRect());
rect.setTopLeft(rect.topLeft() - QPoint(5, 5));
rect.setBottomRight(rect.bottomRight() + QPoint(5, 5));
result = result.copy(rect);
return result.scaled(size, Qt::KeepAspectRatio, Qt::SmoothTransformation);
}
void GroupElement::collectReachable(std::set<LayerWrapper*>& set) const
{
if (sourceLayer != nullptr)
{
sourceLayer->collectDownReachable(set);
}
}

View File

@ -2,7 +2,6 @@
#include "LayerWrapper.h" #include "LayerWrapper.h"
#include "LayerStyle.h" #include "LayerStyle.h"
#include <QJsonArray>
#include <QJsonObject> #include <QJsonObject>
#include <QPainterPath> #include <QPainterPath>
#include <QImage> #include <QImage>
@ -21,29 +20,40 @@ class ComposedPainterPath;
class GraphicElement class GraphicElement
{ {
public: public:
size_t referencedCount = 0;
Renderer::ElementRenderer *renderer; Renderer::ElementRenderer *renderer;
QString name = ""; QString name = "";
int index; int index;
// TODO: ¸ÄΪBitmapPath // TODO: ¸ÄΪBitmapPath
virtual QJsonObject toJson() const; virtual QJsonObject toJson() const = 0;
virtual PixelPath getPaintObject() const = 0; virtual PixelPath getPaintObject() const = 0;
virtual PixelPath getPaintObject(std::vector<std::shared_ptr<LayerStyle>>*) const = 0; virtual PixelPath getPaintObject(const LayerStyleContainer& styles) const = 0;
virtual void paint(QPainter* painter, QTransform transform, const LayerStyleContainer& styles) = 0;
virtual bool isClosed() const = 0;
virtual QPixmap getPreview(QSize size) = 0;
}; };
class SimpleElement : public GraphicElement class SimpleElement : public GraphicElement
{ {
private: private:
bool closed;
public:
QJsonObject jsonSource; QJsonObject jsonSource;
// TODO: ¸ÄΪComposedPainterPath // TODO: ¸ÄΪComposedPainterPath
QPainterPath painterPath; QPainterPath painterPath;
QString filePath;
void loadSvgFile(const QString& filePath); void loadSvgFile(const QString& filePath);
public: public:
QJsonObject toJson() const override;
SimpleElement(QJsonObject jsonSource); SimpleElement(QJsonObject jsonSource);
SimpleElement(QString filePath); SimpleElement(QString filePath);
~SimpleElement() = default; ~SimpleElement() = default;
PixelPath getPaintObject() const override; PixelPath getPaintObject() const override;
PixelPath getPaintObject(std::vector<std::shared_ptr<LayerStyle>>*) const override; PixelPath getPaintObject(const LayerStyleContainer& styles) const override;
void paint(QPainter* painter, QTransform transform, const LayerStyleContainer& styles) override;
bool isClosed() const override;
QPixmap getPreview(QSize size) override;
}; };
class GroupElement : public GraphicElement class GroupElement : public GraphicElement
@ -52,12 +62,17 @@ public:
FolderLayerWrapper* sourceLayer; FolderLayerWrapper* sourceLayer;
public: public:
QJsonObject toJson() const override;
GroupElement() = default; GroupElement() = default;
GroupElement(FolderLayerWrapper* mSourceLayer); GroupElement(FolderLayerWrapper* mSourceLayer);
~GroupElement() = default; ~GroupElement() = default;
PixelPath getPaintObject() const override; PixelPath getPaintObject() const override;
PixelPath getPaintObject(std::vector<std::shared_ptr<LayerStyle>>*) const override; PixelPath getPaintObject(const LayerStyleContainer& styles) const override;
void setSourceLayer(FolderLayerWrapper* sourceLayer); void setSourceLayer(FolderLayerWrapper* sourceLayer);
void paint(QPainter* painter, QTransform transform, const LayerStyleContainer& styles) override;
bool isClosed() const override;
QPixmap getPreview(QSize size) override;
void collectReachable(std::set<LayerWrapper*>& set) const;
}; };
//******************************** BitmapPath ********************************// //******************************** BitmapPath ********************************//

View File

@ -1,60 +1,298 @@
#include "LayerStyle.h" #include "LayerStyle.h"
#include "./EditorWidgetComponent/StrokeStyleWidget.h"
#include "./EditorWidgetComponent/FillStyleWidget.h"
#include "./util/EncodeUtil.hpp"
#include <qtmaterialcheckbox.h>
#include <QHBoxLayout> #include <QHBoxLayout>
#include <QDialogButtonBox>
#include <QPushButton> #include <QPushButton>
#include <QLabel> #include <QLabel>
#include <QLineEdit> #include <QObject>
#include <QDebug>
const std::vector<std::pair<QString, std::function<std::unique_ptr<LayerStyle>()>>> LayerStyle::types = { #define _USE_JOIN_VIEW_INPUT_RANGE
{ #include <QJsonArray>
QStringLiteral("Ãè±ß"), #include <ranges>
[]() { return std::make_unique<StrokeElementLayerStyle>(); }
},
{
QStringLiteral("Ìî³ä"),
[]() { return std::make_unique<FillElementLayerStyle>(); }
}
};
std::vector<Renderer::BaseStyle> StrokeElementLayerStyle::toBaseStyles() const std::vector<Renderer::BaseStyle> StrokeElementLayerStyle::toBaseStyles() const
{ {
return std::vector<Renderer::BaseStyle>(); std::vector<Renderer::BaseStyle> baseStyles;
if (enableEachSideIndependent)
{
if (!radialStroke(strokePair.first)->materialMap.empty())
{
baseStyles.push_back({ std::make_shared<Renderer::TransformStyle>(), strokePair.first });
}
if (!radialStroke(strokePair.second)->materialMap.empty())
{
baseStyles.push_back({ std::make_shared<Renderer::TransformStyle>(), strokePair.second });
}
}
else if (!radialStroke(strokePair.first)->materialMap.empty())
{
const auto material = std::shared_ptr(std::move(strokePair.first->clone()));
std::static_pointer_cast<MaterialStyleStroke>(material)->strokeType = Renderer::StrokeType::kBothSides;
baseStyles.push_back({ std::make_shared<Renderer::TransformStyle>(), material });
}
return baseStyles;
} }
QString StrokeElementLayerStyle::getStyleName() const QWidget* StrokeElementLayerStyle::getInputWidget()
{ {
return QStringLiteral("Ãè±ß"); auto* w = new QWidget;
} auto* materialList = new QListView;
QWidget* StrokeElementLayerStyle::getInputWidget() const auto* layout = new QVBoxLayout(w);
{ layout->setMargin(0);
// TODO
QLabel* le = new QLabel; auto* leftStrokeView = new StrokeStyleWidget(this->strokePair.first, w);
le->setText(QStringLiteral("Ãè±ß")); layout->addWidget(leftStrokeView);
return le;
auto* checkEachSideIndependent = new QtMaterialCheckBox(w);
checkEachSideIndependent->setText(QStringLiteral("ÓÒ²à¶ÀÁ¢Ãè±ß"));
checkEachSideIndependent->setChecked(enableEachSideIndependent);
layout->addWidget(checkEachSideIndependent);
auto* rightStrokeView = new StrokeStyleWidget(this->strokePair.second, w);
layout->addWidget(rightStrokeView);
rightStrokeView->setDisabled(!this->enableEachSideIndependent);
QObject::connect(checkEachSideIndependent, &QtMaterialCheckBox::toggled, [this, rightStrokeView](bool toggled) {
this->enableEachSideIndependent = toggled;
rightStrokeView->setDisabled(!toggled);
});
return w;
} }
QWidget* StrokeElementLayerStyle::getListDisplayWidget() const QWidget* StrokeElementLayerStyle::getListDisplayWidget() const
{ {
QWidget* w = new QWidget; auto* w = new QWidget;
QLabel* name = new QLabel(w); auto* name = new QLabel(w);
name->setText(QStringLiteral("Ãè±ß")); name->setText(QStringLiteral("Ãè±ß"));
QHBoxLayout* layout = new QHBoxLayout(w); auto* layout = new QHBoxLayout(w);
layout->setMargin(0); layout->setMargin(0);
layout->addWidget(name); layout->addWidget(name);
return w; return w;
} }
StrokeElementLayerStyle::StrokeElementLayerStyle(const StrokeElementLayerStyle& other) void LayerStyleContainer::computeNewHash()
{ {
materialStyles = std::vector<std::shared_ptr<Renderer::MaterialStyleStroke>>(other.materialStyles.size()); hash = 0;
for (size_t i = 0; i < other.materialStyles.size(); i++) for (auto& f : styles
| std::views::values
| std::views::transform(&LayerStyle::toBaseStyles)
| std::views::join
| std::views::transform(&Renderer::BaseStyle::material)
| std::views::transform(&MaterialStyle::encoded)
| std::views::join)
{ {
materialStyles[i] = std::make_shared<Renderer::MaterialStyleStroke>(*other.materialStyles[i]); const unsigned int u = *reinterpret_cast<unsigned int*>(&f);
hash ^= u + 0x9e3779b9 + (hash << 6) + (hash >> 2);
} }
} }
std::unique_ptr<LayerStyle> StrokeElementLayerStyle::clonePtr() const LayerStyleContainer LayerStyleContainer::fromJson(bool isClosedElement, const QJsonArray& jsonArray)
{
LayerStyleContainer container(isClosedElement);
for (const auto& style : jsonArray)
{
container.useStyle(LayerStyle::fromJson(style.toObject()));
}
return container;
}
LayerStyleContainer::LayerStyleContainer(bool isClosedElement) : hash(0)
{
for (const auto& style : commonStyles)
{
unusedStyles.insert(style);
}
if (isClosedElement)
{
for (const auto& style : closedOnlyStyles)
{
unusedStyles.insert(style);
}
}
else
{
for (const auto& style : unclosedOnlyStyles)
{
unusedStyles.insert(style);
}
}
}
std::vector<Renderer::BaseStyle> LayerStyleContainer::toBaseStyles() const
{
std::vector<Renderer::BaseStyle> result;
for (const auto& style : styles | std::views::values)
{
auto baseStyles = style->toBaseStyles();
result.insert(result.end(),
std::make_move_iterator(baseStyles.begin()),
std::make_move_iterator(baseStyles.end()));
}
return result;
}
QJsonArray LayerStyleContainer::toJson() const
{
QJsonArray json;
for (const auto& style : styles | std::views::values)
{
json.append(style->toJson());
}
return json;
}
QStringList LayerStyleContainer::unusedStyleNames() const
{
QStringList result;
for(const auto& name : unusedStyles | std::views::keys)
{
result << name;
}
return result;
}
std::unique_ptr<LayerStyle> LayerStyleContainer::makeUnusedStyle(const QString& styleName) const
{
return unusedStyles.at(styleName)();
}
bool LayerStyleContainer::empty() const
{
return styles.empty();
}
bool LayerStyleContainer::full() const
{
return unusedStyles.empty();
}
std::map<QString, std::shared_ptr<LayerStyle>>::iterator LayerStyleContainer::begin()
{
return styles.begin();
}
std::map<QString, std::shared_ptr<LayerStyle>>::iterator LayerStyleContainer::end()
{
return styles.end();
}
bool LayerStyleContainer::useStyle(const std::shared_ptr<LayerStyle>& style)
{
auto styleNode = unusedStyles.extract(style->getDisplayName());
if (styleNode.empty())
{
return false;
}
styles[styleNode.key()] = style;
usedStyles.insert(std::move(styleNode));
return true;
}
bool LayerStyleContainer::dropStyle(const QString& styleName)
{
auto styleNode = usedStyles.extract(styleName);
if (styleNode.empty())
{
return false;
}
styles.erase(styleName);
unusedStyles.insert(std::move(styleNode));
return true;
}
float LayerStyleContainer::boundingBoxAffectValue() const {
float maxLineWidth = 0;
const auto strokeStyle = styles.at(StrokeElementLayerStyle::displayName());
if (strokeStyle != nullptr)
{
if (const auto strokeElementLayerStyle =
std::dynamic_pointer_cast<StrokeElementLayerStyle>(strokeStyle);
strokeElementLayerStyle != nullptr)
{
const auto& leftStyleStroke = strokeElementLayerStyle->strokePair.first;
const auto& rightStyleStroke = strokeElementLayerStyle->strokePair.second;
if (leftStyleStroke != nullptr)
{
maxLineWidth = std::max(maxLineWidth, leftStyleStroke->halfWidth);
}
if (rightStyleStroke != nullptr)
{
maxLineWidth = std::max(maxLineWidth, rightStyleStroke->halfWidth);
}
}
}
return maxLineWidth;
}
size_t LayerStyleContainer::getHash() const
{
return hash;
}
std::unique_ptr<StrokeElementLayerStyle> StrokeElementLayerStyle::fromJson(const QJsonObject& json)
{
auto ptr = std::make_unique<StrokeElementLayerStyle>(
std::static_pointer_cast<MaterialStyleStroke>(
std::shared_ptr(std::move(MaterialStyle::decoded(EncodeUtil::fromBase64<GLfloat>(json["left"].toString()))))
),
std::static_pointer_cast<MaterialStyleStroke>(
std::shared_ptr(std::move(MaterialStyle::decoded(EncodeUtil::fromBase64<GLfloat>(json["right"].toString()))))
)
);
ptr->enableEachSideIndependent = json["enableEachSideIndependent"].toBool();
return ptr;
}
StrokeElementLayerStyle::StrokeElementLayerStyle()
{
const auto materialMap = std::map<float, Renderer::Material>();
this->strokePair.first = std::make_shared<MaterialStyleStroke>(
7,
Renderer::StrokeType::kLeftSide, Renderer::StrokeEndType::kFlat,
std::make_shared<Renderer::StrokeRadialGradient>(materialMap, false)
);
this->strokePair.second = std::make_shared<MaterialStyleStroke>(
7,
Renderer::StrokeType::kRightSide, Renderer::StrokeEndType::kFlat,
std::make_shared<Renderer::StrokeRadialGradient>(materialMap, false)
);
}
StrokeElementLayerStyle::StrokeElementLayerStyle(const PMaterialStyleStroke& left, const PMaterialStyleStroke& right)
{
this->strokePair.first = left;
this->strokePair.second = right ? right : std::static_pointer_cast<MaterialStyleStroke>(
std::shared_ptr(std::move(left->clone()))
);
}
StrokeElementLayerStyle::StrokeElementLayerStyle(const StrokeElementLayerStyle& other)
{
strokePair.first = std::static_pointer_cast<MaterialStyleStroke>(
std::shared_ptr(std::move(other.strokePair.first->clone()))
);
strokePair.second = std::static_pointer_cast<MaterialStyleStroke>(
std::shared_ptr(std::move(other.strokePair.second->clone()))
);
enableEachSideIndependent = other.enableEachSideIndependent;
}
QJsonObject StrokeElementLayerStyle::toJson() const
{
auto json = LayerStyle::toJson();
json["enableEachSideIndependent"] = enableEachSideIndependent;
json["left"] = EncodeUtil::toBase64<GLfloat>(strokePair.first->encoded());
json["right"] = EncodeUtil::toBase64<GLfloat>(strokePair.second->encoded());
return json;
}
std::unique_ptr<LayerStyle> StrokeElementLayerStyle::clone() const
{ {
LayerStyleContainer container(isClosedElement); LayerStyleContainer container(isClosedElement);
for (const auto& style : jsonArray) for (const auto& style : jsonArray)
@ -261,45 +499,84 @@ std::unique_ptr<LayerStyle> StrokeElementLayerStyle::clone() const
return std::make_unique<StrokeElementLayerStyle>(StrokeElementLayerStyle(*this)); return std::make_unique<StrokeElementLayerStyle>(StrokeElementLayerStyle(*this));
} }
std::unique_ptr<FillElementLayerStyle> FillElementLayerStyle::fromJson(const QJsonObject& json)
{
auto ptr = std::make_unique<FillElementLayerStyle>(
std::static_pointer_cast<MaterialStyleFill>(
std::shared_ptr(std::move(MaterialStyle::decoded(EncodeUtil::fromBase64<GLfloat>(json["material"].toString()))))
)
);
return ptr;
}
std::vector<Renderer::BaseStyle> FillElementLayerStyle::toBaseStyles() const std::vector<Renderer::BaseStyle> FillElementLayerStyle::toBaseStyles() const
{ {
return std::vector<Renderer::BaseStyle>(); return { {std::make_shared<Renderer::TransformStyle>(), fillMaterialStyle} };
} }
QString FillElementLayerStyle::getStyleName() const QWidget* FillElementLayerStyle::getInputWidget()
{ {
return QStringLiteral("Ìî³ä"); return new FillStyleWidget(fillMaterialStyle);
}
QWidget* FillElementLayerStyle::getInputWidget() const
{
// TODO
QLineEdit* name = new QLineEdit;
name->setText(QStringLiteral("Ìî³ä"));
return name;
} }
QWidget* FillElementLayerStyle::getListDisplayWidget() const QWidget* FillElementLayerStyle::getListDisplayWidget() const
{ {
QWidget* w = new QWidget; auto* w = new QWidget;
QLabel* name = new QLabel(w); auto* name = new QLabel(w);
name->setText(QStringLiteral("Ìî³ä")); name->setText(QStringLiteral("Ìî³ä"));
QHBoxLayout* layout = new QHBoxLayout(w); auto* layout = new QHBoxLayout(w);
layout->setMargin(0); layout->setMargin(0);
layout->addWidget(name); layout->addWidget(name);
return w; return w;
} }
FillElementLayerStyle::FillElementLayerStyle(const FillElementLayerStyle& other) FillElementLayerStyle::FillElementLayerStyle(const PMaterialStyleFill& fillMaterialStyle)
: fillMaterialStyle(fillMaterialStyle)
{ {
materialStyles = std::vector<std::shared_ptr<Renderer::MaterialStyleFill>>(other.materialStyles.size()); if (!fillMaterialStyle)
for (size_t i = 0; i < other.materialStyles.size(); i++)
{ {
materialStyles[i] = std::make_shared<Renderer::MaterialStyleFill>(*other.materialStyles[i]); this->fillMaterialStyle = std::make_shared<MaterialStyleFill>(
std::make_shared<Renderer::FillPlain>(Renderer::Material(QColor::fromRgb(0, 0, 0)))
);
} }
} }
std::unique_ptr<LayerStyle> FillElementLayerStyle::clonePtr() const FillElementLayerStyle::FillElementLayerStyle(const FillElementLayerStyle& other)
{
this->fillMaterialStyle = std::static_pointer_cast<MaterialStyleFill>(
std::shared_ptr(std::move(other.fillMaterialStyle->clone()))
);
}
QJsonObject FillElementLayerStyle::toJson() const
{
auto json = LayerStyle::toJson();
json["material"] = EncodeUtil::toBase64<GLfloat>(fillMaterialStyle->encoded());
return json;
}
std::unique_ptr<LayerStyle> FillElementLayerStyle::clone() const
{ {
return std::make_unique<FillElementLayerStyle>(FillElementLayerStyle(*this)); return std::make_unique<FillElementLayerStyle>(FillElementLayerStyle(*this));
} }
std::unique_ptr<LayerStyle> LayerStyle::fromJson(const QJsonObject& json)
{
QString type = json["type"].toString();
if (type == StrokeElementLayerStyle::typeName())
{
return StrokeElementLayerStyle::fromJson(json);
}
if (type == FillElementLayerStyle::typeName())
{
return FillElementLayerStyle::fromJson(json);
}
return nullptr;
}
QJsonObject LayerStyle::toJson() const
{
QJsonObject json;
json["type"] = this->getTypeName();
return json;
}

View File

@ -1,56 +1,130 @@
#pragma once #pragma once
#include <map>
#include <functional> #include <functional>
#include <utility> #include <utility>
#include <QWidget> #include <set>
#include <QObject> #include <map>
#include <QListWidget> #include <QListWidget>
#include <QJsonObject>
#include <QJsonArray>
#include "../Renderer/Painting/ElementStyle.h" #include "../Renderer/Painting/ElementStyle.h"
#include "../Renderer/Painting/MaterialStyleStroke.h" #include "../Renderer/Painting/MaterialStyleStroke.h"
#include "../Renderer/Painting/MaterialStyleFill.h" #include "../Renderer/Painting/MaterialStyleFill.h"
/** using Renderer::MaterialStyle;
* StylegetInputWidget() using Renderer::MaterialStyleStroke;
* StylegetInputWidget() using Renderer::MaterialStyleFill;
*
* LayerStyleElementStylestyle #define STYLE_NAME(display_name, type_name) \
* static QString displayName() { return QStringLiteral(display_name); } \
*/ QString getDisplayName() const override { return QStringLiteral(display_name); } \
class LayerStyle static QString typeName() { return type_name; } \
QString getTypeName() const override { return type_name; }
#define radialStroke(stroke) std::static_pointer_cast<Renderer::StrokeRadialGradient>(stroke->materialStroke)
#define plainFill(fill) std::static_pointer_cast<Renderer::FillPlain>(fill->materialFill)
class LayerStyle : public Renderer::ElementStyle
{ {
public: public:
const static std::vector<std::pair<QString, std::function<std::unique_ptr<LayerStyle>()>>> types; static std::unique_ptr<LayerStyle> fromJson(const QJsonObject& json);
virtual QString getStyleName() const = 0; virtual ~LayerStyle() = default;
virtual QWidget* getInputWidget() const = 0;
virtual QString getDisplayName() const = 0;
virtual QString getTypeName() const = 0;
virtual QWidget* getInputWidget() = 0;
virtual QWidget* getListDisplayWidget() const = 0; virtual QWidget* getListDisplayWidget() const = 0;
virtual ~LayerStyle() {};
virtual std::unique_ptr<LayerStyle> clonePtr() const = 0; virtual QJsonObject toJson() const;
virtual std::unique_ptr<LayerStyle> clone() const = 0;
}; };
class StrokeElementLayerStyle : public Renderer::ElementStyle, public LayerStyle class StrokeElementLayerStyle : public LayerStyle
{ {
using PMaterialStyleStroke = std::shared_ptr<MaterialStyleStroke>;
public: public:
std::vector<Renderer::BaseStyle> toBaseStyles() const override; STYLE_NAME("描边", "stroke")
QString getStyleName() const override; static std::unique_ptr<StrokeElementLayerStyle> fromJson(const QJsonObject& json);
QWidget* getInputWidget() const override;
QWidget* getListDisplayWidget() const override; StrokeElementLayerStyle();
StrokeElementLayerStyle() = default; StrokeElementLayerStyle(const PMaterialStyleStroke& left, const PMaterialStyleStroke& right = nullptr);
StrokeElementLayerStyle(const StrokeElementLayerStyle& other); StrokeElementLayerStyle(const StrokeElementLayerStyle& other);
~StrokeElementLayerStyle() = default; ~StrokeElementLayerStyle() override = default;
std::vector<std::shared_ptr<Renderer::MaterialStyleStroke>> materialStyles;
std::unique_ptr<LayerStyle> clonePtr() const override; std::vector<Renderer::BaseStyle> toBaseStyles() const override;
QWidget* getInputWidget() override;
QWidget* getListDisplayWidget() const override;
QJsonObject toJson() const override;
std::unique_ptr<LayerStyle> clone() const override;
std::pair<PMaterialStyleStroke, PMaterialStyleStroke> strokePair;
bool enableEachSideIndependent = false;
}; };
class FillElementLayerStyle : public Renderer::ElementStyle, public LayerStyle class FillElementLayerStyle : public LayerStyle
{ {
using PMaterialStyleFill = std::shared_ptr<MaterialStyleFill>;
public: public:
std::vector<Renderer::BaseStyle> toBaseStyles() const override; STYLE_NAME("填充", "fill")
QString getStyleName() const override; static std::unique_ptr<FillElementLayerStyle> fromJson(const QJsonObject& json);
QWidget* getInputWidget() const override;
QWidget* getListDisplayWidget() const override; FillElementLayerStyle(const PMaterialStyleFill& fillMaterialStyle = nullptr);
FillElementLayerStyle() = default;
FillElementLayerStyle(const FillElementLayerStyle& other); FillElementLayerStyle(const FillElementLayerStyle& other);
~FillElementLayerStyle() = default; ~FillElementLayerStyle() override = default;
std::vector<std::shared_ptr<Renderer::MaterialStyleFill>> materialStyles;
std::unique_ptr<LayerStyle> clonePtr() const override; std::vector<Renderer::BaseStyle> toBaseStyles() const override;
QWidget* getInputWidget() override;
QWidget* getListDisplayWidget() const override;
QJsonObject toJson() const override;
std::unique_ptr<LayerStyle> clone() const override;
PMaterialStyleFill fillMaterialStyle;
};
/**
* LayerStylecomputeNewHash()
*/
class LayerStyleContainer : public Renderer::ElementStyle
{
using DisplayNameWithSupplier = std::map<QString, std::function<std::unique_ptr<LayerStyle>()>>;
private:
inline const static DisplayNameWithSupplier commonStyles = { {
StrokeElementLayerStyle::displayName(),
[] { return std::make_unique<StrokeElementLayerStyle>(); }
} };
inline const static DisplayNameWithSupplier closedOnlyStyles = { {
FillElementLayerStyle::displayName(),
[] { return std::make_unique<FillElementLayerStyle>(); }
} };
inline const static DisplayNameWithSupplier unclosedOnlyStyles = { };
DisplayNameWithSupplier unusedStyles;
DisplayNameWithSupplier usedStyles;
std::map<QString, std::shared_ptr<LayerStyle>> styles;
size_t hash;
public:
static LayerStyleContainer fromJson(bool isClosedElement, const QJsonArray& jsonArray);
LayerStyleContainer(bool isClosedElement);
std::vector<Renderer::BaseStyle> toBaseStyles() const override;
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();
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);
float boundingBoxAffectValue() const;
size_t getHash() const;
/**
*
*/
void computeNewHash();
}; };

View File

@ -46,6 +46,7 @@ LayerWrapper::LayerWrapper(QJsonObject json, FolderLayerWrapper*parent, ElementM
property.scale = {transformJson.value("scale").toObject().value("x").toDouble(), property.scale = {transformJson.value("scale").toObject().value("x").toDouble(),
transformJson.value("scale").toObject().value("y").toDouble()}; transformJson.value("scale").toObject().value("y").toDouble()};
property.rotation = {transformJson.value("rotation").toDouble()}; property.rotation = {transformJson.value("rotation").toDouble()};
selected = false;
} }
FolderLayerWrapper::FolderLayerWrapper(QJsonObject json, ElementManager *elementManager, FolderLayerWrapper*parent) FolderLayerWrapper::FolderLayerWrapper(QJsonObject json, ElementManager *elementManager, FolderLayerWrapper*parent)
@ -73,26 +74,34 @@ FolderLayerWrapper::FolderLayerWrapper(QJsonObject json, ElementManager *element
} }
LeafLayerWrapper::LeafLayerWrapper(QJsonObject json, ElementManager* elementManager, FolderLayerWrapper* parent) LeafLayerWrapper::LeafLayerWrapper(QJsonObject json, ElementManager* elementManager, FolderLayerWrapper* parent)
: LayerWrapper(json, parent) : LayerWrapper(json, parent),
wrappedElement(elementManager->getElementById(json.value("element").toInt())),
styles(LayerStyleContainer::fromJson(wrappedElement->isClosed(), json.value("styles").toArray()))
{ {
qDebug() << json.value("name").toString() << " " << this; qDebug() << json.value("name").toString() << " " << this;
int elementIndex = json.value("element").toInt(); if(wrappedElement != nullptr)
wrappedElement = elementManager->getElementById(elementIndex); wrappedElement->referencedCount++;
} }
void LayerWrapper::SimpleProperty::apply(PixelPath&cache) const LeafLayerWrapper::~LeafLayerWrapper()
{ {
QTransform trans; if (wrappedElement != nullptr)
wrappedElement->referencedCount--;
}
void LayerWrapper::SimpleProperty::apply(PixelPath&cache)
{
transform.reset();
double centerX = cache.getBoundingRect().center().x(); double centerX = cache.getBoundingRect().center().x();
double centerY = cache.getBoundingRect().center().y(); double centerY = cache.getBoundingRect().center().y();
//qDebug() << name << " " << cache.boundingRect().center(); //qDebug() << name << " " << cache.boundingRect().center();
//qDebug() << name << " " << cache.boundingRect(); //qDebug() << name << " " << cache.boundingRect();
trans.translate(centerX, centerY); transform.translate(centerX, centerY);
trans.translate(offset.x(), offset.y()); transform.translate(offset.x(), offset.y());
trans.rotate(rotation); transform.rotate(rotation);
trans.scale(scale.x(), scale.y()); transform.scale(scale.x(), scale.y());
trans.translate(-centerX, -centerY); transform.translate(-centerX, -centerY);
cache = cache.trans(trans); cache = cache.trans(transform);
} }
QTransform LayerWrapper::getTransform() QTransform LayerWrapper::getTransform()
@ -129,7 +138,7 @@ void LeafLayerWrapper::refresh(LayerWrapper* layer)
cache.clear(); cache.clear();
if (wrappedElement != nullptr) if (wrappedElement != nullptr)
{ {
cache.addPath(wrappedElement->getPaintObject(&(this->styles))); cache.addPath(wrappedElement->getPaintObject(this->styles));
} }
LayerWrapper::refresh(); LayerWrapper::refresh();
} }
@ -189,19 +198,39 @@ void FolderLayerWrapper::delSelf() {
QTreeWidgetItem* LayerWrapper::getQTreeItem() QTreeWidgetItem* LayerWrapper::getQTreeItem()
{ {
this->qTreeWidgetItem->setText(0, this->property.name);
this->qTreeWidgetItem->setData(0, Qt::UserRole, QVariant::fromValue(this)); this->qTreeWidgetItem->setData(0, Qt::UserRole, QVariant::fromValue(this));
return this->qTreeWidgetItem; return this->qTreeWidgetItem;
} }
QTreeWidgetItem* LeafLayerWrapper::getQTreeItem()
{
if (this->qTreeWidgetItem == nullptr)
this->qTreeWidgetItem = new QTreeWidgetItem();
if (typeid(*wrappedElement) == typeid(GroupElement))
{
this->qTreeWidgetItem->setText(0, "@ "+this->property.name);
}
else
{
this->qTreeWidgetItem->setText(0, this->property.name);
}
this->qTreeWidgetItem->setText(1,">> "+this->wrappedElement->name);
this->qTreeWidgetItem->setTextColor(1, Qt::blue);
return LayerWrapper::getQTreeItem();
}
QTreeWidgetItem* FolderLayerWrapper::getQTreeItem() QTreeWidgetItem* FolderLayerWrapper::getQTreeItem()
{ {
while (this->qTreeWidgetItem->childCount() > 0) {
this->qTreeWidgetItem->removeChild(this->qTreeWidgetItem->child(0));
}
for (auto& child : this->children) { for (auto& child : this->children) {
this->qTreeWidgetItem->addChild(child->getQTreeItem()); this->qTreeWidgetItem->addChild(child->getQTreeItem());
} }
this->qTreeWidgetItem->setText(0, this->property.name);
auto ele = this->elementManager->getElementById(this->getReferencedBy());
if (ele != nullptr)
{
this->qTreeWidgetItem->setText(1, "<< " + ele->name);
this->qTreeWidgetItem->setTextColor(1, Qt::darkGreen);
}
return LayerWrapper::getQTreeItem(); return LayerWrapper::getQTreeItem();
} }
@ -239,6 +268,7 @@ QJsonObject LeafLayerWrapper::toJson() const
QJsonObject json = LayerWrapper::toJson(); QJsonObject json = LayerWrapper::toJson();
json.insert("element", wrappedElement->index); json.insert("element", wrappedElement->index);
json.insert("is-folder", false); json.insert("is-folder", false);
json.insert("styles", styles.toJson());
return json; return json;
} }
@ -250,31 +280,152 @@ int FolderLayerWrapper::getReferencedBy()const
return -1; return -1;
} }
void LayerWrapper::paint(QPainter* painter) void LayerWrapper::paint(QPainter* painter, QTransform transform, bool ignoreSelected)
{ {
if (!ignoreSelected && this->selected)
{
painter->save();
painter->setTransform(transform, ignoreSelected);
painter->setPen(QPen(Qt::gray, 2));
painter->setPen(Qt::DashLine);
painter->drawRect(cache.getBoundingRect());
painter->restore();
}
} }
void FolderLayerWrapper::paint(QPainter* painter) void FolderLayerWrapper::paint(QPainter* painter, QTransform transform, bool ignoreSelected)
{ {
LayerWrapper::paint(painter, transform, ignoreSelected);
transform = property.transform * transform;
//qDebug() << transform;
for (auto& child : children) for (auto& child : children)
child->paint(painter); child->paint(painter, transform, ignoreSelected);
} }
void LeafLayerWrapper::paint(QPainter* painter) void LeafLayerWrapper::paint(QPainter* painter, QTransform transform, bool ignoreSelected)
{ {
LayerWrapper::paint(painter, transform, ignoreSelected);
transform = property.transform * transform;
//qDebug() << transform;
if (wrappedElement != nullptr) if (wrappedElement != nullptr)
{ {
//painter->save(); wrappedElement->paint(painter, transform, styles);
QTransform trans;
LayerWrapper* layer = this;
while (layer != nullptr) {
trans *= layer->getTransform();
layer = layer->getParent();
}
auto p = wrappedElement->getPaintObject(&this->styles);
p.trans(trans);
painter->drawPixmap(0, 0, p.getPixmap());
//painter->restore();
} }
} }
void LayerWrapper::collectUpReachable(std::set<LayerWrapper*>& reachable)
{
auto cPos = this;
while (cPos != nullptr)
{
reachable.insert(cPos);
cPos = cPos->parent;
}
}
void LayerWrapper::collectDownReachable(std::set<LayerWrapper*>& reachable)
{
reachable.insert(this);
}
void LeafLayerWrapper::collectDownReachable(std::set<LayerWrapper*>& reachable)
{
LayerWrapper::collectDownReachable(reachable);
auto ele = dynamic_cast<GroupElement*>(wrappedElement);
if (ele != nullptr)
{
ele->collectReachable(reachable);
}
}
void FolderLayerWrapper::collectDownReachable(std::set<LayerWrapper*>& reachable)
{
LayerWrapper::collectDownReachable(reachable);
for (auto& child : children)
child->collectDownReachable(reachable);
}
void LayerWrapper::refreshTreeItem()
{
}
void LeafLayerWrapper::refreshTreeItem()
{
if (typeid(*wrappedElement) == typeid(GroupElement))
{
this->qTreeWidgetItem->setText(0, "@ " + this->property.name);
}
else
{
this->qTreeWidgetItem->setText(0, this->property.name);
}
this->qTreeWidgetItem->setText(1, ">> " + this->wrappedElement->name);
this->qTreeWidgetItem->setTextColor(1, Qt::blue);
}
void FolderLayerWrapper::refreshTreeItem()
{
for (auto& child : this->children) {
child->refreshTreeItem();
}
this->qTreeWidgetItem->setText(0, this->property.name);
auto ele = this->elementManager->getElementById(this->getReferencedBy());
if (ele != nullptr)
{
this->qTreeWidgetItem->setText(1, "<< " + ele->name);
this->qTreeWidgetItem->setTextColor(1, Qt::darkGreen);
}
if (this->referencedCount() > 0)
{
this->qTreeWidgetItem->setToolTip(0,QString::fromLocal8Bit("子树被引用计数:") + QString::number(this->referencedCount(true)) + "\n"
+ QString::fromLocal8Bit("当前节点被引用计数:") + QString::number(this->getReferencedBy() != -1));
}
else
{
this->qTreeWidgetItem->setToolTip(0, "");
}
}
size_t LayerWrapper::referencedCount(bool excludeSelf) const
{
return 0;
}
size_t FolderLayerWrapper::referencedCount(bool excludeSelf) const
{
size_t count = 0;
for (auto& child : children)
count += child->referencedCount();
if (!excludeSelf && this->getReferencedBy() != -1)
count++;
return count;
}
bool LayerWrapper::deleteable(bool excludeSubTree) const
{
return true;
}
bool FolderLayerWrapper::deleteable(bool excludeSubTree) const
{
if (excludeSubTree)
return this->getReferencedBy() == -1;
else
return this->referencedCount() == 0;
}
bool LayerWrapper::referencingGroupElement() const
{
return false;
}
bool LeafLayerWrapper::referencingGroupElement() const
{
return typeid(*wrappedElement) == typeid(GroupElement);
}
bool LayerWrapper::canApplyStyles() const
{
return typeid(*this) == typeid(LeafLayerWrapper) && !referencingGroupElement();
}

View File

@ -6,6 +6,7 @@
#include <QGraphicsItemGroup> #include <QGraphicsItemGroup>
#include <QGraphicsScene> #include <QGraphicsScene>
#include <QJSonObject> #include <QJSonObject>
#include <QJsonArray>
#include <QLine> #include <QLine>
#include <QObject> #include <QObject>
#include <QPoint> #include <QPoint>
@ -35,6 +36,7 @@ class LayerWrapper
public: public:
QTreeWidgetItem* qTreeWidgetItem; QTreeWidgetItem* qTreeWidgetItem;
bool selected;
struct SimpleProperty struct SimpleProperty
{ {
QString name = ""; QString name = "";
@ -43,8 +45,9 @@ class LayerWrapper
double rotation = 0; double rotation = 0;
bool flipHorizontally = 0; bool flipHorizontally = 0;
bool flipVertically = 0; bool flipVertically = 0;
QTransform transform;
// TODO: ½«QPainterPath¸ÄΪBitmapPath // TODO: ½«QPainterPath¸ÄΪBitmapPath
void apply(PixelPath&cache) const; void apply(PixelPath&cache);
} property; } property;
virtual void setParent(FolderLayerWrapper*newParent); virtual void setParent(FolderLayerWrapper*newParent);
virtual void refresh(LayerWrapper* layer = nullptr); virtual void refresh(LayerWrapper* layer = nullptr);
@ -55,7 +58,7 @@ class LayerWrapper
FolderLayerWrapper*getParent() const; // invoke by manager, then invoke parent's applyStyles FolderLayerWrapper*getParent() const; // invoke by manager, then invoke parent's applyStyles
LayerWrapper(QJsonObject json, FolderLayerWrapper*parent, ElementManager* elementManager=nullptr); LayerWrapper(QJsonObject json, FolderLayerWrapper*parent, ElementManager* elementManager=nullptr);
LayerWrapper() = default; LayerWrapper() = default;
virtual void paint(QPainter* painter); virtual void paint(QPainter* painter, QTransform transform=QTransform(), bool ignoreSelected = false);
// TODO : export Function // TODO : export Function
// virtual LayerWrapper *addChild() = 0; // Leaf Child Only // virtual LayerWrapper *addChild() = 0; // Leaf Child Only
// virtual LayerWrapper *addParent() = 0; // Folder Parent Only // virtual LayerWrapper *addParent() = 0; // Folder Parent Only
@ -65,7 +68,13 @@ class LayerWrapper
virtual void delSelf(); virtual void delSelf();
virtual QJsonObject toJson() const; virtual QJsonObject toJson() const;
~LayerWrapper() = default; ~LayerWrapper() = default;
virtual void collectUpReachable(std::set<LayerWrapper*>& reachable);
virtual void collectDownReachable(std::set<LayerWrapper*>& reachable);
virtual void refreshTreeItem();
virtual size_t referencedCount(bool excludeSelf = false) const;
virtual bool deleteable(bool excludeSubTree = false) const;
virtual bool referencingGroupElement() const;
bool canApplyStyles() const;
}; };
class FolderLayerWrapper : public LayerWrapper class FolderLayerWrapper : public LayerWrapper
@ -88,23 +97,29 @@ class FolderLayerWrapper : public LayerWrapper
QTreeWidgetItem* getQTreeItem() override; QTreeWidgetItem* getQTreeItem() override;
QJsonObject toJson() const override; QJsonObject toJson() const override;
int getReferencedBy()const; int getReferencedBy()const;
void paint(QPainter* painter) override; void paint(QPainter* painter, QTransform transform = QTransform(), bool ignoreSelected = 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;
}; };
class LeafLayerWrapper : public LayerWrapper class LeafLayerWrapper : public LayerWrapper
{ {
public: public:
GraphicElement *wrappedElement; GraphicElement *wrappedElement;
//const vector<Renderer::ElementStyleStrokeDemo> styles; LayerStyleContainer styles;
vector<std::shared_ptr<LayerStyle>> styles;
public: public:
~LeafLayerWrapper() = default; ~LeafLayerWrapper();
void refresh(LayerWrapper* layer = nullptr) override; void refresh(LayerWrapper* layer = nullptr) override;
LeafLayerWrapper() = default;
LeafLayerWrapper(QJsonObject json, ElementManager *elementManager, FolderLayerWrapper*parent); LeafLayerWrapper(QJsonObject json, ElementManager *elementManager, FolderLayerWrapper*parent);
QJsonObject toJson() const override; QJsonObject toJson() const override;
void paint(QPainter* painter) override; void paint(QPainter* painter, QTransform transform = QTransform(), bool ignoreSelected = false) override;
void collectDownReachable(std::set<LayerWrapper*>& reachable) override;
QTreeWidgetItem* getQTreeItem() override;
void refreshTreeItem() override;
bool referencingGroupElement() const override;
}; };
Q_DECLARE_METATYPE(LayerWrapper *) Q_DECLARE_METATYPE(LayerWrapper *)

View File

@ -66,6 +66,7 @@ void PixelPath::clear()
{ {
pixmap.fill(Qt::transparent); pixmap.fill(Qt::transparent);
boundingRect = QRectF(0, 0, 0, 0); boundingRect = QRectF(0, 0, 0, 0);
painterPath.clear();
} }
PixelPath PixelPath::trans(QTransform& mat)const PixelPath PixelPath::trans(QTransform& mat)const
@ -76,6 +77,7 @@ PixelPath PixelPath::trans(QTransform& mat)const
painter.setRenderHint(QPainter::HighQualityAntialiasing); painter.setRenderHint(QPainter::HighQualityAntialiasing);
painter.setTransform(mat); painter.setTransform(mat);
painter.drawPixmap(0, 0, pixmap); painter.drawPixmap(0, 0, pixmap);
result.painterPath.addPath(this->painterPath);
result.boundingRect = mat.mapRect(boundingRect); result.boundingRect = mat.mapRect(boundingRect);
return result; return result;
} }

View File

@ -7,14 +7,14 @@
class PixelPath class PixelPath
{ {
private: public:
QRectF boundingRect; QRectF boundingRect;
QPixmap pixmap; QPixmap pixmap;
QPainterPath painterPath; QPainterPath painterPath;
int w,h; int w,h;
public: public:
PixelPath(int w=1080, int h= 1080); PixelPath(int w=16, int h= 16);
PixelPath(QPainterPath painterPath,int w = 1080, int h = 1080); PixelPath(QPainterPath painterPath,int w = 16, int h = 16);
~PixelPath() = default; ~PixelPath() = default;
QRectF getBoundingRect() const; QRectF getBoundingRect() const;
QPixmap getPixmap() const; QPixmap getPixmap() const;

View File

@ -2,7 +2,8 @@
PreviewWindow::PreviewWindow(QWidget *parent) : QOpenGLWidget(parent) PreviewWindow::PreviewWindow(QWidget *parent) : QOpenGLWidget(parent)
{ {
this->setFixedSize(QSize(1080, 1080)); //this->setFixedSize(QSize(108, 108));
this->setStyleSheet("border: 1px solid black");
this->renderer = Renderer::ElementRenderer::instance(); this->renderer = Renderer::ElementRenderer::instance();
QSurfaceFormat surfaceFormat; QSurfaceFormat surfaceFormat;
surfaceFormat.setSamples(16); surfaceFormat.setSamples(16);
@ -12,6 +13,7 @@ PreviewWindow::PreviewWindow(QWidget *parent) : QOpenGLWidget(parent)
painter->setRenderHint(QPainter::HighQualityAntialiasing); painter->setRenderHint(QPainter::HighQualityAntialiasing);
layerManager = nullptr; layerManager = nullptr;
currentLayer = nullptr; currentLayer = nullptr;
backgroundColor = QColor(255, 255, 255, 255);
} }
void PreviewWindow::initialize(LayerManager *layerManager,QSize windowSize) void PreviewWindow::initialize(LayerManager *layerManager,QSize windowSize)
@ -43,7 +45,7 @@ void PreviewWindow::initializeGL()
void PreviewWindow::paintGL() void PreviewWindow::paintGL()
{ {
glClearColor(1.0, 1.0, 1.0, 1.0); glClearColor(backgroundColor.redF(), backgroundColor.greenF(), backgroundColor.blueF(), backgroundColor.alphaF());
glClear(GL_COLOR_BUFFER_BIT); glClear(GL_COLOR_BUFFER_BIT);
painter->begin(this); painter->begin(this);
painter->setRenderHint(QPainter::Antialiasing); painter->setRenderHint(QPainter::Antialiasing);
@ -86,14 +88,21 @@ void PreviewWindow::mouseMoveEvent(QMouseEvent* event)
// 如果按下的是左键,那么平移图形 // 如果按下的是左键,那么平移图形
currentLayer->property.offset.setX(currentLayer->property.offset.x() + dx); currentLayer->property.offset.setX(currentLayer->property.offset.x() + dx);
currentLayer->property.offset.setY(currentLayer->property.offset.y() + dy); currentLayer->property.offset.setY(currentLayer->property.offset.y() + dy);
qDebug() << dx << "----" << dy;
emit layerInfoChanged();
} }
else if (event->buttons() & Qt::RightButton) { else if (event->buttons() & Qt::RightButton) {
// 如果按下的是右键,那么旋转图形 // 如果按下的是右键,那么旋转图形
qreal angle = -sqrt(dx * dx + dy * dy) / 1.0; qreal angle = dx;
currentLayer->property.rotation += angle; currentLayer->property.rotation += angle;
emit layerInfoChanged(); }
auto layer = currentLayer;
while (layer != nullptr)
{
auto index = -1;
if (typeid(*layer) == typeid(FolderLayerWrapper))
index = dynamic_cast<FolderLayerWrapper*>(layer)->getReferencedBy();
if (index != -1)
emit refreshElementPreviewByIndex(index);
layer = layer->getParent();
} }
} }
// 更新上一次的位置 // 更新上一次的位置
@ -103,5 +112,11 @@ void PreviewWindow::mouseMoveEvent(QMouseEvent* event)
void PreviewWindow::mouseReleaseEvent(QMouseEvent* event) void PreviewWindow::mouseReleaseEvent(QMouseEvent* event)
{ {
// 当鼠标释放时,不做任何操作 emit layerInfoChanged();
}
void PreviewWindow::setBackgroundColor(QColor color)
{
this->backgroundColor = color;
this->repaint();
} }

View File

@ -23,6 +23,7 @@ class PreviewWindow : public QOpenGLWidget, protected QOpenGLFunctions
QRectF viewportRect; QRectF viewportRect;
LayerWrapper* currentLayer; LayerWrapper* currentLayer;
QPointF m_lastPos; QPointF m_lastPos;
QColor backgroundColor;
void mousePressEvent(QMouseEvent* event) override; void mousePressEvent(QMouseEvent* event) override;
void mouseMoveEvent(QMouseEvent* event) override; void mouseMoveEvent(QMouseEvent* event) override;
void mouseReleaseEvent(QMouseEvent* event) override; void mouseReleaseEvent(QMouseEvent* event) override;
@ -35,6 +36,7 @@ class PreviewWindow : public QOpenGLWidget, protected QOpenGLFunctions
void paintGL() override; void paintGL() override;
void resizeGL(int w, int h) override; void resizeGL(int w, int h) override;
Renderer::ElementRenderer* const getRenderer()const; Renderer::ElementRenderer* const getRenderer()const;
void setBackgroundColor(QColor color);
public slots: public slots:
void currentLayerChanged(LayerWrapper*); void currentLayerChanged(LayerWrapper*);
@ -42,4 +44,6 @@ class PreviewWindow : public QOpenGLWidget, protected QOpenGLFunctions
signals: signals:
void layerInfoChanged(); void layerInfoChanged();
void refreshElementPreview(GraphicElement*);
void refreshElementPreviewByIndex(int);
}; };

View File

@ -0,0 +1,21 @@
#include "EditorSettingWidget.h"
#include <QPushButton>
#include <QColorDialog>
#include <QInputDialog>
EditorSettingWidget::EditorSettingWidget(QWidget* parent)
: QWidget(parent)
{
ui.setupUi(this);
connect(ui.backgroundColorButton, &QPushButton::clicked, this, [this]() {
QColor color = QColorDialog::getColor(Qt::white, this, QString::fromLocal8Bit("选择背景颜色"));
if (color.isValid())
{
emit backgroundColorChanged(color);
}
});
connect(ui.renameButton, &QPushButton::clicked, this, [this]() {
QString name = QInputDialog::getText(this, QString::fromLocal8Bit("重命名"), QString::fromLocal8Bit("请输入新的项目名称"));
emit projectNameChanged(name);
});
}

View File

@ -0,0 +1,21 @@
#pragma once
#include <qwidget.h>
#include "ui_EditorSettingWidget.h"
class EditorSettingWidget :
public QWidget
{
Q_OBJECT;
private:
Ui::EditorSettingWidget ui;
public:
EditorSettingWidget(QWidget* parent = nullptr);
signals:
void backgroundColorChanged(QColor);
void projectNameChanged(QString);
};

View File

@ -5,7 +5,6 @@
#include <QListWidget> #include <QListWidget>
#include <QDialog> #include <QDialog>
#include <QComboBox> #include <QComboBox>
#include <QDialogButtonBox>
#include <qtmaterialraisedbutton.h> #include <qtmaterialraisedbutton.h>
#include <qtmaterialflatbutton.h> #include <qtmaterialflatbutton.h>
@ -46,32 +45,27 @@ void InfoDisplayWidget::generateLayerForm()
rotation->setValidator(new QIntValidator(-10000, 10000, this)); rotation->setValidator(new QIntValidator(-10000, 10000, this));
connect(rotation, &QLineEdit::textChanged, [=](QString content) { connect(rotation, &QLineEdit::textChanged, [=](QString content) {
this->displayLayer->property.rotation = content.toDouble(); this->displayLayer->property.rotation = content.toDouble();
emit requireRefreshElementWidget(); emit triggerCentralRefresh();
emit requireRefreshPreview();
}); });
offsetX->setValidator(new QIntValidator(-10000, 10000, this)); offsetX->setValidator(new QIntValidator(-10000, 10000, this));
connect(offsetX, &QLineEdit::textChanged, [=](QString content) { connect(offsetX, &QLineEdit::textChanged, [=](QString content) {
this->displayLayer->property.offset = {content.toDouble(), this->displayLayer->property.offset.y()}; this->displayLayer->property.offset = {content.toDouble(), this->displayLayer->property.offset.y()};
emit requireRefreshElementWidget(); emit triggerCentralRefresh();
emit requireRefreshPreview();
}); });
offsetY->setValidator(new QIntValidator(-10000, 10000, this)); offsetY->setValidator(new QIntValidator(-10000, 10000, this));
connect(offsetY, &QLineEdit::textChanged, [=](QString content) { connect(offsetY, &QLineEdit::textChanged, [=](QString content) {
this->displayLayer->property.offset = {this->displayLayer->property.offset.x(), content.toDouble()}; this->displayLayer->property.offset = {this->displayLayer->property.offset.x(), content.toDouble()};
emit requireRefreshElementWidget(); emit triggerCentralRefresh();
emit requireRefreshPreview();
}); });
scaleX->setValidator(new QDoubleValidator(0.001, 1000, 4, this)); scaleX->setValidator(new QDoubleValidator(-1000, 1000, 4, this));
connect(scaleX, &QLineEdit::textChanged, [=](QString content) { connect(scaleX, &QLineEdit::textChanged, [=](QString content) {
this->displayLayer->property.scale = {content.toDouble(), this->displayLayer->property.scale.y()}; this->displayLayer->property.scale = {content.toDouble(), this->displayLayer->property.scale.y()};
emit requireRefreshElementWidget(); emit triggerCentralRefresh();
emit requireRefreshPreview();
}); });
scaleY->setValidator(new QDoubleValidator(0.001, 1000, 4, this)); scaleY->setValidator(new QDoubleValidator(-1000, 1000, 4, this));
connect(scaleY, &QLineEdit::textChanged, [=](QString content) { connect(scaleY, &QLineEdit::textChanged, [=](QString content) {
this->displayLayer->property.scale = {this->displayLayer->property.scale.x(), content.toDouble()}; this->displayLayer->property.scale = {this->displayLayer->property.scale.x(), content.toDouble()};
emit requireRefreshElementWidget(); emit triggerCentralRefresh();
emit requireRefreshPreview();
}); });
layout->addRow("layer name:", name); layout->addRow("layer name:", name);
@ -82,38 +76,35 @@ void InfoDisplayWidget::generateLayerForm()
layout->addRow("scale-Y:", scaleY); layout->addRow("scale-Y:", scaleY);
layout->setRowWrapPolicy(QFormLayout::DontWrapRows); layout->setRowWrapPolicy(QFormLayout::DontWrapRows);
LeafLayerWrapper* leafP = dynamic_cast<LeafLayerWrapper*>(this->displayLayer); if (auto* leafP = dynamic_cast<LeafLayerWrapper*>(this->displayLayer); leafP) {
if (leafP) { auto* styleList = new QListWidget(this);
QListWidget* styleList = new QListWidget(this);
QListWidgetItem* header = new QListWidgetItem; auto* header = new QListWidgetItem;
QWidget* headerWidget = new QWidget(styleList); auto* headerWidget = new QWidget(styleList);
QHBoxLayout* headerLayout = new QHBoxLayout; auto* headerLayout = new QHBoxLayout;
QLabel* headerLabel = new QLabel(headerWidget); auto* headerLabel = new QLabel(headerWidget);
headerLabel->setText("样式列表"); headerLabel->setText("样式列表");
headerLabel->setSizePolicy(QSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding)); headerLabel->setSizePolicy(QSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding));
//QtMaterialRaisedButton* addStyleButton = new QtMaterialRaisedButton("+", headerWidget); //QtMaterialRaisedButton* addStyleButton = new QtMaterialRaisedButton("+", headerWidget);
QPushButton* addStyleButton = new QPushButton("+", headerWidget); auto* addStyleButton = new QPushButton("+", headerWidget);
addStyleButton->setFixedSize(QSize(20, 20)); addStyleButton->setFixedSize(QSize(20, 20));
if (leafP->styles.size() >= LayerStyle::types.size()) if (leafP->styles.full())
{ {
addStyleButton->setDisabled(true); addStyleButton->setDisabled(true);
} }
else else
{ {
connect(addStyleButton, &QPushButton::clicked, [&, leafP]() { connect(addStyleButton, &QPushButton::clicked, [&, leafP] {
LayerStyleDialog* dialog = new LayerStyleDialog(nullptr, nullptr, &(leafP->styles)); auto* dialog = new LayerStyleDialog(leafP->styles, nullptr, this);
dialog->exec(); dialog->exec();
if (dialog->layerStyle) if (dialog->layerStyle)
{ {
leafP->styles.push_back(dialog->layerStyle); leafP->styles.useStyle(dialog->layerStyle);
emit requireRefreshPreview(); leafP->styles.computeNewHash();
emit requireSelfRefresh(); emit triggerCentralRefresh();
emit requireRefreshElementWidget();
} }
dialog->deleteLater();
}); });
} }
@ -125,103 +116,48 @@ void InfoDisplayWidget::generateLayerForm()
header->setFlags(Qt::NoItemFlags); header->setFlags(Qt::NoItemFlags);
styleList->addItem(header); styleList->addItem(header);
styleList->setItemWidget(header, headerWidget); styleList->setItemWidget(header, headerWidget);
//static vector<QString> styleNames = { "样例1", "样例2", "样例3" };
// auto createStyleItem = [this, styleList](int index) {
// QListWidgetItem* item = new QListWidgetItem;
// QWidget* w = new QWidget;
// item->setSizeHint(QSize(50, 40));
// QHBoxLayout* layout = new QHBoxLayout;
// QPushButton* deleteButton = new QPushButton(w);
// QPushButton* detailButton = new QPushButton(w);
// QLabel* name = new QLabel(w);
// name->setText(styleNames[index]);
// detailButton->setText("...");
// detailButton->setFixedSize(QSize(20, 20));
// deleteButton->setText("×");
// deleteButton->setFixedSize(QSize(20, 20));
// connect(detailButton, &QPushButton::clicked, [styleList, item, this, index]() {
// QDialog dlg(this);
// dlg.setWindowTitle("样式详情");
// dlg.resize(400, 200);
// QGridLayout *contentLayout = new QGridLayout(&dlg);
// QLineEdit* name = new QLineEdit(styleNames[index], &dlg);
// auto buttonBox = new QDialogButtonBox(QDialogButtonBox::Save | QDialogButtonBox::Cancel);
// contentLayout->addWidget(buttonBox);
// connect(buttonBox, &QDialogButtonBox::accepted, &dlg, &QDialog::accept);
// connect(buttonBox, &QDialogButtonBox::rejected, &dlg, &QDialog::reject);
// bool updateStyle = dlg.exec();
// if (updateStyle) {
// styleNames[index] = name->text();
// qDebug() << name->text();
// // 在此处修改新样式信息至内存
// emit requireRefreshPreview();
// emit requireSelfRefresh();
// }
// });
// connect(deleteButton, &QPushButton::clicked, [styleList,item,this]() {
// styleList->removeItemWidget(item);
// delete item;
// // 删除layer对应样式
// emit requireRefreshPreview();
// emit requireSelfRefresh();
// });
// layout->addWidget(name);
// layout->addWidget(detailButton);
// layout->addWidget(deleteButton);
// w->setLayout(layout);
// styleList->addItem(item);
// styleList->setItemWidget(item, w);
// };
// for (int i = 0; i < styleNames.size(); i++)
// createStyleItem(i);
/*if (leafP->styles.empty()) auto* styles = &leafP->styles;
for (auto styleIterator = styles->begin(); styleIterator != styles->end(); ++styleIterator)
{ {
leafP->styles.push_back(std::shared_ptr<LayerStyle>(new StrokeElementLayerStyle())); auto* item = new QListWidgetItem;
}*/ auto* w = new QWidget(this);
std::vector<std::shared_ptr<LayerStyle>>* styles = &(leafP->styles);
for (auto styleIterator = styles->begin(); styleIterator != styles->end(); styleIterator++)
{
QListWidgetItem* item = new QListWidgetItem;
QWidget* w = new QWidget;
item->setSizeHint(QSize(50, 40)); item->setSizeHint(QSize(50, 40));
QHBoxLayout* layout = new QHBoxLayout; auto* layout = new QHBoxLayout(w);
layout->setAlignment(Qt::AlignmentFlag::AlignRight); layout->setAlignment(Qt::AlignmentFlag::AlignRight);
//QtMaterialFlatButton* detailButton = new QtMaterialFlatButton(w); //QtMaterialFlatButton* detailButton = new QtMaterialFlatButton(w);
//QtMaterialFlatButton* removeButton = new QtMaterialFlatButton(w); //QtMaterialFlatButton* removeButton = new QtMaterialFlatButton(w);
QPushButton* detailButton = new QPushButton(w); auto* detailButton = new QPushButton(w);
QPushButton* removeButton = new QPushButton(w); auto* removeButton = new QPushButton(w);
detailButton->setText("..."); detailButton->setText("...");
detailButton->setFixedSize(QSize(20, 20)); detailButton->setFixedSize(QSize(20, 20));
removeButton->setText("×"); removeButton->setText("×");
removeButton->setFixedSize(QSize(20, 20)); removeButton->setFixedSize(QSize(20, 20));
connect(detailButton, &QPushButton::clicked, this, connect(detailButton, &QPushButton::clicked, this,
[this, styleIterator]() [this, styles, styleIterator]
{ {
LayerStyleDialog* dialog = new LayerStyleDialog(nullptr, *styleIterator); auto* dialog =
new LayerStyleDialog(*styles, styleIterator->second, this);
dialog->exec(); dialog->exec();
if (dialog->layerStyle) if (dialog->layerStyle)
{ {
(*styleIterator) = dialog->layerStyle; styleIterator->second = dialog->layerStyle;
emit requireRefreshPreview(); styles->computeNewHash();
emit requireSelfRefresh(); emit triggerCentralRefresh();
emit requireRefreshElementWidget();
} }
dialog->deleteLater();
}); });
connect(removeButton, &QPushButton::clicked, this, connect(removeButton, &QPushButton::clicked, this,
[this, styleIterator, styles]() [this, styleIterator, styles]
{ {
styles->erase(styleIterator); styles->dropStyle(styleIterator->first);
emit requireRefreshPreview(); styles->computeNewHash();
emit requireSelfRefresh(); emit triggerCentralRefresh();
emit requireRefreshElementWidget();
}); });
QWidget* styleDisplayWidget = (*styleIterator)->getListDisplayWidget(); QWidget* styleDisplayWidget = styleIterator->second->getListDisplayWidget();
styleDisplayWidget->setParent(w); styleDisplayWidget->setParent(w);
styleDisplayWidget->setSizePolicy(QSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding)); styleDisplayWidget->setSizePolicy(QSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding));
@ -244,3 +180,9 @@ void InfoDisplayWidget::triggerSelfRefresh()
if (this->displayLayer != nullptr) if (this->displayLayer != nullptr)
this->generateLayerForm(); this->generateLayerForm();
} }
void InfoDisplayWidget::refresh()
{
if (this->displayLayer != nullptr)
this->generateLayerForm();
}

View File

@ -16,11 +16,13 @@ class InfoDisplayWidget : public QWidget
public: public:
void setLayer(LayerWrapper *layer); void setLayer(LayerWrapper *layer);
void generateLayerForm(); void generateLayerForm();
void refresh();
public slots: public slots:
void triggerSelfRefresh(); void triggerSelfRefresh();
signals: signals:
void triggerCentralRefresh();
void requireRefreshPreview(); void requireRefreshPreview();
void requireSelfRefresh(); void requireSelfRefresh();
void requireRefreshElementWidget(); void requireRefreshElementWidget();

View File

@ -9,18 +9,22 @@ LayerTreeWidget::LayerTreeWidget(QWidget *parent)
this->selectedItem = nullptr; this->selectedItem = nullptr;
this->copiedItem = nullptr; this->copiedItem = nullptr;
this->setContextMenuPolicy(Qt::CustomContextMenu); this->setContextMenuPolicy(Qt::CustomContextMenu);
this->setHeaderLabel("Layer Content"); this->setColumnWidth(0, 240);
connect(this, &QTreeWidget::customContextMenuRequested, this, &LayerTreeWidget::popMenu); connect(this, &QTreeWidget::customContextMenuRequested, this, &LayerTreeWidget::popMenu);
connect(this, &QTreeWidget::currentItemChanged, [=](QTreeWidgetItem *currentItem) { connect(this, &QTreeWidget::currentItemChanged, [=](QTreeWidgetItem *currentItem) {
if (this->selectedItem != nullptr) {
this->selectedItem->data(0, Qt::UserRole).value<LayerWrapper*>()->selected = false;
}
this->selectedItem = currentItem; this->selectedItem = currentItem;
if (this->selectedItem != nullptr) { if (this->selectedItem != nullptr) {
auto layer = this->selectedItem->data(0, Qt::UserRole).value<LayerWrapper*>(); auto layer = this->selectedItem->data(0, Qt::UserRole).value<LayerWrapper*>();
layer->selected = true;
emit displayLayerChange(layer); emit displayLayerChange(layer);
} }
else { else {
emit displayLayerChange(nullptr); emit displayLayerChange(nullptr);
} }
emit requireRefreshPreview(); emit triggerCentralRefresh();
}); });
// connect(this, &QTreeWidget::itemDoubleClicked, this, &LayerTreeWidget::onItemDoubleClicked); // connect(this, &QTreeWidget::itemDoubleClicked, this, &LayerTreeWidget::onItemDoubleClicked);
} }
@ -46,7 +50,7 @@ void LayerTreeWidget::popMenu(const QPoint &pos)
if (layer != nullptr) { if (layer != nullptr) {
if (typeid(*layer) == typeid(FolderLayerWrapper)) { if (typeid(*layer) == typeid(FolderLayerWrapper)) {
menu.addAction(QString::fromLocal8Bit("创建子节点"), this, [this, layer]() { menu.addAction(QString::fromLocal8Bit("创建子节点"), this, [this, layer]() {
auto dialog = new LayerCreateWidget(elementManager, this); auto dialog = new LayerCreateWidget(elementManager, dynamic_cast<FolderLayerWrapper*>(layer), this);
connect(dialog, &LayerCreateWidget::LayerInfoReturned, this, [this, layer](QJsonObject jsonObj) { connect(dialog, &LayerCreateWidget::LayerInfoReturned, this, [this, layer](QJsonObject jsonObj) {
auto folderLayer = dynamic_cast<FolderLayerWrapper*>(layer); auto folderLayer = dynamic_cast<FolderLayerWrapper*>(layer);
LayerWrapper* newLayer; LayerWrapper* newLayer;
@ -58,29 +62,30 @@ void LayerTreeWidget::popMenu(const QPoint &pos)
folderLayer->addChild(std::shared_ptr<LayerWrapper>(newLayer)); folderLayer->addChild(std::shared_ptr<LayerWrapper>(newLayer));
folderLayer->qTreeWidgetItem->addChild(newLayer->getQTreeItem()); folderLayer->qTreeWidgetItem->addChild(newLayer->getQTreeItem());
qDebug() << jsonObj<<"----------------------"; qDebug() << jsonObj<<"----------------------";
this->refresh(); emit triggerCentralRefresh();
emit requireRefreshPreview();
emit requireRefreshElementWidget();
}); });
dialog->exec(); dialog->exec();
}); });
menu.addAction(QString::fromLocal8Bit("ɾ³ý£¨±£Áô×ӽڵ㣩"), this, [this]() {
auto layer = this->selectedItem->data(0, Qt::UserRole).value<LayerWrapper*>();
layer->delSelf();
layer->getParent()->removeChild(layer);
this->refresh();
emit requireRefreshPreview();
});
} }
if (layer != root) { if (layer != root) {
menu.addAction(QString::fromLocal8Bit("删除"), this, [this]() { menu.addAction(QString::fromLocal8Bit("删除"), this, [this]() {
auto layer = this->selectedItem->data(0, Qt::UserRole).value<LayerWrapper*>(); auto layer = this->selectedItem->data(0, Qt::UserRole).value<LayerWrapper*>();
layer->del(); layer->del();
layer->getParent()->removeChild(layer); layer->getParent()->removeChild(layer);
this->refresh(); emit triggerCentralRefresh();
emit requireRefreshPreview();
}); });
menu.actions().last()->setEnabled(layer->deleteable());
menu.addAction(QString::fromLocal8Bit("重命名"), this, &LayerTreeWidget::onRenameEvent); menu.addAction(QString::fromLocal8Bit("重命名"), this, &LayerTreeWidget::onRenameEvent);
if(typeid(*layer) == typeid(FolderLayerWrapper))
{
menu.addAction(QString::fromLocal8Bit("ɾ³ý£¨±£Áô×ӽڵ㣩"), this, [this]() {
auto layer = this->selectedItem->data(0, Qt::UserRole).value<LayerWrapper*>();
layer->delSelf();
layer->getParent()->removeChild(layer);
emit triggerCentralRefresh();
});
menu.actions().last()->setEnabled(layer->deleteable(true));
}
} }
if (typeid(*layer) == typeid(FolderLayerWrapper) && ((FolderLayerWrapper*)layer)->getReferencedBy() == -1) { if (typeid(*layer) == typeid(FolderLayerWrapper) && ((FolderLayerWrapper*)layer)->getReferencedBy() == -1) {
menu.addAction(QString::fromLocal8Bit("创建组合元素"), this, [this]() { menu.addAction(QString::fromLocal8Bit("创建组合元素"), this, [this]() {
@ -92,7 +97,7 @@ void LayerTreeWidget::popMenu(const QPoint &pos)
"", &ok); "", &ok);
if (ok && !name.isEmpty()) { if (ok && !name.isEmpty()) {
elementManager->createGroupElement(name, layer); elementManager->createGroupElement(name, layer);
emit requireRefreshElementWidget(); emit triggerCentralRefresh();
} }
} }
}); });
@ -119,9 +124,5 @@ void LayerTreeWidget::onRenameEvent()
} }
void LayerTreeWidget::refresh() { void LayerTreeWidget::refresh() {
// if(this->root!=nullptr) this->root->refreshTreeItem();
//{
// this->clear();
// this->addTopLevelItem(this->root->getQTreeItem());
//}
} }

View File

@ -21,6 +21,7 @@ class LayerTreeWidget : public QTreeWidget
// void onItemDoubleClicked(QTreeWidgetItem *item, int column = 0); // void onItemDoubleClicked(QTreeWidgetItem *item, int column = 0);
signals: signals:
void triggerCentralRefresh();
void displayLayerChange(LayerWrapper *); void displayLayerChange(LayerWrapper *);
void requireRefreshPreview(); void requireRefreshPreview();
void requireRefreshElementWidget(); void requireRefreshElementWidget();

View File

@ -0,0 +1,25 @@
#pragma once
#include <QByteArray>
namespace EncodeUtil
{
#include <vector>
template<typename S>
QString toBase64(const std::vector<S>& vec)
{
QByteArray ba;
ba.resize(vec.size() * sizeof(S));
memcpy_s(ba.data(), ba.size(), vec.data(), vec.size() * sizeof(S));
return ba.toBase64();
}
template<typename T>
std::vector<T> fromBase64(const QString& str)
{
QByteArray ba = QByteArray::fromBase64(str.toUtf8());
std::vector<T> vec;
vec.resize(ba.size() / sizeof(T));
memcpy_s(vec.data(), vec.size() * sizeof(T), ba.data(), ba.size());
return vec;
}
}

View File

@ -0,0 +1,14 @@
#pragma once
#include <QJsonArray>
namespace JsonUtil
{
#include <vector>
template<typename T>
QJsonArray toQJsonArray(const std::vector<T>& vec)
{
QJsonArray array;
std::copy(vec.begin(), vec.end(), std::back_inserter(array));
return array;
}
}

View File

@ -15,7 +15,7 @@ vector<vector<Point>> PainterPathUtil::transformToLines(const QPainterPath& pai
for (int elementIndex = 0; elementIndex < painterPath.elementCount(); elementIndex++) { for (int elementIndex = 0; elementIndex < painterPath.elementCount(); elementIndex++) {
auto element = painterPath.elementAt(elementIndex); auto element = painterPath.elementAt(elementIndex);
point = element; point = element;
qDebug() << element; //qDebug() << element;
if (element.isMoveTo()) { if (element.isMoveTo()) {
if (line.size() >= 2) { if (line.size() >= 2) {
lines.push_back(line); lines.push_back(line);
@ -43,7 +43,7 @@ vector<vector<Point>> PainterPathUtil::transformToLines(const QPainterPath& pai
return lines; return lines;
} }
QPainterPath PainterPathUtil::monotonization(QPainterPath& painterPath) { QPainterPath PainterPathUtil::monotonization(const QPainterPath& painterPath) {
QPainterPath resPath; QPainterPath resPath;
vector<vector<Point> > lines = transformToLines(painterPath); vector<vector<Point> > lines = transformToLines(painterPath);
vector<shared_ptr<Line>> linePtrVector; vector<shared_ptr<Line>> linePtrVector;
@ -62,15 +62,15 @@ QPainterPath PainterPathUtil::monotonization(QPainterPath& painterPath) {
return resPath; return resPath;
} }
std::pair<QPainterPath, float> PainterPathUtil::normalized(const QPainterPath& path) std::pair<QPainterPath, float> PainterPathUtil::normalized(const QPainterPath& path, float width)
{ {
auto rect = path.boundingRect(); auto rect = path.boundingRect();
return { (QTransform::fromTranslate(-rect.center().x(), -rect.center().y()) * QTransform::fromScale(1 / (rect.width() / 1.999999), -1 / (rect.height() / 1.999999))).map(path), return { (QTransform::fromTranslate(-rect.center().x(), -rect.center().y()) * QTransform::fromScale(1 / (rect.width() / 1.999999), -1 / (rect.height() / 1.999999)) * QTransform::fromScale(1 / (1 + width),1 / (1 + width))).map(path),
rect.width() / rect.height() }; rect.width() / rect.height() };
} }
std::pair<std::vector<std::vector<Renderer::Point>>, float> PainterPathUtil::toNormalizedLines(const QPainterPath& path) std::pair<std::vector<std::vector<Renderer::Point>>, float> PainterPathUtil::toNormalizedLines(const QPainterPath& path, float width)
{ {
auto [p, ratio] = normalized(path); auto [p, ratio] = normalized(path, width);
return { transformToLines(p), ratio }; return { transformToLines(p), ratio };
} }

View File

@ -7,8 +7,8 @@ class PainterPathUtil
{ {
public: public:
static std::vector<std::vector<Renderer::Point>> transformToLines(const QPainterPath& painterPath); static std::vector<std::vector<Renderer::Point>> transformToLines(const QPainterPath& painterPath);
static QPainterPath monotonization(QPainterPath& painterPath); static QPainterPath monotonization(const QPainterPath& painterPath);
static std::pair<QPainterPath, float> normalized(const QPainterPath& path); static std::pair<QPainterPath, float> normalized(const QPainterPath& path, float width = 0);
static std::pair<std::vector<std::vector<Renderer::Point>>, float> toNormalizedLines(const QPainterPath& path); static std::pair<std::vector<std::vector<Renderer::Point>>, float> toNormalizedLines(const QPainterPath& path, float width = 0);
}; };

View File

@ -2,6 +2,7 @@
#include <QFile> #include <QFile>
#include <QJsondocument> #include <QJsondocument>
#include "PainterPathUtil.h" #include "PainterPathUtil.h"
#include <queue>
using Renderer::Painting; using Renderer::Painting;
using Renderer::Element; using Renderer::Element;
@ -9,6 +10,10 @@ using Renderer::ElementTransform;
using Renderer::BaseElement; using Renderer::BaseElement;
using glm::bvec2; using glm::bvec2;
using std::max; using std::max;
using std::shared_ptr;
using std::make_shared;
using std::min;
using std::queue;
const double PaintingUtil::pi = acos(-1); const double PaintingUtil::pi = acos(-1);
@ -29,7 +34,6 @@ QJsonObject PaintingUtil::readJsonFile(QString jsonFilePath) {
Painting PaintingUtil::transfromToPainting(QString jsonFilePath) { Painting PaintingUtil::transfromToPainting(QString jsonFilePath) {
Painting painting; Painting painting;
QTransform transform;
glm::bvec2 flip(0, 0); glm::bvec2 flip(0, 0);
QJsonObject jsonObj = readJsonFile(jsonFilePath); QJsonObject jsonObj = readJsonFile(jsonFilePath);
qDebug() << jsonObj; qDebug() << jsonObj;
@ -61,13 +65,8 @@ Painting PaintingUtil::transfromToPainting(QString jsonFilePath) {
FolderLayerWrapper* PaintingUtil::handleLayerWrapper(LayerWrapper* nowLayer, double& maxLineWidth, QTransform& transform, Painting& painting) { FolderLayerWrapper* PaintingUtil::handleLayerWrapper(LayerWrapper* nowLayer, double& maxLineWidth, QTransform& transform, Painting& painting) {
LeafLayerWrapper* leafLayer = dynamic_cast<LeafLayerWrapper*>(nowLayer); LeafLayerWrapper* leafLayer = dynamic_cast<LeafLayerWrapper*>(nowLayer);
QRectF transBound = transform.map(pixelPath.getPainterPath()).boundingRect(); transform = nowLayer->property.transform * transform;
transform.translate(nowLayer->property.offset.x(), nowLayer->property.offset.y())
.translate(-centerX, -centerY)
.rotate(nowLayer->property.rotation)
.scale(nowLayer->property.scale.x(), nowLayer->property.scale.y())
.translate(centerX, centerY);
if (leafLayer != nullptr) { if (leafLayer != nullptr) {
GroupElement* wrapperElement = dynamic_cast<GroupElement*>(leafLayer->wrappedElement); GroupElement* wrapperElement = dynamic_cast<GroupElement*>(leafLayer->wrappedElement);
@ -85,6 +84,7 @@ FolderLayerWrapper* PaintingUtil::handleLayerWrapper(LayerWrapper* nowLayer, dou
//ElementTransform elementTrans; //ElementTransform elementTrans;
//element.ratio = bound.width() / bound.height(); //element.ratio = bound.width() / bound.height();
// transform to initial painterPath // transform to initial painterPath
// transfrom to -1£¬ 1
QTransform trans; QTransform trans;
trans.translate(-centerX, -centerY) trans.translate(-centerX, -centerY)
.scale(1 / nowLayer->property.scale.x(), 1 / nowLayer->property.scale.y()) .scale(1 / nowLayer->property.scale.x(), 1 / nowLayer->property.scale.y())
@ -147,28 +147,37 @@ FolderLayerWrapper* PaintingUtil::handleLayerWrapper(LayerWrapper* nowLayer, dou
(2 * bound.center().x() - screenSize.width()) / screenSize.width(), (2 * bound.center().x() - screenSize.width()) / screenSize.width(),
(2 * bound.center().y() - screenSize.height()) / screenSize.height() (2 * bound.center().y() - screenSize.height()) / screenSize.height()
); );
qDebug() << elementTrans.center.x << elementTrans.center.y;
decomposeTransform(transform, elementTrans.rotation, elementTrans.scale); decomposeTransform(transform, elementTrans.rotation, elementTrans.scale);
elementTrans.scale = glm::vec2(
bound.width() * 2 / screenSize.width(),
bound.height() * 2 / screenSize.height()
);
elementTrans.flip = glm::bvec2( elementTrans.flip = glm::bvec2(
nowLayer->property.flipHorizontally, nowLayer->property.flipHorizontally,
nowLayer->property.flipVertically nowLayer->property.flipVertically
); );
return; qDebug() << elementTrans.scale.x << elementTrans.scale.y;
painting.addElement(element, elementTrans);*/
return nullptr;
} }
FolderLayerWrapper* folderLayer = dynamic_cast<FolderLayerWrapper*>(nowLayer); FolderLayerWrapper* folderLayer = dynamic_cast<FolderLayerWrapper*>(nowLayer);
for (auto sonLayer : folderLayer->children) { return folderLayer;
traverseLayTree(sonLayer.get(), transform, flip, painting);
}
return;
} }
void PaintingUtil::decomposeTransform(QTransform trans, float& angle, glm::vec2& scale) { void PaintingUtil::decomposeTransform(QTransform trans, float& angle, glm::vec2& scale) {
trans.translate(-trans.dx(), -trans.dy()); //qDebug() << trans;
trans.setMatrix(
trans.m11(), trans.m12(), trans.m13(),
trans.m21(), trans.m22(), trans.m23(),
0, 0, 1);
//qDebug() << trans.dx() << trans.dy();
int count = 0; int count = 0;
double norm = 0, n = 0; double norm = 0, n = 0;
QTransform R = trans, Rit, Rnext; QTransform R = trans, Rit, Rnext;
do { do {
++count; ++count;
Rit = R.inverted(); Rit = R.transposed().inverted();
Rnext.setMatrix( Rnext.setMatrix(
(R.m11() + Rit.m11()) / 2, (R.m11() + Rit.m11()) / 2,
(R.m12() + Rit.m12()) / 2, (R.m12() + Rit.m12()) / 2,
@ -195,9 +204,16 @@ void PaintingUtil::decomposeTransform(QTransform trans, float& angle, glm::vec2&
+ fabs(R.m33() - Rnext.m33())); + fabs(R.m33() - Rnext.m33()));
R = Rnext; R = Rnext;
} while (count < 100 && norm > 0.0001); } while (count < 100 && norm > 0.0001);
angle = acos(R.m11()); double cosValue = max(-1.0, min(R.m11(), 1.0));
R = R.inverted() * trans; double sinValue = max(-1.0, min(R.m12(), 1.0));
scale = glm::vec2(R.m11(), R.m22()); angle = acos(cosValue) * 180 / pi;
if (sinValue < 0) {
angle = 360 - angle;
}
qDebug() << angle;
//R = R.inverted() * trans;
//scale = glm::vec2(R.m11(), R.m22());
//qDebug() << scale.x << scale.y;
return; return;
} }

View File

@ -5,11 +5,13 @@
class PaintingUtil class PaintingUtil
{ {
private: private:
static const double pi;
static QJsonObject readJsonFile(QString jsonFilePath); static QJsonObject readJsonFile(QString jsonFilePath);
static FolderLayerWrapper* handleLayerWrapper(LayerWrapper* nowLayer, double& maxLineWidth, QTransform& transform, Renderer::Painting& painting); static FolderLayerWrapper* handleLayerWrapper(LayerWrapper* nowLayer, double& maxLineWidth, QTransform& transform, Renderer::Painting& painting);
static double getMaxLineWidth(LayerWrapper* root); static double getMaxLineWidth(LayerWrapper* root);
public: public:
static Renderer::Painting transfromToPainting(QString jsonFilePath); static Renderer::Painting transfromToPainting(QString jsonFilePath);
static void decomposeTransform(QTransform trans, float& angle, glm::vec2& scale);
}; };

View File

@ -18,7 +18,7 @@ namespace Renderer
glm::vec3 Position; glm::vec3 Position;
glm::vec3 Normal; glm::vec3 Normal;
glm::vec2 TexCoords; glm::vec2 TexCoords;
Vertex(const aiVector3D& position, const aiVector3D& Normal, const aiVector3D& TexCoords); Vertex(const aiVector3D& position, const aiVector3D& normal, const aiVector3D& texCoords);
}; };
struct Texture struct Texture

View File

@ -14,6 +14,9 @@
#include "../Editor/util/PainterPathUtil.h" #include "../Editor/util/PainterPathUtil.h"
#include "../Editor/util/SvgFileLoader.h" #include "../Editor/util/SvgFileLoader.h"
#include <ThirdPartyLib/qquick/qquicksvgparser_p.h> #include <ThirdPartyLib/qquick/qquicksvgparser_p.h>
#include <util/PaintingUtil.h>
#include "Painting/MaterialStyleStroke.h"
#include <glm/gtc/matrix_transform.hpp>
using namespace Renderer; using namespace Renderer;
using std::vector; using std::vector;
@ -30,14 +33,12 @@ Model::Model(QOpenGLContext* context, QOpenGLShaderProgram* shaderProgram,
} }
void Model::draw() { void Model::draw() {
//shaderProgram->bind();
for (auto& mesh : meshes) { for (auto& mesh : meshes) {
mesh->draw(); mesh->draw();
} }
} }
void Model::drawShadow() { void Model::drawShadow() {
//shaderProgram->bind();
for (auto& mesh : meshes) { for (auto& mesh : meshes) {
mesh->drawShadow(); mesh->drawShadow();
} }
@ -50,10 +51,10 @@ void Renderer::Model::loadModel(QString path)
directory = modelFile.dir(); directory = modelFile.dir();
Assimp::Importer importer; Assimp::Importer importer;
const aiScene* scene = importer.ReadFile(modelFile.absoluteFilePath().toUtf8(), aiProcess_Triangulate /*| aiProcess_FlipUVs*/); const aiScene* scene = importer.ReadFile(modelFile.absoluteFilePath().toUtf8(), aiProcess_Triangulate);
if (!scene || scene->mFlags & AI_SCENE_FLAGS_INCOMPLETE || !scene->mRootNode) if (!scene || scene->mFlags & AI_SCENE_FLAGS_INCOMPLETE || !scene->mRootNode)
{ {
qCritical() << "ERROR::ASSIMP::" << importer.GetErrorString() << endl; qCritical() << "ERROR::ASSIMP::" << importer.GetErrorString();
return; return;
} }
qDebug() << modelFile.absoluteFilePath() << "Loaded Successfully"; qDebug() << modelFile.absoluteFilePath() << "Loaded Successfully";
@ -61,9 +62,11 @@ void Renderer::Model::loadModel(QString path)
qDebug() << "NumMaterials: " << scene->mNumMaterials; qDebug() << "NumMaterials: " << scene->mNumMaterials;
qDebug() << "NumTextures: " << scene->mNumTextures; qDebug() << "NumTextures: " << scene->mNumTextures;
unloadModel();
if (QFile paintingConfigFile(directory.filePath(name + ".txt")); paintingConfigFile.open(QFile::ReadOnly | QIODevice::Text)) if (QFile paintingConfigFile(directory.filePath(name + ".txt")); paintingConfigFile.open(QFile::ReadOnly | QIODevice::Text))
{ {
paintingMap.clear();
QTextStream stream(&paintingConfigFile); QTextStream stream(&paintingConfigFile);
while (!stream.atEnd()) while (!stream.atEnd())
{ {
@ -80,23 +83,36 @@ void Renderer::Model::loadModel(QString path)
qWarning() << "Painting Config Not Found!"; qWarning() << "Painting Config Not Found!";
} }
aiMatrix4x4 transform;
aiMatrix4x4::Scaling(aiVector3D(1 / 0.008), transform);
processNode(scene->mRootNode, scene, transform * scene->mRootNode->mTransformation);
AABB.clear();
AABB.emplace_back(minX, minY, minZ);
AABB.emplace_back(minX, minY, maxZ);
AABB.emplace_back(minX, maxY, minZ);
AABB.emplace_back(minX, maxY, maxZ);
AABB.emplace_back(maxX, minY, minZ);
AABB.emplace_back(maxX, minY, maxZ);
AABB.emplace_back(maxX, maxY, minZ);
AABB.emplace_back(maxX, maxY, maxZ);
}
void Renderer::Model::unloadModel()
{
paintingMap.clear();
for (auto& i : paintingLoaded)
vtManager->deleteVirtualTexture(i.second);
paintingLoaded.clear();
texturesLoaded.clear();
meshes.clear();
minX = std::numeric_limits<float>::max(); minX = std::numeric_limits<float>::max();
maxX = std::numeric_limits<float>::min(); maxX = std::numeric_limits<float>::min();
minY = std::numeric_limits<float>::max(); minY = std::numeric_limits<float>::max();
maxY = std::numeric_limits<float>::min(); maxY = std::numeric_limits<float>::min();
minZ = std::numeric_limits<float>::max(); minZ = std::numeric_limits<float>::max();
maxZ = std::numeric_limits<float>::min(); maxZ = std::numeric_limits<float>::min();
aiMatrix4x4 transform;
aiMatrix4x4::Scaling(aiVector3D(1 / 0.008), transform);
processNode(scene->mRootNode, scene, transform * scene->mRootNode->mTransformation);
AABB.push_back(QVector3D(minX, minY, minZ));
AABB.push_back(QVector3D(minX, minY, maxZ));
AABB.push_back(QVector3D(minX, maxY, minZ));
AABB.push_back(QVector3D(minX, maxY, maxZ));
AABB.push_back(QVector3D(maxX, minY, minZ));
AABB.push_back(QVector3D(maxX, minY, maxZ));
AABB.push_back(QVector3D(maxX, maxY, minZ));
AABB.push_back(QVector3D(maxX, maxY, maxZ));
} }
void Model::processNode(aiNode* node, const aiScene* scene, aiMatrix4x4 mat4) void Model::processNode(aiNode* node, const aiScene* scene, aiMatrix4x4 mat4)
@ -156,7 +172,6 @@ std::unique_ptr<Drawable> Model::processMesh(aiMesh* mesh, const aiScene* scene,
for (auto& v : vertices) for (auto& v : vertices)
{ {
//qDebug() << v.TexCoords.x << v.TexCoords.y;
v.TexCoords = (v.TexCoords - leftBottom) / (rightTop - leftBottom); v.TexCoords = (v.TexCoords - leftBottom) / (rightTop - leftBottom);
//qDebug() << v.TexCoords.x << v.TexCoords.y; //qDebug() << v.TexCoords.x << v.TexCoords.y;
} }
@ -229,6 +244,13 @@ GLuint Renderer::Model::loadPainting(std::string path)
return iter->second; return iter->second;
Painting painting; Painting painting;
if (auto file = QFileInfo(QString(path.c_str())); file.isFile())
painting = PaintingUtil::transfromToPainting(file.path());
else
{
qDebug() << path.c_str() << "Not Found, Using Default Painting";
vector<std::pair<std::shared_ptr<Contour>, float>> contours;
Painting painting;
//if (auto file = QFileInfo(QString(path.c_str())); file.isFile()) //if (auto file = QFileInfo(QString(path.c_str())); file.isFile())
if(auto file = QFileInfo(QString("../test.json")); file.isFile()) if(auto file = QFileInfo(QString("../test.json")); file.isFile())
painting = PaintingUtil::transfromToPainting("../test.json"); painting = PaintingUtil::transfromToPainting("../test.json");
@ -255,22 +277,93 @@ GLuint Renderer::Model::loadPainting(std::string path)
vector<std::shared_ptr<ElementStyle>> style = { vector<std::shared_ptr<ElementStyle>> style = {
std::make_shared<ElementStyleFillDemo>(), std::make_shared<ElementStyleFillDemo>(),
std::make_shared<ElementStyleStrokeDemo>(0.06), std::make_shared<ElementStyleStrokeDemo>(0.02),
std::make_shared<ElementStyleStrokeRadialGradientDemo>(0.06) std::make_shared<ElementStyleStrokeRadialGradientDemo>(0.2)
}; };
vector<std::shared_ptr<Element>> element = { vector<std::shared_ptr<Element>> element = {
std::make_shared<Element>(Element{ contours[0].first, style[0], contours[0].second}), std::make_shared<Element>(Element{ contours[0].first, style[0], contours[0].second}),
std::make_shared<Element>(Element{ contours[1].first, style[2], contours[1].second}), std::make_shared<Element>(Element{ contours[1].first, style[2], contours[1].second}),
std::make_shared<Element>(Element{ contours[2].first, style[1], contours[2].second}), std::make_shared<Element>(Element{ contours[2].first, style[0], contours[2].second}),
}; };
Painting painting;
if (path == "0.json") if (path == "0.json")
{ {
painting.addElement(*element[0], ElementTransform{ glm::vec2(-0.45,-0.45), glm::vec2(0.5,0.5) / 2.f, 0, glm::bvec2(false), 0 }); //painting.addElement(*element[0], ElementTransform{ glm::vec2(-0.45,-0.45), glm::vec2(0.5,0.5) / 2.f, 0, glm::bvec2(false), 0 });
//painting.addElement(*element[1], ElementTransform{ glm::vec2(-0.45, 0.45), glm::vec2(0.5,0.5) / 2.f, 0, glm::bvec2(false), 0 }); //painting.addElement(*element[1], ElementTransform{ glm::vec2(-0.45, 0.45), glm::vec2(0.5,0.5) / 2.f, 0, glm::bvec2(false), 0 });
painting.addElement(*element[2], ElementTransform{ glm::vec2(0.50,-0.45), glm::vec2(0.6,0.7) / 2.f, 0, glm::bvec2(false), 0 }); //painting.addElement(*element[2], ElementTransform{ glm::vec2(0.50,-0.45), glm::vec2(0.6,0.7) / 2.f, 0, glm::bvec2(false), 0 });
}
else if (path == "1.json")
{
//painting.backgroundColor = QColor(196, 81, 35);
float widths[] = { 0.22, 0.22 * 0.25 / 0.15, 0.22 * 0.25 / 0.15 };
QPainterPath painterPaths[6];
for (int i = 0; i < 6; i++)
if (!SvgFileLoader().loadSvgFile(QString(std::format("../svg/{}.svg", i + 1).c_str()), painterPaths[i]))
qCritical() << "load error";
vector<std::pair<std::shared_ptr<Contour>, float>> contours;
for (int i = 0; i < 3; i++)
{
auto [contour, ratio] = PainterPathUtil::toNormalizedLines(painterPaths[i], widths[i]);
contours.emplace_back(std::make_shared<Contour>(contour), ratio);
}
class StyleStrokeRadialGradient : public Renderer::ElementStyle
{
public:
float width;
StrokeType type;
StyleStrokeRadialGradient(float width, StrokeType type) :width(width), type(type) {};
virtual std::vector<Renderer::BaseStyle> toBaseStyles() const override
{
std::map<float, Material> materialMap = {
{0.09, Material{QColor(255,255,255),0,0.8}},
{0.63, Material{QColor(165,176,207),0,0.8}},
{1.00, Material{QColor(58,64,151),0,0.8}}
};
return { BaseStyle(std::make_shared<TransformStyle>(),
std::make_shared<MaterialStyleStroke>(width, type, StrokeEndType::kFlat,
std::make_shared<StrokeRadialGradient>(materialMap, false))) };
}
};
vector<std::shared_ptr<ElementStyle>> style = {
std::make_shared<StyleStrokeRadialGradient>(widths[0], StrokeType::kLeftSide),
std::make_shared<StyleStrokeRadialGradient>(widths[1], StrokeType::kRightSide),
std::make_shared<StyleStrokeRadialGradient>(widths[2], StrokeType::kLeftSide),
};
std::map<float, Material> materialMap = {
{0.09, Material{QColor(255,255,255),0,0.8}},
{0.63, Material{QColor(165,176,207),0,0.8}},
{1.00, Material{QColor(58,64,151),0,0.8}}
};
vector<std::shared_ptr<BaseElement>> element = {
std::make_shared<BaseElement>(contours[0].first, std::make_shared<MaterialStyleStroke>(
widths[0], StrokeType::kLeftSide, StrokeEndType::kFlat, std::make_shared<StrokeRadialGradient>(materialMap, false))),
std::make_shared<BaseElement>(contours[1].first, std::make_shared<MaterialStyleStroke>(
widths[1], StrokeType::kRightSide, StrokeEndType::kFlat, std::make_shared<StrokeRadialGradient>(materialMap, false))),
std::make_shared<BaseElement>(contours[2].first, std::make_shared<MaterialStyleStroke>(
widths[2], StrokeType::kLeftSide, StrokeEndType::kFlat, std::make_shared<StrokeRadialGradient>(materialMap, false))),
};
QTransform trans = QTransform::fromScale(0.3, 0.3).inverted();
glm::mat3x2 mat(trans.m11(), trans.m12(), trans.m21(), trans.m22(), trans.m31(), trans.m32());
trans = QTransform::fromTranslate(0.5, 0).scale(0.3, 0.3).inverted();
glm::mat3x2 mat2(trans.m11(), trans.m12(), trans.m21(), trans.m22(), trans.m31(), trans.m32());
//mat = glm::mat3x2(11, 22, 33, 44, 55, 66);
//mat2 = glm::mat3x2(1, 2, 3, 4, 5, 6);
qDebug() << std::format("\n{} {} {}\n {} {} {}\n{} {} {}",
trans.m11(), trans.m21(), trans.m31(),
trans.m12(), trans.m22(), trans.m32(),
trans.m13(), trans.m23(), trans.m33()).c_str();
//painting.addElement(*element[0], ElementTransform{ glm::vec4(-1,-1,1,1), mat, 0});
painting.addElement(*element[1], ElementTransform{ glm::vec4(-1,-1,0,1), mat, 0 });
painting.addElement(*element[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 });
//painting.addElement(*element[2], ElementTransform{ glm::vec2(-0.535,0.23), glm::vec2(0.15), 0, glm::bvec2(false), 0 });
} }
else else
{ {
@ -278,10 +371,10 @@ GLuint Renderer::Model::loadPainting(std::string path)
{ {
float x = (float)rand() / RAND_MAX * 2 - 1; float x = (float)rand() / RAND_MAX * 2 - 1;
float y = (float)rand() / RAND_MAX * 2 - 1; float y = (float)rand() / RAND_MAX * 2 - 1;
painting.addElement(*element[i % 3], ElementTransform{ glm::vec2(x,y), glm::vec2(0.025), (float)rand() / RAND_MAX * 360, glm::bvec2(false), 0 }); //painting.addElement(*element[i % 3], ElementTransform{ glm::vec2(x,y), glm::vec2(0.025), (float)rand() / RAND_MAX * 360, glm::bvec2(false), 0 });
}
} }
} }
painting.generateBuffers(glFunc); painting.generateBuffers(glFunc);
auto index = vtManager->createVirtualTexture(painting); auto index = vtManager->createVirtualTexture(painting);

View File

@ -15,6 +15,7 @@ namespace Renderer
void draw(); void draw();
void drawShadow(); void drawShadow();
void loadModel(QString path); void loadModel(QString path);
void unloadModel();
Model(QOpenGLContext* context, QOpenGLShaderProgram* shaderProgram, QOpenGLShaderProgram* paintingProgram, QOpenGLShaderProgram* shadowProgram, VirtualTextureManager* vtManager); Model(QOpenGLContext* context, QOpenGLShaderProgram* shaderProgram, QOpenGLShaderProgram* paintingProgram, QOpenGLShaderProgram* shadowProgram, VirtualTextureManager* vtManager);
private: private:
QOpenGLContext* context = nullptr; QOpenGLContext* context = nullptr;
@ -25,8 +26,8 @@ namespace Renderer
VirtualTextureManager* vtManager = nullptr; VirtualTextureManager* vtManager = nullptr;
/** /**
* @param key BaseColor * @brief key BaseColor \n
* @param value json, * value json,
*/ */
std::unordered_map<std::string, std::tuple<std::string, glm::vec2, glm::vec2>> paintingMap; std::unordered_map<std::string, std::tuple<std::string, glm::vec2, glm::vec2>> paintingMap;
std::unordered_map<std::string, GLuint> paintingLoaded; std::unordered_map<std::string, GLuint> paintingLoaded;

View File

@ -1,6 +1,43 @@
#include "BaseStyle.h" #include "BaseStyle.h"
#include "MaterialStyleFill.h"
#include "MaterialStyleStroke.h"
using namespace Renderer; using namespace Renderer;
using namespace glm;
Renderer::Material::Material(const QColor& color, float metallic, float roughness)
: color(color)
{
setMetallicF(metallic);
setRoughnessF(roughness);
}
Renderer::Material::Material(const glm::vec4& color, const glm::vec2& metallicRoughness)
: color(QColor::fromRgbF(color.r, color.g, color.b, color.a))
{
setMetallicF(metallicRoughness.r);
setRoughnessF(metallicRoughness.g);
}
float Renderer::Material::metallicF() const
{
return metallic / 255.f;
}
float Renderer::Material::roughnessF() const
{
return roughness / 255.f;
}
void Renderer::Material::setMetallicF(float metallic)
{
this->metallic = metallic * 255;
}
void Renderer::Material::setRoughnessF(float roughness)
{
this->roughness = roughness * 255;
}
bool Renderer::Material::operator==(const Material& m) const bool Renderer::Material::operator==(const Material& m) const
{ {
@ -9,5 +46,52 @@ bool Renderer::Material::operator==(const Material& m) const
std::pair<glm::vec4, glm::vec2> Renderer::Material::toVec() const std::pair<glm::vec4, glm::vec2> Renderer::Material::toVec() const
{ {
return { glm::vec4(color.redF(), color.greenF(), color.blueF(), color.alphaF()), glm::vec2(metallic, roughness)}; return { glm::vec4(color.redF(), color.greenF(), color.blueF(), color.alphaF()), glm::vec2(metallicF(), roughnessF())};
}
std::unique_ptr<MaterialStyle> Renderer::MaterialStyle::decoded(const std::vector<GLfloat>& encoded)
{
if (encoded[0] <= 0) /// MaterialStyleFill
{
std::unique_ptr<MaterialFill> materiallFill;
glm::vec4 head = glm::unpackUnorm4x8(glm::floatBitsToUint(encoded[0]));
if (head.z == 0)
{
materiallFill = std::make_unique<FillPlain>(Material(glm::unpackUnorm4x8(glm::floatBitsToUint(encoded[1])), glm::vec2(head.r, head.g)));
}
return std::make_unique<MaterialStyleFill>(std::move(materiallFill));
}
else /// MaterialStyleStroke
{
std::unique_ptr<MaterialStroke> materialStroke;
uint headUint = floatBitsToUint(encoded[1]);
vec4 head = unpackUnorm4x8(headUint);
StrokeType strokeType = (StrokeType)floor(head.b * 10);
StrokeEndType endType = (StrokeEndType)(int(round(head.b * 100)) % 10);
switch (int(head.a * 100) % 10)
{
/// Plain
case 0: {
materialStroke = std::make_unique<StrokePlain>(Material(glm::unpackUnorm4x8(glm::floatBitsToUint(encoded[2])), glm::vec2(head.r, head.g)));
break;
}
/// RadialGradient
case 1: {
uint size = headUint % (1 << 15);
bool gradual = (headUint & (1 << 15)) != 0;
std::map<float, Material> materialMap;
for (uint i = 0; i < size; i++)
{
auto data = floatBitsToUint(encoded[2 + i * 2]);
auto level = unpackUnorm2x16(data).y;
auto color = unpackUnorm4x8(floatBitsToUint(encoded[3 + i * 2]));
auto metallicRoughness = unpackUnorm4x8(data);
materialMap.emplace(level, Material(color, glm::vec2(metallicRoughness.r, metallicRoughness.g)));
}
materialStroke = std::make_unique<StrokeRadialGradient>(materialMap, gradual);
break;
}
}
return std::make_unique<MaterialStyleStroke>(encoded[0], strokeType, endType, std::move(materialStroke));
}
} }

View File

@ -25,6 +25,7 @@ namespace Renderer
virtual std::vector<GLfloat> encoded() const = 0; virtual std::vector<GLfloat> encoded() const = 0;
virtual std::unique_ptr<MaterialStyle> clone() const = 0; virtual std::unique_ptr<MaterialStyle> clone() const = 0;
virtual bool operator==(const MaterialStyle&) const = 0; virtual bool operator==(const MaterialStyle&) const = 0;
static std::unique_ptr<MaterialStyle> decoded(const std::vector<GLfloat>& encoded);
}; };
struct BaseStyle struct BaseStyle
@ -36,8 +37,16 @@ namespace Renderer
struct Material struct Material
{ {
QColor color; QColor color;
float metallic; std::uint8_t metallic;
float roughness; std::uint8_t roughness;
Material() = default;
Material(const QColor& color, float metallic = 0, float roughness = 0.5);
Material(const glm::vec4& color, const glm::vec2& metallicRoughness);
float metallicF() const;
float roughnessF() const;
void setMetallicF(float metallic);
void setRoughnessF(float roughness);
bool operator==(const Material&) const; bool operator==(const Material&) const;
std::pair<glm::vec4, glm::vec2> toVec() const; std::pair<glm::vec4, glm::vec2> toVec() const;
}; };

View File

@ -1,16 +1,16 @@
#include "Element.h" #include "Element.h"
void Renderer::ElementTransform::applyTransformStyle(const TransformStyle& t) //void Renderer::ElementTransform::applyTransformStyle(const TransformStyle& t)
{ //{
center += t.translation; // /*center += t.translation;
scale *= t.scale; // scale *= t.scale;
rotation += t.rotation; // rotation += t.rotation;
flip ^= t.flip; // flip ^= t.flip;*/
} //}
//
Renderer::ElementTransform Renderer::ElementTransform::appliedTransformStyle(const TransformStyle& t) const //Renderer::ElementTransform Renderer::ElementTransform::appliedTransformStyle(const TransformStyle& t) const
{ //{
ElementTransform result = *this; // ElementTransform result = *this;
result.applyTransformStyle(t); // result.applyTransformStyle(t);
return result; // return result;
} //}

View File

@ -18,14 +18,15 @@ namespace Renderer
struct ElementTransform struct ElementTransform
{ {
glm::vec2 center; //glm::vec2 center;
//glm::vec2 size; //glm::vec2 scale; /// 相对于画布
glm::vec2 scale; /// 相对于画布 //float rotation; /// 角度制
float rotation; /// 角度制 //glm::bvec2 flip;
glm::bvec2 flip; glm::vec4 bound; /// 包围盒,不影响变换
glm::mat3x2 transform; /// 逆变换
GLuint zIndex; GLuint zIndex;
void applyTransformStyle(const TransformStyle& t); //void applyTransformStyle(const TransformStyle& t);
ElementTransform appliedTransformStyle(const TransformStyle& t) const; //ElementTransform appliedTransformStyle(const TransformStyle& t) const;
}; };
} }

View File

@ -3,7 +3,12 @@
using namespace Renderer; using namespace Renderer;
Renderer::FillPlain::FillPlain(QColor color, float metallic, float roughness) Renderer::FillPlain::FillPlain(QColor color, float metallic, float roughness)
: color(color), metallic(metallic), roughness(roughness) : material(color, metallic, roughness)
{
}
Renderer::FillPlain::FillPlain(const Material& material)
: material(material)
{ {
} }
@ -14,8 +19,9 @@ MaterialFillType Renderer::FillPlain::type() const
std::vector<GLfloat> Renderer::FillPlain::encoded() const std::vector<GLfloat> Renderer::FillPlain::encoded() const
{ {
return { glm::uintBitsToFloat(glm::packUnorm4x8(glm::vec4(metallic, roughness, 0, 1))), auto pair = material.toVec();
glm::uintBitsToFloat(glm::packUnorm4x8(glm::vec4(color.redF(), color.greenF(), color.blueF(), color.alphaF())))}; return { glm::uintBitsToFloat(glm::packUnorm4x8(glm::vec4(pair.second, 0, 1))),
glm::uintBitsToFloat(glm::packUnorm4x8(pair.first))};
} }
std::unique_ptr<MaterialFill> Renderer::FillPlain::clone() const std::unique_ptr<MaterialFill> Renderer::FillPlain::clone() const
@ -26,9 +32,7 @@ std::unique_ptr<MaterialFill> Renderer::FillPlain::clone() const
bool Renderer::FillPlain::operator==(const MaterialFill& m) const bool Renderer::FillPlain::operator==(const MaterialFill& m) const
{ {
return type() == m.type() return type() == m.type()
&& color == static_cast<const FillPlain&>(m).color && material == static_cast<const FillPlain&>(m).material;
&& metallic == static_cast<const FillPlain&>(m).metallic
&& roughness == static_cast<const FillPlain&>(m).roughness;
} }

View File

@ -18,14 +18,13 @@ namespace Renderer
{ {
public: public:
FillPlain(QColor color, float metallic, float roughness); FillPlain(QColor color, float metallic, float roughness);
FillPlain(const Material& material);
virtual MaterialFillType type() const override; virtual MaterialFillType type() const override;
virtual std::vector<GLfloat> encoded() const override; virtual std::vector<GLfloat> encoded() const override;
virtual std::unique_ptr<MaterialFill> clone() const override; virtual std::unique_ptr<MaterialFill> clone() const override;
virtual bool operator==(const MaterialFill&) const override; virtual bool operator==(const MaterialFill&) const override;
QColor color; Material material;
float metallic;
float roughness;
}; };
class MaterialStyleFill : public MaterialStyle class MaterialStyleFill : public MaterialStyle

View File

@ -25,6 +25,7 @@ std::unique_ptr<MaterialStroke> Renderer::StrokePlain::clone() const
std::vector<GLfloat> Renderer::StrokePlain::encoded() const std::vector<GLfloat> Renderer::StrokePlain::encoded() const
{ {
qDebug() << material.color;
return { glm::uintBitsToFloat(glm::packUnorm4x8(glm::vec4(material.toVec().second, 0.f, 0.f))), return { glm::uintBitsToFloat(glm::packUnorm4x8(glm::vec4(material.toVec().second, 0.f, 0.f))),
glm::uintBitsToFloat(glm::packUnorm4x8(material.toVec().first)) }; glm::uintBitsToFloat(glm::packUnorm4x8(material.toVec().first)) };
} }
@ -58,7 +59,7 @@ std::vector<GLfloat> Renderer::StrokeRadialGradient::encoded() const
v.y = pair.first; v.y = pair.first;
result.push_back(glm::uintBitsToFloat(glm::packUnorm2x16(v))); result.push_back(glm::uintBitsToFloat(glm::packUnorm2x16(v)));
result.push_back(glm::uintBitsToFloat(glm::packUnorm4x8(color))); result.push_back(glm::uintBitsToFloat(glm::packUnorm4x8(color)));
qDebug() << pair.first; //qDebug() << pair.first;
} }
return result; return result;
} }
@ -75,8 +76,8 @@ bool Renderer::StrokeRadialGradient::operator==(const MaterialStroke& m) const
&& materialMap == static_cast<const StrokeRadialGradient&>(m).materialMap; && materialMap == static_cast<const StrokeRadialGradient&>(m).materialMap;
} }
Renderer::MaterialStyleStroke::MaterialStyleStroke(float width, StrokeType strokeType, StrokeEndType endType, std::shared_ptr<MaterialStroke> materialStroke) Renderer::MaterialStyleStroke::MaterialStyleStroke(float halfWidth, StrokeType strokeType, StrokeEndType endType, std::shared_ptr<MaterialStroke> materialStroke)
: halfWidth(width/2), strokeType(strokeType), endType(endType), materialStroke(materialStroke) : halfWidth(halfWidth), strokeType(strokeType), endType(endType), materialStroke(materialStroke)
{ {
} }
@ -99,12 +100,16 @@ std::vector<GLfloat> Renderer::MaterialStyleStroke::encoded() const
std::unique_ptr<MaterialStyle> Renderer::MaterialStyleStroke::clone() const std::unique_ptr<MaterialStyle> Renderer::MaterialStyleStroke::clone() const
{ {
return std::make_unique<MaterialStyleStroke>(halfWidth*2, strokeType, endType, materialStroke->clone()); return std::make_unique<MaterialStyleStroke>(halfWidth, strokeType, endType, materialStroke->clone());
} }
bool Renderer::MaterialStyleStroke::operator==(const MaterialStyle& m) const bool Renderer::MaterialStyleStroke::operator==(const MaterialStyle& m) const
{ {
return type() == m.type() && *materialStroke == *static_cast<const MaterialStyleStroke&>(m).materialStroke; return type() == m.type()
&& halfWidth == static_cast<const MaterialStyleStroke&>(m).halfWidth
&& strokeType == static_cast<const MaterialStyleStroke&>(m).strokeType
&& endType == static_cast<const MaterialStyleStroke&>(m).endType
&& *materialStroke == *static_cast<const MaterialStyleStroke&>(m).materialStroke;
} }
float Renderer::MaterialStyleStroke::getHalfWidth() const float Renderer::MaterialStyleStroke::getHalfWidth() const

View File

@ -43,7 +43,7 @@ namespace Renderer
}; };
enum class StrokeType { kBothSides = 2, kLeftSide = 1, kRightSide = 0 }; enum class StrokeType { kBothSides = 2, kLeftSide = 1, kRightSide = 0 };
enum class StrokeEndType { kRound = 0, kFlat = 1 }; enum class StrokeEndType { kRound = 0b00, kFlat = 0b11, kRoundFlat = 0b10, kFlatRound = 0b01 };
class MaterialStyleStroke : public MaterialStyle class MaterialStyleStroke : public MaterialStyle
{ {
@ -54,7 +54,7 @@ namespace Renderer
virtual std::unique_ptr<MaterialStyle> clone() const override; virtual std::unique_ptr<MaterialStyle> clone() const override;
virtual bool operator==(const MaterialStyle&) const override; virtual bool operator==(const MaterialStyle&) const override;
float getHalfWidth() const; float getHalfWidth() const;
//protected:
float halfWidth; float halfWidth;
StrokeType strokeType; StrokeType strokeType;
StrokeEndType endType; StrokeEndType endType;

View File

@ -9,7 +9,7 @@ using namespace Renderer;
constexpr int kMaxLineCount = 20; constexpr int kMaxLineCount = 20;
Painting::Painting() Painting::Painting(QColor backgroundColor) : backgroundColor(backgroundColor)
{ {
} }
@ -53,87 +53,40 @@ void Renderer::Painting::addElement(ElementWithTransform elementWithTransform)
elementPool.insert({ element, 0 }); elementPool.insert({ element, 0 });
} }
elements.push_back(elementWithTransform); elements.push_back(elementWithTransform);
elementTransformPool.emplace(elementWithTransform.transform.transform, 0);
} }
void Renderer::Painting::addElement(const Element& element, const ElementTransform& transform) void Renderer::Painting::addElement(const BaseElement& element, const ElementTransform& transform)
{ {
auto contour = element.contour; addElement({ element , transform });
auto it = elementStyleMap.find(element.style);
if (it == elementStyleMap.end())
{
std::vector<BaseStyle> baseStyles;
for (auto& style : element.style->toBaseStyles())
{
auto [iter, _] = styleSet.insert(style.material);
baseStyles.push_back(BaseStyle{ style.transform, *iter });
}
it = elementStyleMap.insert({ element.style, baseStyles }).first;
}
for (int i = 0; i < it->second.size(); i++)
{
auto& style = it->second[i];
ElementTransform trans = transform;
trans.applyTransformStyle(*style.transform);
trans.zIndex = trans.zIndex * 10 + i;
addElement(ElementWithTransform{ BaseElement{element.contour, style.material, element.ratio}, trans });
}
} }
Renderer::BaseTransform::BaseTransform(ElementTransform t) BvhTreeData Painting::encodeElementLeaf(const ElementWithTransform& e)
: bound(glm::vec4(t.center - t.scale, t.center + t.scale))
, rotation(t.rotation)
, flip(t.flip)
, zIndex(t.zIndex)
{ {
} QVector4D bound(e.transform.bound.x, e.transform.bound.y, e.transform.bound.z, e.transform.bound.w);
GLuint rightSon = elementPool[e.element] + (e.transform.zIndex << 18);
void Renderer::Painting::addElement(std::shared_ptr<Element> element, QVector4D bound, float rotation, int zIndex) return { bound, elementTransformPool[e.transform.transform], rightSon };
{
}
BvhTreeData Painting::encodeElementLeaf(ElementWithTransform e)
{
glm::vec4 bound;
switch (e.element.style->type())
{
case MaterialStyleType::kStroke:
{
auto w = std::static_pointer_cast<MaterialStyleStroke>(e.element.style)->getHalfWidth();
glm::vec2 size = e.element.ratio < 1 ? glm::vec2(e.element.ratio, 1) : glm::vec2(1, 1 / e.element.ratio);
glm::vec2 scale = size * e.transform.scale;
bound = glm::vec4(e.transform.center - scale, e.transform.center + scale);
break;
}
case MaterialStyleType::kFill:
{
bound = glm::vec4(e.transform.center - e.transform.scale, e.transform.center + e.transform.scale);
break;
}
}
//bound = glm::vec4(e.transform.center - e.transform.scale, e.transform.center + e.transform.scale);
GLuint rightSon = (GLuint)(glm::mod(e.transform.rotation, 360.f) / 360.f * (1 << 16))
+ (e.transform.flip.x << 16) + (e.transform.flip.y << 17) + (e.transform.zIndex << 18);
return BvhTreeData(QVector4D(bound.x, bound.y, bound.z, bound.w), elementPool[e.element], rightSon);
} }
void Painting::generateBuffers(QOpenGLFunctions_4_5_Core* glFunc) void Painting::generateBuffers(QOpenGLFunctions_4_5_Core* glFunc)
{ {
qDebug() << "Element Count: " << elementPool.size(); qDebug() << "Element Count: " << elementPool.size();
qDebug() << "Coutour Count: " << contourPool.size(); qDebug() << "Contour Count: " << contourPool.size();
qDebug() << " Style Count: " << stylePool.size(); qDebug() << " Style Count: " << stylePool.size();
bvhChildren.clear(); bvhChildren.clear();
bvhBounds.clear(); bvhBounds.clear();
elementTransform.clear();
elementOffsets.clear(); elementOffsets.clear();
elementIndex.clear(); elementIndex.clear();
elementData.clear(); elementData.clear();
for (int index = 0; auto & i : elementPool) for (int index = 0; auto & i : elementPool)
{
i.second = index++; i.second = index++;
} for (int index = 0; auto & i : elementTransformPool)
i.second = index++;
std::vector<BvhTreeData> rootBvhTreeData; std::vector<BvhTreeData> rootBvhTreeData;
for (auto& i : elements) for (auto& i : elements)
@ -162,27 +115,35 @@ void Painting::generateBuffers(QOpenGLFunctions_4_5_Core* glFunc)
elementData.insert(elementData.end(), encodedStyle.begin(), encodedStyle.end()); elementData.insert(elementData.end(), encodedStyle.begin(), encodedStyle.end());
} }
for (auto & i : elementTransformPool)
elementTransform.emplace_back(i.first);
for (auto& i : elementPool) for (auto& i : elementPool)
{ {
//qDebug() <<"element:" << i.second; //qDebug() <<"element:" << i.second;
//std::shared_ptr<ContourBuffer> contourBuffer = i.first.style->type() == MaterialStyleType::kStroke ? contourPool[i.first.contour].second : contourPool[i.first.contour].first; //std::shared_ptr<ContourBuffer> contourBuffer = i.first.style->type() == MaterialStyleType::kStroke ? contourPool[i.first.contour].second : contourPool[i.first.contour].first;
auto& contourBuffer = contourPool[{i.first.contour, i.first.style->type()}]; auto& contourBuffer = contourPool[{i.first.contour, i.first.style->type()}];
elementOffsets.push_back({ contourBuffer.bvhOffset, stylePool[i.first.style], contourBuffer.pointsOffset, contourBuffer.linesOffset, glm::floatBitsToUint(i.first.ratio) }); elementOffsets.push_back({ contourBuffer.bvhOffset, stylePool[i.first.style], contourBuffer.pointsOffset, contourBuffer.linesOffset });
//std::cout << std::format("{} {} {} {}\n", contourBuffer->bvhOffset, stylePool[i.first->style], contourBuffer->pointsOffset, contourBuffer->linesOffset); //std::cout << std::format("{} {} {} {}\n", contourBuffer->bvhOffset, stylePool[i.first->style], contourBuffer->pointsOffset, contourBuffer->linesOffset);
} }
glFunc->glCreateBuffers(5, buffers.data()); glFunc->glCreateBuffers(7, buffers.data());
GLuint& bvhSSBO = buffers[0]; GLuint& bvhSSBO = buffers[0];
GLuint& bvhBoundSSBO = buffers[1]; GLuint& bvhBoundSSBO = buffers[1];
GLuint& elementOffsetSSBO = buffers[2]; GLuint& elementTransformSSBO = buffers[2];
GLuint& elementIndexSSBO = buffers[3]; GLuint& elementOffsetSSBO = buffers[3];
GLuint& elementDataSSBO = buffers[4]; GLuint& elementIndexSSBO = buffers[4];
GLuint& elementDataSSBO = buffers[5];
GLuint& backgroundColorUBO = buffers[6];
glFunc->glNamedBufferData(buffers[0], bvhChildren.size() * sizeof(bvhChildren[0]), bvhChildren.data(), GL_STATIC_READ); glFunc->glNamedBufferData(buffers[0], bvhChildren.size() * sizeof(bvhChildren[0]), bvhChildren.data(), GL_STATIC_READ);
glFunc->glNamedBufferData(buffers[1], bvhBounds.size() * sizeof(bvhBounds[0]), bvhBounds.data(), GL_STATIC_READ); glFunc->glNamedBufferData(buffers[1], bvhBounds.size() * sizeof(bvhBounds[0]), bvhBounds.data(), GL_STATIC_READ);
glFunc->glNamedBufferData(buffers[2], elementOffsets.size() * sizeof(elementOffsets[0]), elementOffsets.data(), GL_STATIC_READ); glFunc->glNamedBufferData(buffers[2], elementTransform.size() * sizeof(elementTransform[0]), elementTransform.data(), GL_STATIC_READ);
glFunc->glNamedBufferData(buffers[3], elementIndex.size() * sizeof(elementIndex[0]), elementIndex.data(), GL_STATIC_READ); glFunc->glNamedBufferData(buffers[3], elementOffsets.size() * sizeof(elementOffsets[0]), elementOffsets.data(), GL_STATIC_READ);
glFunc->glNamedBufferData(buffers[4], elementData.size() * sizeof(elementData[0]), elementData.data(), GL_STATIC_READ); glFunc->glNamedBufferData(buffers[4], elementIndex.size() * sizeof(elementIndex[0]), elementIndex.data(), GL_STATIC_READ);
glFunc->glNamedBufferData(buffers[5], elementData.size() * sizeof(elementData[0]), elementData.data(), GL_STATIC_READ);
glm::vec3 color(backgroundColor.redF(), backgroundColor.greenF(), backgroundColor.blueF());
glFunc->glNamedBufferData(buffers[6], sizeof(glm::vec3), &color, GL_STATIC_READ);
} }
GLuint Renderer::Painting::getElementCount() GLuint Renderer::Painting::getElementCount()
@ -220,3 +181,17 @@ bool Renderer::Painting::CompareMaterialStyle::operator()(const std::shared_ptr<
else else
return left < right; return left < right;
} }
std::size_t Renderer::Painting::HashMat3x2::operator()(const glm::mat3x2& mat) const
{
std::size_t result = 0;
constexpr long long P = 998244353;
long long base = 1;
for (int i = 0; i < 3; i++)
for (int j = 0; j < 2; j++)
{
result += base * mat[i][j];
base *= P;
}
return result;
}

View File

@ -15,24 +15,13 @@ namespace Renderer
{ {
std::shared_ptr<Contour> contour; std::shared_ptr<Contour> contour;
std::shared_ptr<MaterialStyle> style; std::shared_ptr<MaterialStyle> style;
float ratio; /// ¿í¸ß±È
bool operator<(const BaseElement& e) const; bool operator<(const BaseElement& e) const;
}; };
struct BaseTransform
{
glm::vec4 bound;
float rotation;
glm::bvec2 flip;
GLuint zIndex;
BaseTransform(ElementTransform t);
};
struct ElementWithTransform struct ElementWithTransform
{ {
BaseElement element; BaseElement element;
ElementTransform transform; ElementTransform transform;
//BaseTransform transform;
}; };
struct ContourBuffer struct ContourBuffer
@ -52,7 +41,6 @@ namespace Renderer
GLuint styleOffset; GLuint styleOffset;
GLuint pointsOffset; GLuint pointsOffset;
GLuint linesOffset; GLuint linesOffset;
GLuint ratio;
}; };
class Painting class Painting
@ -60,15 +48,16 @@ namespace Renderer
public: public:
std::vector<GLuint> bvhChildren; std::vector<GLuint> bvhChildren;
std::vector<QVector4D> bvhBounds; std::vector<QVector4D> bvhBounds;
std::vector<glm::mat3x2> elementTransform;
std::vector<ElementOffset> elementOffsets; std::vector<ElementOffset> elementOffsets;
std::vector<GLuint> elementIndex; std::vector<GLuint> elementIndex;
std::vector<GLfloat> elementData; std::vector<GLfloat> elementData;
int paintingId = 0; int paintingId = 0;
std::array<GLuint, 5> buffers; std::array<GLuint, 7> buffers;
QColor backgroundColor;
Painting(); Painting(QColor backgroundColor = Qt::white);
void addElement(const Element& element, const ElementTransform& transform); void addElement(const BaseElement& element, const ElementTransform& transform);
void addElement(std::shared_ptr<Element> element, QVector4D bound, float rotation, int zIndex);
void generateBuffers(QOpenGLFunctions_4_5_Core* glFunc); void generateBuffers(QOpenGLFunctions_4_5_Core* glFunc);
GLuint getElementCount(); GLuint getElementCount();
private: private:
@ -76,14 +65,15 @@ namespace Renderer
std::unordered_map<std::tuple<std::shared_ptr<Contour>, MaterialStyleType>, ContourBuffer, ContourHash> contourPool; std::unordered_map<std::tuple<std::shared_ptr<Contour>, MaterialStyleType>, ContourBuffer, ContourHash> contourPool;
struct CompareMaterialStyle { bool operator()(const std::shared_ptr<MaterialStyle>&, const std::shared_ptr<MaterialStyle>&) const; }; struct CompareMaterialStyle { bool operator()(const std::shared_ptr<MaterialStyle>&, const std::shared_ptr<MaterialStyle>&) const; };
std::set<std::shared_ptr<MaterialStyle>, CompareMaterialStyle> styleSet; std::set<std::shared_ptr<MaterialStyle>, CompareMaterialStyle> styleSet;
std::unordered_map<std::shared_ptr<ElementStyle>, std::vector<BaseStyle>> elementStyleMap;
std::unordered_map<std::shared_ptr<MaterialStyle>, GLuint> stylePool; std::unordered_map<std::shared_ptr<MaterialStyle>, GLuint> stylePool;
struct HashMat3x2 { std::size_t operator()(const glm::mat3x2&) const; };
std::unordered_map<glm::mat3x2, GLuint, HashMat3x2> elementTransformPool;
std::map<BaseElement, GLuint> elementPool; std::map<BaseElement, GLuint> elementPool;
std::vector<ElementWithTransform> elements; std::vector<ElementWithTransform> elements;
void addElement(ElementWithTransform element); void addElement(ElementWithTransform element);
void insertContourBuffer(ContourBuffer& buffer); void insertContourBuffer(ContourBuffer& buffer);
BvhTreeData encodeElementLeaf(ElementWithTransform e); BvhTreeData encodeElementLeaf(const ElementWithTransform& e);
}; };
} }

View File

@ -10,6 +10,7 @@
#include "../Painting/Element.h" #include "../Painting/Element.h"
#include "../Painting/Painting.h" #include "../Painting/Painting.h"
#include "../Painting/MaterialStyleStroke.h" #include "../Painting/MaterialStyleStroke.h"
#include <util/PainterPathUtil.h>
using namespace Renderer; using namespace Renderer;
std::vector<glm::vec2> generatePathBuffer(const QPainterPath& path) std::vector<glm::vec2> generatePathBuffer(const QPainterPath& path)
@ -75,8 +76,10 @@ std::vector<GLfloat> generateStyleBuffer(const std::vector<BaseStyle>& styles)
styleBuffer.push_back(style.transform->scale.y); styleBuffer.push_back(style.transform->scale.y);
styleBuffer.push_back(style.transform->rotation); styleBuffer.push_back(style.transform->rotation);
styleBuffer.push_back(glm::uintBitsToFloat(glm::packUnorm2x16(style.transform->flip))); styleBuffer.push_back(glm::uintBitsToFloat(glm::packUnorm2x16(style.transform->flip)));
auto encoded = style.material->encoded(); auto encoded = style.material->encoded();
styleBuffer.insert(styleBuffer.end(), encoded.begin(), encoded.end()); styleBuffer.insert(styleBuffer.end(), encoded.begin(), encoded.end());
//qDebug() << "style size" << styleBuffer.size();
} }
return styleBuffer; return styleBuffer;
} }
@ -85,13 +88,23 @@ QRectF calcBoundingRect(const QPainterPath& path, const std::vector<BaseStyle>&
{ {
QRectF bound = path.boundingRect(); QRectF bound = path.boundingRect();
ElementTransform originTransform{ glm::vec2(bound.center().x(), bound.center().y()), glm::vec2(bound.width(), bound.height()), 0, glm::bvec2(false), 0 }; //ElementTransform originTransform{ glm::vec2(bound.center().x(), bound.center().y()), glm::vec2(bound.width(), bound.height()), 0, glm::bvec2(false), 0 };
glm::vec2 leftTop(std::numeric_limits<float>::max()), rightBottom(std::numeric_limits<float>::min()); glm::vec2 leftTop(std::numeric_limits<float>::max()), rightBottom(std::numeric_limits<float>::min());
for (auto& baseStyle : styles) for (auto& baseStyle : styles)
{ {
BaseTransform transform(originTransform.appliedTransformStyle(*baseStyle.transform)); struct BaseTransform
{
glm::vec4 bound;
float rotation = 0;
glm::bvec2 flip = glm::bvec2(false);
GLuint zIndex = 0;
};
//BaseTransform transform(originTransform.appliedTransformStyle(*baseStyle.transform));
glm::vec2 center(bound.center().x(), bound.center().y());
glm::vec2 scale(bound.width(), bound.height());
BaseTransform transform{ glm::vec4(center - scale, center + scale) };
if (baseStyle.material->type() == MaterialStyleType::kStroke) if (baseStyle.material->type() == MaterialStyleType::kStroke)
{ {
float halfWidth = std::static_pointer_cast<MaterialStyleStroke>(baseStyle.material)->getHalfWidth(); float halfWidth = std::static_pointer_cast<MaterialStyleStroke>(baseStyle.material)->getHalfWidth();
@ -197,7 +210,8 @@ std::pair<QImage, QPointF> Renderer::ElementRenderer::drawElement(const QPainter
std::unique_lock<std::mutex> lock(drawMutex); std::unique_lock<std::mutex> lock(drawMutex);
draw.wait(lock, [&] {return drawFinished; }); draw.wait(lock, [&] {return drawFinished; });
drawFinished = false; drawFinished = false;
this->path = &path; QPainterPath monotonized = PainterPathUtil::monotonization(path);
this->path = &monotonized;
this->style = &style; this->style = &style;
this->pixelRatio = pixelRatio; this->pixelRatio = pixelRatio;
needDraw = true; needDraw = true;

View File

@ -12,9 +12,11 @@ Renderer::RendererWidget::RendererWidget(QWidget* parent)
auto openAction = new QAction(QStringLiteral("´ò¿ª"), menu); auto openAction = new QAction(QStringLiteral("´ò¿ª"), menu);
auto saveAction = new QAction(QStringLiteral("±£´æ"), menu); auto saveAction = new QAction(QStringLiteral("±£´æ"), menu);
auto testAction = new QAction(QStringLiteral("²âÊÔ"), menu); auto testAction = new QAction(QStringLiteral("²âÊÔ"), menu);
auto test2Action = new QAction(QStringLiteral("²âÊÔ2"), menu);
menu->addAction(openAction); menu->addAction(openAction);
menu->addAction(saveAction); menu->addAction(saveAction);
menu->addAction(testAction); menu->addAction(testAction);
menu->addAction(test2Action);
ui.openButton->setHaloVisible(false); ui.openButton->setHaloVisible(false);
ui.openButton->setOverlayStyle(::Material::TintedOverlay); ui.openButton->setOverlayStyle(::Material::TintedOverlay);
@ -47,7 +49,9 @@ Renderer::RendererWidget::RendererWidget(QWidget* parent)
QObject::connect(testAction, &QAction::triggered, [&] { QObject::connect(testAction, &QAction::triggered, [&] {
ui.openGLWidget->setModel("Models/Sponza/Sponza.gltf"); ui.openGLWidget->setModel("Models/Sponza/Sponza.gltf");
}); });
QObject::connect(test2Action, &QAction::triggered, [&] {
ui.openGLWidget->setModel("E:\\3D Objects\\Gate\\Gate.gltf");
});
ui.horizontalSlider->setValue(105); ui.horizontalSlider->setValue(105);
ui.horizontalSlider_2->setValue(80); ui.horizontalSlider_2->setValue(80);
ui.exposureSlider->setValue(60); ui.exposureSlider->setValue(60);

View File

@ -13,6 +13,7 @@ namespace Renderer
~RendererWidget(); ~RendererWidget();
public slots: public slots:
void currentTabChanged(int index); void currentTabChanged(int index);
private: private:
Ui::RendererWidgetClass ui; Ui::RendererWidgetClass ui;
}; };

View File

@ -124,8 +124,9 @@ std::uint16_t Renderer::VirtualTextureManager::createVirtualTexture(Painting pai
glMain->TextureParameteri(metallicRoughness, GL_TEXTURE_MAG_FILTER, GL_LINEAR); glMain->TextureParameteri(metallicRoughness, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
glMain->TextureStorage2D(metallicRoughness, levels, GL_RG8, GLsizei(textureSize), GLsizei(textureSize)); glMain->TextureStorage2D(metallicRoughness, levels, GL_RG8, GLsizei(textureSize), GLsizei(textureSize));
for (int i = 0; i < 5; i++) for (int i = 0; i < 6; i++)
glMain->BindBufferBase(GL_SHADER_STORAGE_BUFFER, i, painting.buffers[i]); glMain->BindBufferBase(GL_SHADER_STORAGE_BUFFER, i, painting.buffers[i]);
glMain->BindBufferBase(GL_UNIFORM_BUFFER, 1, painting.buffers[6]);
for (auto level = levels - 1; level < levels; ++level) for (auto level = levels - 1; level < levels; ++level)
{ {
@ -143,7 +144,7 @@ std::uint16_t Renderer::VirtualTextureManager::createVirtualTexture(Painting pai
GL_TRUE); GL_TRUE);
program.bind(); program.bind();
glMain->Uniform2i(gl->GetUniformLocation(program.programId(), "pixelOffset"), static_cast<GLsizei>(pageSize) * i, static_cast<GLsizei>(pageSize) * j); glMain->Uniform2i(0 /*pixelOffset*/, static_cast<GLsizei>(pageSize) * i, static_cast<GLsizei>(pageSize) * j);
glMain->BindImageTexture(0, baseColor, level, GL_FALSE, 0, GL_READ_WRITE, GL_RGBA8); glMain->BindImageTexture(0, baseColor, level, GL_FALSE, 0, GL_READ_WRITE, GL_RGBA8);
glMain->BindImageTexture(1, metallicRoughness, level, GL_FALSE, 0, GL_READ_WRITE, GL_RG8); glMain->BindImageTexture(1, metallicRoughness, level, GL_FALSE, 0, GL_READ_WRITE, GL_RG8);
glMain->DispatchCompute(pageSize / 8, pageSize / 8, 1); glMain->DispatchCompute(pageSize / 8, pageSize / 8, 1);
@ -151,10 +152,30 @@ std::uint16_t Renderer::VirtualTextureManager::createVirtualTexture(Painting pai
} }
} }
if (const auto it = std::find_if(paintings.begin(), paintings.end(), [](const PaintingHandle& i) { return i.baseColor == 0; });
it != paintings.end())
{
*it = { baseColor, metallicRoughness, painting.buffers };
return it - paintings.begin() + 1;
}
else
{
paintings.emplace_back(baseColor, metallicRoughness, painting.buffers); paintings.emplace_back(baseColor, metallicRoughness, painting.buffers);
return paintings.size(); return paintings.size();
} }
}
void Renderer::VirtualTextureManager::deleteVirtualTexture(std::uint16_t id)
{
auto& painting = getPaintingHandle(id);
glMain->DeleteTextures(2, (GLuint*)&painting);
glMain->DeleteBuffers(7, painting.buffers.data());
painting.baseColor = 0;
painting.metallicRoughness = 0;
painting.buffers.fill(0);
}
Renderer::PaintingHandle& Renderer::VirtualTextureManager::getPaintingHandle(std::uint16_t id) Renderer::PaintingHandle& Renderer::VirtualTextureManager::getPaintingHandle(std::uint16_t id)
{ {
return paintings[id - 1]; return paintings[id - 1];
@ -188,8 +209,9 @@ void Renderer::VirtualTextureManager::pageCommitmentById(const glm::u16vec2& pag
if (commit) if (commit)
{ {
program.bind(); program.bind();
for (int i = 0; i < 5; i++) for (int i = 0; i < 6; i++)
gl->BindBufferBase(GL_SHADER_STORAGE_BUFFER, i, painting.buffers[i]); gl->BindBufferBase(GL_SHADER_STORAGE_BUFFER, i, painting.buffers[i]);
glMain->BindBufferBase(GL_UNIFORM_BUFFER, 1, painting.buffers[6]);
gl->Uniform2i(gl->GetUniformLocation(program.programId(), "pixelOffset"), static_cast<GLsizei>(pageSize) * page.x, static_cast<GLsizei>(pageSize) * page.y); gl->Uniform2i(gl->GetUniformLocation(program.programId(), "pixelOffset"), static_cast<GLsizei>(pageSize) * page.x, static_cast<GLsizei>(pageSize) * page.y);
gl->BindImageTexture(0, painting.baseColor, level, GL_FALSE, 0, GL_READ_WRITE, GL_RGBA8); gl->BindImageTexture(0, painting.baseColor, level, GL_FALSE, 0, GL_READ_WRITE, GL_RGBA8);
gl->BindImageTexture(1, painting.metallicRoughness, level, GL_FALSE, 0, GL_READ_WRITE, GL_RG8); gl->BindImageTexture(1, painting.metallicRoughness, level, GL_FALSE, 0, GL_READ_WRITE, GL_RG8);

View File

@ -15,7 +15,7 @@ namespace Renderer
{ {
GLuint baseColor; GLuint baseColor;
GLuint metallicRoughness; GLuint metallicRoughness;
std::array<GLuint, 5> buffers; std::array<GLuint, 7> buffers;
}; };
class VirtualTextureManager class VirtualTextureManager
@ -24,11 +24,12 @@ namespace Renderer
VirtualTextureManager(GladGLContext* glMain); VirtualTextureManager(GladGLContext* glMain);
/** /**
* @brief * @brief
* @param painting * @param painting
* @return ÐéÄâÎÆÀíid * @return ÐéÄâÎÆÀíid
*/ */
std::uint16_t createVirtualTexture(Painting painting); std::uint16_t createVirtualTexture(Painting painting);
void deleteVirtualTexture(std::uint16_t id);
PaintingHandle& getPaintingHandle(std::uint16_t id); PaintingHandle& getPaintingHandle(std::uint16_t id);
void tryUpdatePages(const std::vector<glm::u16vec2>& pageIds); void tryUpdatePages(const std::vector<glm::u16vec2>& pageIds);

View File

@ -5,6 +5,7 @@
#include <iostream> #include <iostream>
#include <format> #include <format>
#include "consoleapi2.h" #include "consoleapi2.h"
#include <lib/qtmaterialstyle.h>
extern "C" extern "C"
{ {
@ -49,6 +50,10 @@ int main(int argc, char* argv[])
//QGuiApplication::setAttribute(Qt::AA_EnableHighDpiScaling); //QGuiApplication::setAttribute(Qt::AA_EnableHighDpiScaling);
QApplication::setHighDpiScaleFactorRoundingPolicy(Qt::HighDpiScaleFactorRoundingPolicy::PassThrough); QApplication::setHighDpiScaleFactorRoundingPolicy(Qt::HighDpiScaleFactorRoundingPolicy::PassThrough);
QApplication a(argc, argv); QApplication a(argc, argv);
Q_INIT_RESOURCE(resources);
QtMaterialTheme theme;
theme.setColor("primary1", QColor(0, 90, 158));
QtMaterialStyle::instance().setTheme(&theme);
//FramelessHelper::Core::setApplicationOSThemeAware(); //FramelessHelper::Core::setApplicationOSThemeAware();
FramelessConfig::instance()->set(Global::Option::ForceNonNativeBackgroundBlur); FramelessConfig::instance()->set(Global::Option::ForceNonNativeBackgroundBlur);
//FramelessConfig::instance()->set(Global::Option::EnableBlurBehindWindow); //FramelessConfig::instance()->set(Global::Option::EnableBlurBehindWindow);

View File

@ -3,23 +3,76 @@
#include <QtWidgets/QApplication> #include <QtWidgets/QApplication>
#include "ElementRendererTest.h" #include "ElementRendererTest.h"
#include "Renderer/Painting/ElementStyle.h" #include "Renderer/Painting/ElementStyle.h"
#include "Renderer/Painting/MaterialStyleFill.h"
using namespace Microsoft::VisualStudio::CppUnitTestFramework; using namespace Microsoft::VisualStudio::CppUnitTestFramework;
using namespace Renderer; using namespace Renderer;
namespace UnitTest namespace UnitTest
{ {
void messageHandler(QtMsgType type, const QMessageLogContext& context, const QString& msg);
TEST_CLASS(ElementRendererFillTest)
{
private:
char* argv[1];
int argc;
QPainterPath path;
public:
ElementRendererFillTest() :argv{ (char*)"" }, argc(1)
{
QQuickSvgParser::parsePathDataFast("M100,100C-.5,100,0,100.5,0,0L40,.07C40,59.5,39.5,60,100,60Z",
path);
QTransform transform;
//transform.scale(10, 10);
path = transform.map(path);
}
TEST_METHOD_INITIALIZE(initialize)
{
qInstallMessageHandler(messageHandler);
QGuiApplication::setAttribute(Qt::AA_EnableHighDpiScaling);
QCoreApplication::setAttribute(Qt::AA_UseHighDpiPixmaps);
QApplication::setHighDpiScaleFactorRoundingPolicy(Qt::HighDpiScaleFactorRoundingPolicy::PassThrough);
}
TEST_METHOD(TestFillPlain)
{
QApplication a(argc, argv);
class Style : public Renderer::ElementStyle
{
virtual std::vector<Renderer::BaseStyle> toBaseStyles() const override
{
return { BaseStyle(std::make_shared<TransformStyle>(),
std::make_shared<MaterialStyleFill>(
std::make_shared<FillPlain>(Material(QColor(255,255,0))))) };
}
} style;
TestGLWidget w(style, path);
w.show();
a.exec();
}
};
TEST_CLASS(ElementRendererStokeTypeTest) TEST_CLASS(ElementRendererStokeTypeTest)
{ {
private: private:
char* argv[1]; char* argv[1];
int argc; int argc;
QPainterPath path;
public: public:
ElementRendererStokeTypeTest() :argv{ (char*)"" }, argc(1) {} ElementRendererStokeTypeTest() :argv{ (char*)"" }, argc(1)
{
QQuickSvgParser::parsePathDataFast("M292.82,107.78s0,0,0,0,0,3.59,0,7.62c0,3.85,0,5.78.06,6.43a19.94,19.94,0,0,0,2.87,7.58,15.85,15.85,0,0,0,6.61,6.23A14.75,14.75,0,0,0,310,137a11.69,11.69,0,0,0,7.59-2.92,11,11,0,0,0,3.2-6.84c.15-1.27.58-4.84-1.79-7.64a8.54,8.54,0,0,0-3.56-2.44c-1.32-.52-3.32-1.31-5.06-.33a5.41,5.41,0,0,0-2.14,3,3.48,3.48,0,0,0-.16,2.71c.78,1.86,3.36,2.14,3.47,2.15", path);
QTransform transform;
transform.scale(10, 10);
path = transform.map(path);
}
TEST_METHOD_INITIALIZE(initialize) TEST_METHOD_INITIALIZE(initialize)
{ {
qInstallMessageHandler(messageHandler);
QGuiApplication::setAttribute(Qt::AA_EnableHighDpiScaling); QGuiApplication::setAttribute(Qt::AA_EnableHighDpiScaling);
QCoreApplication::setAttribute(Qt::AA_UseHighDpiPixmaps); QCoreApplication::setAttribute(Qt::AA_UseHighDpiPixmaps);
QApplication::setHighDpiScaleFactorRoundingPolicy(Qt::HighDpiScaleFactorRoundingPolicy::PassThrough); QApplication::setHighDpiScaleFactorRoundingPolicy(Qt::HighDpiScaleFactorRoundingPolicy::PassThrough);
@ -37,11 +90,11 @@ namespace UnitTest
{1.00, Material{QColor(58,64,151)}} {1.00, Material{QColor(58,64,151)}}
}; };
return { BaseStyle(std::make_shared<TransformStyle>(), return { BaseStyle(std::make_shared<TransformStyle>(),
std::make_shared<MaterialStyleStroke>(60, StrokeType::kBothSides, StrokeEndType::kRound, std::make_shared<MaterialStyleStroke>(30, StrokeType::kBothSides, StrokeEndType::kRound,
std::make_shared<StrokeRadialGradient>(materialMap, false))) }; std::make_shared<StrokeRadialGradient>(materialMap, false))) };
} }
} style; } style;
TestGLWidget w(style); TestGLWidget w(style, path);
w.show(); w.show();
a.exec(); a.exec();
} }
@ -58,11 +111,11 @@ namespace UnitTest
{1.00, Material{QColor(58,64,151)}} {1.00, Material{QColor(58,64,151)}}
}; };
return { BaseStyle(std::make_shared<TransformStyle>(), return { BaseStyle(std::make_shared<TransformStyle>(),
std::make_shared<MaterialStyleStroke>(60, StrokeType::kBothSides, StrokeEndType::kFlat, std::make_shared<MaterialStyleStroke>(30, StrokeType::kBothSides, StrokeEndType::kFlat,
std::make_shared<StrokeRadialGradient>(materialMap, false))) }; std::make_shared<StrokeRadialGradient>(materialMap, false))) };
} }
} style; } style;
TestGLWidget w(style); TestGLWidget w(style, path);
w.show(); w.show();
a.exec(); a.exec();
} }
@ -79,11 +132,11 @@ namespace UnitTest
{1.00, Material{QColor(58,64,151)}} {1.00, Material{QColor(58,64,151)}}
}; };
return { BaseStyle(std::make_shared<TransformStyle>(), return { BaseStyle(std::make_shared<TransformStyle>(),
std::make_shared<MaterialStyleStroke>(60, StrokeType::kLeftSide, StrokeEndType::kRound, std::make_shared<MaterialStyleStroke>(30, StrokeType::kLeftSide, StrokeEndType::kRound,
std::make_shared<StrokeRadialGradient>(materialMap, false))) }; std::make_shared<StrokeRadialGradient>(materialMap, false))) };
} }
} style; } style;
TestGLWidget w(style); TestGLWidget w(style, path);
w.show(); w.show();
a.exec(); a.exec();
} }
@ -100,11 +153,32 @@ namespace UnitTest
{1.00, Material{QColor(58,64,151)}} {1.00, Material{QColor(58,64,151)}}
}; };
return { BaseStyle(std::make_shared<TransformStyle>(), return { BaseStyle(std::make_shared<TransformStyle>(),
std::make_shared<MaterialStyleStroke>(60, StrokeType::kLeftSide, StrokeEndType::kFlat, std::make_shared<MaterialStyleStroke>(30, StrokeType::kLeftSide, StrokeEndType::kFlat,
std::make_shared<StrokeRadialGradient>(materialMap, false))) }; std::make_shared<StrokeRadialGradient>(materialMap, false))) };
} }
} style; } style;
TestGLWidget w(style); TestGLWidget w(style, path);
w.show();
a.exec();
}
TEST_METHOD(TestRightSideFlatRound)
{
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>(80, StrokeType::kRightSide, StrokeEndType::kFlatRound,
std::make_shared<StrokeRadialGradient>(materialMap, false))) };
}
} style;
TestGLWidget w(style, path);
w.show(); w.show();
a.exec(); a.exec();
} }
@ -115,8 +189,15 @@ namespace UnitTest
private: private:
char* argv[1]; char* argv[1];
int argc; int argc;
QPainterPath path;
public: public:
ElementRendererStokeMaterialTest() :argv{ (char*)"" }, argc(1) {} ElementRendererStokeMaterialTest() :argv{ (char*)"" }, argc(1)
{
QQuickSvgParser::parsePathDataFast("M292.82,107.78s0,0,0,0,0,3.59,0,7.62c0,3.85,0,5.78.06,6.43a19.94,19.94,0,0,0,2.87,7.58,15.85,15.85,0,0,0,6.61,6.23A14.75,14.75,0,0,0,310,137a11.69,11.69,0,0,0,7.59-2.92,11,11,0,0,0,3.2-6.84c.15-1.27.58-4.84-1.79-7.64a8.54,8.54,0,0,0-3.56-2.44c-1.32-.52-3.32-1.31-5.06-.33a5.41,5.41,0,0,0-2.14,3,3.48,3.48,0,0,0-.16,2.71c.78,1.86,3.36,2.14,3.47,2.15", path);
QTransform transform;
transform.scale(10, 10);
path = transform.map(path);
}
TEST_METHOD(TestStrokePlain) TEST_METHOD(TestStrokePlain)
{ {
QApplication a(argc, argv); QApplication a(argc, argv);
@ -125,11 +206,11 @@ namespace UnitTest
virtual std::vector<Renderer::BaseStyle> toBaseStyles() const override virtual std::vector<Renderer::BaseStyle> toBaseStyles() const override
{ {
return { BaseStyle(std::make_shared<TransformStyle>(), return { BaseStyle(std::make_shared<TransformStyle>(),
std::make_shared<MaterialStyleStroke>(60, StrokeType::kBothSides, StrokeEndType::kRound, std::make_shared<MaterialStyleStroke>(30, StrokeType::kBothSides, StrokeEndType::kRound,
std::make_shared<StrokePlain>(QColor(255,255,255),1,1))) }; std::make_shared<StrokePlain>(QColor(255,255,255),1,1))) };
} }
} style; } style;
TestGLWidget w(style); TestGLWidget w(style, path);
w.show(); w.show();
a.exec(); a.exec();
} }
@ -146,11 +227,11 @@ namespace UnitTest
{1.00, Material{QColor(58,64,151)}} {1.00, Material{QColor(58,64,151)}}
}; };
return { BaseStyle(std::make_shared<TransformStyle>(), return { BaseStyle(std::make_shared<TransformStyle>(),
std::make_shared<MaterialStyleStroke>(60, StrokeType::kBothSides, StrokeEndType::kRound, std::make_shared<MaterialStyleStroke>(30, StrokeType::kBothSides, StrokeEndType::kRound,
std::make_shared<StrokeRadialGradient>(materialMap, false))) }; std::make_shared<StrokeRadialGradient>(materialMap, false))) };
} }
} style; } style;
TestGLWidget w(style); TestGLWidget w(style, path);
w.show(); w.show();
a.exec(); a.exec();
} }
@ -171,7 +252,7 @@ namespace UnitTest
std::make_shared<StrokeRadialGradient>(materialMap, true))) }; std::make_shared<StrokeRadialGradient>(materialMap, true))) };
} }
} style; } style;
TestGLWidget w(style); TestGLWidget w(style, path);
w.show(); w.show();
a.exec(); a.exec();
} }

View File

@ -16,9 +16,10 @@ namespace UnitTest
private: private:
Renderer::ElementRenderer& renderer; Renderer::ElementRenderer& renderer;
Renderer::ElementStyle& style; Renderer::ElementStyle& style;
QPainterPath& path;
public: public:
TestGLWidget(Renderer::ElementStyle& style, QWidget* parent = nullptr) TestGLWidget(Renderer::ElementStyle& style, QPainterPath& path, QWidget* parent = nullptr)
: QOpenGLWidget(parent), renderer(*Renderer::ElementRenderer::instance()), style(style) {}; : QOpenGLWidget(parent), renderer(*Renderer::ElementRenderer::instance()), style(style), path(path) {};
void initializeGL() override void initializeGL() override
{ {
initializeOpenGLFunctions(); initializeOpenGLFunctions();
@ -27,12 +28,7 @@ namespace UnitTest
{ {
glClearColor(219 / 255., 78 / 255., 32 / 255., 1.0); glClearColor(219 / 255., 78 / 255., 32 / 255., 1.0);
glClear(GL_COLOR_BUFFER_BIT); glClear(GL_COLOR_BUFFER_BIT);
QPainterPath path;
QQuickSvgParser::parsePathDataFast("M292.82,107.78s0,0,0,0,0,3.59,0,7.62c0,3.85,0,5.78.06,6.43a19.94,19.94,0,0,0,2.87,7.58,15.85,15.85,0,0,0,6.61,6.23A14.75,14.75,0,0,0,310,137a11.69,11.69,0,0,0,7.59-2.92,11,11,0,0,0,3.2-6.84c.15-1.27.58-4.84-1.79-7.64a8.54,8.54,0,0,0-3.56-2.44c-1.32-.52-3.32-1.31-5.06-.33a5.41,5.41,0,0,0-2.14,3,3.48,3.48,0,0,0-.16,2.71c.78,1.86,3.36,2.14,3.47,2.15", path);
path = PainterPathUtil::monotonization(path);
QTransform transform;
transform.scale(10, 10);
path = transform.map(path);
float pixelRatio = devicePixelRatioF(); float pixelRatio = devicePixelRatioF();
auto [img, pos] = renderer.drawElement(path, style, pixelRatio); auto [img, pos] = renderer.drawElement(path, style, pixelRatio);
QPainter painter(this); QPainter painter(this);

54
UnitTest/StyleTest.cpp Normal file
View File

@ -0,0 +1,54 @@
#include "CppUnitTest.h"
#include <QGuiApplication>
#include <QtWidgets/QApplication>
#include "Renderer/Painting/ElementStyle.h"
#include <Renderer/Painting/MaterialStyleStroke.h>
using namespace Microsoft::VisualStudio::CppUnitTestFramework;
using namespace Renderer;
namespace UnitTest
{
TEST_CLASS(StyleTest)
{
public:
TEST_METHOD(TestEncodeDecode)
{
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)}}
};
auto style = std::make_unique<MaterialStyleStroke>(
30, StrokeType::kBothSides, StrokeEndType::kRound,
std::make_shared<StrokeRadialGradient>(materialMap, false)
);
/* auto style = std::make_shared<MaterialStyleStroke>(
30, StrokeType::kBothSides, StrokeEndType::kRound,
std::make_shared<StrokePlain>(Material(QColor(255, 255, 255)))
);*/
std::shared_ptr<MaterialStyle> decoded = MaterialStyle::decoded(style->encoded());
/* Assert::IsTrue(decoded->type() == MaterialStyleType::kStroke);
Assert::IsTrue(std::static_pointer_cast<MaterialStyleStroke>(decoded)->halfWidth == 30);
Assert::IsTrue(std::static_pointer_cast<MaterialStyleStroke>(decoded)->strokeType == StrokeType::kBothSides);
Assert::IsTrue(std::static_pointer_cast<MaterialStyleStroke>(decoded)->endType == StrokeEndType::kRound);
std::shared_ptr<MaterialStroke> materialStroke = std::static_pointer_cast<MaterialStyleStroke>(decoded)->materialStroke;
Assert::IsTrue(materialStroke->type() == MaterialStrokeType::kPlain);
Material material = std::static_pointer_cast<StrokePlain>(materialStroke)->material;
Assert::IsTrue(material == Material(QColor(255, 255, 255)));
Logger::WriteMessage(std::format("({} {} {} {}), ({} {})\n",
material.color.red(),
material.color.green(),
material.color.blue(),
material.color.alpha(),
material.metallic,
material.roughness
).c_str());*/
Assert::IsTrue(*style == *decoded);
}
};
}

View File

@ -7,6 +7,7 @@
#include "Renderer/Painting/CubicBezier.h" #include "Renderer/Painting/CubicBezier.h"
#include <Renderer/Painting/StraightLine.h> #include <Renderer/Painting/StraightLine.h>
#include <ElementPoolWidget.h> #include <ElementPoolWidget.h>
#include <util/PaintingUtil.h>
using namespace Microsoft::VisualStudio::CppUnitTestFramework; using namespace Microsoft::VisualStudio::CppUnitTestFramework;
@ -119,4 +120,22 @@ namespace UnitTest
a.exec(); a.exec();
} }
}; };
TEST_CLASS(PaintingUtilTest)
{
TEST_METHOD(TransfromTest)
{
qInstallMessageHandler(messageHandler);
QPainterPath path;
path.addRect(0, 0, 20, 20);
QTransform trans;
qDebug() << path.boundingRect();
//qDebug() << trans;
//qDebug() << acos(-0.707107);
glm::vec2 scale;
float rotate;
PaintingUtil::decomposeTransform(trans, rotate, scale);
qDebug() << rotate;
qDebug() << scale.x << scale.y;
}
};
} }

View File

@ -117,6 +117,7 @@
<DynamicSource Condition="'$(Configuration)|$(Platform)'=='Release|x64'">input</DynamicSource> <DynamicSource Condition="'$(Configuration)|$(Platform)'=='Release|x64'">input</DynamicSource>
<QtMocFileName Condition="'$(Configuration)|$(Platform)'=='Release|x64'">%(Filename).moc</QtMocFileName> <QtMocFileName Condition="'$(Configuration)|$(Platform)'=='Release|x64'">%(Filename).moc</QtMocFileName>
</ClCompile> </ClCompile>
<ClCompile Include="StyleTest.cpp" />
<ClCompile Include="UnitTest.cpp" /> <ClCompile Include="UnitTest.cpp" />
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>

View File

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

View File

@ -1,4 +1,6 @@
{ {
"project-name": "样例1",
"background-color": "#ffffff",
"height": 1080, "height": 1080,
"width": 1080, "width": 1080,
"elements": [ "elements": [
@ -6,7 +8,7 @@
"name": "ababa", "name": "ababa",
"type": "svg-file", "type": "svg-file",
"data": { "data": {
"include": "./svg/2.svg" "include": "../svg/2.svg"
} }
}, },
{ {
@ -15,6 +17,13 @@
"data": { "data": {
"reference-layer": "0.0" "reference-layer": "0.0"
} }
},
{
"name": "ababa2",
"type": "svg-file",
"data": {
"include": "../svg/0.svg"
}
} }
], ],
"root-layer": { "root-layer": {

5
svg/4_L0.svg Normal file
View File

@ -0,0 +1,5 @@
<svg id="万字笔画" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 1068 1045">
<title>4_L0</title>
<polyline points="873 0 360 390 427 483 490 439 600 592 690 524 750 608 189 1045" fill="none"/>
<polyline points="1068 830 651 303 566 373 629 455 487 566 538 632 461 696 0 99" fill="none"/>
</svg>

After

Width:  |  Height:  |  Size: 307 B

31
svg/ex.json Normal file
View File

@ -0,0 +1,31 @@
{
"height": 1080,
"width": 1080,
"elements": [
{
"name": "ababa",
"type": "svg-file",
"data": {
"include": "./svg/0.svg"
}
}
],
"root-layer": {
"name": "root",
"transform": {
"offset": {
"x": 0,
"y": 0
},
"scale": {
"x": 1.0,
"y": 1.0
},
"rotation": 0.0
},
"effects": [],
"is-folder": true,
"referenced-by": null,
"children":[]
}
}