Allow drawing behind vector lines on brush and geometry tools. (#188)

* Allow drawing behind vector lines with Alt + Shift

* Add caps control for windows.

* Add geometry tool too.

* Remove lost comment
This commit is contained in:
Jeremy Bullock 2020-09-22 21:04:03 -06:00 committed by GitHub
parent d8756f4eae
commit 6356c6f7c3
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
9 changed files with 149 additions and 94 deletions

View file

@ -147,7 +147,8 @@ int TVectorImage::addStrokeToGroup(TStroke *stroke, int strokeIndex) {
//-----------------------------------------------------------------------------
int TVectorImage::addStroke(TStroke *stroke, bool discardPoints) {
int TVectorImage::addStroke(TStroke *stroke, bool discardPoints,
bool sendToBack) {
if (discardPoints) {
TRectD bBox = stroke->getBBox();
if (bBox.x0 == bBox.x1 && bBox.y0 == bBox.y1) // empty stroke: discard
@ -158,9 +159,20 @@ int TVectorImage::addStroke(TStroke *stroke, bool discardPoints) {
int i;
for (i = m_imp->m_strokes.size() - 1; i >= 0; i--)
if (m_imp->m_insideGroup.isParentOf(m_imp->m_strokes[i]->m_groupId)) {
m_imp->insertStrokeAt(
new VIStroke(stroke, m_imp->m_strokes[i]->m_groupId), i + 1);
return i + 1;
if (sendToBack) {
int k = i;
while (
m_imp->m_insideGroup.isParentOf(m_imp->m_strokes[k]->m_groupId)) {
k--;
}
m_imp->insertStrokeAt(
new VIStroke(stroke, m_imp->m_strokes[i]->m_groupId), k + 1);
return k + 1;
} else {
m_imp->insertStrokeAt(
new VIStroke(stroke, m_imp->m_strokes[i]->m_groupId), i + 1);
return i + 1;
}
}
}
@ -171,9 +183,15 @@ int TVectorImage::addStroke(TStroke *stroke, bool discardPoints) {
else
gid = m_imp->m_strokes.back()->m_groupId;
m_imp->m_strokes.push_back(new VIStroke(stroke, gid));
if (!sendToBack)
m_imp->m_strokes.push_back(new VIStroke(stroke, gid));
else
m_imp->insertStrokeAt(new VIStroke(stroke, gid), 0);
m_imp->m_areValidRegions = false;
return m_imp->m_strokes.size() - 1;
if (sendToBack)
return 0;
else
return m_imp->m_strokes.size() - 1;
}
//-----------------------------------------------------------------------------
@ -574,13 +592,13 @@ TRectD TVectorImage::getBBox() const {
for (UINT i = 0; i < strokeCount; ++i) {
TRectD r = m_imp->m_strokes[i]->m_s->getBBox();
TColorStyle *style = 0;
if (plt) style = plt->getStyle(m_imp->m_strokes[i]->m_s->getStyle());
if (plt) style = plt->getStyle(m_imp->m_strokes[i]->m_s->getStyle());
if (dynamic_cast<TRasterImagePatternStrokeStyle *>(style) ||
dynamic_cast<TVectorImagePatternStrokeStyle *>(
style)) // con i pattern style, il render a volte taglia sulla bbox
// dello stroke....
// aumento la bbox della meta' delle sue dimensioni:pezzaccia.
r = r.enlarge(std::max(r.getLx() * 0.25, r.getLy() * 0.25));
r = r.enlarge(std::max(r.getLx() * 0.25, r.getLy() * 0.25));
bbox = ((i == 0) ? r : bbox + r);
}
@ -681,7 +699,7 @@ int TVectorImage::fillStrokes(const TPointD &p, int styleId) {
double outW, dist2;
if (getNearestStroke(p, outW, index, dist2, true)) {
double thick = getStroke(index)->getThickPoint(outW).thick * 1.25;
double thick = getStroke(index)->getThickPoint(outW).thick * 1.25;
if (thick < 0.5) thick = 0.5;
if (dist2 > thick * thick) return -1;
@ -1035,7 +1053,8 @@ bool TVectorImage::Imp::areWholeGroups(const std::vector<int> &indexes) const {
for (j = 0; j < m_strokes.size(); j++) {
int ret = areDifferentGroup(indexes[i], false, j, false);
if (ret == -1 ||
(ret >= 1 && find(indexes.begin(), indexes.end(), j) == indexes.end()))
(ret >= 1 &&
find(indexes.begin(), indexes.end(), j) == indexes.end()))
return false;
}
}
@ -1082,7 +1101,7 @@ void TVectorImage::Imp::notifyChangedStrokes(
std::list<TEdge *>::iterator it = s->m_edgeList.begin();
for (; it != s->m_edgeList.end(); it++) {
TEdge *e = new TEdge(**it, false);
TEdge *e = new TEdge(**it, false);
if (!oldStrokeArray.empty()) e->m_s = oldStrokeArray[i];
oldEdgeListArray[i].push_back(e); // bisogna allocare nuovo edge,
// perche'la eraseIntersection poi lo
@ -1282,7 +1301,7 @@ void TVectorImage::mergeImage(const TVectorImageP &img, const TAffine &affine,
} else {
img->m_imp->m_strokes[i]->m_groupId =
TGroupId(groupId, img->m_imp->m_strokes[i]->m_groupId);
}
}
}
}
}
@ -2070,7 +2089,7 @@ VIStroke *TVectorImage::Imp::extendStrokeSmoothly(int index,
}
std::vector<TThickPoint> points(cpCount);
for (int i = 0; i < cpCount - 1; i++)
for (int i = 0; i < cpCount - 1; i++)
points[i] = stroke->getControlPoint((cpIndex == 0) ? cpCount - i - 1 : i);
points[cpCount - 1] = pos;
@ -2227,12 +2246,12 @@ VIStroke *TVectorImage::Imp::joinStrokeSmoothly(int index1, int index2,
double len1 = q1->getLength();
assert(len1 >= 0);
if (len1 <= 0) len1 = 0;
double w1 = exp(-len1 * 0.01);
double w1 = exp(-len1 * 0.01);
double len2 = q2->getLength();
assert(len2 >= 0);
if (len2 <= 0) len2 = 0;
double w2 = exp(-len2 * 0.01);
double w2 = exp(-len2 * 0.01);
TThickPoint extreme1 = cpIndex1 == 0 ? q1->getThickP0() : q1->getThickP2();
TThickPoint extreme2 = cpIndex2 == 0 ? q2->getThickP0() : q2->getThickP2();
@ -2820,25 +2839,25 @@ bool TVectorImage::Imp::canMoveStrokes(int strokeIndex, int count,
std::vector<TGroupId> groupsAfterMoving(m_strokes.size());
if (strokeIndex < moveBefore) {
for (i = 0; i < strokeIndex; i++)
for (i = 0; i < strokeIndex; i++)
groupsAfterMoving[j++] = m_strokes[i]->m_groupId;
for (i = strokeIndex + count; i < moveBefore; i++)
for (i = strokeIndex + count; i < moveBefore; i++)
groupsAfterMoving[j++] = m_strokes[i]->m_groupId;
for (i = strokeIndex; i < strokeIndex + count; i++)
for (i = strokeIndex; i < strokeIndex + count; i++)
groupsAfterMoving[j++] = m_strokes[i]->m_groupId;
for (i = moveBefore; i < (int)m_strokes.size(); i++)
for (i = moveBefore; i < (int)m_strokes.size(); i++)
groupsAfterMoving[j++] = m_strokes[i]->m_groupId;
} else {
for (i = 0; i < moveBefore; i++)
for (i = 0; i < moveBefore; i++)
groupsAfterMoving[j++] = m_strokes[i]->m_groupId;
for (i = strokeIndex; i < strokeIndex + count; i++)
for (i = strokeIndex; i < strokeIndex + count; i++)
groupsAfterMoving[j++] = m_strokes[i]->m_groupId;
for (i = moveBefore; i < strokeIndex; i++)
for (i = moveBefore; i < strokeIndex; i++)
groupsAfterMoving[j++] = m_strokes[i]->m_groupId;
for (i = strokeIndex + count; i < (int)m_strokes.size(); i++)
@ -2908,7 +2927,7 @@ void TVectorImage::Imp::regroupGhosts(std::vector<int> &changedStrokes) {
((currGroupId.isGrouped(false) != 0 &&
m_strokes[i]->m_groupId == currGroupId) ||
(currGroupId.isGrouped(true) != 0 &&
m_strokes[i]->m_groupId.isGrouped(true) != 0))) {
m_strokes[i]->m_groupId.isGrouped(true) != 0))) {
if (m_strokes[i]->m_groupId != currGroupId) {
m_strokes[i]->m_groupId = currGroupId;
changedStrokes.push_back(i);

View file

@ -410,7 +410,7 @@ class GeometricToolOptionsBox final : public ToolOptionsBox {
ToolOptionCombo *m_shapeField;
ToolOptionCheckbox *m_pencilMode;
ToolOptionIntSlider *m_miterField;
ToolOptionCheckbox *m_snapCheckbox, *m_smoothCheckbox;
ToolOptionCheckbox *m_snapCheckbox, *m_smoothCheckbox, *m_drawOrderCheckbox;
ToolOptionCombo *m_snapSensitivityCombo;
TTool *m_tool;
@ -530,7 +530,7 @@ class BrushToolOptionsBox final : public ToolOptionsBox {
ToolOptionPopupButton *m_joinStyleCombo;
ToolOptionIntSlider *m_miterField;
ToolOptionCombo *m_presetCombo;
ToolOptionCheckbox *m_snapCheckbox;
ToolOptionCheckbox *m_snapCheckbox, *m_drawOrderCheckbox;
ToolOptionCombo *m_snapSensitivityCombo;
QPushButton *m_addPresetButton;
QPushButton *m_removePresetButton;

View file

@ -301,12 +301,13 @@ class UndoPencil final : public TToolUndo {
bool m_autogroup;
bool m_autofill;
bool m_sendToBack;
public:
UndoPencil(TStroke *stroke, std::vector<TFilledRegionInf> *fillInformation,
TXshSimpleLevel *level, const TFrameId &frameId,
bool m_createdFrame, bool m_createdLevel, bool autogroup = false,
bool autofill = false);
bool autofill = false, bool sendToBack = false);
~UndoPencil();
void undo() const override;
void redo() const override;

View file

@ -225,7 +225,7 @@ get the stroke nearest at point
//! Add a stroke at the end of the vector; returns position of the stroke (or
//! -1 if not added)
int addStroke(TStroke *, bool discardPoints = true);
int addStroke(TStroke *, bool discardPoints = true, bool sendToBack = false);
int addStrokeToGroup(TStroke *stroke, int strokeIndex);
//! Replace the stroke at index \b index with \b newStroke
void replaceStroke(int index, TStroke *newStroke);

View file

@ -56,6 +56,7 @@ TEnv::IntVar GeometricJoinStyle("InknpaintGeometricJoinStyle", 0);
TEnv::IntVar GeometricMiterValue("InknpaintGeometricMiterValue", 4);
TEnv::IntVar GeometricSnap("InknpaintGeometricSnap", 0);
TEnv::IntVar GeometricSnapSensitivity("InknpaintGeometricSnapSensitivity", 0);
TEnv::IntVar GeometricDrawBehind("InknpaintGeometricDrawBehind", 0);
//-------------------------------------------------------------------
@ -82,9 +83,9 @@ static TPointD rectify(const TPointD &oldPos, const TPointD &pos) {
const TPointD directions[] = {TPointD(1, 0), TPointD(h, h), TPointD(0, 1),
TPointD(-h, h), TPointD(-1, 0), TPointD(-h, -h),
TPointD(0, -1), TPointD(h, -h)};
TPointD v = pos - oldPos;
int j = 0;
double bestValue = v * directions[j];
TPointD v = pos - oldPos;
int j = 0;
double bestValue = v * directions[j];
for (int k = 1; k < 8; k++) {
double value = v * directions[k];
if (value > bestValue) {
@ -386,6 +387,7 @@ public:
TIntProperty m_miterJoinLimit;
TBoolProperty m_snap;
TEnumProperty m_snapSensitivity;
TBoolProperty m_sendToBack;
TPropertyGroup m_prop[2];
int m_targetType;
@ -414,6 +416,7 @@ public:
, m_miterJoinLimit("Miter:", 0, 100, 4)
, m_snap("Snap", false)
, m_snapSensitivity("Sensitivity:")
, m_sendToBack("Draw Under", 0)
, m_targetType(targetType) {
if (targetType & TTool::Vectors) m_prop[0].bind(m_toolSize);
if (targetType & TTool::ToonzImage || targetType & TTool::RasterImage) {
@ -427,6 +430,8 @@ public:
if (targetType & TTool::Vectors) {
m_prop[0].bind(m_autogroup);
m_prop[0].bind(m_autofill);
m_prop[0].bind(m_sendToBack);
m_sendToBack.setId("DrawUnder");
m_prop[0].bind(m_snap);
m_snap.setId("Snap");
m_prop[0].bind(m_snapSensitivity);
@ -506,6 +511,7 @@ public:
m_snapSensitivity.setItemUIName(LOW_WSTR, tr("Low"));
m_snapSensitivity.setItemUIName(MEDIUM_WSTR, tr("Med"));
m_snapSensitivity.setItemUIName(HIGH_WSTR, tr("High"));
m_sendToBack.setQStringName(tr("Draw Under"));
}
}
};
@ -598,7 +604,7 @@ TPointD Primitive::calculateSnap(TPointD pos) {
else if (areAlmostEqual(outW, 1.0, 1e-3))
m_param->m_w1 = 1.0;
else
m_param->m_w1 = outW;
m_param->m_w1 = outW;
TThickPoint point1 = stroke->getPoint(m_param->m_w1);
snapPoint = TPointD(point1.x, point1.y);
m_param->m_foundSnap = true;
@ -1094,6 +1100,7 @@ public:
m_param.m_miterJoinLimit.setValue(GeometricMiterValue);
m_firstTime = false;
m_param.m_snap.setValue(GeometricSnap);
m_param.m_sendToBack.setValue(GeometricDrawBehind);
if (m_targetType & TTool::Vectors) {
m_param.m_snapSensitivity.setIndex(GeometricSnapSensitivity);
switch (GeometricSnapSensitivity) {
@ -1202,6 +1209,8 @@ public:
GeometricJoinStyle = m_param.m_joinStyle.getIndex();
else if (propertyName == m_param.m_miterJoinLimit.getName())
GeometricMiterValue = m_param.m_miterJoinLimit.getValue();
else if (propertyName == m_param.m_sendToBack.getName())
GeometricDrawBehind = m_param.m_sendToBack.getValue();
else if (propertyName == m_param.m_snap.getName())
GeometricSnap = m_param.m_snap.getValue();
else if (propertyName == m_param.m_snapSensitivity.getName()) {
@ -1292,12 +1301,12 @@ public:
ImageUtils::getFillingInformationOverlappingArea(vi, *fillInformation,
stroke->getBBox());
vi->addStroke(stroke);
vi->addStroke(stroke, true, m_param.m_sendToBack.getValue() > 0);
TUndoManager::manager()->add(new UndoPencil(
vi->getStroke(vi->getStrokeCount() - 1), fillInformation, sl, id,
m_isFrameCreated, m_isLevelCreated, m_param.m_autogroup.getValue(),
m_param.m_autofill.getValue()));
stroke, fillInformation, sl, id, m_isFrameCreated, m_isLevelCreated,
m_param.m_autogroup.getValue(), m_param.m_autofill.getValue(),
m_param.m_sendToBack.getValue() > 0));
if ((Preferences::instance()->getGuidedDrawingType() == 1 ||
Preferences::instance()->getGuidedDrawingType() == 2) &&
@ -1309,7 +1318,8 @@ public:
vbTool->setViewer(m_viewer);
vbTool->doGuidedAutoInbetween(id, vi, stroke, false,
m_param.m_autogroup.getValue(),
m_param.m_autofill.getValue(), false);
m_param.m_autofill.getValue(), false,
m_param.m_sendToBack.getValue() > 0);
}
}
}
@ -1494,7 +1504,7 @@ void RectanglePrimitive::leftButtonDown(const TPointD &pos,
else
m_startPoint = TPointD((int)pos.x + 0.5, (int)pos.y + 0.5);
} else
m_startPoint = newPos;
m_startPoint = newPos;
m_selectingRect.x0 = m_startPoint.x;
m_selectingRect.y0 = m_startPoint.y;
m_selectingRect.x1 = m_startPoint.x;
@ -1631,7 +1641,7 @@ void RectanglePrimitive::onEnter() {
m_color = TPixel32::Red;
else {
const TColorStyle *style = app->getCurrentLevelStyle();
if (style) m_color = style->getAverageColor();
if (style) m_color = style->getAverageColor();
}
}
@ -1719,7 +1729,7 @@ void CirclePrimitive::onEnter() {
m_color = TPixel32::Red;
else {
const TColorStyle *style = app->getCurrentLevelStyle();
if (style) m_color = style->getAverageColor();
if (style) m_color = style->getAverageColor();
}
}
@ -1902,7 +1912,7 @@ void MultiLinePrimitive::leftButtonDown(const TPointD &pos,
newPos = getSnap(pos);
// Se clicco nell'ultimo vertice chiudo la linea.
TPointD _pos = pos;
TPointD _pos = pos;
if (m_closed) _pos = m_vertex.front();
if (e.isShiftPressed() && !m_vertex.empty())
@ -1920,8 +1930,9 @@ void MultiLinePrimitive::leftButtonDown(const TPointD &pos,
void MultiLinePrimitive::leftButtonDrag(const TPointD &pos,
const TMouseEvent &e) {
if (m_vertex.size() == 0 || m_isSingleLine) return;
if (m_speedMoved || tdistance2(m_vertex[m_vertex.size() - 1], pos) >
sq(7.0 * m_tool->getPixelSize())) {
if (m_speedMoved ||
tdistance2(m_vertex[m_vertex.size() - 1], pos) >
sq(7.0 * m_tool->getPixelSize())) {
moveSpeed(m_mousePosition - pos);
m_speedMoved = true;
m_undo->setNewVertex(m_vertex);
@ -2066,7 +2077,7 @@ void MultiLinePrimitive::onEnter() {
m_color = TPixel32::Red;
else {
const TColorStyle *style = app->getCurrentLevelStyle();
if (style) m_color = style->getAverageColor();
if (style) m_color = style->getAverageColor();
}
}
@ -2267,9 +2278,8 @@ TStroke *EllipsePrimitive::makeStroke() const {
return 0;
return makeEllipticStroke(
getThickness(),
TPointD(0.5 * (m_selectingRect.x0 + m_selectingRect.x1),
0.5 * (m_selectingRect.y0 + m_selectingRect.y1)),
getThickness(), TPointD(0.5 * (m_selectingRect.x0 + m_selectingRect.x1),
0.5 * (m_selectingRect.y0 + m_selectingRect.y1)),
fabs(0.5 * (m_selectingRect.x1 - m_selectingRect.x0)),
fabs(0.5 * (m_selectingRect.y1 - m_selectingRect.y0)));
}
@ -2301,7 +2311,7 @@ void EllipsePrimitive::onEnter() {
m_color = TPixel32::Red;
} else {
const TColorStyle *style = app->getCurrentLevelStyle();
if (style) m_color = style->getAverageColor();
if (style) m_color = style->getAverageColor();
}
}
@ -2567,7 +2577,7 @@ void MultiArcPrimitive::onEnter() {
m_color = TPixel32::Red;
else {
const TColorStyle *style = app->getCurrentLevelStyle();
if (style) m_color = style->getAverageColor();
if (style) m_color = style->getAverageColor();
}
}

View file

@ -536,7 +536,7 @@ ArrowToolOptionsBox::ArrowToolOptionsBox(
m_nsPosField =
new PegbarChannelField(m_tool, TStageObject::T_Y, "field", frameHandle,
objHandle, xshHandle, this);
m_zField = new PegbarChannelField(m_tool, TStageObject::T_Z, "field",
m_zField = new PegbarChannelField(m_tool, TStageObject::T_Z, "field",
frameHandle, objHandle, xshHandle, this);
m_noScaleZField = new NoScaleField(m_tool, "field");
@ -665,7 +665,7 @@ ArrowToolOptionsBox::ArrowToolOptionsBox(
m_zField->setPrecision(4);
m_noScaleZField->setPrecision(4);
bool splined = isCurrentObjectSplined();
bool splined = isCurrentObjectSplined();
if (splined != m_splined) m_splined = splined;
setSplined(m_splined);
@ -1298,7 +1298,7 @@ SelectionToolOptionsBox::SelectionToolOptionsBox(QWidget *parent, TTool *tool,
// assert(ret);
bool ret = connect(m_scaleXField, SIGNAL(valueChange(bool)),
SLOT(onScaleXValueChanged(bool)));
ret = ret && connect(m_scaleYField, SIGNAL(valueChange(bool)),
ret = ret && connect(m_scaleYField, SIGNAL(valueChange(bool)),
SLOT(onScaleYValueChanged(bool)));
if (m_setSaveboxCheckbox)
ret = ret && connect(m_setSaveboxCheckbox, SIGNAL(toggled(bool)),
@ -1493,6 +1493,8 @@ GeometricToolOptionsBox::GeometricToolOptionsBox(QWidget *parent, TTool *tool,
}
if (tool->getTargetType() & TTool::Vectors) {
m_drawOrderCheckbox =
dynamic_cast<ToolOptionCheckbox *>(m_controls.value("Draw Under"));
m_snapCheckbox =
dynamic_cast<ToolOptionCheckbox *>(m_controls.value("Snap"));
m_snapSensitivityCombo =
@ -1719,11 +1721,11 @@ FillToolOptionsBox::FillToolOptionsBox(QWidget *parent, TTool *tool,
bool ret = connect(m_colorMode, SIGNAL(currentIndexChanged(int)), this,
SLOT(onColorModeChanged(int)));
ret = ret && connect(m_toolType, SIGNAL(currentIndexChanged(int)), this,
ret = ret && connect(m_toolType, SIGNAL(currentIndexChanged(int)), this,
SLOT(onToolTypeChanged(int)));
ret = ret && connect(m_onionMode, SIGNAL(toggled(bool)), this,
ret = ret && connect(m_onionMode, SIGNAL(toggled(bool)), this,
SLOT(onOnionModeToggled(bool)));
ret = ret && connect(m_multiFrameMode, SIGNAL(toggled(bool)), this,
ret = ret && connect(m_multiFrameMode, SIGNAL(toggled(bool)), this,
SLOT(onMultiFrameModeToggled(bool)));
assert(ret);
if (m_colorMode->getProperty()->getValue() == L"Lines") {
@ -1844,6 +1846,7 @@ BrushToolOptionsBox::BrushToolOptionsBox(QWidget *parent, TTool *tool,
, m_joinStyleCombo(0)
, m_snapCheckbox(0)
, m_snapSensitivityCombo(0)
, m_drawOrderCheckbox(0)
, m_miterField(0) {
TPropertyGroup *props = tool->getProperties(0);
assert(props->getPropertyCount() > 0);
@ -1889,6 +1892,8 @@ BrushToolOptionsBox::BrushToolOptionsBox(QWidget *parent, TTool *tool,
addSeparator();
if (tool && tool->getProperties(1)) tool->getProperties(1)->accept(builder);
m_drawOrderCheckbox =
dynamic_cast<ToolOptionCheckbox *>(m_controls.value("Draw Under"));
m_snapCheckbox =
dynamic_cast<ToolOptionCheckbox *>(m_controls.value("Snap"));
m_snapSensitivityCombo =
@ -2322,9 +2327,9 @@ TapeToolOptionsBox::TapeToolOptionsBox(QWidget *parent, TTool *tool,
bool ret = connect(m_typeMode, SIGNAL(currentIndexChanged(int)), this,
SLOT(onToolTypeChanged(int)));
ret = ret && connect(m_toolMode, SIGNAL(currentIndexChanged(int)), this,
ret = ret && connect(m_toolMode, SIGNAL(currentIndexChanged(int)), this,
SLOT(onToolModeChanged(int)));
ret = ret && connect(m_joinStrokesMode, SIGNAL(toggled(bool)), this,
ret = ret && connect(m_joinStrokesMode, SIGNAL(toggled(bool)), this,
SLOT(onJoinStrokesModeChanged()));
assert(ret);
}
@ -2411,11 +2416,10 @@ protected:
p.setPen(Qt::black);
p.setBrush(Qt::NoBrush);
p.drawText(rect(), Qt::AlignCenter,
QString("R:%1 G:%2 B:%3")
.arg(m_color.red())
.arg(m_color.green())
.arg(m_color.blue()));
p.drawText(rect(), Qt::AlignCenter, QString("R:%1 G:%2 B:%3")
.arg(m_color.red())
.arg(m_color.green())
.arg(m_color.blue()));
}
};
@ -2847,7 +2851,7 @@ void ToolOptions::onToolSwitched() {
TTool *tool = app->getCurrentTool()->getTool();
if (tool) {
// c'e' un tool corrente
ToolOptionsBox *panel = 0;
ToolOptionsBox *panel = 0;
std::map<TTool *, ToolOptionsBox *>::iterator it = m_panels.find(tool);
if (it == m_panels.end()) {
// ... senza panel associato

View file

@ -1035,12 +1035,13 @@ int ToolUtils::UndoModifyListStroke::getSize() const {
ToolUtils::UndoPencil::UndoPencil(
TStroke *stroke, std::vector<TFilledRegionInf> *fillInformation,
TXshSimpleLevel *level, const TFrameId &frameId, bool createdFrame,
bool createdLevel, bool autogroup, bool autofill)
bool createdLevel, bool autogroup, bool autofill, bool sendToBack)
: TToolUndo(level, frameId, createdFrame, createdLevel, 0)
, m_strokeId(stroke->getId())
, m_fillInformation(fillInformation)
, m_autogroup(autogroup)
, m_autofill(autofill) {
, m_autofill(autofill)
, m_sendToBack(sendToBack) {
m_stroke = new TStroke(*stroke);
}
@ -1113,7 +1114,7 @@ void ToolUtils::UndoPencil::redo() const {
QMutexLocker sl(image->getMutex());
TStroke *stroke = new TStroke(*m_stroke);
stroke->setId(m_strokeId);
image->addStroke(stroke);
image->addStroke(stroke, true, m_sendToBack);
if (image->isComputedRegionAlmostOnce()) image->findRegions();
if (m_autogroup && stroke->isSelfLoop()) {

View file

@ -41,6 +41,10 @@
#include "tgl.h"
#include "trop.h"
#ifdef Q_OS_WIN
#include <WinUser.h> // for Sleep
#endif
// Qt includes
#include <QPainter>
@ -58,6 +62,7 @@ TEnv::IntVar V_BrushPressureSensitivity("InknpaintBrushPressureSensitivity", 1);
TEnv::IntVar V_VectorBrushFrameRange("VectorBrushFrameRange", 0);
TEnv::IntVar V_VectorBrushSnap("VectorBrushSnap", 0);
TEnv::IntVar V_VectorBrushSnapSensitivity("VectorBrushSnapSensitivity", 0);
TEnv::IntVar V_VectorBrushDrawBehind("VectorBrushDrawBehind", 0);
TEnv::StringVar V_VectorBrushPreset("VectorBrushPreset", "<custom>");
//-------------------------------------------------------------------
@ -338,7 +343,8 @@ static void addStroke(TTool::Application *application, const TVectorImageP &vi,
TStroke *stroke, bool breakAngles, bool autoGroup,
bool autoFill, bool frameCreated, bool levelCreated,
TXshSimpleLevel *sLevel = NULL,
TFrameId fid = TFrameId::NO_FRAME) {
TFrameId fid = TFrameId::NO_FRAME,
bool sendToBack = false) {
QMutexLocker lock(vi->getMutex());
if (application->getCurrentObject()->isSpline()) {
@ -379,10 +385,10 @@ static void addStroke(TTool::Application *application, const TVectorImageP &vi,
ImageUtils::getFillingInformationOverlappingArea(vi, *fillInformation,
stroke->getBBox());
TStroke *str = new TStroke(*strokes[i]);
vi->addStroke(str);
TUndoManager::manager()->add(new UndoPencil(str, fillInformation, sl, id,
frameCreated, levelCreated,
autoGroup, autoFill));
vi->addStroke(str, true, sendToBack);
TUndoManager::manager()->add(
new UndoPencil(str, fillInformation, sl, id, frameCreated,
levelCreated, autoGroup, autoFill, sendToBack));
}
TUndoManager::manager()->endBlock();
} else {
@ -391,10 +397,10 @@ static void addStroke(TTool::Application *application, const TVectorImageP &vi,
ImageUtils::getFillingInformationOverlappingArea(vi, *fillInformation,
stroke->getBBox());
TStroke *str = new TStroke(*stroke);
vi->addStroke(str);
TUndoManager::manager()->add(new UndoPencil(str, fillInformation, sl, id,
frameCreated, levelCreated,
autoGroup, autoFill));
vi->addStroke(str, true, sendToBack);
TUndoManager::manager()->add(
new UndoPencil(str, fillInformation, sl, id, frameCreated, levelCreated,
autoGroup, autoFill, sendToBack));
}
if (autoGroup && stroke->isSelfLoop()) {
@ -445,10 +451,11 @@ void addStrokeToImage(TTool::Application *application, const TVectorImageP &vi,
TStroke *stroke, bool breakAngles, bool autoGroup,
bool autoFill, bool frameCreated, bool levelCreated,
TXshSimpleLevel *sLevel = NULL,
TFrameId id = TFrameId::NO_FRAME) {
TFrameId id = TFrameId::NO_FRAME,
bool sendToBack = false) {
QMutexLocker lock(vi->getMutex());
addStroke(application, vi.getPointer(), stroke, breakAngles, autoGroup,
autoFill, frameCreated, levelCreated, sLevel, id);
autoFill, frameCreated, levelCreated, sLevel, id, sendToBack);
// la notifica viene gia fatta da addStroke!
// getApplication()->getCurrentTool()->getTool()->notifyImageChanged();
}
@ -514,6 +521,7 @@ ToonzVectorBrushTool::ToonzVectorBrushTool(std::string name, int targetType)
, m_frameRange("Range:")
, m_snap("Snap", false)
, m_snapSensitivity("Sensitivity:")
, m_sendToBack("Draw Under", false)
, m_targetType(targetType)
, m_workingFrameId(TFrameId()) {
bind(targetType);
@ -526,6 +534,9 @@ ToonzVectorBrushTool::ToonzVectorBrushTool(std::string name, int targetType)
m_prop[0].bind(m_breakAngles);
m_prop[0].bind(m_pressure);
m_prop[0].bind(m_sendToBack);
m_sendToBack.setId("DrawUnder");
m_prop[0].bind(m_frameRange);
m_frameRange.addValue(L"Off");
m_frameRange.addValue(LINEAR_WSTR);
@ -606,6 +617,7 @@ void ToonzVectorBrushTool::updateTranslation() {
m_joinStyle.setItemUIName(MITER_WSTR, tr("Miter join"));
m_joinStyle.setItemUIName(ROUNDJ_WSTR, tr("Round join"));
m_joinStyle.setItemUIName(BEVEL_WSTR, tr("Bevel join"));
m_sendToBack.setQStringName(tr("Draw Under"));
}
//---------------------------------------------------------------------------------------------------
@ -1159,7 +1171,7 @@ void ToonzVectorBrushTool::leftButtonUp(const TPointD &pos,
bool success = doFrameRangeStrokes(
m_firstFrameId, m_firstStroke, getFrameId(), stroke,
m_frameRange.getIndex(), m_breakAngles.getValue(), false, false,
m_firstFrameRange);
m_firstFrameRange, true, true, m_sendToBack.getValue() > 0);
if (e.isCtrlPressed() && e.isAltPressed() && e.isShiftPressed()) {
if (application) {
if (m_firstFrameId > currentId) {
@ -1196,9 +1208,14 @@ void ToonzVectorBrushTool::leftButtonUp(const TPointD &pos,
stroke->setSelfLoop(true);
m_snapSelf = false;
}
// bool m_sendToBack =
// e.isAltPressed() && e.isShiftPressed() && !e.isCtrlPressed();
//#ifdef Q_OS_WIN
// m_sendToBack = (GetKeyState(VK_CAPITAL) & 0x0001);
//#endif
addStrokeToImage(getApplication(), vi, stroke, m_breakAngles.getValue(),
false, false, m_isFrameCreated, m_isLevelCreated);
false, false, m_isFrameCreated, m_isLevelCreated, 0,
TFrameId::NO_FRAME, m_sendToBack.getValue() > 0);
TRectD bbox = stroke->getBBox().enlarge(2) + m_track.getModifiedRegion();
invalidate(); // should use bbox?
@ -1210,7 +1227,7 @@ void ToonzVectorBrushTool::leftButtonUp(const TPointD &pos,
TFrameId fId = getFrameId();
doGuidedAutoInbetween(fId, vi, stroke, m_breakAngles.getValue(), false,
false, false);
false, false, m_sendToBack.getValue() > 0);
if (getApplication()->getCurrentFrame()->isEditingScene())
getApplication()->getCurrentFrame()->setFrame(fidx);
@ -1239,7 +1256,7 @@ bool ToonzVectorBrushTool::doFrameRangeStrokes(
TFrameId firstFrameId, TStroke *firstStroke, TFrameId lastFrameId,
TStroke *lastStroke, int interpolationType, bool breakAngles,
bool autoGroup, bool autoFill, bool drawFirstStroke, bool drawLastStroke,
bool withUndo) {
bool withUndo, bool sendToBack) {
TXshSimpleLevel *sl =
TTool::getApplication()->getCurrentLevel()->getLevel()->getSimpleLevel();
TStroke *first = new TStroke();
@ -1257,8 +1274,8 @@ bool ToonzVectorBrushTool::doFrameRangeStrokes(
swapped = true;
}
firstImage->addStroke(first, false);
lastImage->addStroke(last, false);
firstImage->addStroke(first, false, sendToBack);
lastImage->addStroke(last, false, sendToBack);
assert(firstFrameId <= lastFrameId);
std::vector<TFrameId> allFids;
@ -1308,13 +1325,13 @@ bool ToonzVectorBrushTool::doFrameRangeStrokes(
} else
addStrokeToImage(getApplication(), img, firstImage->getStroke(0),
breakAngles, autoGroup, autoFill, m_isFrameCreated,
m_isLevelCreated, sl, fid);
m_isLevelCreated, sl, fid, sendToBack);
} else if (t == 1) {
if (swapped && !drawFirstStroke) {
} else if (drawLastStroke)
addStrokeToImage(getApplication(), img, lastImage->getStroke(0),
breakAngles, autoGroup, autoFill, m_isFrameCreated,
m_isLevelCreated, sl, fid);
m_isLevelCreated, sl, fid, sendToBack);
} else {
assert(firstImage->getStrokeCount() == 1);
assert(lastImage->getStrokeCount() == 1);
@ -1322,7 +1339,7 @@ bool ToonzVectorBrushTool::doFrameRangeStrokes(
assert(vi->getStrokeCount() == 1);
addStrokeToImage(getApplication(), img, vi->getStroke(0), breakAngles,
autoGroup, autoFill, m_isFrameCreated, m_isLevelCreated,
sl, fid);
sl, fid, sendToBack);
}
}
if (row != -1)
@ -1338,7 +1355,7 @@ bool ToonzVectorBrushTool::doFrameRangeStrokes(
//--------------------------------------------------------------------------------------------------
bool ToonzVectorBrushTool::doGuidedAutoInbetween(
TFrameId cFid, const TVectorImageP &cvi, TStroke *cStroke, bool breakAngles,
bool autoGroup, bool autoFill, bool drawStroke) {
bool autoGroup, bool autoFill, bool drawStroke, bool sendToBack) {
TApplication *app = TTool::getApplication();
if (cFid.isEmptyFrame() || cFid.isNoFrame() || !cvi || !cStroke) return false;
@ -1387,7 +1404,7 @@ bool ToonzVectorBrushTool::doGuidedAutoInbetween(
resultBack = doFrameRangeStrokes(
oFid, fStroke, cFid, cStroke,
Preferences::instance()->getGuidedInterpolation(), breakAngles,
autoGroup, autoFill, false, drawStroke, false);
autoGroup, autoFill, false, drawStroke, false, sendToBack);
m_isFrameCreated = frameCreated;
}
}
@ -1422,7 +1439,7 @@ bool ToonzVectorBrushTool::doGuidedAutoInbetween(
resultFront = doFrameRangeStrokes(
cFid, cStroke, oFid, fStroke,
Preferences::instance()->getGuidedInterpolation(), breakAngles,
autoGroup, autoFill, drawFirstStroke, false, false);
autoGroup, autoFill, drawFirstStroke, false, false, sendToBack);
m_isFrameCreated = frameCreated;
}
}
@ -1875,6 +1892,8 @@ bool ToonzVectorBrushTool::onPropertyChanged(std::string propertyName) {
int index = m_frameRange.getIndex();
V_VectorBrushFrameRange = index;
if (index == 0) resetFrameRange();
} else if (propertyName == m_sendToBack.getName()) {
V_VectorBrushDrawBehind = m_sendToBack.getValue();
} else if (propertyName == m_snap.getName()) {
V_VectorBrushSnap = m_snap.getValue();
} else if (propertyName == m_snapSensitivity.getName()) {
@ -2015,6 +2034,7 @@ void ToonzVectorBrushTool::loadLastBrush() {
m_frameRange.setIndex(V_VectorBrushFrameRange);
m_snap.setValue(V_VectorBrushSnap);
m_snapSensitivity.setIndex(V_VectorBrushSnapSensitivity);
m_sendToBack.setValue(V_VectorBrushDrawBehind);
switch (V_VectorBrushSnapSensitivity) {
case 0:
m_minDistance2 = SNAPPING_LOW;

View file

@ -135,13 +135,14 @@ public:
int interpolationType, bool breakAngles,
bool autoGroup = false, bool autoFill = false,
bool drawFirstStroke = true,
bool drawLastStroke = true, bool withUndo = true);
bool drawLastStroke = true, bool withUndo = true,
bool sendToBack = false);
void checkGuideSnapping(bool beforeMousePress, bool invertCheck);
void checkStrokeSnapping(bool beforeMousePress, bool invertCheck);
bool doGuidedAutoInbetween(TFrameId cFid, const TVectorImageP &cvi,
TStroke *cStroke, bool breakAngles,
bool autoGroup = false, bool autoFill = false,
bool drawStroke = true);
bool drawStroke = true, bool sendToBack = false);
protected:
TPropertyGroup m_prop[2];
@ -153,6 +154,7 @@ protected:
TBoolProperty m_breakAngles;
TBoolProperty m_pressure;
TBoolProperty m_snap;
TBoolProperty m_sendToBack;
TEnumProperty m_frameRange;
TEnumProperty m_snapSensitivity;
TEnumProperty m_capStyle;
@ -201,9 +203,7 @@ protected:
//! substitution.
m_firstTime, m_isPath, m_presetsLoaded, m_firstFrameRange;
/*---
FrameIdをクリック時に保存しUndoの登録時
---*/
TFrameId m_workingFrameId;
TPointD m_lastDragPos; //!< Position where mouse was last dragged.