mirror of
https://github.com/amyinspace/MagicSetEditor2.git
synced 2026-06-10 13:06:59 -04:00
Be explicit about type of angles: either Radians or Degrees.
Angles are always doubles. Internally use radians as much as possible. git-svn-id: svn://svn.code.sf.net/p/magicseteditor/code/trunk@1605 0fc631ac-6414-0410-93d0-97cfa31319b6
This commit is contained in:
@@ -129,8 +129,8 @@ void SymbolPartRotateAction::perform(bool to_undo) {
|
||||
angle = -angle;
|
||||
}
|
||||
|
||||
void SymbolPartRotateAction::rotateTo(double newAngle) {
|
||||
double oldAngle = angle;
|
||||
void SymbolPartRotateAction::rotateTo(Radians newAngle) {
|
||||
Radians oldAngle = angle;
|
||||
angle = newAngle;
|
||||
// constrain?
|
||||
if (constrain) {
|
||||
@@ -141,7 +141,7 @@ void SymbolPartRotateAction::rotateTo(double newAngle) {
|
||||
if (oldAngle != angle) rotateBy(angle - oldAngle);
|
||||
}
|
||||
|
||||
void SymbolPartRotateAction::rotateBy(double deltaAngle) {
|
||||
void SymbolPartRotateAction::rotateBy(Radians deltaAngle) {
|
||||
// Rotation 'matrix'
|
||||
transform(
|
||||
Matrix2D(cos(deltaAngle), -sin(deltaAngle)
|
||||
|
||||
@@ -85,13 +85,13 @@ class SymbolPartRotateAction : public SymbolPartMatrixAction {
|
||||
virtual void perform(bool to_undo);
|
||||
|
||||
/// Update this action to rotate to a different angle
|
||||
void rotateTo(double newAngle);
|
||||
void rotateTo(Radians newAngle);
|
||||
|
||||
/// Update this action to rotate by a deltaAngle
|
||||
void rotateBy(double deltaAngle);
|
||||
void rotateBy(Radians deltaAngle);
|
||||
|
||||
private:
|
||||
double angle; ///< How much to rotate?
|
||||
Radians angle; ///< How much to rotate?
|
||||
public:
|
||||
bool constrain; ///< Constrain movement?
|
||||
};
|
||||
|
||||
@@ -459,8 +459,8 @@ void SymmetryMoveAction::move(const Vector2D& deltaDelta) {
|
||||
symmetry.handle = snap_vector(symmetry.center + original + delta, snap) - symmetry.center;
|
||||
if (constrain) {
|
||||
// constrain to multiples of 2pi/24 i.e. 24 stops
|
||||
double angle = atan2(symmetry.handle.y, symmetry.handle.x);
|
||||
double mult = (2 * M_PI) / 24;
|
||||
Radians angle = atan2(symmetry.handle.y, symmetry.handle.x);
|
||||
Radians mult = (2 * M_PI) / 24;
|
||||
angle = floor(angle / mult + 0.5) * mult;
|
||||
symmetry.handle = Vector2D(cos(angle), sin(angle)) * symmetry.handle.length();
|
||||
}
|
||||
|
||||
+1
-1
@@ -170,7 +170,7 @@ int Style::update(Context& ctx) {
|
||||
else {int tb = int(top + bottom); top = (tb - height) / 2; bottom = (tb + height) / 2; }
|
||||
// adjust rotation point
|
||||
if (angle != 0 && (automatic_side & (AUTO_LEFT | AUTO_TOP))) {
|
||||
double s = sin(angle * M_PI / 180), c = cos(angle * M_PI / 180);
|
||||
double s = sin(deg_to_rad(angle)), c = cos(deg_to_rad(angle));
|
||||
if (automatic_side & AUTO_LEFT) { // attach right corner instead of left
|
||||
left = left + width * (1 - c);
|
||||
top = top + width * s;
|
||||
|
||||
+1
-1
@@ -101,7 +101,7 @@ class Style : public IntrusivePtrVirtualBase {
|
||||
Scriptable<double> left, top; ///< Position of this field
|
||||
Scriptable<double> width, height; ///< Position of this field
|
||||
Scriptable<double> right, bottom; ///< Position of this field
|
||||
Scriptable<int> angle; ///< Rotation of the box
|
||||
Scriptable<Degrees> angle; ///< Rotation of the box
|
||||
Scriptable<bool> visible; ///< Is this field visible?
|
||||
CachedScriptableMask mask; ///< Mask image
|
||||
|
||||
|
||||
@@ -12,6 +12,7 @@
|
||||
#include <util/prec.hpp>
|
||||
#include <util/reflect.hpp>
|
||||
#include <util/defaultable.hpp>
|
||||
#include <util/angle.hpp>
|
||||
|
||||
class Game;
|
||||
class StyleSheet;
|
||||
@@ -98,7 +99,7 @@ class StyleSheetSettings : public IntrusivePtrBase<StyleSheetSettings> {
|
||||
|
||||
// Rendering/display settings
|
||||
Defaultable<double> card_zoom;
|
||||
Defaultable<int> card_angle;
|
||||
Defaultable<Degrees> card_angle;
|
||||
Defaultable<bool> card_anti_alias;
|
||||
Defaultable<bool> card_borders;
|
||||
Defaultable<bool> card_draw_editing;
|
||||
|
||||
+1
-1
@@ -255,7 +255,7 @@ String SymbolSymmetry::expectedName() const {
|
||||
Bounds SymbolSymmetry::calculateBounds(const Vector2D& origin, const Matrix2D& m, bool is_identity) {
|
||||
Bounds bounds;
|
||||
// See SymbolViewer::draw
|
||||
double b = 2 * handle.angle();
|
||||
Radians b = 2 * handle.angle();
|
||||
int copies = kind == SYMMETRY_REFLECTION ? this->copies & ~1 : this->copies;
|
||||
FOR_EACH_CONST(p, parts) {
|
||||
for (int i = 0 ; i < copies ; ++i) {
|
||||
|
||||
@@ -36,7 +36,7 @@ class GeneratedImage : public ScriptValue {
|
||||
mutable int width, height; ///< Width to force the image to, or 0 to keep the width of the input
|
||||
///< In that case, width and height will be later set to the actual size
|
||||
double zoom; ///< Zoom factor to use, when width=height=0
|
||||
int angle; ///< Angle to rotate image by afterwards
|
||||
Radians angle; ///< Angle to rotate image by afterwards
|
||||
PreserveAspect preserve_aspect;
|
||||
bool saturate;
|
||||
Package* package; ///< Package to load images from
|
||||
@@ -264,13 +264,13 @@ class FlipImageVertical : public SimpleFilterImage {
|
||||
/// Rotate an image
|
||||
class RotateImage : public SimpleFilterImage {
|
||||
public:
|
||||
inline RotateImage(const GeneratedImageP& image, double angle)
|
||||
inline RotateImage(const GeneratedImageP& image, Radians angle)
|
||||
: SimpleFilterImage(image), angle(angle)
|
||||
{}
|
||||
virtual Image generate(const Options& opt) const;
|
||||
virtual bool operator == (const GeneratedImage& that) const;
|
||||
private:
|
||||
double angle;
|
||||
Radians angle;
|
||||
};
|
||||
|
||||
// ----------------------------------------------------------------------------- : EnlargeImage
|
||||
|
||||
+3
-16
@@ -16,6 +16,7 @@
|
||||
|
||||
#include <util/prec.hpp>
|
||||
#include <util/real_point.hpp>
|
||||
#include <util/angle.hpp>
|
||||
#include <gfx/color.hpp>
|
||||
|
||||
// ----------------------------------------------------------------------------- : Resampling
|
||||
@@ -52,29 +53,15 @@ void sharp_resample_and_clip(const Image& img_in, Image& img_out, wxRect rect, i
|
||||
* rect = rectangle to draw in (a rectangle somewhere around pos)
|
||||
* stretch = amount to stretch in the direction of the text after drawing
|
||||
*/
|
||||
void draw_resampled_text(DC& dc, const RealPoint& pos, const RealRect& rect, double stretch, int angle, AColor color, const String& text, int blur_radius = 0, int repeat = 1);
|
||||
void draw_resampled_text(DC& dc, const RealPoint& pos, const RealRect& rect, double stretch, Radians angle, AColor color, const String& text, int blur_radius = 0, int repeat = 1);
|
||||
|
||||
// scaling factor to use when drawing resampled text
|
||||
extern const int text_scaling;
|
||||
|
||||
// ----------------------------------------------------------------------------- : Image rotation
|
||||
|
||||
/// Is an angle a {0,90,180,270}?
|
||||
inline bool straight(int angle) { return angle % 90 == 0; }
|
||||
|
||||
/// Is an angle sideways (90 or 270 degrees)?
|
||||
inline bool sideways(int angle) {
|
||||
int a = (angle + 3600) % 180;
|
||||
return (a > 45 && a < 135);
|
||||
}
|
||||
|
||||
/// Convert radians to degrees
|
||||
inline double rad_to_deg(double rad) { return rad * (180.0 / M_PI); }
|
||||
/// Convert degrees to radians
|
||||
inline double deg_to_rad(double deg) { return deg * (M_PI / 180.0); }
|
||||
|
||||
/// Rotates an image counter clockwise
|
||||
Image rotate_image(const Image& image, double angle);
|
||||
Image rotate_image(const Image& image, Radians angle);
|
||||
|
||||
/// Flip an image horizontally
|
||||
Image flip_image_horizontal(const Image& image);
|
||||
|
||||
@@ -165,7 +165,7 @@ void blur_image_alpha(Image& img) {
|
||||
|
||||
// Draw text by first drawing it using a larger font and then downsampling it
|
||||
// optionally rotated by an angle
|
||||
void draw_resampled_text(DC& dc, const RealPoint& pos, const RealRect& rect, double stretch, int angle, AColor color, const String& text, int blur_radius, int repeat) {
|
||||
void draw_resampled_text(DC& dc, const RealPoint& pos, const RealRect& rect, double stretch, Radians angle, AColor color, const String& text, int blur_radius, int repeat) {
|
||||
// transparent text can be ignored
|
||||
if (color.alpha == 0) return;
|
||||
// enlarge slightly; some fonts are larger then the GetTextExtent tells us (especially italic fonts)
|
||||
@@ -183,11 +183,11 @@ void draw_resampled_text(DC& dc, const RealPoint& pos, const RealRect& rect, dou
|
||||
// now draw the text
|
||||
mdc.SetFont(dc.GetFont());
|
||||
mdc.SetTextForeground(*wxWHITE);
|
||||
mdc.DrawRotatedText(text, xsub, ysub, angle);
|
||||
mdc.DrawRotatedText(text, xsub, ysub, rad_to_deg(angle));
|
||||
// get image
|
||||
mdc.SelectObject(wxNullBitmap);
|
||||
// step 2. sample down
|
||||
double ca = fabs(cos(deg_to_rad(angle))), sa = fabs(sin(deg_to_rad(angle)));
|
||||
double ca = fabs(cos(angle)), sa = fabs(sin(angle));
|
||||
w += int(w * (stretch - 1) * ca); // GCC makes annoying conversion warnings if *= is used here.
|
||||
h += int(h * (stretch - 1) * sa);
|
||||
Image img_small(w, h, false);
|
||||
|
||||
+10
-14
@@ -46,7 +46,7 @@ Image rotate_image_impl(Image img) {
|
||||
// ----------------------------------------------------------------------------- : Rotations
|
||||
|
||||
// Function object to handle rotation
|
||||
struct Rotate90 {
|
||||
struct Rotate90deg {
|
||||
/// Init a rotated image, where the source is w * h pixels
|
||||
inline static void init(Image& img, UInt w, UInt h) {
|
||||
img.Create(h, w, false);
|
||||
@@ -59,7 +59,7 @@ struct Rotate90 {
|
||||
}
|
||||
};
|
||||
|
||||
struct Rotate180 {
|
||||
struct Rotate180deg {
|
||||
inline static void init(Image& img, UInt w, UInt h) {
|
||||
img.Create(w, h, false);
|
||||
}
|
||||
@@ -70,7 +70,7 @@ struct Rotate180 {
|
||||
}
|
||||
};
|
||||
|
||||
struct Rotate270 {
|
||||
struct Rotate270deg {
|
||||
inline static void init(Image& img, UInt w, UInt h) {
|
||||
img.Create(h, w, false);
|
||||
}
|
||||
@@ -83,19 +83,15 @@ struct Rotate270 {
|
||||
|
||||
// ----------------------------------------------------------------------------- : Interface
|
||||
|
||||
double almost_equal(double x, double y) {
|
||||
return fabs(x-y) < 1e-6;
|
||||
}
|
||||
|
||||
Image rotate_image(const Image& image, double angle) {
|
||||
double a = fmod(angle, 360);
|
||||
if (almost_equal(a, 0)) return image;
|
||||
if (almost_equal(a, 90)) return rotate_image_impl<Rotate90> (image);
|
||||
if (almost_equal(a,180)) return rotate_image_impl<Rotate180>(image);
|
||||
if (almost_equal(a,270)) return rotate_image_impl<Rotate270>(image);
|
||||
Image rotate_image(const Image& image, Radians angle) {
|
||||
double a = constrain_radians(angle);
|
||||
if (is_rad0(a)) return image;
|
||||
if (is_rad90(a)) return rotate_image_impl<Rotate90deg> (image);
|
||||
if (is_rad180(a)) return rotate_image_impl<Rotate180deg>(image);
|
||||
if (is_rad270(a)) return rotate_image_impl<Rotate270deg>(image);
|
||||
else {
|
||||
if (!image.HasAlpha()) const_cast<Image&>(image).InitAlpha();
|
||||
return image.Rotate(angle * M_PI / 180, wxPoint(0,0));
|
||||
return image.Rotate(angle, wxPoint(0,0));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -30,7 +30,7 @@ wxSize CardViewer::DoGetBestSize() const {
|
||||
if (!stylesheet) stylesheet = set->stylesheet;
|
||||
StyleSheetSettings& ss = settings.stylesheetSettingsFor(*stylesheet);
|
||||
wxSize size(int(stylesheet->card_width * ss.card_zoom()), int(stylesheet->card_height * ss.card_zoom()));
|
||||
if (sideways(ss.card_angle())) swap(size.x, size.y);
|
||||
if (is_sideways(deg_to_rad(ss.card_angle()))) swap(size.x, size.y);
|
||||
return size + ws - cs;
|
||||
}
|
||||
return cs;
|
||||
@@ -143,7 +143,7 @@ Rotation CardViewer::getRotation() const {
|
||||
if (!stylesheet) stylesheet = set->stylesheet;
|
||||
StyleSheetSettings& ss = settings.stylesheetSettingsFor(*stylesheet);
|
||||
int dx = GetScrollPos(wxHORIZONTAL), dy = GetScrollPos(wxVERTICAL);
|
||||
return Rotation(ss.card_angle(), stylesheet->getCardRect().move(-dx,-dy,0,0), ss.card_zoom(), 1.0, ROTATION_ATTACH_TOP_LEFT);
|
||||
return Rotation(deg_to_rad(ss.card_angle()), stylesheet->getCardRect().move(-dx,-dy,0,0), ss.card_zoom(), 1.0, ROTATION_ATTACH_TOP_LEFT);
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------------- : Event table
|
||||
|
||||
@@ -493,7 +493,7 @@ void PieGraph::draw(RotatedDC& dc, int current, DrawLayer layer) const {
|
||||
Color fg = wxSystemSettings::GetColour(wxSYS_COLOUR_WINDOWTEXT);
|
||||
dc.SetPen(fg);
|
||||
// draw pies
|
||||
double angle = M_PI/2;
|
||||
Radians angle = M_PI/2;
|
||||
int i = 0;
|
||||
FOR_EACH_CONST(g, axis.groups) {
|
||||
// draw pie
|
||||
@@ -501,7 +501,7 @@ void PieGraph::draw(RotatedDC& dc, int current, DrawLayer layer) const {
|
||||
if (g.size > 0) {
|
||||
bool active = i == current;
|
||||
dc.SetPen(active ? fg : lerp(fg,g.color,0.5));
|
||||
double end_angle = angle - 2 * M_PI * (double)g.size / axis.total;
|
||||
Radians end_angle = angle - 2 * M_PI * (double)g.size / axis.total;
|
||||
dc.DrawEllipticArc(pie_pos, active ? pie_size_large : pie_size, end_angle, angle);
|
||||
angle = end_angle;
|
||||
}
|
||||
@@ -510,7 +510,7 @@ void PieGraph::draw(RotatedDC& dc, int current, DrawLayer layer) const {
|
||||
// draw spokes
|
||||
if (axis.groups.size() > 1) {
|
||||
int i = 0;
|
||||
double angle = M_PI/2;
|
||||
Radians angle = M_PI/2;
|
||||
FOR_EACH_CONST(g, axis.groups) {
|
||||
if (true) {
|
||||
int i2 = (i - 1 + (int)axis.groups.size()) % (int)axis.groups.size();
|
||||
@@ -539,7 +539,7 @@ int PieGraph::findItem(const RealPoint& pos, const RealRect& screen_rect, bool t
|
||||
double pos_angle = atan2(-delta.y, delta.x) - M_PI/2; // in range [-pi..pi]
|
||||
if (pos_angle < 0) pos_angle += 2 * M_PI;
|
||||
// find angle
|
||||
double angle = 2 * M_PI;
|
||||
Radians angle = 2 * M_PI;
|
||||
int i = 0;
|
||||
FOR_EACH_CONST(g, axis.groups) {
|
||||
angle -= 2 * M_PI * (double)g.size / axis.total;
|
||||
@@ -705,14 +705,14 @@ void ScatterPieGraph::draw(RotatedDC& dc, const vector<int>& current, DrawLayer
|
||||
RealSize radius_s(radius,radius);
|
||||
RealPoint center(screen_rect.left() + (x+0.5) * size.width + 0.5, screen_rect.bottom() - (y+0.5) * size.height + 0.5);
|
||||
// draw pie slices
|
||||
double angle = 0;
|
||||
Radians angle = 0;
|
||||
size_t j = 0;
|
||||
FOR_EACH(g, axis3.groups) {
|
||||
UInt val = values3D[i * axis3.groups.size() + j++];
|
||||
if (val > 0) {
|
||||
dc.SetBrush(g.color);
|
||||
dc.SetPen(active ? fg : lerp(fg,g.color,0.5));
|
||||
double end_angle = angle + 2 * M_PI * (double)val / value;
|
||||
Radians end_angle = angle + 2 * M_PI * (double)val / value;
|
||||
dc.DrawEllipticArc(center, radius_s, angle, end_angle);
|
||||
angle = end_angle;
|
||||
}
|
||||
|
||||
@@ -38,7 +38,7 @@ void NativeLookEditor::drawViewer(RotatedDC& dc, ValueViewer& v) {
|
||||
if (!e || e->drawLabel()) {
|
||||
// draw control border and box
|
||||
Style& s = *v.getStyle();
|
||||
draw_control_box(this, dc.getDC(), dc.trRectStraight(s.getInternalRect().grow(1)), current_editor == e, e != nullptr);
|
||||
draw_control_box(this, dc.getDC(), dc.trRectToBB(s.getInternalRect().grow(1)), current_editor == e, e != nullptr);
|
||||
// draw label
|
||||
dc.SetFont(*wxNORMAL_FONT);
|
||||
dc.SetTextForeground(wxSystemSettings::GetColour(wxSYS_COLOUR_BTNTEXT));
|
||||
|
||||
@@ -39,7 +39,7 @@ class TextBufferDC : public wxMemoryDC {
|
||||
TextBufferDC(int width, int height, bool buffer_text);
|
||||
|
||||
virtual void DoDrawText(const String& str, int x, int y);
|
||||
virtual void DoDrawRotatedText(const String& str, int x, int y, double angle);
|
||||
virtual void DoDrawRotatedText(const String& str, int x, int y, Radians angle);
|
||||
|
||||
/// Copy the contents of the DC to a target device, this DC becomes invalid
|
||||
void drawToDevice(DC& dc, int x = 0, int y = 0);
|
||||
@@ -51,10 +51,10 @@ class TextBufferDC : public wxMemoryDC {
|
||||
Color color;
|
||||
int x, y;
|
||||
String text;
|
||||
double angle;
|
||||
Radians angle;
|
||||
double user_scale_x, user_scale_y;
|
||||
|
||||
TextDraw(wxFont font, Color color, double user_scale_x, double user_scale_y, int x, int y, String text, double angle = 0)
|
||||
TextDraw(wxFont font, Color color, double user_scale_x, double user_scale_y, int x, int y, String text, Radians angle = 0)
|
||||
: font(font), color(color), x(x), y(y), text(text), angle(angle), user_scale_x(user_scale_x), user_scale_y(user_scale_y)
|
||||
{}
|
||||
};
|
||||
@@ -83,13 +83,13 @@ void TextBufferDC::DoDrawText(const String& str, int x, int y) {
|
||||
wxMemoryDC::DoDrawText(str,x,y);
|
||||
}
|
||||
}
|
||||
void TextBufferDC::DoDrawRotatedText(const String& str, int x, int y, double angle) {
|
||||
void TextBufferDC::DoDrawRotatedText(const String& str, int x, int y, Radians angle) {
|
||||
if (buffer_text) {
|
||||
double usx,usy;
|
||||
GetUserScale(&usx, &usy);
|
||||
text.push_back( intrusive(new TextDraw(GetFont(), GetTextForeground(), usx, usy, x, y, str, angle)) );
|
||||
} else {
|
||||
wxMemoryDC::DoDrawRotatedText(str,x,y,angle);
|
||||
wxMemoryDC::DoDrawRotatedText(str,x,y,rad_to_deg(angle));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -104,8 +104,8 @@ void TextBufferDC::drawToDevice(DC& dc, int x, int y) {
|
||||
dc.SetUserScale(usx * t->user_scale_x, usx * t->user_scale_y);
|
||||
dc.SetFont (t->font);
|
||||
dc.SetTextForeground(t->color);
|
||||
if (t->angle) {
|
||||
dc.DrawRotatedText(t->text, t->x + x, t->y + y, t->angle);
|
||||
if (!is_rad0(t->angle)) {
|
||||
dc.DrawRotatedText(t->text, t->x + x, t->y + y, rad_to_deg(t->angle));
|
||||
} else {
|
||||
dc.DrawText(t->text, t->x + x, t->y + y);
|
||||
}
|
||||
|
||||
@@ -14,6 +14,7 @@
|
||||
#include <gui/about_window.hpp> // for HoverButton
|
||||
#include <gui/update_checker.hpp>
|
||||
#include <gui/icon_menu.hpp>
|
||||
#include <gui/drop_down_list.hpp>
|
||||
#include <gui/util.hpp>
|
||||
#include <data/set.hpp>
|
||||
#include <data/game.hpp>
|
||||
@@ -34,6 +35,25 @@ DECLARE_TYPEOF_COLLECTION(AddCardsScriptP);
|
||||
#define HAVE_TOOLBAR_DROPDOWN_MENU 1
|
||||
#endif
|
||||
|
||||
// ----------------------------------------------------------------------------- : DropDownMRUList
|
||||
|
||||
/// A drop down list of recent choices, for autocomplete
|
||||
class DropDownMRUList : public DropDownList {
|
||||
public:
|
||||
DropDownMRUList(Window* parent, vector<String> const& choices)
|
||||
: DropDownList(parent)
|
||||
, choices(choices)
|
||||
{}
|
||||
|
||||
vector<String> choices;
|
||||
|
||||
protected:
|
||||
virtual size_t selection() const { return NO_SELECTION; }
|
||||
virtual size_t itemCount() const { return choices.size(); }
|
||||
virtual String itemText(size_t item) const { return choices.at(item); }
|
||||
virtual void select(size_t item);
|
||||
};
|
||||
|
||||
// ----------------------------------------------------------------------------- : FilterControl
|
||||
|
||||
/// Text control that forwards focus events to the parent
|
||||
@@ -467,7 +487,7 @@ void CardsPanel::onCommand(int id) {
|
||||
case ID_CARD_ROTATE_0: case ID_CARD_ROTATE_90: case ID_CARD_ROTATE_180: case ID_CARD_ROTATE_270: {
|
||||
StyleSheetSettings& ss = settings.stylesheetSettingsFor(set->stylesheetFor(card_list->getCard()));
|
||||
ss.card_angle.assign(
|
||||
id == ID_CARD_ROTATE ? (ss.card_angle() + 90) % 360
|
||||
id == ID_CARD_ROTATE ? sane_fmod(ss.card_angle() + 90, 360)
|
||||
: id == ID_CARD_ROTATE_0 ? 0
|
||||
: id == ID_CARD_ROTATE_90 ? 90
|
||||
: id == ID_CARD_ROTATE_180 ? 180
|
||||
|
||||
@@ -258,16 +258,18 @@ void StatDimensionList::drawItem(DC& dc, int x, int y, size_t item) {
|
||||
RealPoint pos = align_in_rect(ALIGN_MIDDLE_LEFT, size, rect);
|
||||
dc.DrawText(str, (int)pos.x, (int)pos.y);
|
||||
// draw selection icon
|
||||
for (size_t j = 1 ; j <= prefered_dimension_count ; ++j) {
|
||||
for (size_t j = 1 ; j <= dimensions.size() ; ++j) {
|
||||
bool prefered = j <= prefered_dimension_count;
|
||||
if (isSelected(item,j)) {
|
||||
// TODO: different icons for different dimensions
|
||||
/*
|
||||
int cx = x + columns[j].offset.x + columns[j].size.x/2;
|
||||
int cy = y + columns[j].offset.y + columns[j].size.y/2;
|
||||
dc.SetPen(*wxTRANSPARENT_PEN);
|
||||
dc.SetBrush(wxSystemSettings::GetColour(wxSYS_COLOUR_WINDOWTEXT));
|
||||
dc.DrawCircle(cx,cy,6);
|
||||
*/
|
||||
int cx = x + subcolumns[j].offset.x + subcolumns[j].size.x/2;
|
||||
int cy = y + subcolumns[j].offset.y + subcolumns[j].size.y/2;
|
||||
dc.SetPen(*wxTRANSPARENT_PEN);
|
||||
dc.SetBrush(prefered ? wxSystemSettings::GetColour(wxSYS_COLOUR_WINDOWTEXT)
|
||||
: lerp(wxSystemSettings::GetColour(wxSYS_COLOUR_WINDOWTEXT),wxSystemSettings::GetColour(wxSYS_COLOUR_WINDOW),0.5));
|
||||
dc.DrawCircle(cx,cy,6);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -31,12 +31,12 @@ SymbolSelectEditor::SymbolSelectEditor(SymbolControl* control, bool rotate)
|
||||
// Load resource images
|
||||
Image rot = load_resource_image(_("handle_rotate"));
|
||||
handleRotateTL = wxBitmap(rot);
|
||||
handleRotateTR = wxBitmap(rotate_image(rot,90));
|
||||
handleRotateBR = wxBitmap(rotate_image(rot,180));
|
||||
handleRotateBL = wxBitmap(rotate_image(rot,270));
|
||||
handleRotateTR = wxBitmap(rotate_image(rot,rad90));
|
||||
handleRotateBR = wxBitmap(rotate_image(rot,rad180));
|
||||
handleRotateBL = wxBitmap(rotate_image(rot,rad270));
|
||||
Image shear = load_resource_image(_("handle_shear_x"));
|
||||
handleShearX = wxBitmap(shear);
|
||||
handleShearY = wxBitmap(rotate_image(shear,90));
|
||||
handleShearY = wxBitmap(rotate_image(shear,rad90));
|
||||
handleCenter = wxBitmap(load_resource_image(_("handle_center")));
|
||||
// Make sure all parts have updated bounds
|
||||
getSymbol()->updateBounds();
|
||||
@@ -353,7 +353,7 @@ void SymbolSelectEditor::onMouseDrag (const Vector2D& from, const Vector2D& to,
|
||||
scaleAction->move(dMin, dMax);
|
||||
} else if (rotateAction) {
|
||||
// rotate the selected parts
|
||||
double angle = angleTo(to);
|
||||
Radians angle = angleTo(to);
|
||||
rotateAction->constrain = ev.ControlDown();
|
||||
rotateAction->rotateTo(startAngle - angle);
|
||||
} else if (shearAction) {
|
||||
|
||||
@@ -84,7 +84,7 @@ class SymbolSelectEditor : public SymbolEditorBase {
|
||||
CLICK_TOGGLE, // same selection, not moved -> switch to rotate mode
|
||||
} click_mode;
|
||||
// At what angle is the handle we started draging for rotation
|
||||
double startAngle;
|
||||
Radians startAngle;
|
||||
// what side are we dragging/rotating on?
|
||||
int scaleX, scaleY;
|
||||
// have we dragged?
|
||||
@@ -110,7 +110,7 @@ class SymbolSelectEditor : public SymbolEditorBase {
|
||||
bool onAnyHandle(const Vector2D& mpos, int* dxOut, int* dyOut);
|
||||
|
||||
/// Angle between center and pos
|
||||
double angleTo(const Vector2D& pos);
|
||||
Radians angleTo(const Vector2D& pos);
|
||||
|
||||
/// Update minV and maxV to be the bounding box of the selected_parts
|
||||
/// Updates center to be the rotation center of the parts
|
||||
|
||||
@@ -297,7 +297,7 @@ void ChoiceValueEditor::onLoseFocus() {
|
||||
void ChoiceValueEditor::draw(RotatedDC& dc) {
|
||||
ChoiceValueViewer::draw(dc);
|
||||
if (nativeLook()) {
|
||||
draw_drop_down_arrow(&editor(), dc.getDC(), dc.trRectStraight(style().getInternalRect().grow(1)), drop_down->IsShown());
|
||||
draw_drop_down_arrow(&editor(), dc.getDC(), dc.trRectToBB(style().getInternalRect().grow(1)), drop_down->IsShown());
|
||||
}
|
||||
}
|
||||
void ChoiceValueEditor::determineSize(bool) {
|
||||
|
||||
@@ -88,7 +88,7 @@ class DropDownChoiceListBase : public DropDownList {
|
||||
|
||||
// ----------------------------------------------------------------------------- : DropDownChoiceList
|
||||
|
||||
/// A drop down list of color choices
|
||||
/// A drop down list of choices
|
||||
class DropDownChoiceList : public DropDownChoiceListBase {
|
||||
public:
|
||||
DropDownChoiceList(Window* parent, bool is_submenu, ValueViewer& cve, ChoiceField::ChoiceP group);
|
||||
|
||||
@@ -142,7 +142,7 @@ void ColorValueEditor::onLoseFocus() {
|
||||
void ColorValueEditor::draw(RotatedDC& dc) {
|
||||
ColorValueViewer::draw(dc);
|
||||
if (nativeLook()) {
|
||||
draw_drop_down_arrow(&editor(), dc.getDC(), dc.trRectStraight(dc.getInternalRect().grow(1)), drop_down->IsShown());
|
||||
draw_drop_down_arrow(&editor(), dc.getDC(), dc.trRectToBB(dc.getInternalRect().grow(1)), drop_down->IsShown());
|
||||
}
|
||||
}
|
||||
void ColorValueEditor::determineSize(bool) {
|
||||
|
||||
@@ -103,7 +103,7 @@ void PackageChoiceValueEditor::onLoseFocus() {
|
||||
void PackageChoiceValueEditor::draw(RotatedDC& dc) {
|
||||
PackageChoiceValueViewer::draw(dc);
|
||||
if (nativeLook()) {
|
||||
draw_drop_down_arrow(&editor(), dc.getDC(), dc.trRectStraight(style().getInternalRect().grow(1)), drop_down && drop_down->IsShown());
|
||||
draw_drop_down_arrow(&editor(), dc.getDC(), dc.trRectToBB(style().getInternalRect().grow(1)), drop_down && drop_down->IsShown());
|
||||
}
|
||||
}
|
||||
void PackageChoiceValueEditor::determineSize(bool) {
|
||||
|
||||
@@ -717,8 +717,8 @@ wxCursor TextValueEditor::cursor(const RealPoint& pos) const {
|
||||
hovered_words = p.get();
|
||||
const_cast<TextValueEditor*>(this)->redrawWordListIndicators();
|
||||
}
|
||||
int angle = viewer.getRotation().getAngle() + style().angle;
|
||||
if (sideways(angle)) { // 90 or 270 degrees
|
||||
Radians angle = viewer.getRotation().getAngle() + deg_to_rad(style().angle);
|
||||
if (is_sideways(angle)) { // 90 or 270 degrees
|
||||
if (!rotated_ibeam.Ok()) {
|
||||
rotated_ibeam = wxCursor(load_resource_cursor(_("rot_text")));
|
||||
}
|
||||
@@ -1248,7 +1248,7 @@ bool TextValueEditor::search(FindInfo& find, bool from_start) {
|
||||
|
||||
void TextValueEditor::determineSize(bool force_fit) {
|
||||
if (!nativeLook()) return;
|
||||
style().angle = 0; // no rotation in nativeLook
|
||||
style().angle = 0; // force no rotation in nativeLook
|
||||
if (scrollbar) {
|
||||
// muliline, determine scrollbar size
|
||||
Rotation rot = viewer.getRotation();
|
||||
|
||||
+3
-2
@@ -292,9 +292,10 @@ void MSE::HandleEvent(wxEvtHandler *handler, wxEventFunction func, wxEvent& even
|
||||
// ----------------------------------------------------------------------------- : Events
|
||||
|
||||
int MSE::FilterEvent(wxEvent& ev) {
|
||||
if (ev.GetEventType() == wxEVT_MOUSE_CAPTURE_LOST) {
|
||||
/*if (ev.GetEventType() == wxEVT_MOUSE_CAPTURE_LOST) {
|
||||
return 1;
|
||||
} else {
|
||||
return -1;
|
||||
}
|
||||
}*/
|
||||
return -1;
|
||||
}
|
||||
|
||||
@@ -2551,6 +2551,10 @@
|
||||
<Filter
|
||||
Name="aux"
|
||||
>
|
||||
<File
|
||||
RelativePath=".\util\angle.hpp"
|
||||
>
|
||||
</File>
|
||||
<File
|
||||
RelativePath=".\util\atomic.hpp"
|
||||
>
|
||||
|
||||
@@ -116,7 +116,7 @@ Context& DataViewer::getContext() const {
|
||||
Rotation DataViewer::getRotation() const {
|
||||
if (!stylesheet) stylesheet = set->stylesheet;
|
||||
StyleSheetSettings& ss = settings.stylesheetSettingsFor(*stylesheet);
|
||||
return Rotation(ss.card_angle(), stylesheet->getCardRect(), ss.card_zoom(), 1.0, ROTATION_ATTACH_TOP_LEFT);
|
||||
return Rotation(deg_to_rad(ss.card_angle()), stylesheet->getCardRect(), ss.card_zoom(), 1.0, ROTATION_ATTACH_TOP_LEFT);
|
||||
}
|
||||
|
||||
Package& DataViewer::getStylePackage() const {
|
||||
|
||||
@@ -151,7 +151,7 @@ void SymbolViewer::combineSymbolPart(DC& dc, const SymbolPart& part, bool& paint
|
||||
}
|
||||
} else if (const SymbolSymmetry* s = part.isSymbolSymmetry()) {
|
||||
// Draw all parts, in reverse order (bottom to top), also draw rotated copies
|
||||
double b = 2 * s->handle.angle();
|
||||
Radians b = 2 * s->handle.angle();
|
||||
Matrix2D old_m = multiply;
|
||||
Vector2D old_o = origin;
|
||||
int copies = s->kind == SYMMETRY_REFLECTION ? s->copies / 2 * 2 : s->copies;
|
||||
@@ -345,11 +345,11 @@ void SymbolViewer::highlightPart(DC& dc, const SymbolSymmetry& sym, HighlightSty
|
||||
// center
|
||||
RealPoint center = rotation.tr(sym.center);
|
||||
// draw 'spokes'
|
||||
double angle = atan2(sym.handle.y, sym.handle.x);
|
||||
Radians angle = atan2(sym.handle.y, sym.handle.x);
|
||||
dc.SetPen(wxPen(color, sym.kind == SYMMETRY_ROTATION ? 1 : 3));
|
||||
int copies = sym.kind == SYMMETRY_REFLECTION ? sym.copies / 2 * 2 : sym.copies;
|
||||
for (int i = 0; i < copies ; ++i) {
|
||||
double a = angle + (i + 0.5) * 2 * M_PI / copies;
|
||||
Radians a = angle + (i + 0.5) * 2 * M_PI / copies;
|
||||
Vector2D dir(cos(a), sin(a));
|
||||
Vector2D dir2 = rotation.tr(sym.center + 2 * dir);
|
||||
dc.DrawLine(int(center.x), int(center.y), int(dir2.x), int(dir2.y));
|
||||
|
||||
@@ -19,7 +19,7 @@ void ImageValueViewer::draw(RotatedDC& dc) {
|
||||
DrawWhat what = viewer.drawWhat(this);
|
||||
// reset?
|
||||
int w = max(0,(int)dc.trX(style().width)), h = max(0,(int)dc.trY(style().height));
|
||||
int a = dc.trAngle(0); //% TODO : Add getAngle()?
|
||||
Radians a = dc.getAngle();
|
||||
const AlphaMask& alpha_mask = getMask(w,h);
|
||||
if (bitmap.Ok() && (a != angle || size.width != w || size.height != h)) {
|
||||
bitmap = Bitmap();
|
||||
@@ -46,7 +46,7 @@ void ImageValueViewer::draw(RotatedDC& dc) {
|
||||
is_default = true;
|
||||
if (what & DRAW_EDITING) {
|
||||
bitmap = imagePlaceholder(dc, w, h, image, what & DRAW_EDITING);
|
||||
if (alpha_mask.isLoaded() || a) {
|
||||
if (alpha_mask.isLoaded() || !is_rad0(a)) {
|
||||
image = bitmap.ConvertToImage(); // we need to convert back to an image
|
||||
} else {
|
||||
image = Image();
|
||||
@@ -57,7 +57,7 @@ void ImageValueViewer::draw(RotatedDC& dc) {
|
||||
if (!image.Ok() && !bitmap.Ok() && style().width > 40) {
|
||||
// placeholder bitmap
|
||||
bitmap = imagePlaceholder(dc, w, h, wxNullImage, what & DRAW_EDITING);
|
||||
if (alpha_mask.isLoaded() || a) {
|
||||
if (alpha_mask.isLoaded() || !is_rad0(a)) {
|
||||
// we need to convert back to an image
|
||||
image = bitmap.ConvertToImage();
|
||||
}
|
||||
|
||||
@@ -29,8 +29,8 @@ class ImageValueViewer : public ValueViewer {
|
||||
private:
|
||||
Bitmap bitmap; ///< Cached bitmap
|
||||
RealSize size; ///< Size of cached bitmap
|
||||
int angle; ///< Angle of cached bitmap
|
||||
int is_default; ///< Is the default placeholder image used?
|
||||
Radians angle; ///< Angle of cached bitmap
|
||||
bool is_default; ///< Is the default placeholder image used?
|
||||
|
||||
/// Generate a placeholder image
|
||||
static Bitmap imagePlaceholder(const Rotation& rot, UInt w, UInt h, const Image& background, bool editing);
|
||||
|
||||
@@ -36,7 +36,7 @@ RealRect ValueViewer::boundingBox() const {
|
||||
}
|
||||
|
||||
Rotation ValueViewer::getRotation() const {
|
||||
return Rotation(getStyle()->angle, getStyle()->getExternalRect(), 1.0, getStretch());
|
||||
return Rotation(deg_to_rad(getStyle()->angle), getStyle()->getExternalRect(), 1.0, getStretch());
|
||||
}
|
||||
|
||||
bool ValueViewer::setFieldBorderPen(RotatedDC& dc) {
|
||||
|
||||
@@ -131,8 +131,8 @@ SCRIPT_FUNCTION(flip_vertical) {
|
||||
|
||||
SCRIPT_FUNCTION(rotate) {
|
||||
SCRIPT_PARAM_C(GeneratedImageP, input);
|
||||
SCRIPT_PARAM_N(double, _("angle"), angle);
|
||||
return intrusive(new RotateImage(input,angle));
|
||||
SCRIPT_PARAM_N(Degrees, _("angle"), angle);
|
||||
return intrusive(new RotateImage(input,deg_to_rad(angle)));
|
||||
}
|
||||
|
||||
SCRIPT_FUNCTION(drop_shadow) {
|
||||
|
||||
@@ -125,11 +125,13 @@ void CachedScriptableImage::generateCached(const GeneratedImage::Options& option
|
||||
}
|
||||
} else {
|
||||
// image
|
||||
if (cached_i.Ok() && (options.angle - cached_angle + 360) % 90 == 0) {
|
||||
Radians relative_rotation = options.angle + rad360 - cached_angle;
|
||||
if (cached_i.Ok() && is_straight(relative_rotation)) {
|
||||
// we need only an {0,90,180,270} degree rotation compared to the cached one, this doesn't reduce image quality
|
||||
if ((w_ok && h_ok) || (options.preserve_aspect == ASPECT_FIT && (w_ok || h_ok))) { // only one dimension has to fit when fitting
|
||||
if (options.angle != cached_angle) {
|
||||
// rotate cached image
|
||||
cached_i = rotate_image(cached_i, options.angle - cached_angle + 360);
|
||||
cached_i = rotate_image(cached_i, relative_rotation);
|
||||
cached_angle = options.angle;
|
||||
}
|
||||
*image = cached_i;
|
||||
@@ -137,8 +139,8 @@ void CachedScriptableImage::generateCached(const GeneratedImage::Options& option
|
||||
}
|
||||
}
|
||||
}
|
||||
// hack: temporarily set angle to 0, do actual rotation after applying mask
|
||||
int a = options.angle;
|
||||
// hack(part1): temporarily set angle to 0, do actual rotation after applying mask
|
||||
Radians a = options.angle;
|
||||
const_cast<GeneratedImage::Options&>(options).angle = 0;
|
||||
// generate
|
||||
cached_i = generate(options);
|
||||
@@ -153,7 +155,7 @@ void CachedScriptableImage::generateCached(const GeneratedImage::Options& option
|
||||
mask->get(mask_opts).setAlpha(cached_i);
|
||||
}
|
||||
if (options.angle != 0) {
|
||||
// hack(pt2) do the actual rotation now
|
||||
// hack(part2) do the actual rotation now
|
||||
cached_i = rotate_image(cached_i, options.angle);
|
||||
}
|
||||
if (*combine <= COMBINE_NORMAL) {
|
||||
|
||||
@@ -105,7 +105,7 @@ class CachedScriptableImage : public ScriptableImage {
|
||||
Image cached_i; ///< The cached image
|
||||
Bitmap cached_b; ///< *or* the cached bitmap
|
||||
RealSize cached_size; ///< The size of the image before rotating
|
||||
int cached_angle;
|
||||
Radians cached_angle;
|
||||
};
|
||||
|
||||
// ----------------------------------------------------------------------------- : CachedScriptableMask
|
||||
|
||||
@@ -0,0 +1,77 @@
|
||||
//+----------------------------------------------------------------------------+
|
||||
//| Description: Magic Set Editor - Program to make Magic (tm) cards |
|
||||
//| Copyright: (C) 2001 - 2010 Twan van Laarhoven and Sean Hunt |
|
||||
//| License: GNU General Public License 2 or later (see file COPYING) |
|
||||
//+----------------------------------------------------------------------------+
|
||||
|
||||
#ifndef HEADER_UTIL_ANGLE
|
||||
#define HEADER_UTIL_ANGLE
|
||||
|
||||
// ----------------------------------------------------------------------------- : Includes
|
||||
|
||||
#include <util/prec.hpp>
|
||||
|
||||
// ----------------------------------------------------------------------------- : Degrees & radians
|
||||
|
||||
typedef double Degrees;
|
||||
typedef double Radians;
|
||||
|
||||
/// Convert radians to degrees
|
||||
inline Degrees rad_to_deg(Radians rad) { return rad * (180.0 / M_PI); }
|
||||
/// Convert degrees to radians
|
||||
inline Radians deg_to_rad(Degrees deg) { return deg * (M_PI / 180.0); }
|
||||
|
||||
// ----------------------------------------------------------------------------- : Angle constants
|
||||
|
||||
const Radians rad0 = 0;
|
||||
const Radians rad45 = 0.25*M_PI;
|
||||
const Radians rad90 = 0.5*M_PI;
|
||||
const Radians rad180 = M_PI;
|
||||
const Radians rad270 = 1.5*M_PI;
|
||||
const Radians rad360 = 2.0*M_PI;
|
||||
|
||||
/// Are two floating point numbers equal up to a small epsilon?
|
||||
inline bool almost_equal(double x, double y) {
|
||||
return fabs(x-y) < 1e-10;
|
||||
}
|
||||
inline bool is_rad0(double x) {
|
||||
return almost_equal(x,0) || almost_equal(x,rad360);
|
||||
}
|
||||
inline bool is_rad90(double x) {
|
||||
return almost_equal(x,rad90);
|
||||
}
|
||||
inline bool is_rad180(double x) {
|
||||
return almost_equal(x,rad180);
|
||||
}
|
||||
inline bool is_rad270(double x) {
|
||||
return almost_equal(x,rad270);
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------------- : Angle functions
|
||||
|
||||
// mod as it should be: answer in range [0..m)
|
||||
inline double sane_fmod(double x, double m) {
|
||||
double ans = fmod(x,m);
|
||||
if (ans < 0) return ans + m;
|
||||
else return ans;
|
||||
}
|
||||
|
||||
// constrain an angle to [0..2pi)
|
||||
inline Radians constrain_radians(Radians angle) {
|
||||
return sane_fmod(angle, 2*M_PI);
|
||||
}
|
||||
|
||||
/// Is an angle a multiple of 90 degrees?
|
||||
inline bool is_straight(Radians angle) {
|
||||
return almost_equal(sane_fmod(angle+rad45,rad90), rad45);
|
||||
}
|
||||
|
||||
/// Is an angle sideways (i.e. closer to 90 or 270 degrees than to 0 or 180 degrees)?
|
||||
inline bool is_sideways(Radians angle) {
|
||||
double a = sane_fmod(angle,M_PI);
|
||||
return (a > 0.25*M_PI && a < 0.75*M_PI);
|
||||
}
|
||||
|
||||
|
||||
// ----------------------------------------------------------------------------- : EOF
|
||||
#endif
|
||||
+42
-67
@@ -13,13 +13,8 @@
|
||||
|
||||
// ----------------------------------------------------------------------------- : Rotation
|
||||
|
||||
// constrain an angle to [0..360)
|
||||
int constrain_angle(int angle) {
|
||||
return (angle + 3600) % 360;
|
||||
}
|
||||
|
||||
Rotation::Rotation(int angle, const RealRect& rect, double zoom, double stretch, RotationFlags flags)
|
||||
: angle(constrain_angle(angle))
|
||||
Rotation::Rotation(Radians angle, const RealRect& rect, double zoom, double stretch, RotationFlags flags)
|
||||
: angle(constrain_radians(angle))
|
||||
, size(rect.size())
|
||||
, origin(rect.position())
|
||||
, zoomX(zoom * stretch)
|
||||
@@ -30,8 +25,7 @@ Rotation::Rotation(int angle, const RealRect& rect, double zoom, double stretch,
|
||||
}
|
||||
// set origin
|
||||
if (flags & ROTATION_ATTACH_TOP_LEFT) {
|
||||
if (revX()) origin.x += zoom * (sideways() ? size.height : size.width);
|
||||
if (revY()) origin.y += zoom * (sideways() ? size.width : size.height);
|
||||
origin -= boundingBoxCorner(size);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -42,59 +36,58 @@ void Rotation::setStretch(double s) {
|
||||
|
||||
|
||||
RealPoint Rotation::tr(const RealPoint& p) const {
|
||||
double a = deg_to_rad(angle), s = sin(a), c = cos(a);
|
||||
double s = sin(angle), c = cos(angle);
|
||||
double x = p.x * zoomX, y = p.y * zoomY;
|
||||
return RealPoint(c * x + s * y + origin.x,
|
||||
-s * x + c * y + origin.y);
|
||||
}
|
||||
RealPoint Rotation::trPixel(const RealPoint& p) const {
|
||||
double a = deg_to_rad(angle), s = sin(a), c = cos(a);
|
||||
double s = sin(angle), c = cos(angle);
|
||||
double x = p.x * zoomX + 0.5, y = p.y * zoomY + 0.5;
|
||||
return RealPoint(c * x + s * y + origin.x - 0.5,
|
||||
-s * x + c * y + origin.y - 0.5);
|
||||
}
|
||||
RealPoint Rotation::trNoZoom(const RealPoint& p) const {
|
||||
double a = deg_to_rad(angle), s = sin(a), c = cos(a);
|
||||
double s = sin(angle), c = cos(angle);
|
||||
double x = p.x, y = p.y;
|
||||
return RealPoint(c * x + s * y + origin.x,
|
||||
-s * x + c * y + origin.y);
|
||||
}
|
||||
RealPoint Rotation::trPixelNoZoom(const RealPoint& p) const {
|
||||
double a = deg_to_rad(angle), s = sin(a), c = cos(a);
|
||||
double s = sin(angle), c = cos(angle);
|
||||
double x = p.x + 0.5, y = p.y + 0.5;
|
||||
return RealPoint(c * x + s * y + origin.x - 0.5,
|
||||
-s * x + c * y + origin.y - 0.5);
|
||||
}
|
||||
/*
|
||||
RealSize Rotation::trSize(const RealSize& size) const {
|
||||
double a = deg_to_rad(angle), s = sin(a), c = cos(a);
|
||||
double x = size.width * zoomX, y = size.height * zoomY;
|
||||
return RealSize(c * x + s * y, s * x + c * y);
|
||||
}
|
||||
*/
|
||||
|
||||
RealSize Rotation::trSizeToBB(const RealSize& size) const {
|
||||
if (straight()) {
|
||||
if (sideways()) {
|
||||
if (is_straight(angle)) {
|
||||
if (is_sideways(angle)) {
|
||||
return RealSize(size.height * zoomY, size.width * zoomX);
|
||||
} else {
|
||||
return RealSize(size.width * zoomX, size.height * zoomY);
|
||||
}
|
||||
} else {
|
||||
double a = deg_to_rad(angle), s = sin(a), c = cos(a);
|
||||
double s = sin(angle), c = cos(angle);
|
||||
double x = size.width * zoomX, y = size.height * zoomY;
|
||||
return RealSize(fabs(c * x) + fabs(s * y), fabs(s * x) + fabs(c * y));
|
||||
}
|
||||
}
|
||||
|
||||
RealRect Rotation::trRectToBB(const RealRect& r) const {
|
||||
if (straight()) {
|
||||
RealSize s = trSizeToBB(r.size());
|
||||
return RealRect(tr(r.position()) - RealSize(revX()?s.width:0, revY()?s.height:0), s);
|
||||
double x = r.x * zoomX, y = r.y * zoomY;
|
||||
double w = r.width * zoomX, h = r.height * zoomY;
|
||||
const bool special_case_optimization = false;
|
||||
if (special_case_optimization && is_rad0(angle)) {
|
||||
return RealRect(origin.x + x, origin.y + y, w, h);
|
||||
} else if (special_case_optimization && is_rad180(angle)) {
|
||||
return RealRect(origin.x - x - w, origin.y - y - h, w, h);
|
||||
} else if (special_case_optimization && is_rad90(angle)) {
|
||||
return RealRect(origin.x + y, origin.y - x - w, h, w);
|
||||
} else if (special_case_optimization && is_rad270(angle)) {
|
||||
return RealRect(origin.x - y - h, origin.y + x, h, w);
|
||||
} else {
|
||||
double a = deg_to_rad(angle), s = sin(a), c = cos(a);
|
||||
double x = r.x * zoomX, y = r.y * zoomY;
|
||||
double w = r.width * zoomX, h = r.height * zoomY;
|
||||
double s = sin(angle), c = cos(angle);
|
||||
RealRect result(c * x + s * y + origin.x,
|
||||
-s * x + c * y + origin.y,
|
||||
0,0);
|
||||
@@ -120,13 +113,8 @@ RealRect Rotation::trRectToBB(const RealRect& r) const {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
RealRect Rotation::trRectStraight(const RealRect& r) const {
|
||||
assert(angle == 0);
|
||||
return RealRect(r.position() + origin, r.size());
|
||||
}
|
||||
wxRegion Rotation::trRectToRegion(const RealRect& r) const {
|
||||
if (straight()) {
|
||||
if (is_straight(angle)) {
|
||||
return trRectToBB(r).toRect();
|
||||
} else {
|
||||
wxPoint points[4] = {trPixel(RealPoint(r.left(), r.top() ))
|
||||
@@ -138,13 +126,13 @@ wxRegion Rotation::trRectToRegion(const RealRect& r) const {
|
||||
}
|
||||
|
||||
RealPoint Rotation::trInv(const RealPoint& p) const {
|
||||
double a = deg_to_rad(angle), s = sin(a), c = cos(a);
|
||||
double s = sin(angle), c = cos(angle);
|
||||
double x = p.x - origin.x, y = p.y - origin.y;
|
||||
return RealPoint((c * x - s * y) / zoomX,
|
||||
(s * x + c * y) / zoomY);
|
||||
}
|
||||
RealSize Rotation::trInv(const RealSize& x) const {
|
||||
double a = deg_to_rad(angle), s = sin(a), c = cos(a);
|
||||
double s = sin(angle), c = cos(angle);
|
||||
return RealSize((c * x.width - s * x.height) / zoomX,
|
||||
(s * x.width + c * x.height) / zoomY);
|
||||
}
|
||||
@@ -153,26 +141,13 @@ RealPoint Rotation::boundingBoxCorner(const RealSize& size) const {
|
||||
// This function is a bit tricky,
|
||||
// I derived it by drawing the four cases.
|
||||
// Two succeeding cases must agree where they overlap (0,90,180,270 degrees)
|
||||
double a = deg_to_rad(angle), s = sin(a), c = cos(a);
|
||||
double s = sin(angle), c = cos(angle);
|
||||
double w = size.width * zoomX, h = size.height * zoomY;
|
||||
if (angle <= 90) return RealPoint(0, -w * s);
|
||||
if (angle <= 180) return RealPoint(w * c, h * c - w * s);
|
||||
if (angle <= 270) return RealPoint(w * c + h * s, h * c);
|
||||
else return RealPoint(h * s, 0);
|
||||
if (angle <= rad90) return RealPoint(0, -w * s);
|
||||
if (angle <= rad180) return RealPoint(w * c, h * c - w * s);
|
||||
if (angle <= rad270) return RealPoint(w * c + h * s, h * c);
|
||||
else return RealPoint(h * s, 0);
|
||||
}
|
||||
/*
|
||||
RealPoint Rotation::boundingBoxCorner(const RealSize& size) const {
|
||||
//if(true)return RealPoint(0,0);
|
||||
// This function is a bit tricky,
|
||||
// I derived it by drawing the four cases.
|
||||
// Two succeeding cases must agree where they overlap (0,90,180,270 degrees)
|
||||
double a = deg_to_rad(angle), s = sin(a), c = cos(a);
|
||||
if (angle <= 90) return RealPoint( + size.width * s * s, - size.width * s * c);
|
||||
if (angle <= 180) return RealPoint(size.width - size.height * s * c, size.height * c * c);
|
||||
if (angle <= 270) return RealPoint(size.width - size.width * s * s, size.height + size.width * s * c);
|
||||
else return RealPoint( size.height * s * c, size.height - size.height * c * c);
|
||||
}
|
||||
*/
|
||||
|
||||
// ----------------------------------------------------------------------------- : Rotater
|
||||
|
||||
@@ -183,7 +158,7 @@ Rotater::Rotater(Rotation& rot, const Rotation& by)
|
||||
// apply rotation
|
||||
rot.origin = rot.tr(by.origin);
|
||||
rot.size = by.size;
|
||||
rot.angle = constrain_angle(rot.angle + by.angle);
|
||||
rot.angle = constrain_radians(rot.angle + by.angle);
|
||||
// zooming is not really correct if rot.zoomX != rot.zoomY
|
||||
rot.zoomX *= by.zoomX;
|
||||
rot.zoomY *= by.zoomY;
|
||||
@@ -195,7 +170,7 @@ Rotater::~Rotater() {
|
||||
|
||||
// ----------------------------------------------------------------------------- : RotatedDC
|
||||
|
||||
RotatedDC::RotatedDC(DC& dc, int angle, const RealRect& rect, double zoom, RenderQuality quality, RotationFlags flags)
|
||||
RotatedDC::RotatedDC(DC& dc, Radians angle, const RealRect& rect, double zoom, RenderQuality quality, RotationFlags flags)
|
||||
: Rotation(angle, rect, zoom, 1.0, flags)
|
||||
, dc(dc), quality(quality)
|
||||
{}
|
||||
@@ -234,12 +209,12 @@ void RotatedDC::DrawText (const String& text, const RealPoint& pos, AColor colo
|
||||
dc.GetUserScale(&usx, &usy);
|
||||
dc.SetUserScale(usx/text_scaling, usy/text_scaling);
|
||||
dc.SetTextForeground(color);
|
||||
dc.DrawRotatedText(text, (int) p_ext.x, (int) p_ext.y, angle);
|
||||
dc.DrawRotatedText(text, (int) p_ext.x, (int) p_ext.y, rad_to_deg(angle));
|
||||
dc.SetUserScale(usx, usy);
|
||||
} else {
|
||||
RealPoint p_ext = tr(pos);
|
||||
dc.SetTextForeground(color);
|
||||
dc.DrawRotatedText(text, (int) p_ext.x, (int) p_ext.y, angle);
|
||||
dc.DrawRotatedText(text, (int) p_ext.x, (int) p_ext.y, rad_to_deg(angle));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -249,7 +224,7 @@ void RotatedDC::DrawTextWithShadow(const String& text, const Font& font, const R
|
||||
}
|
||||
|
||||
void RotatedDC::DrawBitmap(const Bitmap& bitmap, const RealPoint& pos) {
|
||||
if (angle == 0) {
|
||||
if (is_rad0(angle)) {
|
||||
RealPoint p_ext = tr(pos);
|
||||
dc.DrawBitmap(bitmap, to_int(p_ext.x), to_int(p_ext.y), true);
|
||||
} else {
|
||||
@@ -275,7 +250,7 @@ void RotatedDC::DrawLine (const RealPoint& p1, const RealPoint& p2) {
|
||||
}
|
||||
|
||||
void RotatedDC::DrawRectangle(const RealRect& r) {
|
||||
if (straight()) {
|
||||
if (is_straight(angle)) {
|
||||
wxRect r_ext = trRectToBB(r);
|
||||
dc.DrawRectangle(r_ext.x, r_ext.y, r_ext.width, r_ext.height);
|
||||
} else {
|
||||
@@ -288,7 +263,7 @@ void RotatedDC::DrawRectangle(const RealRect& r) {
|
||||
}
|
||||
|
||||
void RotatedDC::DrawRoundedRectangle(const RealRect& r, double radius) {
|
||||
if (straight()) {
|
||||
if (is_straight(angle)) {
|
||||
wxRect r_ext = trRectToBB(r);
|
||||
dc.DrawRoundedRectangle(r_ext.x, r_ext.y, r_ext.width, r_ext.height, trS(radius));
|
||||
} else {
|
||||
@@ -307,16 +282,16 @@ void RotatedDC::DrawEllipse(const RealPoint& center, const RealSize& size) {
|
||||
wxSize s_ext = trSizeToBB(size);
|
||||
dc.DrawEllipse(c_ext.x, c_ext.y, s_ext.x, s_ext.y);
|
||||
}
|
||||
void RotatedDC::DrawEllipticArc(const RealPoint& center, const RealSize& size, double start, double end) {
|
||||
void RotatedDC::DrawEllipticArc(const RealPoint& center, const RealSize& size, Radians start, Radians end) {
|
||||
wxPoint c_ext = tr(center - size/2);
|
||||
wxSize s_ext = trSizeToBB(size);
|
||||
dc.DrawEllipticArc(c_ext.x, c_ext.y, s_ext.x, s_ext.y, rad_to_deg(start) + angle, rad_to_deg(end) + angle);
|
||||
dc.DrawEllipticArc(c_ext.x, c_ext.y, s_ext.x, s_ext.y, rad_to_deg(start + angle), rad_to_deg(end + angle));
|
||||
}
|
||||
void RotatedDC::DrawEllipticSpoke(const RealPoint& center, const RealSize& size, double angle) {
|
||||
void RotatedDC::DrawEllipticSpoke(const RealPoint& center, const RealSize& size, Radians angle) {
|
||||
wxPoint c_ext = tr(center - size/2);
|
||||
wxSize s_ext = trSizeToBB(size);
|
||||
double rot_angle = angle + deg_to_rad(this->angle);
|
||||
double sin_angle = sin(rot_angle), cos_angle = cos(rot_angle);
|
||||
Radians rot_angle = angle + this->angle;
|
||||
Radians sin_angle = sin(rot_angle), cos_angle = cos(rot_angle);
|
||||
// position of center and of point on the boundary can vary because of rounding errors,
|
||||
// this code matches DrawEllipticArc (at least on windows xp).
|
||||
dc.DrawLine(
|
||||
|
||||
+9
-22
@@ -33,7 +33,7 @@ class Rotation {
|
||||
/** with the given rectangle of external coordinates and a given rotation angle and zoom factor.
|
||||
* if is_internal then the rect gives the internal coordinates, its origin should be (0,0)
|
||||
*/
|
||||
Rotation(int angle, const RealRect& rect = RealRect(0,0,0,0), double zoom = 1.0, double strectch = 1.0, RotationFlags flags = ROTATION_NORMAL);
|
||||
Rotation(Radians angle = 0, const RealRect& rect = RealRect(0,0,0,0), double zoom = 1.0, double strectch = 1.0, RotationFlags flags = ROTATION_NORMAL);
|
||||
|
||||
/// Change the zoom factor
|
||||
inline void setZoom(double z) { zoomX = zoomY = z; }
|
||||
@@ -44,7 +44,7 @@ class Rotation {
|
||||
/// Stretch factor
|
||||
inline double getStretch() const { return zoomX / zoomY; }
|
||||
/// Get the angle
|
||||
inline int getAngle() const { return angle; }
|
||||
inline Radians getAngle() const { return angle; }
|
||||
/// Change the origin
|
||||
inline void setOrigin(const RealPoint& o) { origin = o; }
|
||||
|
||||
@@ -67,7 +67,7 @@ class Rotation {
|
||||
inline RealSize trS(const RealSize& s) const { return RealSize(s.width * zoomX, s.height * zoomY); }
|
||||
|
||||
/// Translate an angle
|
||||
inline int trAngle(int a) { return (angle + a) % 360; }
|
||||
inline Radians trAngle(Radians a) { return constrain_radians(angle + a); }
|
||||
|
||||
/// Translate a single point
|
||||
RealPoint tr(const RealPoint& p) const;
|
||||
@@ -81,12 +81,9 @@ class Rotation {
|
||||
RealSize trSize(const RealSize& s) const;
|
||||
/// Translate a single size, returns the bounding box size (non-negative)
|
||||
RealSize trSizeToBB(const RealSize& s) const;
|
||||
/// Translate a rectangle, returns the bounding box
|
||||
/* //%%the size of the result may be negative*/
|
||||
/// Translate a rectangle, returns the bounding box, the size will be non-negative
|
||||
RealRect trRectToBB(const RealRect& r) const;
|
||||
/// Translate a rectangle, can only be used when not rotating
|
||||
RealRect trRectStraight(const RealRect& r) const;
|
||||
/// Translate a rectangle into a region (supports rotation
|
||||
/// Translate a rectangle into a region (supports rotation)
|
||||
wxRegion trRectToRegion(const RealRect& rect) const;
|
||||
|
||||
/// Translate a size or length back to internal 'coordinates'
|
||||
@@ -102,7 +99,7 @@ class Rotation {
|
||||
RealSize trInv(const RealSize& p) const;
|
||||
|
||||
protected:
|
||||
int angle; ///< The angle of rotation in degrees (counterclockwise)
|
||||
Radians angle; ///< The angle of rotation in radians (counterclockwise)
|
||||
RealSize size; ///< Size of the rectangle, in internal coordinates
|
||||
RealPoint origin; ///< tr(0,0)
|
||||
double zoomX; ///< Zoom factor, zoom = 2.0 means that 1 internal = 2 external
|
||||
@@ -110,16 +107,6 @@ class Rotation {
|
||||
|
||||
friend class Rotater;
|
||||
|
||||
/// Is the x axis 'reversed' (after turning sideways)?
|
||||
inline bool revX() const { return angle >= 180; }
|
||||
/// Is the y axis 'reversed' (after turning sideways)?
|
||||
inline bool revY() const { return angle == 90 || angle == 180; }
|
||||
/// Is the rotation 'simple', i.e. a multiple of 90 degrees?
|
||||
inline bool straight() const { return ::straight(angle); }
|
||||
/// Is the rotation sideways (90 or 270 degrees)?
|
||||
// Note: angle & 2 == 0 for angle in {0, 180} and != 0 for angle in {90, 270)
|
||||
inline bool sideways() const { return (angle & 2) != 0; }
|
||||
|
||||
/// Determine the top-left corner of the bounding box around the rotated box s (in external coordinates)
|
||||
RealPoint boundingBoxCorner(const RealSize& s) const;
|
||||
};
|
||||
@@ -165,7 +152,7 @@ enum RenderQuality {
|
||||
*/
|
||||
class RotatedDC : public Rotation {
|
||||
public:
|
||||
RotatedDC(DC& dc, int angle, const RealRect& rect, double zoom, RenderQuality quality, RotationFlags flags = ROTATION_NORMAL);
|
||||
RotatedDC(DC& dc, Radians angle, const RealRect& rect, double zoom, RenderQuality quality, RotationFlags flags = ROTATION_NORMAL);
|
||||
RotatedDC(DC& dc, const Rotation& rotation, RenderQuality quality);
|
||||
|
||||
// --------------------------------------------------- : Drawing
|
||||
@@ -190,9 +177,9 @@ class RotatedDC : public Rotation {
|
||||
void DrawCircle(const RealPoint& center, double radius);
|
||||
void DrawEllipse(const RealPoint& center, const RealSize& size);
|
||||
/// Draw an arc of an ellipse, angles are in radians
|
||||
void DrawEllipticArc(const RealPoint& center, const RealSize& size, double start, double end);
|
||||
void DrawEllipticArc(const RealPoint& center, const RealSize& size, Radians start, Radians end);
|
||||
/// Draw spokes of an ellipse
|
||||
void DrawEllipticSpoke(const RealPoint& center, const RealSize& size, double start);
|
||||
void DrawEllipticSpoke(const RealPoint& center, const RealSize& size, Radians start);
|
||||
|
||||
// Fill the dc with the color of the current brush
|
||||
void Fill();
|
||||
|
||||
@@ -10,6 +10,7 @@
|
||||
// ----------------------------------------------------------------------------- : Includes
|
||||
|
||||
#include <util/prec.hpp>
|
||||
#include <util/angle.hpp>
|
||||
#include <limits>
|
||||
|
||||
// ----------------------------------------------------------------------------- : Rounding
|
||||
@@ -100,7 +101,7 @@ class Vector2D {
|
||||
return *this / length();
|
||||
}
|
||||
/// Angle between this vector and the x axis
|
||||
inline double angle() const {
|
||||
inline Radians angle() const {
|
||||
return atan2(y,x);
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user