diff --git a/c++/vector/.gitignore b/c++/vector/.gitignore new file mode 100644 index 0000000..fa79505 --- /dev/null +++ b/c++/vector/.gitignore @@ -0,0 +1,2 @@ +/vector +/*.d diff --git a/c++/vector/activearea.cpp b/c++/vector/activearea.cpp new file mode 100644 index 0000000..90c186f --- /dev/null +++ b/c++/vector/activearea.cpp @@ -0,0 +1,125 @@ + + +#include "activearea.h" + + +ActiveArea::ActiveArea() { + add_events( Gdk::BUTTON_PRESS_MASK + | Gdk::BUTTON_RELEASE_MASK + | Gdk::POINTER_MOTION_MASK ); +} + +bool ActiveArea::is_point_added(const ActivePoint::Handle &point) const { + for(PointList::const_iterator i = points.begin(); i != points.end(); ++i) + if (*i == point) return true; + return false; +} + +void ActiveArea::add_point(const ActivePoint::Handle &point) { + if (point && !is_point_added(point)) { + points.push_back(point); + queue_draw(); + } +} + +void ActiveArea::remove_point(const ActivePoint::Handle &point) { + ActivePoint::Handle p = point; + for(PointList::iterator i = points.begin(); i != points.end();) + if (*i == p) { i = points.erase(i); queue_draw(); } else ++i; + if (drag_point == p) + hover_point.reset(); + if (hover_point == p) + { hover_point->active = false; hover_point.reset(); } +} + +void ActiveArea::bring_to_front(const ActivePoint::Handle &point) { + ActivePoint::Handle p = point; + for(PointList::iterator i = points.begin(); i != points.end(); ++i) + if (*i == p) + { i = points.erase(i); points.push_back(p); break; } +} + +ActivePoint::Handle ActiveArea::get_point_at(const Vector &position) const { + for(PointList::const_reverse_iterator ri = points.rbegin(); ri != points.rend(); ++ri) + if ((*ri)->enabled && (*ri)->visible && (*ri)->is_inside(position)) + return *ri; + return nohandle; +} + +bool ActiveArea::on_button_press_event(GdkEventButton* event) { + if (event->button == 1) { + Vector position(event->x, event->y); + ActivePoint::Handle old_point = drag_point; + drag_point = get_point_at(position); + if (drag_point) { + if (hover_point) hover_point->active = false; + hover_point = drag_point; + hover_point->active = true; + bring_to_front(drag_point); + drag_offset = drag_point->position - position; + } + if (old_point != drag_point) queue_draw(); + return true; + } + return false; +} + +bool ActiveArea::on_button_release_event(GdkEventButton* event) { + if (event->button == 1) { + if (drag_point) { + drag_point.reset(); + if (hover_point) hover_point->active = false; + hover_point = get_point_at(Vector(event->x, event->y)); + if (hover_point) hover_point->active = true; + queue_draw(); + } + return true; + } + return false; +} + +bool ActiveArea::on_motion_notify_event(GdkEventMotion* event) { + Vector position(event->x, event->y); + if (drag_point) { + Vector oldposition = drag_point->position; + Vector newposition = position + drag_offset; + if (newposition != oldposition) { + drag_point->position = newposition; + on_point_move(drag_point, oldposition); + queue_draw(); + } + } else { + ActivePoint::Handle old_point = hover_point; + if (hover_point) hover_point->active = false; + hover_point = get_point_at(position); + if (hover_point) hover_point->active = true; + if (old_point != hover_point) queue_draw(); + } + return true; +} + +bool ActiveArea::on_draw(const Cairo::RefPtr &context) { + on_draw(Context(context)); + return true; +} + +void ActiveArea::on_draw(const Context &context) { + // fill background + context->save(); + context->rectangle(0, 0, get_width(), get_height()); + context->set_source_rgba(1, 1, 1, 1); + context->fill(); + context->restore(); + + on_draw_content(context); + + // draw points + for(PointList::const_iterator i = points.begin(); i != points.end(); ++i) + context.point(**i); +} + +void ActiveArea::on_point_move(const ActivePoint::Handle &/*point*/, const Vector &/*oldposition*/) + { } + +void ActiveArea::on_draw_content(const Context &/*context*/) + { } diff --git a/c++/vector/activearea.h b/c++/vector/activearea.h new file mode 100644 index 0000000..6839412 --- /dev/null +++ b/c++/vector/activearea.h @@ -0,0 +1,46 @@ +#ifndef ACTIVEAREA_H +#define ACTIVEAREA_H + + +#include + +#include + +#include "activepoint.h" +#include "context.h" + + +class ActiveArea: public Gtk::DrawingArea { +public: + typedef std::list PointList; + +private: + PointList points; + ActivePoint::Handle hover_point; + ActivePoint::Handle drag_point; + Vector drag_offset; + +protected: + const PointList& get_points() const { return points; } + bool is_point_added(const ActivePoint::Handle &point) const; + void add_point(const ActivePoint::Handle &point); + void remove_point(const ActivePoint::Handle &point); + void bring_to_front(const ActivePoint::Handle &point); + + ActivePoint::Handle get_point_at(const Vector &position) const; + + virtual void on_point_move(const ActivePoint::Handle &point, const Vector &oldposition); + virtual void on_draw(const Context &context); + virtual void on_draw_content(const Context &context); + + bool on_draw(const Cairo::RefPtr &context) override; + bool on_button_press_event(GdkEventButton* event) override; + bool on_button_release_event(GdkEventButton* event) override; + bool on_motion_notify_event(GdkEventMotion* event) override; + +public: + ActiveArea(); +}; + + +#endif diff --git a/c++/vector/activepoint.h b/c++/vector/activepoint.h new file mode 100644 index 0000000..03b9048 --- /dev/null +++ b/c++/vector/activepoint.h @@ -0,0 +1,51 @@ +#ifndef ACTIVEPOINT_H +#define ACTIVEPOINT_H + + +#include "vector.h" +#include "color.h" +#include "handle.h" + + +class ActivePoint: public Shared { +public: + typedef HandleT Handle; + + Vector position; + Color color; + Real radius; + bool enabled; + bool visible; + + bool active; + +private: + ActivePoint(const ActivePoint&): ActivePoint() { } + ActivePoint& operator= (const ActivePoint&) { return *this; } + +public: + ActivePoint( + const Vector &position, + const Color &color, + const Real &radius = 5, + bool enabled = true, + bool visible = true + ): + position(position), + color(color), + radius(radius), + enabled(enabled), + visible(visible), + active() + { } + + explicit ActivePoint( + const Vector &position = Vector() + ): ActivePoint(position, Color::white()) { } + + bool is_inside(const Vector &p) const + { return lesseq((position - p).lensqr(), radius*radius); } +}; + + +#endif diff --git a/c++/vector/build.sh b/c++/vector/build.sh new file mode 100755 index 0000000..0938a05 --- /dev/null +++ b/c++/vector/build.sh @@ -0,0 +1,18 @@ +#!/bin/bash + +set -e + +function call() { + echo "$@" + "$@" +} + + +FLAGS="-Wall -std=c++11" +if [ "$1" = "debug" ]; then + FLAGS="$FLAGS -g -O0" +else + FLAGS="$FLAGS -O3" +fi + +call c++ $FLAGS `pkg-config --cflags --libs gtkmm-3.0` *.cpp -o vector diff --git a/c++/vector/color.h b/c++/vector/color.h new file mode 100644 index 0000000..81d87b7 --- /dev/null +++ b/c++/vector/color.h @@ -0,0 +1,88 @@ +#ifndef COLOR_H +#define COLOR_H + + +#include "real.h" + + +typedef Real ColorReal; + + +class Color { +public: + union { + struct { ColorReal channels[4]; }; + struct { ColorReal r, g, b, a; }; + }; + + Color(): r(), g(), b(), a() { } + + explicit Color( + const ColorReal r, + const ColorReal g, + const ColorReal b, + const ColorReal a = 1 + ): + r(r), g(g), b(b), a(a) { } + + const Real& operator[] (int i) const { return channels[i]; } + Real& operator[] (int i) { return channels[i]; } + + bool operator< (const Color &c) const { + return less(r, c.r) ? true : less(c.r, r) ? false + : less(g, c.g) ? true : less(c.g, g) ? false + : less(b, c.b) ? true : less(c.b, b) ? false + : less(a, c.a); + } + bool operator== (const Color &c) const { return equal(r, c.r) && equal(g, c.g) && equal(b, c.b) && equal(a, c.a); } + bool operator!= (const Color &c) const { return !(*this == c); } + + bool istransparent() const { return *this == Color(); } + void reset() { *this = Color(); } + + Color withalpha(const ColorReal &a) const { return Color(r, g, b, a); } + Color mulalpha(const ColorReal &a = 0.5) const { return Color(r, g, b, this->a*a); } + + Color blend(const Color &c, const ColorReal &value = 1) const { + ColorReal asrc = c.a*value; + ColorReal adst = a*(1 - asrc); + ColorReal ares = asrc + adst; + + if (lesseq(ares, 0)) + return Color(); + + ColorReal k = 1/ares; + return Color( + (r*asrc + c.r*adst)*k, + (g*asrc + c.g*adst)*k, + (b*asrc + c.b*adst)*k, + ares ); + } + + Color mergecolor(const Color &c, const ColorReal &value = 0.5) const { + ColorReal v = 1 - value; + return Color( + r*v + c.r*value, + g*v + c.g*value, + b*v + c.b*value, + a ); + } + + Color merge(const Color &c, const ColorReal &value = 0.5) const + { return mergecolor(c, value).withalpha(a*(1 - value) + c.a*value); } + + static Color transparent() { return Color(); } + static Color black() { return Color(0, 0, 0); } + static Color white() { return Color(1, 1, 1); } + static Color red() { return Color(1, 0, 0); } + static Color green() { return Color(0, 1, 0); } + static Color blue() { return Color(0, 0, 1); } + static Color yellow() { return Color(1, 1, 0); } + static Color cyan() { return Color(0, 1, 1); } + static Color magenta() { return Color(1, 0, 1); } + + static Color gray(const ColorReal &value = 0.5) { return Color(value, value, value); } +}; + + +#endif diff --git a/c++/vector/context.cpp b/c++/vector/context.cpp new file mode 100644 index 0000000..b2d3242 --- /dev/null +++ b/c++/vector/context.cpp @@ -0,0 +1,46 @@ + + +#include + +#include "activepoint.h" + +#include "context.h" + + +Real Context::pixelsize_sqr() const { + Cairo::Matrix m; + ctx()->get_matrix(m); + return 0.25*sqrt(m.xx*m.xx + m.xy*m.xy + m.yx*m.yx + m.yy*m.yy); +} + +Real Context::pixelsize() const { + return sqrt(pixelsize_sqr()); +} + +void Context::point(const ActivePoint &point) const { + if (!point.visible) return; + + Color color = point.color; + Color stroke_color = Color::black(); + if (!point.enabled) { + color = color.mergecolor(Color::gray()).mulalpha(); + stroke_color = stroke_color.mergecolor(Color::gray()).mulalpha(); + } + + ctx()->save(); + ctx()->arc(point.position.x, point.position.y, point.radius, 0, 2*pi); + set_source_rgba(color); + ctx()->fill_preserve(); + set_line_width_px(point.active ? 2 : 1); + set_source_rgba(stroke_color); + ctx()->stroke(); + + ctx()->restore(); +} + +void Context::segment(const Segment &segment) const { + line_to(segment.p0); + Vector b0 = segment.p0 + segment.t0/3; + Vector b1 = segment.p1 - segment.t1/3; + ctx()->curve_to(b0.x, b0.y, b1.x, b1.y, segment.p1.x, segment.p1.y); +} diff --git a/c++/vector/context.h b/c++/vector/context.h new file mode 100644 index 0000000..1fd9f0b --- /dev/null +++ b/c++/vector/context.h @@ -0,0 +1,46 @@ +#ifndef CONTEXT_H +#define CONTEXT_H + + +#include + +#include + +#include "vector.h" +#include "color.h" + +class ActivePoint; + + +class Context { +public: + Cairo::RefPtr context; + + explicit Context( + const Cairo::RefPtr &context = Cairo::RefPtr() + ): context(context) { } + + Cairo::RefPtr ctx() const { assert(context); return context; } + Cairo::Context* operator->() const { return context.operator->(); } + + Real pixelsize_sqr() const; + Real pixelsize() const; + + void set_source_rgb(const Color &c) const + { ctx()->set_source_rgb(c.r, c.g, c.b); } + void set_source_rgba(const Color &c) const + { ctx()->set_source_rgba(c.r, c.g, c.b, c.a); } + void set_line_width_px(const Real &pixels) const + { ctx()->set_line_width( pixels*pixelsize() ); } + + void move_to(const Vector &p) const + { ctx()->move_to(p.x, p.y); } + void line_to(const Vector &p) const + { ctx()->line_to(p.x, p.y); } + + void point(const ActivePoint &point) const; + void segment(const Segment &segment) const; +}; + + +#endif diff --git a/c++/vector/curvesarea.cpp b/c++/vector/curvesarea.cpp new file mode 100644 index 0000000..9672fda --- /dev/null +++ b/c++/vector/curvesarea.cpp @@ -0,0 +1,103 @@ + + +#include "curvesarea.h" + + +namespace { + struct DrawSegment { + private: + const Context &context; + const Segment &segment; + const Real width; + const Real precision_sqr; + const int max_level = 16; + int level; + Vector p0; + Real l0; + + Vector func(const Real &l) + { return segment(l) + segment.t(l).perp().norm()*width; } + + void step(const Real &l1) { + Vector p1 = segment(l1) + segment.t(l1).perp().norm()*width; + if (++level < max_level) + while (greater((p1 - p0).lensqr(), precision_sqr)) + step(l0 + 0.5*(l1 - l0)); + --level; + context.line_to(p1); + p0 = p1; l0 = l1; + } + + public: + DrawSegment(const Context &context, const Segment &segment, const Real &width): + context(context), segment(segment), width(width), precision_sqr(context.pixelsize_sqr()), level(), l0() + { + context.line_to(p0 = func(0)); + step(1); + } + }; +} + + +CurvesArea::CurvesArea() +{ + set_size_request(500, 500); + + Vector center(250, 250); + const Real radius = 100; + const Real tangent_len = 50; + const int count = 5; + for(int i = 0; i < count; ++i) { + Real a = 2*pi*i/count; + Vector v = Vector::from_angle(a); + curve.push_back(CurvePoint(center + v*radius, v*tangent_len)); + curve.back().add_to(*this); + } + curve.loop = true; +} + +void CurvesArea::put_segment(const Context &context, const Segment &segment, const Real width) { + if (equal(width, 0)) context.segment(segment); + else DrawSegment(context, segment, width); +} + +void CurvesArea::put_curve(const Context &context, const Curve &curve, const Real width) { + for(Curve::const_iterator curr = curve.begin(); curr != curve.end(); ++curr) { + Curve::const_iterator next = curve.next(curr); + put_segment(context, Segment(curr->get_p(), next->get_p(), curr->get_t(), next->get_t()), width); + } +} + +void CurvesArea::draw_curve(const Context &context, const Curve &curve) { + context->save(); + + Real width = 50; + context.set_line_width_px(1); + + // tangents + context.set_source_rgba(Color::gray()); + for(Curve::const_iterator i = curve.begin(); i != curve.end(); ++i) { + context.move_to(i->p->position); + context.line_to(i->t->position); + context->stroke(); + } + + // middle line + context.set_source_rgba(Color::black()); + put_curve(context, curve); + context->stroke(); + + // borders + context.set_source_rgba(Color::blue()); + put_curve(context, curve, width); + context->stroke(); + put_curve(context, curve, -width); + context->stroke(); + + context->restore(); +} + + +void CurvesArea::on_draw_content(const Context &context) { + draw_curve(context, curve); +} diff --git a/c++/vector/curvesarea.h b/c++/vector/curvesarea.h new file mode 100644 index 0000000..3d26d35 --- /dev/null +++ b/c++/vector/curvesarea.h @@ -0,0 +1,51 @@ +#ifndef CURVEAREA_H +#define CURVEAREA_H + + +#include + +#include "activearea.h" + + +class CurvesArea: public ActiveArea { +public: + class CurvePoint { + public: + ActivePoint::Handle p, t; + + explicit CurvePoint(const Vector &p = Vector(), const Vector &t = Vector()): + p(new ActivePoint(p)), + t(new ActivePoint(p + t, Color::yellow())) + { } + + const Vector& get_p() const { return p->position; } + Vector get_t() const { return t->position - p->position; } + + void add_to(CurvesArea &area) const { area.add_point(p); area.add_point(t); } + void remove_from(CurvesArea &area) const { area.remove_point(p); area.remove_point(t); } + }; + + class Curve: public std::vector { + public: + bool loop; + Curve(): loop() { } + Curve::const_iterator next(Curve::const_iterator i) const + { return ++i != end() ? i : loop ? begin() : --i; } + Curve::const_iterator prev(Curve::const_iterator i) const + { return --i != begin() ? i : loop ? end() : ++i; } + }; + +protected: + Curve curve; + + void put_segment(const Context &context, const Segment &segment, const Real width = 0); + void put_curve(const Context &context, const Curve &curve, const Real width = 0); + void draw_curve(const Context &context, const Curve &curve); + +public: + CurvesArea(); + void on_draw_content(const Context &context) override; +}; + + +#endif diff --git a/c++/vector/handle.h b/c++/vector/handle.h new file mode 100644 index 0000000..4a2438b --- /dev/null +++ b/c++/vector/handle.h @@ -0,0 +1,98 @@ +#ifndef HANDLE_H +#define HANDLE_H + + +#include + +#include +#include + + +class Shared { +private: + int refcount; +public: + Shared(): refcount(1) { } + Shared(const Shared&): refcount(1) { } + virtual ~Shared() { }; + Shared& operator=(const Shared&) { return *this; } + void reference() { ++refcount; } + void unreference() { if (--refcount <= 0) delete this; } + int get_refcount() const { return refcount; } +}; + +class NoHandle { }; +const NoHandle nohandle; + +template +class HandleT +{ +public: + typedef T Type; + +protected: + Type *pointer; + +public: + HandleT(): pointer() { } + HandleT(const NoHandle &): HandleT() { } + explicit HandleT(Type *pointer): HandleT() { set(pointer); } + HandleT(const HandleT &x): HandleT(x.get()) { } + HandleT(HandleT &&x): pointer(x.pointer) { x.pointer = 0; } + ~HandleT() { reset(); } + + Type* get() const { return pointer; } + Type& getref() const { assert(pointer); return *pointer; } + Type& operator*() const { return getref(); } + Type* operator->() const { return &getref(); } + + operator bool() const { return pointer; } + bool empty() const { return !pointer; } + int get_refcount() const { return pointer ? pointer->get_refcount() : 0; } + bool unique() const { return get_refcount() == 1; } + + bool operator<(const HandleT &x) const { return pointer < x.get(); } + bool operator==(const HandleT &x) const { return pointer == x.get(); } + bool operator!=(const HandleT &x) const { return pointer != x.get(); } + + void set(Type *x) { + if (x != pointer) { + if (x) x->reference(); + if (pointer) pointer->unreference(); + pointer = x; + } + } + + void swap(HandleT &x) + { swap(pointer, x.pointer); } + + void grab(HandleT &x) { + if (&x != this) { + if (pointer) pointer->unreference(); + pointer = x.pointer; + x.pointer = 0; + } + } + + void reset() { set(0); } + HandleT& operator=(const HandleT &x) { set(x.get()); return *this; } + HandleT& operator=(HandleT &&x) { grab(x); return *this; } + + template static HandleT cast(TT *x) + { return HandleT(dynamic_cast(x)); } + template static HandleT cast(const HandleT &x) + { return cast(x.get()); } + + template operator HandleT() const + { return HandleT(pointer); } + + template bool type_is() const + { return cast(pointer); } + template TT* type_pointer() const + { return dynamic_cast(pointer); } + template bool type_equal() const + { return typeid(*pointer) == typeid(TT); } +}; + + +#endif diff --git a/c++/vector/main.cpp b/c++/vector/main.cpp new file mode 100644 index 0000000..992ac10 --- /dev/null +++ b/c++/vector/main.cpp @@ -0,0 +1,12 @@ + + +#include + +#include "mainwindow.h" + + +int main(int argc, char **argv) { + Glib::RefPtr application = Gtk::Application::create(argc, argv); + MainWindow window; + return application->run(window); +} diff --git a/c++/vector/mainwindow.cpp b/c++/vector/mainwindow.cpp new file mode 100644 index 0000000..8fd8055 --- /dev/null +++ b/c++/vector/mainwindow.cpp @@ -0,0 +1,12 @@ + + +#include "curvesarea.h" + +#include "mainwindow.h" + + +MainWindow::MainWindow() { + CurvesArea *curves = manage(new CurvesArea()); + curves->show(); + add(*curves); +} diff --git a/c++/vector/mainwindow.h b/c++/vector/mainwindow.h new file mode 100644 index 0000000..e0ab109 --- /dev/null +++ b/c++/vector/mainwindow.h @@ -0,0 +1,14 @@ +#ifndef MAINWINDOW_H +#define MAINWINDOW_H + + +#include + + +class MainWindow: public Gtk::Window { +public: + MainWindow(); +}; + + +#endif diff --git a/c++/vector/real.h b/c++/vector/real.h new file mode 100644 index 0000000..6919d30 --- /dev/null +++ b/c++/vector/real.h @@ -0,0 +1,24 @@ +#ifndef REAL_H +#define REAL_H + + +typedef double Real; + + +const Real precision = 1e-5; +const Real pi = 3.1415926535897932384626433; + + +inline bool equal(const Real &a, const Real &b) + { return (a < b ? b - a : a - b) < precision; } +inline bool less(const Real &a, const Real &b) + { return !equal(a, b) && a < b; } +inline bool greater(const Real &a, const Real &b) + { return less(b, a); } +inline bool lesseq(const Real &a, const Real &b) + { return equal(a, b) || a < b; } +inline bool greatereq(const Real &a, const Real &b) + { return lesseq(b, a); } + + +#endif diff --git a/c++/vector/vector.h b/c++/vector/vector.h new file mode 100644 index 0000000..ade2d2d --- /dev/null +++ b/c++/vector/vector.h @@ -0,0 +1,98 @@ +#ifndef VECTOR_H +#define VECTOR_H + + +#include +#include "real.h" + + +class Vector { +public: + union { + struct { Real x, y; }; + struct { Real coords[2]; }; + }; + + explicit Vector(const Real &x = Real(), const Real &y = Real()): x(x), y(y) { } + + const Real& operator[] (int i) const { return coords[i]; } + Real& operator[] (int i) { return coords[i]; } + + bool operator< (const Vector &a) const { return less(x, a.x) || (!less(a.x, x) && less(y, a.y)); } + bool operator== (const Vector &a) const { return equal(x, a.x) && equal(y, a.y); } + bool operator!= (const Vector &a) const { return !(*this == a); } + + Vector operator+ (const Vector &a) const { return Vector(x + a.x, y + a.y); } + Vector operator- (const Vector &a) const { return Vector(x - a.x, y - a.y); } + Vector operator* (const Real &a) const { return Vector(x*a, y*a); } + Vector operator/ (const Real &a) const { return *this * (Real(1)/a); } + + Vector& operator+= (const Vector &a) { x += a.x; y += a.y; return *this; } + Vector& operator-= (const Vector &a) { x -= a.x; y -= a.y; return *this; } + Vector& operator*= (const Real &a) { x *= a; y *= a; return *this; } + Vector& operator/= (const Real &a) { return *this *= (Real(1)/a); } + + friend inline Vector operator*(const Real &a, const Vector &b) { return b*a; } + friend inline Real dot(const Vector &a, const Vector &b) { return a.x*b.x + a.y*b.y; } + + bool iszero() const { return *this == Vector(); } + void reset() { *this = Vector(); } + + Real lensqr() const { return dot(*this, *this); } + Real len() const { return sqrt(lensqr()); } + + Vector norm() const { return iszero() ? Vector() : *this/len(); } + Vector perp() const { return Vector(-y, x); } + Vector scale(const Vector &a) const { return Vector(x*a.x, y*a.y); } + Vector rotate(const Real &angle) const { + Vector a = from_angle(angle); + return Vector(x*a.x - y*a.y, x*a.y + y*a.x); + } + + static Vector from_angle(Real angle) + { return Vector(cos(angle), sin(angle)); } +}; + + +class Segment { +public: + Vector p0, p1, t0, t1; + + explicit Segment( + const Vector &p0 = Vector(), + const Vector &p1 = Vector(), + const Vector &t0 = Vector(), + const Vector &t1 = Vector() + ): + p0(p0), p1(p1), t0(t0), t1(t1) { } + + bool operator< (const Segment &a) const { + return p0 < a.p0 ? true : a.p0 < p0 ? false + : p1 < a.p1 ? true : a.p1 < p1 ? false + : t0 < a.t0 ? true : a.t0 < t0 ? false + : t1 < a.t1; + } + bool operator> (const Segment &a) const { return a < *this; } + bool operator== (const Segment &a) const { return p0 == a.p0 && p1 == a.p1 && t0 == a.t0 && t1 == a.t1; } + bool operator!= (const Segment &a) const { return !(*this == a); } + + Vector p(const Real &l) const { + return p0*((( 2.0*l - 3.0)*l )*l + 1.0) + + p1*(((-2.0*l + 3.0)*l )*l ) + + t0*((( l - 2.0)*l + 1.0)*l ) + + t1*((( l - 1.0)*l )*l ); + } + + Vector t(const Real &l) const { + return p0*(( 6.0*l - 6.0)*l ) + + p1*((-6.0*l + 6.0)*l ) + + t0*(( 3.0*l - 4.0)*l + 1.0) + + t1*(( 3.0*l - 2.0)*l ); + } + + Vector operator() (const Real &l) const { return p(l); } + Segment sub(const Real &a, const Real &b) const { return Segment(p(a), p(b), t(a), t(b)); } +}; + + +#endif