Merge pull request #713 from jeremybullock/geometric_rotate
Add rotate option to geometry tool by Pojienie (OpenToonz 3846)
This commit is contained in:
commit
b3a3993514
1 changed files with 142 additions and 2 deletions
|
@ -45,6 +45,7 @@ TEnv::DoubleVar GeometricRasterSize("InknpaintGeometricRasterSize", 1);
|
||||||
TEnv::StringVar GeometricType("InknpaintGeometricType", "Rectangle");
|
TEnv::StringVar GeometricType("InknpaintGeometricType", "Rectangle");
|
||||||
TEnv::IntVar GeometricEdgeCount("InknpaintGeometricEdgeCount", 3);
|
TEnv::IntVar GeometricEdgeCount("InknpaintGeometricEdgeCount", 3);
|
||||||
TEnv::IntVar GeometricSelective("InknpaintGeometricSelective", 0);
|
TEnv::IntVar GeometricSelective("InknpaintGeometricSelective", 0);
|
||||||
|
TEnv::IntVar GeometricRotate("InknpaintGeometricRotate", 0);
|
||||||
TEnv::IntVar GeometricGroupIt("InknpaintGeometricGroupIt", 0);
|
TEnv::IntVar GeometricGroupIt("InknpaintGeometricGroupIt", 0);
|
||||||
TEnv::IntVar GeometricAutofill("InknpaintGeometricAutofill", 0);
|
TEnv::IntVar GeometricAutofill("InknpaintGeometricAutofill", 0);
|
||||||
TEnv::IntVar GeometricSmooth("InknpaintGeometricSmooth", 0);
|
TEnv::IntVar GeometricSmooth("InknpaintGeometricSmooth", 0);
|
||||||
|
@ -377,6 +378,7 @@ public:
|
||||||
TDoubleProperty m_hardness;
|
TDoubleProperty m_hardness;
|
||||||
TEnumProperty m_type;
|
TEnumProperty m_type;
|
||||||
TIntProperty m_edgeCount;
|
TIntProperty m_edgeCount;
|
||||||
|
TBoolProperty m_rotate;
|
||||||
TBoolProperty m_autogroup;
|
TBoolProperty m_autogroup;
|
||||||
TBoolProperty m_autofill;
|
TBoolProperty m_autofill;
|
||||||
TBoolProperty m_smooth;
|
TBoolProperty m_smooth;
|
||||||
|
@ -406,6 +408,7 @@ public:
|
||||||
, m_opacity("Opacity:", 0, 100, 100)
|
, m_opacity("Opacity:", 0, 100, 100)
|
||||||
, m_hardness("Hardness:", 0, 100, 100)
|
, m_hardness("Hardness:", 0, 100, 100)
|
||||||
, m_edgeCount("Polygon Sides:", 3, 15, 3)
|
, m_edgeCount("Polygon Sides:", 3, 15, 3)
|
||||||
|
, m_rotate("rotate", false)
|
||||||
, m_autogroup("Auto Group", false)
|
, m_autogroup("Auto Group", false)
|
||||||
, m_autofill("Auto Fill", false)
|
, m_autofill("Auto Fill", false)
|
||||||
, m_smooth("Smooth", false)
|
, m_smooth("Smooth", false)
|
||||||
|
@ -427,6 +430,7 @@ public:
|
||||||
m_prop[0].bind(m_type);
|
m_prop[0].bind(m_type);
|
||||||
|
|
||||||
m_prop[0].bind(m_edgeCount);
|
m_prop[0].bind(m_edgeCount);
|
||||||
|
m_prop[0].bind(m_rotate);
|
||||||
if (targetType & TTool::Vectors) {
|
if (targetType & TTool::Vectors) {
|
||||||
m_prop[0].bind(m_autogroup);
|
m_prop[0].bind(m_autogroup);
|
||||||
m_prop[0].bind(m_autofill);
|
m_prop[0].bind(m_autofill);
|
||||||
|
@ -465,6 +469,7 @@ public:
|
||||||
m_prop[1].bind(m_miterJoinLimit);
|
m_prop[1].bind(m_miterJoinLimit);
|
||||||
|
|
||||||
m_selective.setId("Selective");
|
m_selective.setId("Selective");
|
||||||
|
m_rotate.setId("Rotate");
|
||||||
m_autogroup.setId("AutoGroup");
|
m_autogroup.setId("AutoGroup");
|
||||||
m_autofill.setId("Autofill");
|
m_autofill.setId("Autofill");
|
||||||
m_smooth.setId("Smooth");
|
m_smooth.setId("Smooth");
|
||||||
|
@ -488,6 +493,7 @@ public:
|
||||||
m_opacity.setQStringName(tr("Opacity:"));
|
m_opacity.setQStringName(tr("Opacity:"));
|
||||||
m_hardness.setQStringName(tr("Hardness:"));
|
m_hardness.setQStringName(tr("Hardness:"));
|
||||||
m_edgeCount.setQStringName(tr("Polygon Sides:"));
|
m_edgeCount.setQStringName(tr("Polygon Sides:"));
|
||||||
|
m_rotate.setQStringName(tr("Rotate"));
|
||||||
m_autogroup.setQStringName(tr("Auto Group"));
|
m_autogroup.setQStringName(tr("Auto Group"));
|
||||||
m_autofill.setQStringName(tr("Auto Fill"));
|
m_autofill.setQStringName(tr("Auto Fill"));
|
||||||
m_smooth.setQStringName(tr("Smooth"));
|
m_smooth.setQStringName(tr("Smooth"));
|
||||||
|
@ -981,12 +987,29 @@ protected:
|
||||||
bool m_active;
|
bool m_active;
|
||||||
bool m_firstTime;
|
bool m_firstTime;
|
||||||
|
|
||||||
|
// for both rotation and move
|
||||||
|
bool m_isRotatingOrMoving;
|
||||||
|
bool m_wasCtrlPressed;
|
||||||
|
TStroke *m_rotatedStroke;
|
||||||
|
TPointD m_originalCursorPos;
|
||||||
|
TPointD m_currentCursorPos;
|
||||||
|
TPixel32 m_color;
|
||||||
|
|
||||||
|
// for rotation
|
||||||
|
double m_lastRotateAngle;
|
||||||
|
TPointD m_rotateCenter;
|
||||||
|
|
||||||
|
// for move
|
||||||
|
TPointD m_lastMoveStrokePos;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
GeometricTool(int targetType)
|
GeometricTool(int targetType)
|
||||||
: TTool("T_Geometric")
|
: TTool("T_Geometric")
|
||||||
, m_primitive(0)
|
, m_primitive(0)
|
||||||
, m_param(targetType)
|
, m_param(targetType)
|
||||||
, m_active(false)
|
, m_active(false)
|
||||||
|
, m_isRotatingOrMoving(false)
|
||||||
|
, m_rotatedStroke(0)
|
||||||
, m_firstTime(true) {
|
, m_firstTime(true) {
|
||||||
bind(targetType);
|
bind(targetType);
|
||||||
if ((targetType & TTool::RasterImage) || (targetType & TTool::ToonzImage)) {
|
if ((targetType & TTool::RasterImage) || (targetType & TTool::ToonzImage)) {
|
||||||
|
@ -1013,6 +1036,7 @@ public:
|
||||||
}
|
}
|
||||||
|
|
||||||
~GeometricTool() {
|
~GeometricTool() {
|
||||||
|
delete m_rotatedStroke;
|
||||||
std::map<std::wstring, Primitive *>::iterator it;
|
std::map<std::wstring, Primitive *>::iterator it;
|
||||||
for (it = m_primitiveTable.begin(); it != m_primitiveTable.end(); ++it)
|
for (it = m_primitiveTable.begin(); it != m_primitiveTable.end(); ++it)
|
||||||
delete it->second;
|
delete it->second;
|
||||||
|
@ -1061,6 +1085,10 @@ public:
|
||||||
getViewer()->doPickGuideStroke(p);
|
getViewer()->doPickGuideStroke(p);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
if (m_isRotatingOrMoving) {
|
||||||
|
addStroke();
|
||||||
|
return;
|
||||||
|
}
|
||||||
if (m_primitive) m_primitive->leftButtonDown(p, e);
|
if (m_primitive) m_primitive->leftButtonDown(p, e);
|
||||||
invalidate();
|
invalidate();
|
||||||
}
|
}
|
||||||
|
@ -1086,6 +1114,9 @@ public:
|
||||||
|
|
||||||
void onImageChanged() override {
|
void onImageChanged() override {
|
||||||
if (m_primitive) m_primitive->onImageChanged();
|
if (m_primitive) m_primitive->onImageChanged();
|
||||||
|
m_isRotatingOrMoving = false;
|
||||||
|
delete m_rotatedStroke;
|
||||||
|
m_rotatedStroke = 0;
|
||||||
invalidate();
|
invalidate();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1095,6 +1126,64 @@ public:
|
||||||
}
|
}
|
||||||
|
|
||||||
void mouseMove(const TPointD &p, const TMouseEvent &e) override {
|
void mouseMove(const TPointD &p, const TMouseEvent &e) override {
|
||||||
|
m_currentCursorPos = p;
|
||||||
|
if (m_isRotatingOrMoving) {
|
||||||
|
// move
|
||||||
|
if (e.isCtrlPressed()) {
|
||||||
|
// if ctrl wasn't pressed, it means the user has switched from
|
||||||
|
// rotation to move. Thus, re-initiate move-relevant variables
|
||||||
|
if (!m_wasCtrlPressed) {
|
||||||
|
m_wasCtrlPressed = true;
|
||||||
|
|
||||||
|
m_originalCursorPos = m_currentCursorPos;
|
||||||
|
m_lastMoveStrokePos = TPointD(0, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
// move the stroke to the original location
|
||||||
|
double x = -m_lastMoveStrokePos.x;
|
||||||
|
double y = -m_lastMoveStrokePos.y;
|
||||||
|
m_rotatedStroke->transform(TTranslation(x, y));
|
||||||
|
|
||||||
|
// move the stroke according to current mouse position
|
||||||
|
double dx = m_currentCursorPos.x - m_originalCursorPos.x;
|
||||||
|
double dy = m_currentCursorPos.y - m_originalCursorPos.y;
|
||||||
|
m_lastMoveStrokePos = TPointD(dx, dy);
|
||||||
|
m_rotatedStroke->transform(TTranslation(dx, dy));
|
||||||
|
invalidate();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// if ctrl was pressed, it means the user has switched from
|
||||||
|
// move to rotation. Thus, re-initiate rotation-relevant variables
|
||||||
|
if (m_wasCtrlPressed) {
|
||||||
|
m_wasCtrlPressed = false;
|
||||||
|
|
||||||
|
m_lastRotateAngle = 0;
|
||||||
|
m_originalCursorPos = m_currentCursorPos;
|
||||||
|
TRectD bbox = m_rotatedStroke->getBBox();
|
||||||
|
m_rotateCenter = 0.5 * (bbox.getP11() + bbox.getP00());
|
||||||
|
}
|
||||||
|
|
||||||
|
// rotate
|
||||||
|
// first, rotate the stroke back to original
|
||||||
|
m_rotatedStroke->transform(TRotation(m_rotateCenter, -m_lastRotateAngle));
|
||||||
|
|
||||||
|
// then, rotate it according to mouse position
|
||||||
|
// this formula is from: https://stackoverflow.com/a/31334882
|
||||||
|
TPointD center = m_rotateCenter;
|
||||||
|
TPointD org = m_originalCursorPos;
|
||||||
|
TPointD cur = m_currentCursorPos;
|
||||||
|
double angle1 = atan2(cur.y - center.y, cur.x - center.x);
|
||||||
|
double angle2 = atan2(org.y - center.y, org.x - center.x);
|
||||||
|
double angle = (angle1 - angle2) * 180 / 3.14;
|
||||||
|
if (e.isShiftPressed()) {
|
||||||
|
angle = ((int)angle / 45) * 45;
|
||||||
|
}
|
||||||
|
m_rotatedStroke->transform(TRotation(m_rotateCenter, angle));
|
||||||
|
m_lastRotateAngle = angle;
|
||||||
|
invalidate();
|
||||||
|
return;
|
||||||
|
}
|
||||||
if (m_primitive) m_primitive->mouseMove(p, e);
|
if (m_primitive) m_primitive->mouseMove(p, e);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1105,6 +1194,7 @@ public:
|
||||||
m_param.m_opacity.setValue(GeometricOpacity);
|
m_param.m_opacity.setValue(GeometricOpacity);
|
||||||
m_param.m_hardness.setValue(GeometricBrushHardness);
|
m_param.m_hardness.setValue(GeometricBrushHardness);
|
||||||
m_param.m_selective.setValue(GeometricSelective ? 1 : 0);
|
m_param.m_selective.setValue(GeometricSelective ? 1 : 0);
|
||||||
|
m_param.m_rotate.setValue(GeometricRotate ? 1 : 0);
|
||||||
m_param.m_autogroup.setValue(GeometricGroupIt ? 1 : 0);
|
m_param.m_autogroup.setValue(GeometricGroupIt ? 1 : 0);
|
||||||
m_param.m_smooth.setValue(GeometricSmooth ? 1 : 0);
|
m_param.m_smooth.setValue(GeometricSmooth ? 1 : 0);
|
||||||
m_param.m_autofill.setValue(GeometricAutofill ? 1 : 0);
|
m_param.m_autofill.setValue(GeometricAutofill ? 1 : 0);
|
||||||
|
@ -1147,6 +1237,11 @@ public:
|
||||||
}
|
}
|
||||||
|
|
||||||
void onDeactivate() override {
|
void onDeactivate() override {
|
||||||
|
if (m_isRotatingOrMoving) {
|
||||||
|
tglColor(m_color);
|
||||||
|
drawStrokeCenterline(*m_rotatedStroke, sqrt(tglGetPixelSize2()));
|
||||||
|
return;
|
||||||
|
}
|
||||||
if (m_primitive) m_primitive->onDeactivate();
|
if (m_primitive) m_primitive->onDeactivate();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1156,6 +1251,11 @@ public:
|
||||||
}
|
}
|
||||||
|
|
||||||
void draw() override {
|
void draw() override {
|
||||||
|
if (m_isRotatingOrMoving) {
|
||||||
|
tglColor(m_color);
|
||||||
|
drawStrokeCenterline(*m_rotatedStroke, sqrt(tglGetPixelSize2()));
|
||||||
|
return;
|
||||||
|
}
|
||||||
if (m_primitive) m_primitive->draw();
|
if (m_primitive) m_primitive->draw();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1192,6 +1292,8 @@ public:
|
||||||
}
|
}
|
||||||
} else if (propertyName == m_param.m_edgeCount.getName())
|
} else if (propertyName == m_param.m_edgeCount.getName())
|
||||||
GeometricEdgeCount = m_param.m_edgeCount.getValue();
|
GeometricEdgeCount = m_param.m_edgeCount.getValue();
|
||||||
|
else if (propertyName == m_param.m_rotate.getName())
|
||||||
|
GeometricRotate = m_param.m_rotate.getValue();
|
||||||
else if (propertyName == m_param.m_autogroup.getName()) {
|
else if (propertyName == m_param.m_autogroup.getName()) {
|
||||||
if (!m_param.m_autogroup.getValue()) {
|
if (!m_param.m_autogroup.getValue()) {
|
||||||
m_param.m_autofill.setValue(false);
|
m_param.m_autofill.setValue(false);
|
||||||
|
@ -1254,9 +1356,47 @@ public:
|
||||||
|
|
||||||
void addStroke() {
|
void addStroke() {
|
||||||
if (!m_primitive) return;
|
if (!m_primitive) return;
|
||||||
TStroke *stroke = m_primitive->makeStroke();
|
//TStroke *stroke = m_primitive->makeStroke();
|
||||||
|
//if (!stroke) return;
|
||||||
|
|
||||||
|
TStroke *stroke = 0;
|
||||||
|
if (!m_isRotatingOrMoving) {
|
||||||
|
stroke = m_primitive->makeStroke();
|
||||||
if (!stroke) return;
|
if (!stroke) return;
|
||||||
|
|
||||||
|
if (m_param.m_rotate.getValue()) {
|
||||||
|
m_isRotatingOrMoving = true;
|
||||||
|
m_rotatedStroke = stroke;
|
||||||
|
TRectD bbox = stroke->getBBox();
|
||||||
|
m_rotateCenter = 0.5 * (bbox.getP11() + bbox.getP00());
|
||||||
|
m_originalCursorPos = m_currentCursorPos;
|
||||||
|
m_lastRotateAngle = 0;
|
||||||
|
m_lastMoveStrokePos = TPointD(0, 0);
|
||||||
|
m_wasCtrlPressed = false;
|
||||||
|
|
||||||
|
const TTool::Application *app = TTool::getApplication();
|
||||||
|
if (!app) {
|
||||||
|
m_color = TPixel32::Red;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const TColorStyle *style = app->getCurrentLevelStyle();
|
||||||
|
if (!style) {
|
||||||
|
m_color = TPixel32::Red;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
m_color = style->getAverageColor();
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
stroke = m_rotatedStroke;
|
||||||
|
m_isRotatingOrMoving = false;
|
||||||
|
m_rotatedStroke = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
TStroke::OutlineOptions &options = stroke->outlineOptions();
|
TStroke::OutlineOptions &options = stroke->outlineOptions();
|
||||||
options.m_capStyle = m_param.m_capStyle.getIndex();
|
options.m_capStyle = m_param.m_capStyle.getIndex();
|
||||||
options.m_joinStyle = m_param.m_joinStyle.getIndex();
|
options.m_joinStyle = m_param.m_joinStyle.getIndex();
|
||||||
|
|
Loading…
Reference in a new issue