Compare commits

...

2 Commits

Author SHA1 Message Date
Maurycy Liebner
37e5158844 Implement NullObject. 2021-03-01 18:41:18 +01:00
Maurycy Liebner
ff2aa12182 Fix cropping when the crop rect does not intersect with the image.
Closes #215.
2021-02-28 11:51:36 +01:00
18 changed files with 357 additions and 3 deletions

View File

@ -909,10 +909,16 @@ void MainWindow::setupToolBar() {
mToolBar->addSeparator();
mNullMode = SwitchButton::sCreate2Switch(
"toolbarButtons/nullCreateUnchecked.png",
"toolbarButtons/nullCreateChecked.png",
gSingleLineTooltip(tr("Add Null Object Mode", "ToolBar"), "F9"), this);
mToolBar->addWidget(mNullMode);
mPickPaintSettingsMode = SwitchButton::sCreate2Switch(
"toolbarButtons/pickUnchecked.png",
"toolbarButtons/pickChecked.png",
gSingleLineTooltip(tr("Pick Mode", "ToolBar"), "F9"), this);
gSingleLineTooltip(tr("Pick Mode", "ToolBar"), "F10"), this);
mToolBar->addWidget(mPickPaintSettingsMode);
mToolBar->widgetForAction(mToolBar->addAction(" "))->
@ -998,6 +1004,8 @@ void MainWindow::connectToolBarActions() {
connect(mTextMode, &ActionButton::pressed,
&mActions, &Actions::setTextMode);
connect(mNullMode, &ActionButton::pressed,
&mActions, &Actions::setNullMode);
connect(mPickPaintSettingsMode, &ActionButton::pressed,
&mActions, &Actions::setPickPaintSettingsMode);
@ -1053,6 +1061,7 @@ void MainWindow::updateCanvasModeButtonsChecked() {
mRectangleMode->setState(mode == CanvasMode::rectCreate);
mTextMode->setState(mode == CanvasMode::textCreate);
mNullMode->setState(mode == CanvasMode::nullCreate);
mPickPaintSettingsMode->setState(mode == CanvasMode::pickFillStroke);
const bool boxMode = mode == CanvasMode::boxTransform;
@ -1200,6 +1209,8 @@ bool handleCanvasModeKeyPress(Document& document, const int key) {
} else if(key == Qt::Key_F8) {
document.setCanvasMode(CanvasMode::textCreate);
} else if(key == Qt::Key_F9) {
document.setCanvasMode(CanvasMode::nullCreate);
} else if(key == Qt::Key_F10) {
document.setCanvasMode(CanvasMode::pickFillStroke);
} else return false;
KeyFocusTarget::KFT_sSetRandomTarget();

View File

@ -252,6 +252,7 @@ private:
SwitchButton *mRectangleMode;
SwitchButton *mTextMode;
//
SwitchButton *mNullMode;
SwitchButton *mPickPaintSettingsMode;
ActionButton *mActionConnectPoints;

View File

@ -0,0 +1,71 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<svg
xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:cc="http://creativecommons.org/ns#"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:svg="http://www.w3.org/2000/svg"
xmlns="http://www.w3.org/2000/svg"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
width="25"
height="25"
viewBox="0 0 6.6145832 6.6145835"
version="1.1"
id="svg1468"
inkscape:version="0.92.3 (2405546, 2018-03-11)"
sodipodi:docname="nullCreate.svg">
<defs
id="defs1462" />
<sodipodi:namedview
id="base"
pagecolor="#ffffff"
bordercolor="#666666"
borderopacity="1.0"
inkscape:pageopacity="0.0"
inkscape:pageshadow="2"
inkscape:zoom="31.678384"
inkscape:cx="10.871939"
inkscape:cy="10.351229"
inkscape:document-units="mm"
inkscape:current-layer="layer1"
showgrid="false"
units="px"
inkscape:window-width="1920"
inkscape:window-height="1054"
inkscape:window-x="0"
inkscape:window-y="0"
inkscape:window-maximized="1"
inkscape:document-rotation="0" />
<metadata
id="metadata1465">
<rdf:RDF>
<cc:Work
rdf:about="">
<dc:format>image/svg+xml</dc:format>
<dc:type
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
<dc:title />
</cc:Work>
</rdf:RDF>
</metadata>
<g
inkscape:label="Layer 1"
inkscape:groupmode="layer"
id="layer1"
transform="translate(0,-290.3854)">
<path
id="path4906"
style="opacity:1;fill:#f9f9f9;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:0.372986;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:9.4;stroke-opacity:1"
d="m 3.3072917,291.45459 a 2.2379143,2.2379143 0 0 0 -2.2381062,2.2381 2.2379143,2.2379143 0 0 0 2.2381062,2.23811 2.2379143,2.2379143 0 0 0 2.2381062,-2.23811 2.2379143,2.2379143 0 0 0 -2.2381062,-2.2381 z m -0.027905,0.45578 a 1.7824545,1.7824545 0 0 1 0.027905,0 1.7824545,1.7824545 0 0 1 1.7823201,1.78232 1.7824545,1.7824545 0 0 1 -1.7823201,1.78232 1.7824545,1.7824545 0 0 1 -1.7823201,-1.78232 1.7824545,1.7824545 0 0 1 1.7544151,-1.78232 z" />
<path
inkscape:connector-curvature="0"
id="path818"
d="m 1.787365,292.27266 2.9441409,2.94414"
style="fill:#f9f9f9;fill-opacity:1;stroke:#f9f9f9;stroke-width:0.52916667;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;stroke-miterlimit:4;stroke-dasharray:none" />
<path
style="fill:#f9f9f9;fill-opacity:1;stroke:#f9f9f9;stroke-width:0.52916667;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;stroke-miterlimit:4;stroke-dasharray:none"
d="m 4.7315054,292.27266 -2.94414,2.94414"
id="path816"
inkscape:connector-curvature="0" />
</g>
</svg>

After

Width:  |  Height:  |  Size: 2.9 KiB

View File

@ -556,6 +556,7 @@ stdsptr<BoxRenderData> BoundingBox::createRenderData(const qreal relFrame) {
BoxRenderData *BoundingBox::updateCurrentRenderData(const qreal relFrame) {
const auto renderData = createRenderData(relFrame);
if(!renderData) return nullptr;
mRenderDataHandler.addItemAtRelFrame(renderData);
return renderData.get();
}

View File

@ -76,6 +76,7 @@ enum class eBoxType {
group,
custom,
deprecated0, // sculptPath,
nullObject,
count
};

View File

@ -1466,6 +1466,7 @@ void ContainerBox::writeBoxOrSoundXEV(const stdsptr<XevZipFileSaver>& xevFileSav
#include "internallinkbox.h"
#include "customboxcreator.h"
#include "svglinkbox.h"
#include "nullobject.h"
qsptr<BoundingBox> createBoxOfNonCustomType(const eBoxType type) {
switch(type) {
@ -1497,6 +1498,8 @@ qsptr<BoundingBox> createBoxOfNonCustomType(const eBoxType type) {
return enve::make_shared<SvgLinkBox>();
case(eBoxType::internalLinkCanvas):
return enve::make_shared<InternalLinkCanvas>(nullptr, false);
case(eBoxType::nullObject):
return enve::make_shared<NullObject>();
case(eBoxType::deprecated0): break;
case(eBoxType::canvas) : break;
case(eBoxType::count) : break;

View File

@ -232,6 +232,7 @@ stdsptr<BoxRenderData> ILBB::createRenderData() {
const auto linkTarget = getLinkTarget();
if(!linkTarget) return nullptr;
const auto renderData = linkTarget->createRenderData();
if(!renderData) return nullptr;
renderData->fParentBox = this;
if(!mInnerLink) renderData->fBlendEffectIdentifier = this;
return renderData;

View File

@ -0,0 +1,184 @@
// enve - 2D animations software
// Copyright (C) 2016-2020 Maurycy Liebner
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>.
#include "nullobject.h"
#include "Animators/transformanimator.h"
#include "canvas.h"
class NullObjectType : public ComboBoxProperty {
enum class Type {
square, circle, triangle
};
public:
NullObjectType(ColorAnimator* const color,
QrealAnimator* const size);
void prp_drawCanvasControls(SkCanvas * const canvas, const CanvasMode mode,
const float invScale, const bool ctrlPressed);
void updatePath();
bool relPointInsidePath(const QPointF &relPos);
QRectF relBoundingRect();
private:
SkPath mCurrentPath;
ColorAnimator* const mColor;
QrealAnimator* const mSize;
};
NullObjectType::NullObjectType(ColorAnimator* const color,
QrealAnimator* const size) :
ComboBoxProperty("type", QStringList() << "square" <<
"circle" <<
"triangle"),
mColor(color), mSize(size) {
prp_setDrawingOnCanvasEnabled(true);
connect(this, &ComboBoxProperty::prp_currentFrameChanged,
this, &NullObjectType::updatePath);
connect(mSize, &QrealAnimator::prp_currentFrameChanged,
this, &NullObjectType::updatePath);
updatePath();
}
void NullObjectType::prp_drawCanvasControls(
SkCanvas* const canvas, const CanvasMode mode,
const float invScale, const bool ctrlPressed) {
Q_UNUSED(mode)
Q_UNUSED(ctrlPressed)
const auto qtrans = getTransform();
const auto sktrans = toSkMatrix(qtrans);
SkPath mappedPath;
mCurrentPath.transform(sktrans, &mappedPath);
const auto color = mColor->getColor();
SkPaint paint;
paint.setAntiAlias(true);
paint.setStrokeWidth(2.f*invScale);
paint.setStyle(SkPaint::kStroke_Style);
paint.setColor(SK_ColorLTGRAY);
canvas->drawPath(mappedPath, paint);
paint.setStrokeWidth(1.f*invScale);
paint.setColor(toSkColor(color));
canvas->drawPath(mappedPath, paint);
}
void NullObjectType::updatePath() {
mCurrentPath.reset();
const int val = getCurrentValue();
const Type type = static_cast<Type>(val);
const qreal size = mSize->getEffectiveValue();
const qreal ten = 10*size;
switch(type) {
case Type::square:
mCurrentPath.moveTo(-ten, -ten);
mCurrentPath.lineTo(ten, -ten);
mCurrentPath.lineTo(ten, ten);
mCurrentPath.lineTo(-ten, ten);
mCurrentPath.lineTo(-ten, -ten);
mCurrentPath.moveTo(-ten, -ten);
mCurrentPath.lineTo(ten, ten);
mCurrentPath.moveTo(ten, -ten);
mCurrentPath.lineTo(-ten, ten);
break;
case Type::circle:
mCurrentPath.addCircle(0, 0, ten);
mCurrentPath.moveTo(-ten, -ten);
mCurrentPath.lineTo(ten, ten);
mCurrentPath.moveTo(ten, -ten);
mCurrentPath.lineTo(-ten, ten);
break;
case Type::triangle:
const qreal a = 3*ten/sqrt(3);
const qreal h = ten + a*sqrt(3)/6;
mCurrentPath.moveTo(0, -ten);
mCurrentPath.lineTo(0.5*a, -ten + h);
mCurrentPath.lineTo(-0.5*a, -ten + h);
mCurrentPath.lineTo(0, -ten);
mCurrentPath.moveTo(0, -ten);
mCurrentPath.lineTo(0, 0);
mCurrentPath.moveTo(0.5*a, -ten + h);
mCurrentPath.lineTo(0, 0);
mCurrentPath.moveTo(-0.5*a, -ten + h);
mCurrentPath.lineTo(0, 0);
break;
}
}
bool NullObjectType::relPointInsidePath(const QPointF& relPos) {
const int val = getCurrentValue();
const Type type = static_cast<Type>(val);
const qreal size = mSize->getEffectiveValue();
const qreal ten = 10*size;
switch(type) {
case Type::square: {
QRectF rect(-ten, -ten, 2*ten, 2*ten);
return rect.contains(relPos);
} case Type::circle: {
QPainterPath path;
path.addEllipse({0., 0.}, ten, ten);
return path.contains(relPos);
} case Type::triangle:
const qreal a = 3*ten/sqrt(3);
const qreal h = ten + a*sqrt(3)/6;
QPainterPath path;
path.moveTo(0, -ten);
path.lineTo(0.5*a, -ten + h);
path.lineTo(-0.5*a, -ten + h);
path.closeSubpath();
return path.contains(relPos);
}
return false;
}
QRectF NullObjectType::relBoundingRect() {
const auto skRect = mCurrentPath.computeTightBounds();
return toQRectF(skRect);
}
void NullObject::queTasks() {
setRelBoundingRect(mType->relBoundingRect());
BoundingBox::queTasks();
}
NullObject::NullObject() : BoundingBox("null object", eBoxType::nullObject) {
mColor = enve::make_shared<ColorAnimator>();
mSize = enve::make_shared<QrealAnimator>(1, 1, 100, 0.1, "size");
mType = enve::make_shared<NullObjectType>(mColor.get(), mSize.get());
ca_addChild(mType);
ca_addChild(mColor);
ca_addChild(mSize);
connect(this, &BoundingBox::prp_sceneChanged,
this, [this](Canvas* const oldS, Canvas* const newS) {
if(oldS) oldS->removeNullObject(this);
if(newS) newS->addNullObject(this);
});
}
bool NullObject::relPointInsidePath(const QPointF &relPos) const {
return mType->relPointInsidePath(relPos);
}
void NullObject::drawNullObject(
SkCanvas* const canvas, const CanvasMode mode,
const float invScale, const bool ctrlPressed) {
mType->prp_drawCanvasControls(canvas, mode, invScale, ctrlPressed);
}

View File

@ -0,0 +1,44 @@
// enve - 2D animations software
// Copyright (C) 2016-2020 Maurycy Liebner
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>.
#ifndef NULLOBJECT_H
#define NULLOBJECT_H
#include "boundingbox.h"
#include "Properties/comboboxproperty.h"
#include "Animators/coloranimator.h"
class NullObjectType;
class NullObject : public BoundingBox {
public:
NullObject();
void queTasks();
stdsptr<BoxRenderData> createRenderData() { return nullptr; }
bool shouldScheduleUpdate() { return false; }
bool relPointInsidePath(const QPointF &relPos) const;
void drawNullObject(SkCanvas* const canvas, const CanvasMode mode,
const float invScale, const bool ctrlPressed);
private:
qsptr<NullObjectType> mType;
qsptr<ColorAnimator> mColor;
qsptr<QrealAnimator> mSize;
};
#endif // NULLOBJECT_H

View File

@ -858,6 +858,7 @@ void AutoTilesData::crop(const QRect &cropRect) {
const QRect iniRect = pixelBoundingRect();
const QRect normalizedCrop = cropRect.normalized();
const QRect clampedCrop = normalizedCrop.intersected(iniRect);
if(!clampedCrop.isValid()) return;
{
const int dl = clampedCrop.left() - iniRect.left();

View File

@ -28,8 +28,9 @@ Property::Property(const QString& name) :
connect(this, &Property::prp_ancestorChanged, this, [this]() {
const auto newScene = mParent_k ? mParent_k->mParentScene : nullptr;
if(mParentScene != newScene) {
const auto old = mParentScene;
mParentScene = newScene;
emit prp_sceneChanged();
emit prp_sceneChanged(old, newScene);
}
emit prp_pathChanged();
});

View File

@ -46,6 +46,7 @@ enum class CanvasMode : short {
rectCreate,
textCreate,
nullCreate,
pickFillStroke
};
@ -268,7 +269,7 @@ signals:
void prp_parentChanged(ComplexAnimator*, QPrivateSignal);
void prp_ancestorChanged(QPrivateSignal);
void prp_pathChanged();
void prp_sceneChanged();
void prp_sceneChanged(Canvas*, Canvas*);
private:
bool prp_mSelected = false;
bool mDrawOnCanvas = false;

View File

@ -783,6 +783,10 @@ void Actions::setPaintMode() {
mDocument.setCanvasMode(CanvasMode::paint);
}
void Actions::setNullMode() {
mDocument.setCanvasMode(CanvasMode::nullCreate);
}
void Actions::finishSmoothChange() {
mSmoothChange = false;
// mDocument.actionFinished();

View File

@ -103,6 +103,7 @@ public:
void setCircleMode();
void setTextMode();
void setNullMode();
void setPickPaintSettingsMode();
//
bool smoothChange() const { return mSmoothChange; }

View File

@ -38,6 +38,7 @@
#include "svgexporter.h"
#include "ReadWrite/evformat.h"
#include "eevent.h"
#include "Boxes/nullobject.h"
Canvas::Canvas(Document &document,
const int canvasWidth, const int canvasHeight,
@ -276,6 +277,11 @@ void Canvas::renderSk(SkCanvas * const canvas,
iBox->drawAllCanvasControls(canvas, mCurrentMode, invZoom, ctrlPressed);
canvas->restore();
}
for(const auto obj : mNullObjects) {
canvas->save();
obj->drawNullObject(canvas, mCurrentMode, invZoom, ctrlPressed);
canvas->restore();
}
}
if(mCurrentMode == CanvasMode::boxTransform ||
@ -1107,6 +1113,14 @@ SceneBoundGradient *Canvas::getGradientWithDocumentId(const int id) const {
return nullptr;
}
void Canvas::addNullObject(NullObject* const obj) {
mNullObjects.append(obj);
}
void Canvas::removeNullObject(NullObject* const obj) {
mNullObjects.removeOne(obj);
}
#include "simpletask.h"
void Canvas::clearGradientRWIds() const {
SimpleTask::sScheduleContexted(this, [this]() {

View File

@ -56,6 +56,7 @@ struct ShaderEffectCreator;
class VideoBox;
class ImageBox;
class Document;
class NullObject;
class eMouseEvent;
class eKeyEvent;
@ -568,6 +569,9 @@ public:
SceneBoundGradient * getGradientWithRWId(const int rwId) const;
SceneBoundGradient * getGradientWithDocumentId(const int id) const;
void addNullObject(NullObject* const obj);
void removeNullObject(NullObject* const obj);
private:
void addGradient(const qsptr<SceneBoundGradient>& grad);
@ -589,6 +593,7 @@ private:
TransformMode mTransMode = TransformMode::none;
QList<qsptr<SceneBoundGradient>> mGradients;
QList<NullObject*> mNullObjects;
protected:
Document& mDocument;
bool mDrawnSinceQue = true;

View File

@ -29,6 +29,7 @@
#include "Boxes/containerbox.h"
#include "Boxes/smartvectorpath.h"
#include "Boxes/paintbox.h"
#include "Boxes/nullobject.h"
#include "pointtypemenu.h"
#include "pointhelpers.h"
@ -237,6 +238,13 @@ void Canvas::handleLeftButtonMousePress(const eMouseEvent& e) {
mCurrentCircle = newPath.get();
} else if(mCurrentMode == CanvasMode::nullCreate) {
const auto newPath = enve::make_shared<NullObject>();
newPath->planCenterPivotPosition();
mCurrentContainer->addContained(newPath);
newPath->setAbsolutePos(e.fPos);
clearBoxesSelection();
addBoxToSelection(newPath.get());
} else if(mCurrentMode == CanvasMode::rectCreate) {
const auto newPath = enve::make_shared<RectangleBox>();
newPath->planCenterPivotPosition();

View File

@ -45,6 +45,7 @@ DEFINES += QT_DEPRECATED_WARNINGS
include(core.pri)
SOURCES += \
Boxes/nullobject.cpp \
Expressions/expression.cpp \
Expressions/framebinding.cpp \
Expressions/propertybinding.cpp \
@ -362,6 +363,7 @@ SOURCES += \
zipfilesaver.cpp
HEADERS += \
Boxes/nullobject.h \
Expressions/expression.h \
Expressions/framebinding.h \
Expressions/propertybinding.h \