Closed asmwarrior closed 1 year ago
DrawEllipticArc
draws the outer-arc line with the current pen (yes, I believe wxCAP_BUTT
is recommended). It then fills from the outer ring to the pie center with the current brush. To draw the lines from the start and end points of the outer arc back to the center point, I have to use geometry::arc_vertex
and manually draw those lines with the current pen.
Unfortunately, this will always give you a full pie slice (filled with the brush). To create the illusion of a ring, I draw another pie slice (with a smaller bounding box) on top of the larger slice. Making the inner slice use the same background color as the plot gives the illusion that the center of the pie chart is hollow.
I originally tried calling DrawEllipticArc
for an outer ring, then again for an inner ring and then flood filling that area. (That would draw a true outer ring, like what I think you are looking for). Unfortunately, flood filling is not available on macOS, so I couldn't rely on that method.
DrawEllipticArc
draws the outer-arc line with the current pen (yes, I believewxCAP_BUTT
is recommended). It then fills from the outer ring to the pie center with the current brush. To draw the lines from the start and end points of the outer arc back to the center point, I have to usegeometry::arc_vertex
and manually draw those lines with the current pen.Unfortunately, this will always give you a full pie slice (filled with the brush). To create the illusion of a ring, I draw another pie slice (with a smaller bounding box) on top of the larger slice. Making the inner slice use the same background color as the plot gives the illusion that the center of the pie chart is hollow.
Thanks for the explanation. I understand the method you use now.
I originally tried calling
DrawEllipticArc
for an outer ring, then again for an inner ring and then flood filling that area. (That would draw a true outer ring, like what I think you are looking for). Unfortunately, flood filling is not available on macOS, so I couldn't rely on that method.
In-fact, I haven't used wxDC::FloodFill
before. From the document wxWidgets: wxDC Class Reference, it said:
The present implementation for non-Windows platforms may fail to find colour borders if the pixels do not match the colour exactly. However the function will still return true. This method shouldn't be used with wxPaintDC under non-Windows platforms as it uses GetPixel() internally and this may give wrong results, notably in wxGTK. If you need to flood fill wxPaintDC, create a temporary wxMemoryDC, flood fill it and then blit it to, or draw as a bitmap on, wxPaintDC. See the example of doing this in the drawing sample and wxBufferedPaintDC class.
So, it may not work under non-Windows OS.
In my post in wxWidgets forum, some one suggest I should use wxGraphicsPath
way, and that maybe another alternative.
You can fill a wxGraphicsPath
, although you have to specify the points in the pie slice polygon to do that. So, you won't be able to use DrawEllipticArc
; you would need to provide a Bezier curve in your polygon instead. In other words, use geometry::arc_vertex
to get the corners of a slice and use Bezier curves between those to create the wxGraphicsPath
. Seems like a lot of work and not sure how precise the curve would look, but that could be a solution.
You could just draw with a really, really thick pen with DrawEllipticArc
; you won't have an fill capabilities, but if you just want a solid-colored ring that could work too.
You can fill a
wxGraphicsPath
, although you have to specify the points in the pie slice polygon to do that. So, you won't be able to useDrawEllipticArc
; you would need to provide a Bezier curve in your polygon instead. In other words, usegeometry::arc_vertex
to get the corners of a slice and use Bezier curves between those to create thewxGraphicsPath
. Seems like a lot of work and not sure how precise the curve would look, but that could be a solution.
I haven't used Bezier curve before, it looks like a complex task. But the below code works, it use some function calls like "path.AddArc" and "path.AddLineToPoint".
#include <wx/wx.h>
#include <wx/graphics.h>
#include <math.h>
class MyFrame : public wxFrame
{
public:
MyFrame()
: wxFrame(NULL, wxID_ANY, wxT("Annular Sector"), wxDefaultPosition, wxSize(640, 480))
{
Connect(wxEVT_PAINT, wxPaintEventHandler(MyFrame::OnPaint));
}
private:
void OnPaint(wxPaintEvent& event)
{
wxPaintDC pdc(this);
wxGraphicsContext* gc = wxGraphicsContext::Create(pdc);
if (gc)
{
gc->SetPen(wxPen(wxColor(255, 0, 0), 2)); // set the pen color and width
gc->SetBrush(wxBrush(wxColor(255, 255, 0))); // set the brush color
// define the outer and inner radii of the annular sector
const double r1 = 100.0;
const double r2 = 60.0;
// define the start and end angles of the annular sector, in radians
const double startAngle = M_PI / 3.0;
const double endAngle = 2.0 * M_PI / 3.0;
wxPoint center;
center.x = GetClientSize().GetWidth() / 2;
center.y = GetClientSize().GetHeight() / 2;
// calculate the coordinate of each endpoint of the annular sector
wxPoint pt1, pt2, pt3, pt4;
pt1.x = static_cast<int>(center.x + r1 * cos(startAngle));
pt1.y = static_cast<int>(center.y - r1 * sin(startAngle));
pt2.x = static_cast<int>(center.x + r1 * cos(endAngle));
pt2.y = static_cast<int>(center.y - r1 * sin(endAngle));
pt3.x = static_cast<int>(center.x + r2 * cos(endAngle));
pt3.y = static_cast<int>(center.y - r2 * sin(endAngle));
pt4.x = static_cast<int>(center.x + r2 * cos(startAngle));
pt4.y = static_cast<int>(center.y - r2 * sin(startAngle));
// create a graphics path object from the coordinate of each endpoint and draw it
wxGraphicsPath path = gc->CreatePath();
path.MoveToPoint(pt1.x, pt1.y);
//path.AddLineToPoint(pt2.x, pt2.y);
//path.AddLineToPoint(pt3.x, pt3.y);
//path.AddLineToPoint(pt4.x, pt4.y);
//path.AddLineToPoint(pt1.x, pt1.y);
path.AddArc(center.x, center.y, static_cast<float>(r1), 2*M_PI - static_cast<float>(startAngle), 2*M_PI - static_cast<float>(endAngle), false);
path.AddLineToPoint(pt3.x, pt3.y);
path.AddArc(center.x, center.y, static_cast<float>(r2), 2*M_PI - static_cast<float>(endAngle), 2*M_PI - static_cast<float>(startAngle), true);
path.AddLineToPoint(pt1.x, pt1.y);
path.CloseSubpath();
gc->FillPath(path);
delete gc;
}
}
};
class MyApp : public wxApp
{
public:
virtual bool OnInit()
{
MyFrame* frame = new MyFrame();
frame->Show();
return true;
}
};
IMPLEMENT_APP(MyApp);
I have post the above code and screen shot in the forum: How to draw a portion of a ring? - wxWidgets Discussion Forum. I'm not sure such code works under macOS.
Nice. It should be fine on all platforms. As far as I know, floodfill is the only drawing feature not available on macOS.
P.S. Bezier curves requite a lot of...trial and error, shall we say. If you're curious, I use them in the "shapes.cpp" file, where I draw male and female outlines.
Hi, I see this image:
https://github.com/Blake-Madden/Wisteria-Dataviz/blob/main/docs/doxygen/images/DonutChart.svg
I just want to now how do you draw the portion of a ring?
I have looked at the source code: Wisteria-Dataviz/piechart.cpp at 6670a45def9539e25f8be0e7c84763ffd202d2ce · Blake-Madden/Wisteria-Dataviz
I see it only have some GDI call like "dc.DrawEllipticArc".
I have a similar question in wx forum, see here: How to draw a portion of a ring?
So, do you use the pen which has the property set like:
pen.SetCap(wxCAP_BUTT);
or you use other method?Thanks.