Fix symmetry mode when cel origin != sprite origin (0,0)

Change ToolLoop::getOffset() to getCelOrigin()
This commit is contained in:
David Capello 2015-11-02 16:44:17 -03:00
parent c12cb26875
commit 08a04fcb64
14 changed files with 69 additions and 51 deletions

View File

@ -39,7 +39,8 @@ namespace app {
// Called when the user starts drawing and each time a new button is
// pressed. The controller could be sure that this method is called
// at least one time.
// at least one time. The point is a position relative to sprite
// bounds.
virtual void pressButton(Stroke& stroke, const gfx::Point& point) = 0;
// Called each time a mouse button is released.
@ -48,6 +49,7 @@ namespace app {
// Called when the mouse is moved.
virtual void movement(ToolLoop* loop, Stroke& stroke, const gfx::Point& point) = 0;
// The input and output strokes are relative to sprite coordinates.
virtual void getStrokeToInterwine(const Stroke& input, Stroke& output) = 0;
virtual void getStatusBarText(const Stroke& stroke, std::string& text) = 0;
};

View File

@ -906,8 +906,8 @@ public:
m_opacity = loop->getOpacity();
m_width = m_brush->bounds().w;
m_height = m_brush->bounds().h;
m_u = (loop->getOffset().x + m_brush->patternOrigin().x) % m_width;
m_v = (loop->getOffset().y + m_brush->patternOrigin().y) % m_height;
m_u = (m_brush->patternOrigin().x - loop->getCelOrigin().x) % m_width;
m_v = (m_brush->patternOrigin().y - loop->getCelOrigin().y) % m_height;
}
void processPixel(int x, int y) {

View File

@ -321,19 +321,19 @@ public:
void inkHline(int x1, int y, int x2, ToolLoop* loop) override {
if (m_modify_selection) {
Point offset = loop->getOffset();
Point origin = loop->getCelOrigin();
switch (loop->getSelectionMode()) {
case SelectionMode::DEFAULT:
case SelectionMode::ADD:
m_mask.add(gfx::Rect(x1-offset.x, y-offset.y, x2-x1+1, 1));
m_mask.add(gfx::Rect(x1+origin.x, y+origin.y, x2-x1+1, 1));
break;
case SelectionMode::SUBTRACT:
m_mask.subtract(gfx::Rect(x1-offset.x, y-offset.y, x2-x1+1, 1));
m_mask.subtract(gfx::Rect(x1+origin.x, y+origin.y, x2-x1+1, 1));
break;
}
m_maxBounds |= gfx::Rect(x1-offset.x, y-offset.y, x2-x1+1, 1);
m_maxBounds |= gfx::Rect(x1+origin.x, y+origin.y, x2-x1+1, 1);
}
// TODO show the selection-preview with a XOR color or something like that
else {

View File

@ -27,14 +27,23 @@ void Intertwine::doPointshapePoint(int x, int y, ToolLoop* loop)
{
Symmetry* symmetry = loop->getSymmetry();
if (symmetry) {
Point origin(loop->getCelOrigin());
// Convert the point to the sprite position so we can apply the
// symmetry transformation.
Stroke main_stroke;
main_stroke.addPoint(gfx::Point(x, y));
main_stroke.addPoint(Point(x, y) + origin);
Strokes strokes;
symmetry->generateStrokes(main_stroke, strokes);
for (const auto& stroke : strokes)
for (const auto& stroke : strokes) {
// We call transformPoint() moving back each point to the cel
// origin.
loop->getPointShape()->transformPoint(
loop, stroke[0].x, stroke[0].y);
loop,
stroke[0].x - origin.x,
stroke[0].y - origin.y);
}
}
else {
loop->getPointShape()->transformPoint(loop, x, y);

View File

@ -29,10 +29,13 @@ namespace app {
virtual ~Intertwine() { }
virtual bool snapByAngle() { return false; }
virtual void prepareIntertwine() { }
// The given stroke must be relative to the cel origin.
virtual void joinStroke(ToolLoop* loop, const Stroke& stroke) = 0;
virtual void fillStroke(ToolLoop* loop, const Stroke& stroke) = 0;
protected:
// The given point must be relative to the cel origin.
static void doPointshapePoint(int x, int y, ToolLoop* loop);
static void doPointshapeHline(int x1, int y, int x2, ToolLoop* loop);
static void doPointshapeLine(int x1, int y1, int x2, int y2, ToolLoop* loop);

View File

@ -22,6 +22,8 @@ namespace app {
virtual bool isFloodFill() { return false; }
virtual bool isSpray() { return false; }
virtual void preparePointShape(ToolLoop* loop) { }
// The x, y position must be relative to the cel/src/dst image origin.
virtual void transformPoint(ToolLoop* loop, int x, int y) = 0;
virtual void getModifiedArea(ToolLoop* loop, int x, int y, gfx::Rect& area) = 0;

View File

@ -52,14 +52,14 @@ public:
if (m_brush->type() == kImageBrushType) {
if (m_brush->pattern() == BrushPattern::ALIGNED_TO_DST ||
m_brush->pattern() == BrushPattern::PAINT_BRUSH) {
m_brush->setPatternOrigin(gfx::Point(x, y)-loop->getOffset());
m_brush->setPatternOrigin(gfx::Point(x, y)+loop->getCelOrigin());
}
}
}
else {
if (m_brush->type() == kImageBrushType &&
m_brush->pattern() == BrushPattern::PAINT_BRUSH) {
m_brush->setPatternOrigin(gfx::Point(x, y)-loop->getOffset());
m_brush->setPatternOrigin(gfx::Point(x, y)+loop->getCelOrigin());
}
}
@ -84,24 +84,25 @@ public:
void transformPoint(ToolLoop* loop, int x, int y) override {
doc::algorithm::floodfill(
const_cast<Image*>(loop->getSrcImage()), x, y,
paintBounds(loop, x, y),
floodfillBounds(loop, x, y),
loop->getTolerance(),
loop->getContiguous(),
loop, (AlgoHLine)doInkHline);
}
void getModifiedArea(ToolLoop* loop, int x, int y, Rect& area) override {
area = paintBounds(loop, x, y);
area = floodfillBounds(loop, x, y);
}
private:
gfx::Rect paintBounds(ToolLoop* loop, int x, int y) {
gfx::Point offset = loop->getOffset();
gfx::Rect bounds(
offset.x, offset.y,
loop->sprite()->width(), loop->sprite()->height());
gfx::Rect floodfillBounds(ToolLoop* loop, int x, int y) const {
gfx::Point origin = loop->getCelOrigin();
gfx::Rect bounds(-origin.x, -origin.y,
loop->sprite()->width(),
loop->sprite()->height());
bounds = bounds.createIntersection(loop->getSrcImage()->bounds());
bounds = bounds.createIntersection(
loop->getSrcImage()->bounds());
// Limit the flood-fill to the current tile if the grid is visible.
if (loop->getStopAtGrid()) {
@ -109,8 +110,8 @@ private:
if (!grid.isEmpty()) {
div_t d, dx, dy;
dx = div(grid.x+loop->getOffset().x, grid.w);
dy = div(grid.y+loop->getOffset().y, grid.h);
dx = div(grid.x-origin.x, grid.w);
dy = div(grid.y-origin.y, grid.h);
if (dx.rem > 0) dx.rem -= grid.w;
if (dy.rem > 0) dy.rem -= grid.h;

View File

@ -20,6 +20,8 @@ namespace app {
class Symmetry {
public:
virtual ~Symmetry() { }
// The "stroke" must be relative to the sprite origin.
virtual void generateStrokes(const Stroke& stroke, Strokes& strokes) = 0;
};

View File

@ -187,8 +187,8 @@ namespace app {
virtual int getSprayWidth() = 0;
virtual int getSpraySpeed() = 0;
// Offset for each point
virtual gfx::Point getOffset() = 0;
// X,Y origin of the cel where we are drawing
virtual gfx::Point getCelOrigin() = 0;
// Velocity vector of the mouse
virtual void setSpeed(const gfx::Point& speed) = 0;

View File

@ -164,15 +164,14 @@ void ToolLoopManager::movement(const Pointer& pointer)
void ToolLoopManager::doLoopStep(bool last_step)
{
// Original set of points to interwine (original user stroke).
// Original set of points to interwine (original user stroke,
// relative to sprite origin).
Stroke main_stroke;
if (!last_step)
m_toolLoop->getController()->getStrokeToInterwine(m_stroke, main_stroke);
else
main_stroke = m_stroke;
main_stroke.offset(m_toolLoop->getOffset());
// Calculate the area to be updated in all document observers.
Symmetry* symmetry = m_toolLoop->getSymmetry();
Strokes strokes;
@ -209,6 +208,9 @@ void ToolLoopManager::doLoopStep(bool last_step)
m_toolLoop->validateDstImage(m_dirtyArea);
// Move the stroke to be relative to the cel origin.
main_stroke.offset(-m_toolLoop->getCelOrigin());
// Join or fill user points
if (!m_toolLoop->getFilled() || (!last_step && !m_toolLoop->getPreviewFilled()))
m_toolLoop->getIntertwine()->joinStroke(m_toolLoop, main_stroke);
@ -235,6 +237,7 @@ void ToolLoopManager::snapToGrid(Point& point)
point = snap_to_grid(m_toolLoop->getGridBounds(), point);
}
// Strokes are relative to sprite origin.
void ToolLoopManager::calculateDirtyArea(const Strokes& strokes)
{
// Save the current dirty area if it's needed
@ -245,6 +248,8 @@ void ToolLoopManager::calculateDirtyArea(const Strokes& strokes)
// Start with a fresh dirty area
m_dirtyArea.clear();
const Point celOrigin = m_toolLoop->getCelOrigin();
for (auto& stroke : strokes) {
gfx::Rect strokeBounds = stroke.bounds();
if (strokeBounds.isEmpty())
@ -255,20 +260,19 @@ void ToolLoopManager::calculateDirtyArea(const Strokes& strokes)
m_toolLoop->getPointShape()->getModifiedArea(
m_toolLoop,
strokeBounds.x,
strokeBounds.y, r1);
strokeBounds.x - celOrigin.x,
strokeBounds.y - celOrigin.y, r1);
m_toolLoop->getPointShape()->getModifiedArea(
m_toolLoop,
strokeBounds.x+strokeBounds.w-1,
strokeBounds.y+strokeBounds.h-1, r2);
strokeBounds.x+strokeBounds.w-1 - celOrigin.x,
strokeBounds.y+strokeBounds.h-1 - celOrigin.y, r2);
m_dirtyArea.createUnion(m_dirtyArea, Region(r1.createUnion(r2)));
}
// Apply offset mode
Point offset(m_toolLoop->getOffset());
m_dirtyArea.offset(-offset);
// Make the dirty area relative to the sprite.
m_dirtyArea.offset(celOrigin);
// Merge new dirty area with the previous one (for tools like line
// or rectangle it's needed to redraw the previous position and

View File

@ -38,6 +38,7 @@ namespace app {
// is called.
// 5. When the user release the mouse:
// - ToolLoopManager::releaseButton
//
class ToolLoopManager {
public:

View File

@ -184,8 +184,7 @@ void BrushPreview::show(const gfx::Point& screenPos)
base::UniquePtr<tools::ToolLoop> loop(
create_tool_loop_preview(
m_editor, extraImage,
-gfx::Point(brushBounds.x,
brushBounds.y)));
brushBounds.getOrigin()));
if (loop) {
loop->getInk()->prepareInk(loop);
loop->getIntertwine()->prepareIntertwine();

View File

@ -70,7 +70,7 @@ protected:
int m_opacity;
int m_tolerance;
bool m_contiguous;
gfx::Point m_offset;
gfx::Point m_celOrigin;
gfx::Point m_speed;
tools::ToolLoop::Button m_button;
base::UniquePtr<tools::Ink> m_ink;
@ -207,7 +207,7 @@ public:
return false;
}
gfx::Rect getGridBounds() override { return m_docPref.grid.bounds(); }
gfx::Point getOffset() override { return m_offset; }
gfx::Point getCelOrigin() override { return m_celOrigin; }
void setSpeed(const gfx::Point& speed) override { m_speed = speed; }
gfx::Point getSpeed() override { return m_speed; }
tools::Ink* getInk() override { return m_ink; }
@ -317,16 +317,11 @@ public:
m_transaction.execute(new cmd::SetMask(m_document, &emptyMask));
}
int x1 = m_expandCelCanvas.getCel()->x();
int y1 = m_expandCelCanvas.getCel()->y();
m_celOrigin = m_expandCelCanvas.getCel()->position();
m_mask = m_document->mask();
m_maskOrigin = (!m_mask->isEmpty() ? gfx::Point(m_mask->bounds().x-x1,
m_mask->bounds().y-y1):
m_maskOrigin = (!m_mask->isEmpty() ? gfx::Point(m_mask->bounds().x-m_celOrigin.x,
m_mask->bounds().y-m_celOrigin.y):
gfx::Point(0, 0));
m_offset.x = -x1;
m_offset.y = -y1;
}
// IToolLoop interface
@ -485,12 +480,12 @@ public:
const app::Color& fgColor,
const app::Color& bgColor,
Image* image,
const gfx::Point& offset)
const gfx::Point& celOrigin)
: ToolLoopBase(editor, tool, ink, document,
tools::ToolLoop::Left, fgColor, bgColor)
, m_image(image)
{
m_offset = offset;
m_celOrigin = celOrigin;
// Avoid preview for spray and flood fill like tools
if (m_pointShape->isSpray()) {
@ -529,7 +524,7 @@ public:
tools::ToolLoop* create_tool_loop_preview(
Editor* editor, Image* image,
const gfx::Point& offset)
const gfx::Point& celOrigin)
{
tools::Tool* current_tool = editor->getCurrentEditorTool();
tools::Ink* current_ink = editor->getCurrentEditorInk();
@ -557,7 +552,7 @@ tools::ToolLoop* create_tool_loop_preview(
current_tool,
current_ink,
editor->document(),
fg, bg, image, offset);
fg, bg, image, celOrigin);
}
catch (const std::exception&) {
return nullptr;

View File

@ -29,7 +29,7 @@ namespace app {
tools::ToolLoop* create_tool_loop_preview(
Editor* editor, doc::Image* image,
const gfx::Point& offset);
const gfx::Point& celOrigin);
} // namespace app