diff --git a/ArchitectureColoredPainting/ArchitectureColoredPainting.vcxproj b/ArchitectureColoredPainting/ArchitectureColoredPainting.vcxproj
index 4041213..d7932a1 100644
--- a/ArchitectureColoredPainting/ArchitectureColoredPainting.vcxproj
+++ b/ArchitectureColoredPainting/ArchitectureColoredPainting.vcxproj
@@ -157,6 +157,7 @@
+
diff --git a/ArchitectureColoredPainting/ArchitectureColoredPainting.vcxproj.filters b/ArchitectureColoredPainting/ArchitectureColoredPainting.vcxproj.filters
index b43e857..ec634e5 100644
--- a/ArchitectureColoredPainting/ArchitectureColoredPainting.vcxproj.filters
+++ b/ArchitectureColoredPainting/ArchitectureColoredPainting.vcxproj.filters
@@ -97,6 +97,9 @@
Form Files
+
+ Form Files
+
@@ -222,9 +225,6 @@
Source Files\Editor
-
- Source Files\Editor
-
Source Files\Editor
@@ -261,6 +261,9 @@
Source Files
+
+ Source Files\Editor
+
diff --git a/ArchitectureColoredPainting/EditorLayerInfoWidget.ui b/ArchitectureColoredPainting/EditorLayerInfoWidget.ui
new file mode 100644
index 0000000..7918384
--- /dev/null
+++ b/ArchitectureColoredPainting/EditorLayerInfoWidget.ui
@@ -0,0 +1,92 @@
+
+
+ EditorLayerInfoWidget
+
+
+
+ 0
+ 0
+ 333
+ 321
+
+
+
+
+
+
+ -
+
+
+ true
+
+
+
+ -
+
+
+ false
+
+
+
+ -
+
+
+ false
+
+
+
+ -
+
+
+ false
+
+
+
+ -
+
+
+ false
+
+
+
+ -
+
+
+ false
+
+
+
+ -
+
+
+ 水平翻转
+
+
+
+ -
+
+
+ 竖直翻转
+
+
+
+ -
+
+
+
+
+
+
+ QtMaterialTextField
+ QLineEdit
+
+
+
+ QtMaterialCheckBox
+ QCheckBox
+
+
+
+
+
+
diff --git a/ArchitectureColoredPainting/res/Shaders/element.comp b/ArchitectureColoredPainting/res/Shaders/element.comp
index 511c470..e8c1ca6 100644
--- a/ArchitectureColoredPainting/res/Shaders/element.comp
+++ b/ArchitectureColoredPainting/res/Shaders/element.comp
@@ -1131,14 +1131,8 @@ void main()
{
vec2 pTemp = path[pathIndex + 1];
if (isinf(pTemp.x))
- {
- if(endType == 4)
- {
- //onVeryEnd = false;
- tangentBeginNext = tangentFirstBegin;
- }
- else
- onVeryEnd = true;
+ {
+ onVeryEnd = true;
}
else
{
@@ -1151,7 +1145,16 @@ void main()
}
}
else
- onVeryEnd = true;
+ {
+ if(endType == 4)
+ {
+ //onVeryEnd = false;
+ tangentBeginNext = tangentFirstBegin;
+ }
+ else
+ onVeryEnd = true;
+ }
+
float d = cubic_bezier_dis(localUV, p[0], p[1], p[2], p[3], true);
if (d <= strokeWidth)
@@ -1199,7 +1202,7 @@ void main()
}
}
tangentEndLast = tangentEnd;
- if(pathIndex == 0)
+ if(pathIndex == 4)
tangentFirstBegin = tangentBegin;
}
p3Last = p[3];
diff --git a/ArchitectureColoredPainting/src/Editor/ElementPoolWidget.cpp b/ArchitectureColoredPainting/src/Editor/ElementPoolWidget.cpp
index 74453d0..95825e1 100644
--- a/ArchitectureColoredPainting/src/Editor/ElementPoolWidget.cpp
+++ b/ArchitectureColoredPainting/src/Editor/ElementPoolWidget.cpp
@@ -3,6 +3,7 @@
#include
#include
#include
+#include
ElementPoolWidget::ElementPoolWidget(QWidget* parent)
: QWidget(parent)
@@ -125,7 +126,12 @@ void ElementPoolWidget::popMenu(const QPoint& pos)
{
menu->addAction(QString::fromLocal8Bit("Ԫأsvg룩"), this, [this]() {
QString filePath = QFileDialog::getOpenFileName(this, QString::fromLocal8Bit("ļ"), "", "SVG Files (*.svg)");
+ filePath = filePath.trimmed();
QFileInfo fileInfo(filePath);
+ if (!fileInfo.exists() || !fileInfo.isFile()) {
+ QMessageBox::warning(this, tr("Error"), QString::fromLocal8Bit("ѡSvgļ"));
+ return;
+ }
QString fileName = fileInfo.fileName();
qDebug() << fileName << " " << filePath;
this->elementManager->createSimpleElement(fileName, filePath);
diff --git a/ArchitectureColoredPainting/src/Editor/PixelPath.cpp b/ArchitectureColoredPainting/src/Editor/PixelPath.cpp
index e673504..7eaf51d 100644
--- a/ArchitectureColoredPainting/src/Editor/PixelPath.cpp
+++ b/ArchitectureColoredPainting/src/Editor/PixelPath.cpp
@@ -41,6 +41,7 @@ void PixelPath::addPath(const PixelPath& path)
QPainter painter(&pixmap);
painter.drawPixmap(0, 0, path.getPixmap());
this->painterPath.addPath(path.getPainterPath());
+ this->originPath.addPath(path.originPath);
boundingRect = boundingRect.united(path.getBoundingRect());
}
@@ -52,6 +53,7 @@ void PixelPath::addPath(const QPainterPath& path)
painter.setPen(QPen(Qt::black,1));
painter.drawPath(path);
this->painterPath.addPath(path);
+ this->originPath.addPath(path);
boundingRect = boundingRect.united(path.boundingRect());
}
@@ -67,6 +69,7 @@ void PixelPath::clear()
pixmap.fill(Qt::transparent);
boundingRect = QRectF(0, 0, 0, 0);
painterPath.clear();
+ originPath.clear();
}
PixelPath PixelPath::trans(QTransform& mat)const
@@ -78,6 +81,7 @@ PixelPath PixelPath::trans(QTransform& mat)const
painter.setTransform(mat);
painter.drawPixmap(0, 0, pixmap);
result.painterPath.addPath(this->painterPath);
+ result.originPath.addPath(this->painterPath);
result.painterPath = mat.map(result.painterPath);
result.boundingRect = result.painterPath.boundingRect();
return result;
diff --git a/ArchitectureColoredPainting/src/Editor/PixelPath.h b/ArchitectureColoredPainting/src/Editor/PixelPath.h
index c3dc704..894ac63 100644
--- a/ArchitectureColoredPainting/src/Editor/PixelPath.h
+++ b/ArchitectureColoredPainting/src/Editor/PixelPath.h
@@ -10,7 +10,7 @@ class PixelPath
public:
QRectF boundingRect;
QPixmap pixmap;
- QPainterPath painterPath;
+ QPainterPath painterPath, originPath;
int w,h;
public:
PixelPath(int w=16, int h= 16);
diff --git a/ArchitectureColoredPainting/src/Editor/PreviewWindow.cpp b/ArchitectureColoredPainting/src/Editor/PreviewWindow.cpp
index 7745de2..6cc9e27 100644
--- a/ArchitectureColoredPainting/src/Editor/PreviewWindow.cpp
+++ b/ArchitectureColoredPainting/src/Editor/PreviewWindow.cpp
@@ -1,61 +1,61 @@
#include "PreviewWindow.h"
#include
-PreviewWindow::PreviewWindow(QWidget *parent) : QOpenGLWidget(parent)
+PreviewWindow::PreviewWindow(QWidget* parent) : QOpenGLWidget(parent)
{
- //this->setFixedSize(QSize(108, 108));
- this->setStyleSheet("border: 1px solid black");
- this->renderer = Renderer::ElementRenderer::instance();
- QSurfaceFormat surfaceFormat;
- surfaceFormat.setSamples(16);
- setFormat(surfaceFormat);
- painter = new QPainter(this);
- painter->setRenderHint(QPainter::SmoothPixmapTransform);
- painter->setRenderHint(QPainter::HighQualityAntialiasing);
- layerManager = nullptr;
- currentLayer = nullptr;
- zoomStep = 0;
+ //this->setFixedSize(QSize(108, 108));
+ this->setStyleSheet("border: 1px solid black");
+ this->renderer = Renderer::ElementRenderer::instance();
+ QSurfaceFormat surfaceFormat;
+ surfaceFormat.setSamples(16);
+ setFormat(surfaceFormat);
+ painter = new QPainter(this);
+ painter->setRenderHint(QPainter::SmoothPixmapTransform);
+ painter->setRenderHint(QPainter::HighQualityAntialiasing);
+ layerManager = nullptr;
+ currentLayer = nullptr;
+ zoomStep = 0;
backgroundColor = QColor(255, 255, 255, 255);
}
-void PreviewWindow::initialize(LayerManager *layerManager,QSize windowSize)
+void PreviewWindow::initialize(LayerManager* layerManager, QSize windowSize)
{
this->logicalSize = windowSize;
- this->layerManager = layerManager;
+ this->layerManager = layerManager;
this->setFixedSize(windowSize);
}
void PreviewWindow::show()
{
- //QFile settingFile;
- //settingFile.setFileName("../data.json");
- //settingFile.open(QFile::ReadOnly);
- //QByteArray setting = settingFile.readAll().trimmed();
- //QJsonDocument jsonDoc(QJsonDocument::fromJson(setting));
- //auto jElements = jsonDoc.object().value("elements").toArray();
+ //QFile settingFile;
+ //settingFile.setFileName("../data.json");
+ //settingFile.open(QFile::ReadOnly);
+ //QByteArray setting = settingFile.readAll().trimmed();
+ //QJsonDocument jsonDoc(QJsonDocument::fromJson(setting));
+ //auto jElements = jsonDoc.object().value("elements").toArray();
- //for (auto &&ele : jElements)
- //{
- // SimpleElement element(ele.toObject());
- // painter->drawPath(element.getPaintObject());
- //}
+ //for (auto &&ele : jElements)
+ //{
+ // SimpleElement element(ele.toObject());
+ // painter->drawPath(element.getPaintObject());
+ //}
}
void PreviewWindow::initializeGL()
{
- initializeOpenGLFunctions();
+ initializeOpenGLFunctions();
}
void PreviewWindow::paintGL()
{
glClearColor(backgroundColor.redF(), backgroundColor.greenF(), backgroundColor.blueF(), backgroundColor.alphaF());
- glClear(GL_COLOR_BUFFER_BIT);
- painter->begin(this);
- painter->setWindow(0, 0, logicalSize.width(), logicalSize.height());
- painter->setRenderHint(QPainter::Antialiasing);
- painter->setRenderHint(QPainter::HighQualityAntialiasing);
- layerManager->paint(painter,this->size(),currentLayer);
- painter->end();
+ glClear(GL_COLOR_BUFFER_BIT);
+ painter->begin(this);
+ painter->setWindow(0, 0, logicalSize.width() * devicePixelRatioF(), logicalSize.height() * devicePixelRatioF());
+ painter->setRenderHint(QPainter::Antialiasing);
+ painter->setRenderHint(QPainter::HighQualityAntialiasing);
+ layerManager->paint(painter, this->size(), currentLayer);
+ painter->end();
}
void PreviewWindow::resizeGL(int w, int h)
@@ -63,64 +63,64 @@ void PreviewWindow::resizeGL(int w, int h)
}
Renderer::ElementRenderer* const PreviewWindow::getRenderer()const {
- return this->renderer;
+ return this->renderer;
}
void PreviewWindow::currentLayerChanged(LayerWrapper* layer)
{
- this->currentLayer = layer;
+ this->currentLayer = layer;
}
void PreviewWindow::refresh()
{
- this->repaint();
+ this->repaint();
}
void PreviewWindow::mousePressEvent(QMouseEvent* event)
{
- // 갴ʱ¼ǰλ
- m_lastPos = event->pos();
+ // 갴ʱ¼ǰλ
+ m_lastPos = event->pos();
}
void PreviewWindow::mouseMoveEvent(QMouseEvent* event)
{
- // ƶʱƶľ룬Ҫͼε״̬
- int dx = event->x() - m_lastPos.x();
- int dy = event->y() - m_lastPos.y();
- if (currentLayer != nullptr) {
- if (QApplication::keyboardModifiers() == Qt::ControlModifier && (event->buttons() & Qt::LeftButton))
- {
- currentLayer->property.scale.setX(std::max(0.0, currentLayer->property.scale.x() + dx / 50.0));
- currentLayer->property.scale.setY(std::max(0.0, currentLayer->property.scale.y() + dy / 50.0));
- }
- else if (QApplication::keyboardModifiers() == (Qt::ControlModifier | Qt::ShiftModifier) && (event->buttons() & Qt::LeftButton))
- {
- currentLayer->property.scale.setX(std::max(0.0, currentLayer->property.scale.x() * (1.0 + dx / 50.0)));
- currentLayer->property.scale.setY(std::max(0.0, currentLayer->property.scale.y() * (1.0 + dx / 50.0)));
- }
- else if (event->buttons() & Qt::LeftButton) {
- // µôƽͼ
+ // ƶʱƶľ룬Ҫͼε״̬
+ int dx = event->x() - m_lastPos.x();
+ int dy = event->y() - m_lastPos.y();
+ if (currentLayer != nullptr) {
+ if (QApplication::keyboardModifiers() == Qt::ControlModifier && (event->buttons() & Qt::LeftButton))
+ {
+ currentLayer->property.scale.setX(std::max(0.0, currentLayer->property.scale.x() + dx / 50.0));
+ currentLayer->property.scale.setY(std::max(0.0, currentLayer->property.scale.y() + dy / 50.0));
+ }
+ else if (QApplication::keyboardModifiers() == (Qt::ControlModifier | Qt::ShiftModifier) && (event->buttons() & Qt::LeftButton))
+ {
+ currentLayer->property.scale.setX(std::max(0.0, currentLayer->property.scale.x() * (1.0 + dx / 50.0)));
+ currentLayer->property.scale.setY(std::max(0.0, currentLayer->property.scale.y() * (1.0 + dx / 50.0)));
+ }
+ else if (event->buttons() & Qt::LeftButton) {
+ // µôƽͼ
currentLayer->property.offset.setX(currentLayer->property.offset.x() + dx);
currentLayer->property.offset.setY(currentLayer->property.offset.y() + dy);
- }
+ }
else if (event->buttons() & Qt::RightButton) {
- // µҼôתͼ
- qreal angle = dx;
+ // µҼôתͼ
+ qreal angle = dx;
currentLayer->property.rotation += angle;
- }
- auto layer = currentLayer;
- while (layer != nullptr)
- {
- auto index = -1;
- if (typeid(*layer) == typeid(FolderLayerWrapper))
- index = dynamic_cast(layer)->getReferencedBy();
- layer = layer->getParent();
- }
- }
- // һελ
- emit triggerCentralRefresh();
- m_lastPos = event->pos();
- this->repaint();
+ }
+ auto layer = currentLayer;
+ while (layer != nullptr)
+ {
+ auto index = -1;
+ if (typeid(*layer) == typeid(FolderLayerWrapper))
+ index = dynamic_cast(layer)->getReferencedBy();
+ layer = layer->getParent();
+ }
+ }
+ // һελ
+ emit triggerCentralRefresh();
+ m_lastPos = event->pos();
+ this->repaint();
}
void PreviewWindow::mouseReleaseEvent(QMouseEvent* event)
@@ -131,23 +131,23 @@ void PreviewWindow::mouseReleaseEvent(QMouseEvent* event)
void PreviewWindow::setBackgroundColor(QColor color)
{
this->backgroundColor = color;
- this->repaint();
+ this->repaint();
}
void PreviewWindow::wheelEvent(QWheelEvent* event)
{
- if (QApplication::keyboardModifiers() == Qt::ControlModifier)
- {
+ if (QApplication::keyboardModifiers() == Qt::ControlModifier)
+ {
if (event->delta() > 0 && zoomStep < ZOOM_STEP_MAX)
{
zoomStep++;
this->setFixedSize(logicalSize * (1 + zoomStep * ZOOM_RATE));
}
- else if(event->delta() < 0 && zoomStep > ZOOM_STEP_MIN)
+ else if (event->delta() < 0 && zoomStep > ZOOM_STEP_MIN)
{
zoomStep--;
- this->setFixedSize(logicalSize * (1 + zoomStep * ZOOM_RATE));
+ this->setFixedSize(logicalSize * (1 + zoomStep * ZOOM_RATE));
}
this->repaint();
- }
+ }
}
\ No newline at end of file
diff --git a/ArchitectureColoredPainting/src/Editor/util/PaintingUtil.cpp b/ArchitectureColoredPainting/src/Editor/util/PaintingUtil.cpp
index 0b84c07..6c92dcf 100644
--- a/ArchitectureColoredPainting/src/Editor/util/PaintingUtil.cpp
+++ b/ArchitectureColoredPainting/src/Editor/util/PaintingUtil.cpp
@@ -79,34 +79,38 @@ FolderLayerWrapper* PaintingUtil::handleLayerWrapper(LayerWrapper* nowLayer, QTr
}
PixelPath pixelPath = nowLayer->getCache();
- QPainterPath painterPath = pixelPath.getPainterPath();
+ QPainterPath painterPath = pixelPath.originPath;
QRectF bound = painterPath.boundingRect();
//qDebug() << leafLayer<<"------" << painterPath;
// transform to -1 1
QTransform trans;
- double maxLen = std::max(bound.width(), bound.height());
+ double maxLen = std::max(bound.width(), bound.height()) * 1.00001;
qDebug() << maxLen << bound;
trans.scale(1 / maxLen, 1 / maxLen);
trans.translate(-bound.center().x(), -bound.center().y());
painterPath = trans.map(painterPath);
shared_ptr >> contour = std::make_shared >>(PainterPathUtil::transformToLines(painterPath));
- QSize screenSize = QSize(1024, 1024);
+ QSize screenSize = QSize(1080, 1080);
ElementTransform elementTransform;
transform = trans.inverted() * transform * QTransform::fromScale(2. / screenSize.width(), 2. / screenSize.height()) * QTransform::fromTranslate(-1, -1) * QTransform::fromScale(1, -1);
auto baseStyles = leafLayer->styles.toBaseStyles();
- Renderer::BaseElement element;
- element.contour = contour;
+
for (auto& baseStyle : baseStyles) {
double lineWidth = 0;
+
+ std::shared_ptr material;
+
if (baseStyle.material->type() == Renderer::MaterialStyleType::kStroke) {
- auto material = std::static_pointer_cast(baseStyle.material);
- material->halfWidth /= maxLen;
- lineWidth = material->halfWidth;
- qDebug() << material->halfWidth;
+ std::shared_ptr copy = baseStyle.material->clone();
+ std::static_pointer_cast(copy)->halfWidth /= maxLen;
+ lineWidth = std::static_pointer_cast(copy)->halfWidth;
+ material = copy;
}
+ else
+ material = baseStyle.material;
QPainterPathStroker stroker;
stroker.setWidth(lineWidth * 2);
stroker.setCapStyle(Qt::RoundCap);
@@ -114,17 +118,14 @@ FolderLayerWrapper* PaintingUtil::handleLayerWrapper(LayerWrapper* nowLayer, QTr
QPainterPath strokePath = stroker.createStroke(painterPath);
auto rect = transform.map(strokePath).boundingRect();
elementTransform.bound = glm::vec4(rect.x(), rect.y(), rect.x() + rect.width(), rect.y() + rect.height());
- qDebug() << elementTransform.bound.x << elementTransform.bound.y << elementTransform.bound.z << elementTransform.bound.z;
+ //qDebug() << elementTransform.bound.x << elementTransform.bound.y << elementTransform.bound.z << elementTransform.bound.z;
transform = transform.inverted();
elementTransform.transform = glm::mat3x2(
transform.m11(), transform.m12(), transform.m21(),
transform.m22(), transform.m31(), transform.m32()
);
- //qDebug() << transform;
elementTransform.zIndex = 0;
-
- element.style = baseStyle.material;
- painting.addElement(element, elementTransform);
+ painting.addElement(BaseElement{ contour, material }, elementTransform);
}
return nullptr;
diff --git a/UnitTest/ElementRendererTest.cpp b/UnitTest/ElementRendererTest.cpp
index 5760980..c1c12ab 100644
--- a/UnitTest/ElementRendererTest.cpp
+++ b/UnitTest/ElementRendererTest.cpp
@@ -4,6 +4,7 @@
#include "ElementRendererTest.h"
#include "Renderer/Painting/ElementStyle.h"
#include "Renderer/Painting/MaterialStyleFill.h"
+#include "Editor/util/SvgFileLoader.h"
using namespace Microsoft::VisualStudio::CppUnitTestFramework;
using namespace Renderer;
@@ -203,6 +204,30 @@ namespace UnitTest
w.show();
a.exec();
}
+ TEST_METHOD(TestBothSidesClosed)
+ {
+ QPainterPath closedPath;
+ SvgFileLoader().loadSvgFile("../../svg/4_L0.svg", closedPath);
+ closedPath = QTransform::fromScale(5, 5).map(closedPath);
+ QApplication a(argc, argv);
+ class StyleStrokeRadialGradient : public Renderer::ElementStyle
+ {
+ virtual std::vector toBaseStyles() const override
+ {
+ std::map 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(),
+ std::make_shared(20, StrokeType::kLeftSide, StrokeEndType::kClosed,
+ std::make_shared(materialMap, false))) };
+ }
+ } style;
+ TestGLWidget w(style, closedPath);
+ w.show();
+ a.exec();
+ }
};
TEST_CLASS(ElementRendererStokeMaterialTest)
diff --git a/test.json b/test.json
index c5158fb..ea5c1ae 100644
--- a/test.json
+++ b/test.json
@@ -3,7 +3,7 @@
"elements": [
{
"data": {
- "include": "../svg/2.svg"
+ "include": "/svg/2.svg"
},
"name": "ababa",
"type": "svg-file"
@@ -17,10 +17,17 @@
},
{
"data": {
- "include": "../svg/0.svg"
+ "include": "/svg/0.svg"
},
"name": "ababa2",
"type": "svg-file"
+ },
+ {
+ "data": {
+ "include": "/svg/4_L0.svg"
+ },
+ "name": "4_L0.svg",
+ "type": "svg-file"
}
],
"height": 1080,
@@ -29,46 +36,22 @@
"children": [
{
"children": [
- {
- "element": 0,
- "is-folder": false,
- "name": "Leaf1",
- "styles": [
- {
- "enableEachSideIndependent": false,
- "left": "AAAAQAEAIZwAf///qqr//w==",
- "right": "AADgQAAACJw=",
- "type": "stroke"
- }
- ],
- "transform": {
- "offset": {
- "x": 0,
- "y": 0
- },
- "rotation": 0,
- "scale": {
- "x": 1,
- "y": 1
- }
- }
- },
{
"element": 0,
"is-folder": false,
"name": "Leaf2",
"styles": [
{
- "enableEachSideIndependent": false,
- "left": "AAAAQAEAIZwAf////1UA/w==",
- "right": "AADgQAEACJwAf///AFqe/w==",
+ "enableEachSideIndependent": true,
+ "left": "AABAQAEAIZwAf///AFqe/w==",
+ "right": "AABAQAAACJw=",
"type": "stroke"
}
],
"transform": {
"offset": {
- "x": 150,
- "y": 0
+ "x": 501,
+ "y": -3
},
"rotation": 0,
"scale": {
@@ -83,8 +66,8 @@
"referenced-by": 1,
"transform": {
"offset": {
- "x": 50,
- "y": 50
+ "x": 503,
+ "y": 36
},
"rotation": 0,
"scale": {
@@ -96,15 +79,73 @@
{
"element": 1,
"is-folder": false,
- "name": "ReferencingGroupLayer",
+ "name": "子图层-2",
"styles": [
],
"transform": {
"offset": {
- "x": 100,
- "y": 0
+ "x": 1,
+ "y": 986
},
- "rotation": 45,
+ "rotation": 0,
+ "scale": {
+ "x": 1,
+ "y": 1
+ }
+ }
+ },
+ {
+ "element": 1,
+ "is-folder": false,
+ "name": "子图层-3",
+ "styles": [
+ ],
+ "transform": {
+ "offset": {
+ "x": -959,
+ "y": -5
+ },
+ "rotation": 0,
+ "scale": {
+ "x": 1,
+ "y": 1
+ }
+ }
+ },
+ {
+ "element": 1,
+ "is-folder": false,
+ "name": "子图层-4",
+ "styles": [
+ ],
+ "transform": {
+ "offset": {
+ "x": -958,
+ "y": 980
+ },
+ "rotation": 0,
+ "scale": {
+ "x": 1,
+ "y": 1
+ }
+ }
+ },
+ {
+ "element": 3,
+ "is-folder": false,
+ "name": "子图层-5",
+ "styles": [
+ {
+ "material": "AH8A/wBanv8=",
+ "type": "fill"
+ }
+ ],
+ "transform": {
+ "offset": {
+ "x": 473,
+ "y": 419
+ },
+ "rotation": 0,
"scale": {
"x": 1,
"y": 1
diff --git a/元素改变导致结构性变化是否安全的证明.md b/元素改变导致结构性变化是否安全的证明.md
new file mode 100644
index 0000000..e51b861
--- /dev/null
+++ b/元素改变导致结构性变化是否安全的证明.md
@@ -0,0 +1,60 @@
+# 图层删除
+当删除一个节点时,该节点一定满足且只满足以下条件中的一条:
+1. 是一个Leaf节点,引用了其他的Element;
+2. 是一个Folder节点,没有GroupElement引用,没有引用任何其他节点(因为是Folder);
+3. 是一个Folder节点,存在对应的GroupElement引用。
+
+其中,对于第1条,由于Leaf节点被删除只影响其父亲Folder的属性,所以显然在删除Leaf节点时是绝对安全的。
+
+对于第2条,对于其父亲Folder,其被删除只影响其父亲Folder的属性,所以不会影响其父亲;对于其孩子,有可能存在以下情况:
+1. 孩子中不存在任何被引用的情况:删除安全;
+2. 孩子中存在Folder节点被GroupElement引用,不安全,需要手动将情况转换为第1条后才可以删除。
+
+对于第3条,必须首先手动解除其本身的引用,然后将转变为第2条。
+
+# 图元删除
+**显然**,无论是什么种类的图元,删除一个图元会影响到的元素只有引用它的图层,所以我们将SimpleElement与GroupElement合并成一种情况讨论。
+
+当删除一个图元时,该图元一定满足且只满足以下条件中的一条:
+1. 是一个GraphicElement,未被其他图层引用;
+2. 是一个GraphicElement,被其他图层引用。
+
+对于第1条,删除绝对安全。
+
+对于第2条,将引用关系手动解除后,删除安全。
+
+# 图层移动
+## 条件分析
+图层移动时,显然,被移动的节点只影响它本身及它的子图层,
+移动的目标节点只影响它本身及它的父图层,那么:
+
+(1) 被移动的图层一定满足且只满足以下条件中的一条:
+1. 是一个Folder节点,无对应的GroupElement引用,子节点无引用GroupElement;
+2. 是一个Folder节点,无对应的GroupElement引用,子节点存在引用GroupElement;
+3. 是一个Folder节点,存在对应的GroupElement引用;
+4. 是一个Leaf节点,引用了GroupElement;
+5. 是一个Leaf节点,未引用GroupElement。
+
+(2) 目标FolderLayer一定满足且只满足以下条件中的一条:
+1. 自身无对应的GroupElement引用,无父节点有GroupElement引用;
+2. 自身无对应的GroupElement引用,存在父节点有GroupElement引用;
+3. 自身存在对应的GroupElement引用。
+
+显然,以上条件的全组合可以覆盖所有情况。
+由于:
+- `(1) 1` 与 `(2) 中的任一条件`进行组合显然是绝对安全的;
+- `(1) 3` 中自身提供的GroupElement并不会影响移动;
+- `(1) 5` 与 `(2) 中的任一条件`进行组合显然是绝对安全的;
+- `(1) 中的任一条件` 与 `(2) 1` 进行组合显然是绝对安全的。
+
+所以接下来对于除去以上情况的所有组合情况进行讨论。
+## 讨论证明
+### `(1) 2` - `(2) 2`
+当且仅当被移动的图层的子节点引用的GroupElement 与 目标图层父节点提供的GroupElement 相同时,移动不安全。
+### `(1) 4` - `(2) 2`
+当且仅当被移动的图层引用的GroupElement 与 目标图层父节点提供的 GroupElement 相同时,移动不安全。
+### `(2) 3` 的特殊讨论
+当且仅当自身提供的GroupElement 被 被移动图层或其子节点引用时,需要处理其自身情况,否则无视其自身提供的GroupElement,与 `(2) 1` 或 `(2) 2` 视为同情况处理。
+
+## 结论
+当且仅当被移动的图层或其子节点所引用的GroupElement 与 目标图层或其父节点所提供的GroupElement 相同时,移动不安全;其余情况均安全。
\ No newline at end of file