diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-13 11:57:42 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-13 11:57:42 +0000 |
commit | 61f3ab8f23f4c924d455757bf3e65f8487521b5a (patch) | |
tree | 885599a36a308f422af98616bc733a0494fe149a /src/toys/elliptical-arc-toy.cpp | |
parent | Initial commit. (diff) | |
download | lib2geom-upstream.tar.xz lib2geom-upstream.zip |
Adding upstream version 1.3.upstream/1.3upstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to '')
-rw-r--r-- | src/toys/elliptical-arc-toy.cpp | 903 |
1 files changed, 903 insertions, 0 deletions
diff --git a/src/toys/elliptical-arc-toy.cpp b/src/toys/elliptical-arc-toy.cpp new file mode 100644 index 0000000..e2518f0 --- /dev/null +++ b/src/toys/elliptical-arc-toy.cpp @@ -0,0 +1,903 @@ +/** @file + * @brief Demonstration of elliptical arc functions + *//* + * Authors: + * Marco Cecchetti <mrcekets at gmail.com> + * Krzysztof KosiĆski <tweenk.pl@gmail.com> + * Copyright 2008-2015 Authors + * + * This library is free software; you can redistribute it and/or + * modify it either under the terms of the GNU Lesser General Public + * License version 2.1 as published by the Free Software Foundation + * (the "LGPL") or, at your option, under the terms of the Mozilla + * Public License Version 1.1 (the "MPL"). If you do not alter this + * notice, a recipient may use your version of this file under either + * the MPL or the LGPL. + * + * You should have received a copy of the LGPL along with this library + * in the file COPYING-LGPL-2.1; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * You should have received a copy of the MPL along with this library + * in the file COPYING-MPL-1.1 + * + * The contents of this file are subject to the Mozilla Public License + * Version 1.1 (the "License"); you may not use this file except in + * compliance with the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY + * OF ANY KIND, either express or implied. See the LGPL or the MPL for + * the specific language governing rights and limitations. + */ + +#include <2geom/elliptical-arc.h> + +#include <toys/path-cairo.h> +#include <toys/toy-framework-2.h> +#include <2geom/cairo-path-sink.h> + +#include <vector> +#include <string> + + +using namespace Geom; + + + +std::string angle_formatter(double angle) +{ + return default_formatter(decimal_round(deg_from_rad(angle),2)); +} + + + +class EllipticalArcToy: public Toy +{ + + enum menu_item_t + { + SHOW_MENU = 0, + TEST_BASIC, + TEST_COMPARISON, + TEST_PORTION, + TEST_REVERSE, + TEST_NEAREST_POINTS, + TEST_DERIVATIVE, + TEST_ROOTS, + TEST_BOUNDS, + TEST_FITTING, + TEST_TRANSFORM, + TOTAL_ITEMS // this one must be the last item + }; + + enum handle_label_t + { + START_POINT = 0, + END_POINT, + POINT + }; + + enum toggle_label_t + { + LARGE_ARC_FLAG = 0, + SWEEP_FLAG, + X_Y_TOGGLE + }; + + enum slider_label_t + { + RX_SLIDER = 0, + RY_SLIDER, + ROT_ANGLE_SLIDER, + T_SLIDER, + FROM_SLIDER = T_SLIDER, + TO_SLIDER, + TM0_SLIDER = T_SLIDER, + TM1_SLIDER, + TM2_SLIDER, + TM3_SLIDER + }; + + static const char* menu_items[TOTAL_ITEMS]; + static const char keys[TOTAL_ITEMS]; + + void first_time(int /*argc*/, char** /*argv*/) override + { + draw_f = &EllipticalArcToy::draw_menu; + } + + void init_common() + { + set_common_control_geometry = true; + set_control_geometry = true; + + double start_angle = (10.0/6.0) * M_PI; + double sweep_angle = (4.0/6.0) * M_PI; + double end_angle = start_angle + sweep_angle; + double rot_angle = (0.0/6.0) * M_PI; + double rx = 200; + double ry = 150; + double cx = 300; + double cy = 300; + + Point start_point( cx + rx * std::cos(start_angle), + cy + ry * std::sin(start_angle) ); + Point end_point( cx + rx * std::cos(end_angle), + cy + ry * std::sin(end_angle) ); + + bool large_arc = false; + bool sweep = true; + + + initial_point.pos = start_point; + final_point.pos = end_point; + + try + { + ea.set (initial_point.pos, + rx, ry, rot_angle, + large_arc, sweep, + final_point.pos); + } + catch (RangeError const &e) + { + no_solution = true; + std::cerr << e.what() << std::endl; + } + + sliders.clear(); + sliders.reserve(50); + sliders.emplace_back(0, 500, 0, ea.ray(X), "ray X"); + sliders.emplace_back(0, 500, 0, ea.ray(Y), "ray Y"); + sliders.emplace_back(0, 2*M_PI, 0, ea.rotationAngle(), "rot angle"); + sliders[ROT_ANGLE_SLIDER].formatter(&angle_formatter); + + toggles.clear(); + toggles.reserve(50); + toggles.emplace_back("Large Arc Flag", ea.largeArc()); + toggles.emplace_back("Sweep Flag", ea.sweep()); + + handles.clear(); + handles.push_back(&initial_point); + handles.push_back(&final_point); + handles.push_back(&(sliders[RX_SLIDER])); + handles.push_back(&(sliders[RY_SLIDER])); + handles.push_back(&(sliders[ROT_ANGLE_SLIDER])); + handles.push_back(&(toggles[LARGE_ARC_FLAG])); + handles.push_back(&(toggles[SWEEP_FLAG])); + } + + virtual void draw_common( cairo_t *cr, std::ostringstream *notify, + int width, int height, bool /*save*/, + std::ostringstream *timer_stream=0) + { + if(timer_stream == 0) + timer_stream = notify; + init_common_ctrl_geom(cr, width, height, notify); + + no_solution = false; + try + { + ea.set( initial_point.pos, + sliders[0].value(), + sliders[1].value(), + sliders[2].value(), + toggles[0].on, + toggles[1].on, + final_point.pos ); + } + catch (RangeError const &e) + { + no_solution = true; + std::cerr << e.what() << std::endl; + return; + } + + degenerate = ea.isDegenerate(); + + point_overlap = false; + if ( are_near(ea.initialPoint(), ea.finalPoint()) ) + { + point_overlap = true; + } + + // calculate the center of the two possible ellipse supporting the arc + std::pair<Point,Point> centers + = calculate_ellipse_centers( ea.initialPoint(), ea.finalPoint(), + ea.ray(X), ea.ray(Y), ea.rotationAngle(), + ea.largeArc(), ea.sweep() ); + + + // draw axes passing through the center of the ellipse supporting the arc + cairo_set_source_rgba(cr, 0.0, 1.0, 0.0, 1.0); + cairo_set_line_width(cr, 0.5); + draw_axes(cr); + + // draw the 2 ellipse with rays rx, ry passing through + // the 2 given point and with the x-axis inclined of rot_angle + if ( !(are_near(ea.ray(X), 0) || are_near(ea.ray(Y), 0)) ) + { + cairo_elliptiarc( cr, + centers.first[X], centers.first[Y], + ea.ray(X), ea.ray(Y), + 0, 2*M_PI, + ea.rotationAngle() ); + cairo_stroke(cr); + cairo_elliptiarc( cr, + centers.second[X], centers.second[Y], + ea.ray(X), ea.ray(Y), + 0, 2*M_PI, + ea.rotationAngle() ); + cairo_stroke(cr); + } + + // convert the elliptical arc to a sbasis path and draw it + D2<SBasis> easb = ea.toSBasis(); + cairo_set_line_width(cr, 0.5); + cairo_set_source_rgba(cr, 0.0, 0.0, 1.0, 1.0); + cairo_d2_sb(cr, easb); + cairo_stroke(cr); + + // draw initial and final point labels + draw_text(cr, ea.initialPoint() + Point(5, -15), "initial"); + draw_text(cr, ea.finalPoint() + Point(5, 0), "final"); + cairo_stroke(cr); + + // TODO re-enable this + //*notify << ea; + } + + + void draw_comparison(cairo_t *cr, std::ostringstream *notify, + int width, int height, bool save, std::ostringstream */*timer_stream*/) + { + draw_common(cr, notify, width, height, save); + if ( no_solution || point_overlap ) return; + + // draw the arc with cairo in order to make a visual comparison + cairo_set_line_width(cr, 1); + cairo_set_source_rgba(cr, 1.0, 0.0, 0.0, 1.0); + + if (ea.isDegenerate()) + { + cairo_move_to(cr, ea.initialPoint()); + cairo_line_to(cr, ea.finalPoint()); + } + else + { + if ( ea.sweep() ) + { + cairo_elliptiarc( cr, + ea.center(X), ea.center(Y), + ea.ray(X), ea.ray(Y), + ea.initialAngle(), ea.finalAngle(), + ea.rotationAngle() ); + } + else + { + cairo_elliptiarc( cr, + ea.center(X), ea.center(Y), + ea.ray(X), ea.ray(Y), + ea.finalAngle(), ea.initialAngle(), + ea.rotationAngle() ); + } + } + cairo_stroke(cr); + } + + + void init_portion() + { + init_common(); + + from_t = 0; + to_t = 1; + + sliders.emplace_back(0, 1, 0, from_t, "from"); + sliders.emplace_back(0, 1, 0, to_t, "to"); + + handles.push_back(&(sliders[FROM_SLIDER])); + handles.push_back(&(sliders[TO_SLIDER])); + } + + void draw_portion(cairo_t *cr, std::ostringstream *notify, + int width, int height, bool save, std::ostringstream */*timer_stream*/) + { + draw_common(cr, notify, width, height, save); + init_portion_ctrl_geom(cr, notify, width, height); + if ( no_solution || point_overlap ) return; + + from_t = sliders[FROM_SLIDER].value(); + to_t = sliders[TO_SLIDER].value(); + + EllipticalArc* eapp + = static_cast<EllipticalArc*>(ea.portion(from_t, to_t)); + EllipticalArc& eap = *eapp; + + cairo_set_line_width(cr, 0.8); + cairo_set_source_rgba(cr, 0.0, 1.0, 1.0, 1.0); + cairo_move_to(cr, eap.center(X), eap.center(Y)); + cairo_line_to(cr, eap.initialPoint()[X], eap.initialPoint()[Y]); + cairo_move_to(cr, eap.center(X), eap.center(Y)); + cairo_line_to(cr, eap.finalPoint()[X], eap.finalPoint()[Y]); + cairo_stroke(cr); + D2<SBasis> sub_arc = eap.toSBasis(); + cairo_d2_sb(cr, sub_arc); + cairo_stroke(cr); + + delete eapp; + + } + + + void init_reverse() + { + init_common(); + time = 0; + + sliders.emplace_back(0, 1, 0, time, "t"); + handles.push_back(&(sliders[T_SLIDER])); + } + + void draw_reverse(cairo_t *cr, std::ostringstream *notify, + int width, int height, bool save, std::ostringstream */*timer_stream*/) + { + draw_common(cr, notify, width, height, save); + init_reverse_ctrl_geom(cr, notify, width, height); + if ( no_solution || point_overlap ) return; + + + time = sliders[T_SLIDER].value(); + + EllipticalArc* eapp = static_cast<EllipticalArc*>(ea.reverse()); + EllipticalArc& eap = *eapp; + + cairo_set_line_width(cr, 0.8); + cairo_set_source_rgba(cr, 0.2, 0.2, 0.2, 1.0); + + cairo_move_to(cr, eap.center(X), eap.center(Y)); + cairo_line_to(cr, eap.valueAt(time,X), eap.valueAt(time,Y)); + draw_circ(cr, eap.pointAt(time)); + cairo_stroke(cr); + cairo_set_source_rgba(cr, 0.0, 1.0, 1.0, 1.0); + D2<SBasis> sub_arc = eap.toSBasis(); + cairo_d2_sb(cr, sub_arc); + cairo_stroke(cr); + + delete eapp; + + } + + + void init_np() + { + init_common(); + nph.pos = Point(10,10); + handles.push_back(&nph); + } + + void draw_np(cairo_t *cr, std::ostringstream *notify, + int width, int height, bool save, std::ostringstream */*timer_stream*/) + { + draw_common(cr, notify, width, height, save); + if ( no_solution || point_overlap ) return; + + std::vector<double> times = ea.allNearestTimes( nph.pos ); + for (double time : times) + { + cairo_move_to(cr,nph.pos); + cairo_line_to( cr, ea.pointAt(time) ); + } + cairo_stroke(cr); + } + + + void init_derivative() + { + init_common(); + time = 0; + + sliders.emplace_back(0, 1, 0, time, "t"); + handles.push_back(&(sliders[T_SLIDER])); + } + + void draw_derivative(cairo_t *cr, std::ostringstream *notify, + int width, int height, bool save, std::ostringstream */*timer_stream*/) + { + draw_common(cr, notify, width, height, save); + init_reverse_ctrl_geom(cr, notify, width, height); + if ( no_solution || point_overlap ) return; + + time = sliders[T_SLIDER].value(); + + Curve* der = ea.derivative(); + Point p = ea.pointAt(time); + Point v = der->pointAt(time) + p; + delete der; +// std::vector<Point> points = ea.pointAndDerivatives(time, 8); +// Point p = points[0]; +// Point v = points[1] + p; + cairo_move_to(cr, p); + cairo_line_to(cr, v); + cairo_stroke(cr); + } + + + void init_roots() + { + init_common(); + ph.pos = Point(10,10); + toggles.emplace_back("X/Y roots", true ); + + handles.push_back(&ph); + handles.push_back(&(toggles[X_Y_TOGGLE])); + } + + void draw_roots(cairo_t *cr, std::ostringstream *notify, + int width, int height, bool save, std::ostringstream */*timer_stream*/) + { + draw_common(cr, notify, width, height, save); + init_roots_ctrl_geom(cr, notify, width, height); + if ( no_solution || point_overlap ) return; + + Dim2 DIM = toggles[X_Y_TOGGLE].on ? X : Y; + + Point p1[2] = { Point(ph.pos[X], -1000), + Point(-1000, ph.pos[Y]) }; + Point p2[2] = { Point(ph.pos[X], 1000), + Point(1000, ph.pos[Y]) }; + cairo_set_line_width(cr, 0.5); + cairo_set_source_rgba(cr, 0.3, 0.3, 0.3, 1.0); + cairo_move_to(cr, p1[DIM]); + cairo_line_to(cr, p2[DIM]); + + std::vector<double> times; + try + { + times = ea.roots(ph.pos[DIM], DIM); + *notify << "winding: " << ea.winding(ph.pos); + } + catch(Geom::Exception e) + { + std::cerr << e.what() << std::endl; + } + for (double time : times) + { + draw_handle(cr, ea.pointAt(time)); + } + cairo_stroke(cr); + } + + + void init_bounds() + { + init_common(); + } + + void draw_bounds(cairo_t *cr, std::ostringstream *notify, + int width, int height, bool save, std::ostringstream */*timer_stream*/) + { + draw_common(cr, notify, width, height, save); + if ( no_solution || point_overlap ) return; + +// const char* msg[] = { "xmax", "xmin", "ymax", "ymin" }; + + Rect bb = ea.boundsFast(); + +// for ( unsigned int i = 0; i < limits.size(); ++i ) +// { +// std::cerr << "angle[" << i << "] = " << deg_from_rad(limits[i]) << std::endl; +// Point extreme = ea.pointAtAngle(limits[i]); +// draw_handle(cr, extreme ); +// draw_text(cr, extreme, msg[i]); +// } + cairo_rectangle( cr, bb.left(), bb.top(), bb.width(), bb.height() ); + cairo_stroke(cr); + } + + + void init_fitting() + { + init_common(); + } + + void draw_fitting(cairo_t * cr, std::ostringstream *notify, + int width, int height, bool save, std::ostringstream */*timer_stream*/) + { + draw_common(cr, notify, width, height, save); + if ( no_solution || point_overlap ) return; + + D2<SBasis> easb = ea.toSBasis(); + try + { + EllipticalArc earc; + if (!arc_from_sbasis(earc, easb, 0.1, 5)) return; + + D2<SBasis> arc = earc.toSBasis(); + arc[0] += Linear(50, 50); + cairo_d2_sb(cr, arc); + cairo_stroke(cr); + } + catch (RangeError const &e) + { + std::cerr << "conversion failure" << std::endl; + std::cerr << e.what() << std::endl; + return; + } + } + + void init_transform() + { + init_common(); + + double max = 4; + double min = -max; + + sliders.emplace_back(min, max, 0, 1, "TM0"); + sliders.emplace_back(min, max, 0, 0, "TM1"); + sliders.emplace_back(min, max, 0, 0, "TM2"); + sliders.emplace_back(min, max, 0, 1, "TM3"); + + handles.push_back(&(sliders[TM0_SLIDER])); + handles.push_back(&(sliders[TM1_SLIDER])); + handles.push_back(&(sliders[TM2_SLIDER])); + handles.push_back(&(sliders[TM3_SLIDER])); + } + + void draw_transform(cairo_t *cr, std::ostringstream *notify, + int width, int height, bool save, std::ostringstream */*timer_stream*/) + { + draw_common(cr, notify, width, height, save); + init_transform_ctrl_geom(cr, notify, width, height); + if ( no_solution || point_overlap ) return; + + Affine TM(sliders[TM0_SLIDER].value(), sliders[TM1_SLIDER].value(), + sliders[TM2_SLIDER].value(), sliders[TM3_SLIDER].value(), + ea.center(X), ea.center(Y)); + + Affine tm( 1, 0, + 0, 1, + -ea.center(X), -ea.center(Y) ); + + + EllipticalArc* tea = static_cast<EllipticalArc*>(ea.transformed(tm)); + EllipticalArc* eat = NULL; + eat = static_cast<EllipticalArc*>(tea->transformed(TM)); + delete tea; + if (eat == NULL) + { + std::cerr << "elliptiarc transformation failed" << std::endl; + return; + } + + CairoPathSink ps(cr); + + //D2<SBasis> sb = eat->toSBasis(); + cairo_set_line_width(cr, 0.8); + cairo_set_source_rgba(cr, 0.8, 0.1, 0.1, 1.0); + //cairo_d2_sb(cr, sb); + ps.feed(*eat); + cairo_stroke(cr); + delete eat; + } + + void init_common_ctrl_geom(cairo_t* /*cr*/, int /*width*/, int height, std::ostringstream* /*notify*/) + { + if ( set_common_control_geometry ) + { + set_common_control_geometry = false; + + sliders[RX_SLIDER].geometry(Point(50, height-120), 250); + sliders[RY_SLIDER].geometry(Point(50, height-85), 250); + sliders[ROT_ANGLE_SLIDER].geometry(Point(50, height-50), 180); + + toggles[LARGE_ARC_FLAG].bounds = Rect(Point(400, height-120), Point(540, height-95)); + toggles[SWEEP_FLAG].bounds = Rect(Point(400, height-70), Point(520, height-45)); + } + } + + void init_portion_ctrl_geom(cairo_t* /*cr*/, std::ostringstream* /*notify*/, int /*width*/, int height) + { + if ( set_control_geometry ) + { + set_control_geometry = false; + + Point from_sp = Point(600, height - 120); + Point to_sp = from_sp + Point(0,45); + double from_to_len = 100; + + sliders[FROM_SLIDER].geometry(from_sp, from_to_len); + sliders[TO_SLIDER].geometry(to_sp, from_to_len); + } + } + + void init_reverse_ctrl_geom(cairo_t* /*cr*/, std::ostringstream* /*notify*/, int /*width*/, int height) + { + if ( set_control_geometry ) + { + set_control_geometry = false; + + Point t_sp = Point(600, height - 120); + double t_len = 200; + + sliders[T_SLIDER].geometry(t_sp, t_len); + } + } + + void init_roots_ctrl_geom(cairo_t* /*cr*/, std::ostringstream* /*notify*/, int /*width*/, int height) + { + if ( set_control_geometry ) + { + set_control_geometry = false; + Point T(600, height - 120); + toggles[X_Y_TOGGLE].bounds = Rect( T, T + Point(100,25) ); + } + } + + void init_transform_ctrl_geom(cairo_t* /*cr*/, std::ostringstream* /*notify*/, int /*width*/, int height) + { + if ( set_control_geometry ) + { + set_control_geometry = false; + + Point sp = Point(600, height - 140); + Point op = Point(0, 30); + double len = 200; + + sliders[TM0_SLIDER].geometry(sp, len); + sliders[TM1_SLIDER].geometry(sp += op, len); + sliders[TM2_SLIDER].geometry(sp += op, len); + sliders[TM3_SLIDER].geometry(sp += op, len); + } + } + + void init_menu() + { + handles.clear(); + sliders.clear(); + toggles.clear(); + } + + void draw_menu( cairo_t * /*cr*/, std::ostringstream *notify, + int /*width*/, int /*height*/, bool /*save*/, + std::ostringstream */*timer_stream*/) + { + *notify << std::endl; + for (int i = SHOW_MENU; i < TOTAL_ITEMS; ++i) + { + *notify << " " << keys[i] << " - " << menu_items[i] << std::endl; + } + } + + void key_hit(GdkEventKey *e) override + { + char choice = std::toupper(e->keyval); + switch ( choice ) + { + case 'A': + init_menu(); + draw_f = &EllipticalArcToy::draw_menu; + break; + case 'B': + init_common(); + draw_f = &EllipticalArcToy::draw_common; + break; + case 'C': + init_common(); + draw_f = &EllipticalArcToy::draw_comparison; + break; + case 'D': + draw_f = &EllipticalArcToy::draw_menu; + init_portion(); + draw_f = &EllipticalArcToy::draw_portion; + break; + case 'E': + init_reverse(); + draw_f = &EllipticalArcToy::draw_reverse; + break; + case 'F': + init_np(); + draw_f = &EllipticalArcToy::draw_np; + break; + case 'G': + init_derivative(); + draw_f = &EllipticalArcToy::draw_derivative; + break; + case 'H': + init_roots(); + draw_f = &EllipticalArcToy::draw_roots; + break; + case 'I': + init_bounds(); + draw_f = &EllipticalArcToy::draw_bounds; + break; + case 'J': + init_fitting(); + draw_f = &EllipticalArcToy::draw_fitting; + break; + case 'K': + init_transform(); + draw_f = &EllipticalArcToy::draw_transform; + break; + } + redraw(); + } + + + void draw_axes(cairo_t* cr) const + { + Point D(std::cos(ea.rotationAngle()), std::sin(ea.rotationAngle())); + Point Dx = (ea.ray(X) + 20) * D; + Point Dy = (ea.ray(Y) + 20) * D.cw(); + Point C(ea.center(X),ea.center(Y)); + Point LP = C - Dx; + Point RP = C + Dx; + Point UP = C - Dy; + Point DP = C + Dy; + + cairo_move_to(cr, LP[X], LP[Y]); + cairo_line_to(cr, RP[X], RP[Y]); + cairo_move_to(cr, UP[X], UP[Y]); + cairo_line_to(cr, DP[X], DP[Y]); + cairo_move_to(cr, 0, 0); + cairo_stroke(cr); + } + + void cairo_elliptiarc( cairo_t *cr, + double _cx, double _cy, + double _rx, double _ry, + double _sa, double _ea, + double _ra = 0 + ) const + { + double cos_rot_angle = std::cos(_ra); + double sin_rot_angle = std::sin(_ra); + cairo_matrix_t transform_matrix; + cairo_matrix_init( &transform_matrix, + _rx * cos_rot_angle, _rx * sin_rot_angle, + -_ry * sin_rot_angle, _ry * cos_rot_angle, + _cx, _cy + ); + cairo_save(cr); + cairo_transform(cr, &transform_matrix); + cairo_arc(cr, 0, 0, 1, _sa, _ea); + cairo_restore(cr); + } + + + std::pair<Point,Point> + calculate_ellipse_centers( Point _initial_point, Point _final_point, + double m_rx, double m_ry, + double m_rot_angle, + bool m_large_arc, bool m_sweep + ) + { + std::pair<Point,Point> result; + if ( _initial_point == _final_point ) + { + result.first = result.second = _initial_point; + return result; + } + + m_rx = std::fabs(m_rx); + m_ry = std::fabs(m_ry); + + Point d = _initial_point - _final_point; + + if ( are_near(m_rx, 0) || are_near(m_ry, 0) ) + { + result.first = result.second + = middle_point(_initial_point, _final_point); + return result; + } + + double sin_rot_angle = std::sin(m_rot_angle); + double cos_rot_angle = std::cos(m_rot_angle); + + + Affine m( cos_rot_angle, -sin_rot_angle, + sin_rot_angle, cos_rot_angle, + 0, 0 ); + + Point p = (d / 2) * m; + double rx2 = m_rx * m_rx; + double ry2 = m_ry * m_ry; + double rxpy = m_rx * p[Y]; + double rypx = m_ry * p[X]; + double rx2py2 = rxpy * rxpy; + double ry2px2 = rypx * rypx; + double num = rx2 * ry2; + double den = rx2py2 + ry2px2; + assert(den != 0); + double rad = num / den; + Point c(0,0); + if (rad > 1) + { + rad -= 1; + rad = std::sqrt(rad); + + if (m_large_arc == m_sweep) rad = -rad; + c = rad * Point(rxpy / m_ry, -rypx / m_rx); + + m[1] = -m[1]; + m[2] = -m[2]; + + c = c * m; + } + + d = middle_point(_initial_point, _final_point); + + result.first = c + d; + result.second = -c + d; + return result; + + } + + void draw( cairo_t *cr, std::ostringstream *notify, + int width, int height, bool save, std::ostringstream *timer_stream) override + { + (this->*draw_f)(cr, notify, width, height, save, timer_stream); + Toy::draw(cr, notify, width, height, save,timer_stream); + } + + public: + EllipticalArcToy() {} + + private: + typedef void (EllipticalArcToy::* draw_func_t) (cairo_t*, std::ostringstream*, int, int, bool, std::ostringstream*); + draw_func_t draw_f; + bool set_common_control_geometry; + bool set_control_geometry; + bool no_solution, point_overlap; + bool degenerate; + PointHandle initial_point, final_point; + PointHandle nph, ph; + std::vector<Toggle> toggles; + std::vector<Slider> sliders; + EllipticalArc ea; + + double from_t; + double to_t; + double time; + +}; + + +const char* EllipticalArcToy::menu_items[] = +{ + "show this menu", + "basic", + "comparison", + "portion, pointAt", + "reverse, valueAt", + "nearest points", + "derivative", + "roots", + "bounding box", + "fitting", + "transformation" +}; + +const char EllipticalArcToy::keys[] = +{ + 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K' +}; + + +int main(int argc, char **argv) +{ + init( argc, argv, new EllipticalArcToy(), 850, 780 ); + return 0; +} + + +/* + Local Variables: + mode:c++ + c-file-style:"stroustrup" + c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +)) + indent-tabs-mode:nil + fill-column:99 + End: +*/ +// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:fileencoding=utf-8:textwidth=99 : |