mirror of https://github.com/aseprite/aseprite.git
Add rounded rectangle support (fix #2184)
This commit is contained in:
parent
fa0b25fe87
commit
16aa3b4aa1
Binary file not shown.
Before Width: | Height: | Size: 17 KiB After Width: | Height: | Size: 17 KiB |
|
@ -393,6 +393,7 @@
|
|||
<part id="icon_slice" x="248" y="264" w="8" h="8"/>
|
||||
<part id="icon_aspect_ratio" x="256" y="264" w="10" h="8"/>
|
||||
<part id="icon_delta" x="266" y="264" w="6" h="8"/>
|
||||
<part id="icon_corner_radius" x="272" y="264" w="8" h="8"/>
|
||||
<part id="icon_add" x="184" y="200" w="5" h="5"/>
|
||||
<part id="tool_rectangular_marquee" x="144" y="0" w="16" h="16"/>
|
||||
<part id="tool_elliptical_marquee" x="160" y="0" w="16" h="16"/>
|
||||
|
|
Binary file not shown.
Before Width: | Height: | Size: 16 KiB After Width: | Height: | Size: 16 KiB |
|
@ -389,6 +389,7 @@
|
|||
<part id="icon_slice" x="248" y="264" w="8" h="8"/>
|
||||
<part id="icon_aspect_ratio" x="256" y="264" w="10" h="8"/>
|
||||
<part id="icon_delta" x="266" y="264" w="6" h="8"/>
|
||||
<part id="icon_corner_radius" x="272" y="264" w="8" h="8"/>
|
||||
<part id="icon_add" x="184" y="200" w="5" h="5"/>
|
||||
<part id="tool_rectangular_marquee" x="144" y="0" w="16" h="16"/>
|
||||
<part id="tool_elliptical_marquee" x="160" y="0" w="16" h="16"/>
|
||||
|
|
|
@ -678,6 +678,8 @@
|
|||
<!-- Modifiers for two-points tool controller -->
|
||||
<key action="SquareAspect" shortcut="Shift" />
|
||||
<key action="DrawFromCenter" shortcut="Ctrl" />
|
||||
<key action="CornerRadius" shortcut="J" />
|
||||
|
||||
<!-- Modifiers for two-or-more-points tools -->
|
||||
<key action="MoveOrigin" shortcut="Space" />
|
||||
<key action="RotateShape" shortcut="Alt" />
|
||||
|
|
|
@ -1006,6 +1006,7 @@ move_origin = Move Origin
|
|||
square_aspect = Square Aspect
|
||||
draw_from_center = Draw From Center
|
||||
rotate_shape = Rotate Shape
|
||||
corner_radius = Corner Radius
|
||||
trigger_left_mouse_button = Trigger Left Mouse Button
|
||||
trigger_right_mouse_button = Trigger Right Mouse Button
|
||||
ok = &OK
|
||||
|
|
|
@ -62,6 +62,10 @@ public:
|
|||
// Returns the angle for a shape-like intertwiner (rectangles,
|
||||
// ellipses, etc.).
|
||||
virtual double getShapeAngle() const { return 0.0; }
|
||||
|
||||
// Returns the radius for each corner for a rectangle intertwiner when drawing
|
||||
// rounded rectangles.
|
||||
virtual int getCornerRadius() const { return 0; }
|
||||
};
|
||||
|
||||
}} // namespace app::tools
|
||||
|
|
|
@ -1,11 +1,15 @@
|
|||
// Aseprite
|
||||
// Copyright (C) 2019-2023 Igara Studio S.A.
|
||||
// Copyright (C) 2019-2025 Igara Studio S.A.
|
||||
// Copyright (C) 2001-2018 David Capello
|
||||
//
|
||||
// This program is distributed under the terms of
|
||||
// the End-User License Agreement for Aseprite.
|
||||
|
||||
#include "app/snap_to_grid.h"
|
||||
#include "app/tools/controller.h"
|
||||
#include "app/tools/intertwine.h"
|
||||
#include "app/tools/tool_loop.h"
|
||||
#include "app/tools/tool_loop_modifiers.h"
|
||||
#include "base/gcd.h"
|
||||
#include "base/pi.h"
|
||||
#include "fmt/format.h"
|
||||
|
@ -135,6 +139,12 @@ public:
|
|||
if (MoveOriginCapability::isMovingOrigin(loop, stroke, pt))
|
||||
return;
|
||||
|
||||
if ((int(loop->getModifiers()) & int(ToolLoopModifiers::kCornerRadius))) {
|
||||
int dr = stroke[1].y - pt.y;
|
||||
m_cornerRadius = ABS(m_lastCornerRadius + dr);
|
||||
return;
|
||||
}
|
||||
|
||||
if (!loop->getIntertwine()->snapByAngle() &&
|
||||
int(loop->getModifiers()) & int(ToolLoopModifiers::kRotateShape)) {
|
||||
if ((int(loop->getModifiers()) & int(ToolLoopModifiers::kFromCenter))) {
|
||||
|
@ -149,8 +159,13 @@ public:
|
|||
return;
|
||||
}
|
||||
|
||||
stroke[0] = m_first;
|
||||
stroke[1] = pt;
|
||||
if (m_lastCornerRadius != m_cornerRadius) {
|
||||
m_lastCornerRadius = m_cornerRadius;
|
||||
}
|
||||
else {
|
||||
stroke[0] = m_first;
|
||||
stroke[1] = pt;
|
||||
}
|
||||
|
||||
bool isoAngle = false;
|
||||
|
||||
|
@ -277,11 +292,16 @@ public:
|
|||
text += fmt::format(" :angle: {:.1f}", 180.0 * angle / PI);
|
||||
}
|
||||
|
||||
if (hasCornerRadius()) {
|
||||
text += fmt::format(" :corner_radius: {}", m_cornerRadius);
|
||||
}
|
||||
|
||||
// Aspect ratio at the end
|
||||
text += fmt::format(" :aspect_ratio: {}:{}", w / gcd, h / gcd);
|
||||
}
|
||||
|
||||
double getShapeAngle() const override { return m_angle; }
|
||||
int getCornerRadius() const override { return m_cornerRadius; }
|
||||
|
||||
private:
|
||||
void snapPointsToGridTiles(ToolLoop* loop, Stroke& stroke)
|
||||
|
@ -301,6 +321,8 @@ private:
|
|||
|
||||
bool hasAngle() const { return (ABS(m_angle) > 0.001); }
|
||||
|
||||
bool hasCornerRadius() const { return (ABS(m_cornerRadius) > 1); }
|
||||
|
||||
void onMoveOrigin(const Point& delta) override
|
||||
{
|
||||
m_first.x += delta.x;
|
||||
|
@ -312,6 +334,8 @@ private:
|
|||
Stroke::Pt m_first;
|
||||
Stroke::Pt m_center;
|
||||
double m_angle;
|
||||
int m_lastCornerRadius = 0;
|
||||
int m_cornerRadius = 0;
|
||||
};
|
||||
|
||||
// Controls clicks for tools like polygon
|
||||
|
|
|
@ -18,6 +18,7 @@
|
|||
#include "app/tools/tool_loop.h"
|
||||
#include "base/pi.h"
|
||||
#include "doc/algo.h"
|
||||
#include "doc/algorithm/hline.h"
|
||||
#include "doc/layer.h"
|
||||
|
||||
#include <cmath>
|
||||
|
@ -158,4 +159,21 @@ doc::AlgoLineWithAlgoPixel Intertwine::getLineAlgo(ToolLoop* loop,
|
|||
}
|
||||
}
|
||||
|
||||
// static
|
||||
void Intertwine::doPointshapeCircle(int xm, int ym, int r, int sx, int sy, ToolLoop* loop, bool fill)
|
||||
{
|
||||
if (fill) {
|
||||
algo_circlefill(xm, ym, sx, sy, r, loop, (AlgoHLine)doPointshapeHline);
|
||||
}
|
||||
else {
|
||||
algo_circle(xm, ym, sx, sy, r, loop, (AlgoPixel)doPointshapePoint);
|
||||
}
|
||||
}
|
||||
|
||||
// static
|
||||
void Intertwine::doPointshapeArc(int xm, int ym, double sa, double ea, int r, ToolLoop* loop)
|
||||
{
|
||||
algo_arc(xm, ym, sa, ea, r, loop, (AlgoPixel)doPointshapePoint);
|
||||
}
|
||||
|
||||
}} // namespace app::tools
|
||||
|
|
|
@ -60,6 +60,11 @@ protected:
|
|||
static doc::AlgoLineWithAlgoPixel getLineAlgo(ToolLoop* loop,
|
||||
const Stroke::Pt& a,
|
||||
const Stroke::Pt& b);
|
||||
|
||||
static void
|
||||
doPointshapeCircle(int xm, int ym, int r, int sx, int sy, ToolLoop* loop, bool fill = false);
|
||||
|
||||
static void doPointshapeArc(int xm, int ym, double sa, double ea, int r, ToolLoop* loop);
|
||||
};
|
||||
|
||||
}} // namespace app::tools
|
||||
|
|
|
@ -5,8 +5,18 @@
|
|||
// This program is distributed under the terms of
|
||||
// the End-User License Agreement for Aseprite.
|
||||
|
||||
#include "app/tools/controller.h"
|
||||
#include "app/tools/intertwine.h"
|
||||
#include "app/tools/point_shape.h"
|
||||
#include "app/tools/tool_loop.h"
|
||||
#include "app/tools/tool_loop_modifiers.h"
|
||||
#include "base/pi.h"
|
||||
#include "doc/algorithm/polygon.h"
|
||||
#include "doc/layer_tilemap.h"
|
||||
#include "gfx/point.h"
|
||||
#include <algorithm>
|
||||
|
||||
using namespace gfx;
|
||||
|
||||
namespace app { namespace tools {
|
||||
|
||||
|
@ -183,22 +193,52 @@ public:
|
|||
std::swap(y1, y2);
|
||||
|
||||
const double angle = loop->getController()->getShapeAngle();
|
||||
const int cornerRadius = loop->getController()->getCornerRadius();
|
||||
if (ABS(angle) < 0.001) {
|
||||
doPointshapeLineWithoutDynamics(x1, y1, x2, y1, loop);
|
||||
doPointshapeLineWithoutDynamics(x1, y2, x2, y2, loop);
|
||||
int r = 0;
|
||||
if (cornerRadius > 1) {
|
||||
int w = x2 - x1;
|
||||
int h = y2 - y1;
|
||||
int xm = x1 + w / 2;
|
||||
int ym = y1 + h / 2;
|
||||
r = std::min(w, std::min(h, 2 * cornerRadius)) / 2;
|
||||
doPointshapeCircle(xm, ym, r, w - r * 2, h - r * 2, loop);
|
||||
}
|
||||
|
||||
for (y = y1; y <= y2; y++) {
|
||||
doPointshapeLineWithoutDynamics(x1 + r, y1, x2 - r, y1, loop);
|
||||
doPointshapeLineWithoutDynamics(x1 + r, y2, x2 - r, y2, loop);
|
||||
|
||||
for (y = y1 + r; y <= y2 - r; y++) {
|
||||
doPointshapePoint(x1, y, loop);
|
||||
doPointshapePoint(x2, y, loop);
|
||||
}
|
||||
}
|
||||
else {
|
||||
Stroke p = rotateRectangle(x1, y1, x2, y2, angle);
|
||||
int n = p.size();
|
||||
for (int i = 0; i + 1 < n; ++i) {
|
||||
doPointshapeLine(p[i], p[i + 1], loop);
|
||||
if (cornerRadius <= 1) {
|
||||
Stroke p = rotateRectangle(x1, y1, x2, y2, angle);
|
||||
int n = p.size();
|
||||
for (int i = 0; i + 1 < n; ++i) {
|
||||
doPointshapeLine(p[i], p[i + 1], loop);
|
||||
}
|
||||
doPointshapeLine(p[n - 1], p[0], loop);
|
||||
}
|
||||
else {
|
||||
int w = x2 - x1;
|
||||
int h = y2 - y1;
|
||||
int r = std::min(w, std::min(h, 2 * cornerRadius)) / 2;
|
||||
Stroke p = rotateRectangle(x1, y1, x2, y2, angle, r);
|
||||
int n = p.size();
|
||||
for (int i = 0; i + 1 < n; i += 3) {
|
||||
doPointshapeLine(p[i], p[i + 1], loop);
|
||||
}
|
||||
const double ang_minus_PI_2 = base::fmod_radians(angle - PI / 2);
|
||||
const double ang_plus_PI_2 = base::fmod_radians(angle + PI / 2);
|
||||
const double ang_plus_PI = base::fmod_radians(angle + PI);
|
||||
doPointshapeArc(p[2].x, p[2].y, ang_minus_PI_2, angle, r, loop);
|
||||
doPointshapeArc(p[5].x, p[5].y, angle, ang_plus_PI_2, r, loop);
|
||||
doPointshapeArc(p[8].x, p[8].y, ang_plus_PI_2, ang_plus_PI, r, loop);
|
||||
doPointshapeArc(p[11].x, p[11].y, ang_plus_PI, ang_minus_PI_2, r, loop);
|
||||
}
|
||||
doPointshapeLine(p[n - 1], p[0], loop);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -224,14 +264,44 @@ public:
|
|||
std::swap(y1, y2);
|
||||
|
||||
const double angle = loop->getController()->getShapeAngle();
|
||||
const int cornerRadius = loop->getController()->getCornerRadius();
|
||||
if (ABS(angle) < 0.001) {
|
||||
for (y = y1; y <= y2; y++)
|
||||
int r = 0;
|
||||
if (cornerRadius > 1) {
|
||||
int w = x2 - x1;
|
||||
int h = y2 - y1;
|
||||
int xm = x1 + w / 2;
|
||||
int ym = y1 + h / 2;
|
||||
r = std::min(w, std::min(h, 2 * cornerRadius)) / 2;
|
||||
doPointshapeCircle(xm, ym, r, w - r * 2, h - r * 2, loop, true);
|
||||
|
||||
for (y = y1; y < y1 + r; y++)
|
||||
doPointshapeLineWithoutDynamics(x1 + r, y, x2 - r, y, loop);
|
||||
for (y = y2 - r + 1; y <= y2; y++)
|
||||
doPointshapeLineWithoutDynamics(x1 + r, y, x2 - r, y, loop);
|
||||
}
|
||||
|
||||
for (y = y1 + r; y <= y2 - r; y++)
|
||||
doPointshapeLineWithoutDynamics(x1, y, x2, y, loop);
|
||||
}
|
||||
else {
|
||||
Stroke p = rotateRectangle(x1, y1, x2, y2, angle);
|
||||
auto v = p.toXYInts();
|
||||
doc::algorithm::polygon(v.size() / 2, &v[0], loop, (AlgoHLine)doPointshapeHline);
|
||||
if (cornerRadius <= 1) {
|
||||
Stroke p = rotateRectangle(x1, y1, x2, y2, angle);
|
||||
auto v = p.toXYInts();
|
||||
doc::algorithm::polygon(v.size() / 2, &v[0], loop, (AlgoHLine)doPointshapeHline);
|
||||
}
|
||||
else {
|
||||
int w = x2 - x1;
|
||||
int h = y2 - y1;
|
||||
int r = std::min(w, std::min(h, 2 * cornerRadius)) / 2;
|
||||
Stroke p = rotateRectangle(x1, y1, x2, y2, angle, cornerRadius);
|
||||
auto v = p.toXYInts();
|
||||
doc::algorithm::polygon(v.size() / 2, &v[0], loop, (AlgoHLine)doPointshapeHline);
|
||||
doPointshapeCircle(p[2].x, p[2].y, r, 0, 0, loop, true);
|
||||
doPointshapeCircle(p[5].x, p[5].y, r, 0, 0, loop, true);
|
||||
doPointshapeCircle(p[8].x, p[8].y, r, 0, 0, loop, true);
|
||||
doPointshapeCircle(p[11].x, p[11].y, r, 0, 0, loop, true);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -274,6 +344,54 @@ private:
|
|||
stroke.addPoint(Point(cx - a * c + b * s, cy + a * s + b * c));
|
||||
return stroke;
|
||||
}
|
||||
|
||||
// Returns a stroke with the rotated points of a rectangle making room for a
|
||||
// rounded corner of the specified radius, and with points where the center of
|
||||
// each corner must be.
|
||||
static Stroke rotateRectangle(int x1, int y1, int x2, int y2, double angle, int cornerRadius)
|
||||
{
|
||||
cornerRadius = std::max(cornerRadius, 0);
|
||||
|
||||
int cx = (x1 + x2) / 2;
|
||||
int cy = (y1 + y2) / 2;
|
||||
int a = ((x2 - x1) / 2);
|
||||
int b = ((y2 - y1) / 2);
|
||||
int ai = a - cornerRadius;
|
||||
int bi = b - cornerRadius;
|
||||
|
||||
double s = -std::sin(angle);
|
||||
double c = std::cos(angle);
|
||||
|
||||
Stroke stroke;
|
||||
// Top segment
|
||||
stroke.addPoint(Point(cx - ai * c - b * s, cy + ai * s - b * c));
|
||||
stroke.addPoint(Point(cx + ai * c - b * s, cy - ai * s - b * c));
|
||||
|
||||
// Center for top-right corner
|
||||
stroke.addPoint(Point(cx + ai * c - bi * s, cy - ai * s - bi * c));
|
||||
|
||||
// Right segment
|
||||
stroke.addPoint(Point(cx + a * c - bi * s, cy - a * s - bi * c));
|
||||
stroke.addPoint(Point(cx + a * c + bi * s, cy - a * s + bi * c));
|
||||
|
||||
// Center for bottom-right corner
|
||||
stroke.addPoint(Point(cx + ai * c + bi * s, cy - ai * s + bi * c));
|
||||
|
||||
// Bottom segment
|
||||
stroke.addPoint(Point(cx + ai * c + b * s, cy - ai * s + b * c));
|
||||
stroke.addPoint(Point(cx - ai * c + b * s, cy + ai * s + b * c));
|
||||
|
||||
// Center for bottom-left corner
|
||||
stroke.addPoint(Point(cx - ai * c + bi * s, cy + ai * s + bi * c));
|
||||
|
||||
// Left segment
|
||||
stroke.addPoint(Point(cx - a * c + bi * s, cy + a * s + bi * c));
|
||||
stroke.addPoint(Point(cx - a * c - bi * s, cy + a * s - bi * c));
|
||||
|
||||
// Center for top-left corner
|
||||
stroke.addPoint(Point(cx - ai * c - bi * s, cy + ai * s - bi * c));
|
||||
return stroke;
|
||||
}
|
||||
};
|
||||
|
||||
class IntertwineAsEllipses : public Intertwine {
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
// Aseprite
|
||||
// Copyright (C) 2018 Igara Studio S.A.
|
||||
// Copyright (C) 2018-2025 Igara Studio S.A.
|
||||
// Copyright (C) 2016-2018 David Capello
|
||||
//
|
||||
// This program is distributed under the terms of
|
||||
|
@ -21,6 +21,7 @@ enum class ToolLoopModifiers {
|
|||
kSquareAspect = 0x00000020,
|
||||
kFromCenter = 0x00000040,
|
||||
kRotateShape = 0x00000080,
|
||||
kCornerRadius = 0x00000100,
|
||||
};
|
||||
|
||||
}} // namespace app::tools
|
||||
|
|
|
@ -1834,7 +1834,7 @@ void Editor::updateToolLoopModifiersIndicators(const bool firstFromMouseDown)
|
|||
// square-aspect/rotation/etc. only when the user presses the
|
||||
// modifier key again in the ToolLoop (and not before starting
|
||||
// the loop). So Alt+selection will add a selection, but
|
||||
// willn't start the square-aspect until we press Alt key
|
||||
// won't start the square-aspect until we press Alt key
|
||||
// again, or Alt+Shift+selection tool will subtract the
|
||||
// selection but will not start the rotation until we release
|
||||
// and press the Alt key again.
|
||||
|
@ -1847,6 +1847,8 @@ void Editor::updateToolLoopModifiersIndicators(const bool firstFromMouseDown)
|
|||
modifiers |= int(tools::ToolLoopModifiers::kFromCenter);
|
||||
if (int(action & KeyAction::RotateShape))
|
||||
modifiers |= int(tools::ToolLoopModifiers::kRotateShape);
|
||||
if (int(action & KeyAction::CornerRadius))
|
||||
modifiers |= int(tools::ToolLoopModifiers::kCornerRadius);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -110,6 +110,10 @@ const std::vector<KeyShortcutAction>& actions()
|
|||
I18N_KEY(rotate_shape),
|
||||
app::KeyAction::RotateShape,
|
||||
app::KeyContext::ShapeTool },
|
||||
{ "CornerRadius",
|
||||
I18N_KEY(corner_radius),
|
||||
app::KeyAction::CornerRadius,
|
||||
app::KeyContext::ShapeTool },
|
||||
{ "LeftMouseButton",
|
||||
I18N_KEY(trigger_left_mouse_button),
|
||||
app::KeyAction::LeftMouseButton,
|
||||
|
@ -392,6 +396,7 @@ Key::Key(const KeyAction action, const KeyContext keyContext)
|
|||
case KeyAction::RotateShape: m_keycontext = KeyContext::ShapeTool; break;
|
||||
case KeyAction::LeftMouseButton:
|
||||
case KeyAction::RightMouseButton: m_keycontext = KeyContext::Any; break;
|
||||
case KeyAction::CornerRadius: m_keycontext = KeyContext::ShapeTool; break;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -64,6 +64,7 @@ enum class KeyAction {
|
|||
AngleSnapFromLastPoint = 0x00010000,
|
||||
RotateShape = 0x00020000,
|
||||
FineControl = 0x00040000,
|
||||
CornerRadius = 0x00080000,
|
||||
};
|
||||
|
||||
enum class WheelAction {
|
||||
|
|
178
src/doc/algo.cpp
178
src/doc/algo.cpp
|
@ -12,6 +12,7 @@
|
|||
#include "doc/algo.h"
|
||||
|
||||
#include "base/debug.h"
|
||||
#include "base/pi.h"
|
||||
|
||||
#include <algorithm>
|
||||
#include <cmath>
|
||||
|
@ -180,6 +181,183 @@ void algo_line_continuous_with_fix_for_line_brush(int x0,
|
|||
}
|
||||
}
|
||||
|
||||
// Circle code based on Alois Zingl work released under the MIT
|
||||
// license http://members.chello.at/easyfilter/bresenham.html
|
||||
//
|
||||
// Adapted for Aseprite by Igara Studio S.A.
|
||||
//
|
||||
// Draws a circle divided in 4 parts, separated sx pixels horizontally and sy
|
||||
// pixels vertically:
|
||||
// |---sx---|
|
||||
//
|
||||
// OOO OOO
|
||||
// O O
|
||||
// O O
|
||||
// T
|
||||
// |
|
||||
// |
|
||||
// sy xm,xy
|
||||
// |
|
||||
// |
|
||||
// _
|
||||
// O O
|
||||
// O O
|
||||
// OOO OOO
|
||||
//
|
||||
// If sx and sy are 0, it draws a regular circle.
|
||||
void algo_circle(int xm, int ym, int sx, int sy, int r, void* data, AlgoPixel proc)
|
||||
{
|
||||
int x = -r, y = 0, err = 2 - 2 * r; /* II. Quadrant */
|
||||
sx = sx < 0 ? 0 : sx;
|
||||
sy = sy < 0 ? 0 : sy;
|
||||
int offsetx = sx / 2;
|
||||
int offsety = sy / 2;
|
||||
// Fix the position when sx or sy are not even.
|
||||
int fixx = sx - 2 * offsetx;
|
||||
int fixy = sy - 2 * offsety;
|
||||
do {
|
||||
proc(xm - x + offsetx + fixx, ym + y + offsety + fixy, data); /* I. Quadrant */
|
||||
proc(xm - y - offsetx, ym - x + offsety + fixy, data); /* II. Quadrant */
|
||||
proc(xm + x - offsetx, ym - y - offsety, data); /* III. Quadrant */
|
||||
proc(xm + y + offsetx + fixx, ym + x - offsety, data); /* IV. Quadrant */
|
||||
r = err;
|
||||
if (r <= y)
|
||||
err += ++y * 2 + 1; /* e_xy+e_y < 0 */
|
||||
if (r > x || err > y)
|
||||
err += ++x * 2 + 1; /* e_xy+e_x > 0 or no 2nd y-step */
|
||||
} while (x < 0);
|
||||
}
|
||||
|
||||
// Same as algo_circle but with the parts filled.
|
||||
void algo_circlefill(int xm, int ym, int sx, int sy, int r, void* data, AlgoHLine proc)
|
||||
{
|
||||
int x = -r, y = 0, err = 2 - 2 * r; /* II. Quadrant */
|
||||
sx = sx < 0 ? 0 : sx;
|
||||
sy = sy < 0 ? 0 : sy;
|
||||
int offsetx = sx / 2;
|
||||
int offsety = sy / 2;
|
||||
// Fix the position when sx or sy are not even.
|
||||
int fixx = sx - 2 * offsetx;
|
||||
int fixy = sy - 2 * offsety;
|
||||
do {
|
||||
proc(xm, ym + y + offsety + fixy, xm - x + offsetx + fixx, data); /* I. Quadrant */
|
||||
proc(xm - y - offsetx, ym - x + offsety + fixy, xm, data); /* II. Quadrant */
|
||||
proc(xm + x - offsetx, ym - y - offsety, xm, data); /* III. Quadrant */
|
||||
proc(xm, ym + x - offsety, xm + y + offsetx + fixx, data); /* IV. Quadrant */
|
||||
r = err;
|
||||
if (r <= y)
|
||||
err += ++y * 2 + 1; /* e_xy+e_y < 0 */
|
||||
if (r > x || err > y)
|
||||
err += ++x * 2 + 1; /* e_xy+e_x > 0 or no 2nd y-step */
|
||||
} while (x < 0);
|
||||
}
|
||||
|
||||
void algo_arc(int xm, int ym, double sa, double ea, int r, void* data, AlgoPixel proc)
|
||||
{
|
||||
int sx = std::cos(sa) * r;
|
||||
int ex = std::cos(ea) * r;
|
||||
|
||||
int startQuadrant;
|
||||
if (sa <= 0 && sa > -PI / 2) {
|
||||
startQuadrant = 4;
|
||||
}
|
||||
else if (sa <= -PI / 2 && sa >= -PI) {
|
||||
startQuadrant = 3;
|
||||
}
|
||||
else if (sa > 0 && sa < PI / 2) {
|
||||
startQuadrant = 1;
|
||||
}
|
||||
else {
|
||||
startQuadrant = 2;
|
||||
}
|
||||
|
||||
int endQuadrant;
|
||||
if (ea <= 0 && ea > -PI / 2) {
|
||||
endQuadrant = 4;
|
||||
}
|
||||
else if (ea <= -PI / 2 && ea >= -PI) {
|
||||
endQuadrant = 3;
|
||||
}
|
||||
else if (ea > 0 && ea < PI / 2) {
|
||||
endQuadrant = 1;
|
||||
}
|
||||
else {
|
||||
endQuadrant = 2;
|
||||
}
|
||||
|
||||
// If start angle and end angle falls in the same quadrant we have to determine
|
||||
// if we have to include the other quadrants or not since the arc is determined
|
||||
// from start angle to end angle in clockwise direction.
|
||||
bool includeQuadrant[4] = { false, false, false, false };
|
||||
if (startQuadrant == endQuadrant) {
|
||||
// If start angle is greater than end angle, include all quadrants for drawing
|
||||
if (sa > ea) {
|
||||
includeQuadrant[0] = true;
|
||||
includeQuadrant[1] = true;
|
||||
includeQuadrant[2] = true;
|
||||
includeQuadrant[3] = true;
|
||||
}
|
||||
else {
|
||||
// start angle is less to or equal to end angle then only include one quadrant
|
||||
// for drawing.
|
||||
includeQuadrant[startQuadrant - 1] = true;
|
||||
}
|
||||
}
|
||||
else {
|
||||
for (int i = startQuadrant - 1; i < startQuadrant - 1 + 4; ++i) {
|
||||
int q = i % 4;
|
||||
includeQuadrant[q] = true;
|
||||
if (q == endQuadrant - 1)
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
int x = -r, y = 0, err = 2 - 2 * r; /* II. Quadrant */
|
||||
do {
|
||||
if (includeQuadrant[0]) {
|
||||
if ((startQuadrant != 1 && endQuadrant != 1) ||
|
||||
(startQuadrant == 1 && endQuadrant != 1 && -x <= sx) ||
|
||||
(startQuadrant != 1 && endQuadrant == 1 && -x >= ex) ||
|
||||
(startQuadrant == 1 && endQuadrant == 1 &&
|
||||
((sa <= ea && -x <= sx && -x >= ex) || (sa > ea && (-x <= sx || -x >= ex)))))
|
||||
proc(xm - x, ym + y, data); /* I. Quadrant */
|
||||
}
|
||||
|
||||
if (includeQuadrant[1]) {
|
||||
if ((startQuadrant != 2 && endQuadrant != 2) ||
|
||||
(startQuadrant == 2 && endQuadrant != 2 && -y <= sx) ||
|
||||
(startQuadrant != 2 && endQuadrant == 2 && -y >= ex) ||
|
||||
(startQuadrant == 2 && endQuadrant == 2 &&
|
||||
((sa <= ea && -y <= sx && -y >= ex) || (sa > ea && (-y <= sx || -y >= ex)))))
|
||||
proc(xm - y, ym - x, data); /* II. Quadrant */
|
||||
}
|
||||
|
||||
if (includeQuadrant[2]) {
|
||||
if ((startQuadrant != 3 && endQuadrant != 3) ||
|
||||
(startQuadrant == 3 && endQuadrant != 3 && x >= sx) ||
|
||||
(startQuadrant != 3 && endQuadrant == 3 && x <= ex) ||
|
||||
(startQuadrant == 3 && endQuadrant == 3 &&
|
||||
((sa <= ea && -x <= -sx && -x >= -ex) || (sa > ea && (-x <= -sx || -x >= -ex)))))
|
||||
proc(xm + x, ym - y, data); /* III. Quadrant */
|
||||
}
|
||||
|
||||
if (includeQuadrant[3]) {
|
||||
if ((startQuadrant != 4 && endQuadrant != 4) ||
|
||||
(startQuadrant == 4 && endQuadrant != 4 && y >= sx) ||
|
||||
(startQuadrant != 4 && endQuadrant == 4 && y <= ex) ||
|
||||
(startQuadrant == 4 && endQuadrant == 4 &&
|
||||
((sa <= ea && y >= sx && y <= ex) || (sa > ea && (y >= sx || y <= ex)))))
|
||||
proc(xm + y, ym + x, data); /* IV. Quadrant */
|
||||
}
|
||||
|
||||
r = err;
|
||||
if (r <= y)
|
||||
err += ++y * 2 + 1; /* e_xy+e_y < 0 */
|
||||
if (r > x || err > y)
|
||||
err += ++x * 2 + 1; /* e_xy+e_x > 0 or no 2nd y-step */
|
||||
} while (x < 0);
|
||||
}
|
||||
|
||||
static int adjust_ellipse_args(int& x0, int& y0, int& x1, int& y1, int& hPixels, int& vPixels)
|
||||
{
|
||||
// hPixels : straight horizontal pixels added to mid region of the ellipse.
|
||||
|
|
|
@ -48,6 +48,12 @@ void algo_line_continuous_with_fix_for_line_brush(int x1,
|
|||
void* data,
|
||||
AlgoPixel proc);
|
||||
|
||||
void algo_circle(int xm, int ym, int sx, int sy, int r, void* data, AlgoPixel proc);
|
||||
|
||||
void algo_circlefill(int xm, int ym, int sx, int sy, int r, void* data, AlgoHLine proc);
|
||||
|
||||
void algo_arc(int xm, int ym, double sa, double ea, int r, void* data, AlgoPixel proc);
|
||||
|
||||
void algo_ellipse(int x1,
|
||||
int y1,
|
||||
int x2,
|
||||
|
|
Loading…
Reference in New Issue