diff --git a/src/CanvasRenderingContext2d.cc b/src/CanvasRenderingContext2d.cc index bb05db1..5ac2756 100644 --- a/src/CanvasRenderingContext2d.cc +++ b/src/CanvasRenderingContext2d.cc @@ -9,6 +9,7 @@ #include #include #include "Canvas.h" +#include "Point.h" #include "Image.h" #include "ImageData.h" #include "CanvasRenderingContext2d.h" @@ -97,6 +98,7 @@ Context2d::Initialize(Handle target) { NODE_SET_PROTOTYPE_METHOD(constructor, "beginPath", BeginPath); NODE_SET_PROTOTYPE_METHOD(constructor, "closePath", ClosePath); NODE_SET_PROTOTYPE_METHOD(constructor, "arc", Arc); + NODE_SET_PROTOTYPE_METHOD(constructor, "arcTo", ArcTo); NODE_SET_PROTOTYPE_METHOD(constructor, "setFont", SetFont); NODE_SET_PROTOTYPE_METHOD(constructor, "setShadowRGBA", SetShadowRGBA); NODE_SET_PROTOTYPE_METHOD(constructor, "setFillRGBA", SetFillRGBA); @@ -1576,10 +1578,10 @@ Context2d::Arc(const Arguments &args) { HandleScope scope; if (!args[0]->IsNumber() - ||!args[1]->IsNumber() - ||!args[2]->IsNumber() - ||!args[3]->IsNumber() - ||!args[4]->IsNumber()) return Undefined(); + || !args[1]->IsNumber() + || !args[2]->IsNumber() + || !args[3]->IsNumber() + || !args[4]->IsNumber()) return Undefined(); bool anticlockwise = args[5]->BooleanValue(); @@ -1604,3 +1606,115 @@ Context2d::Arc(const Arguments &args) { return Undefined(); } + +/* + * Adds an arcTo point (x0,y0) to (x1,y1) with the given radius. + * + * Implementation influenced by WebKit. + */ + +Handle +Context2d::ArcTo(const Arguments &args) { + HandleScope scope; + + if (!args[0]->IsNumber() + || !args[1]->IsNumber() + || !args[2]->IsNumber() + || !args[3]->IsNumber() + || !args[4]->IsNumber()) return Undefined(); + + Context2d *context = ObjectWrap::Unwrap(args.This()); + cairo_t *ctx = context->context(); + + // Current path point + double x, y; + cairo_get_current_point(ctx, &x, &y); + Point p0(x, y); + + // Point (x0,y0) + Point p1(args[0]->NumberValue(), args[1]->NumberValue()); + + // Point (x1,y1) + Point p2(args[2]->NumberValue(), args[3]->NumberValue()); + + float radius = args[4]->NumberValue(); + + if ((p1.x == p0.x && p1.y == p0.y) + || (p1.x == p2.x && p1.y == p2.y) + || radius == 0.f) { + cairo_line_to(ctx, p1.x, p1.y); + return Undefined(); + } + + Point p1p0((p0.x - p1.x),(p0.y - p1.y)); + Point p1p2((p2.x - p1.x),(p2.y - p1.y)); + float p1p0_length = sqrtf(p1p0.x * p1p0.x + p1p0.y * p1p0.y); + float p1p2_length = sqrtf(p1p2.x * p1p2.x + p1p2.y * p1p2.y); + + double cos_phi = (p1p0.x * p1p2.x + p1p0.y * p1p2.y) / (p1p0_length * p1p2_length); + // all points on a line logic + if (-1 == cos_phi) { + cairo_line_to(ctx, p1.x, p1.y); + return Undefined(); + } + + if (1 == cos_phi) { + // add infinite far away point + unsigned int max_length = 65535; + double factor_max = max_length / p1p0_length; + Point ep((p0.x + factor_max * p1p0.x), (p0.y + factor_max * p1p0.y)); + cairo_line_to(ctx, ep.x, ep.y); + return Undefined(); + } + + float tangent = radius / tan(acos(cos_phi) / 2); + float factor_p1p0 = tangent / p1p0_length; + Point t_p1p0((p1.x + factor_p1p0 * p1p0.x), (p1.y + factor_p1p0 * p1p0.y)); + + Point orth_p1p0(p1p0.y, -p1p0.x); + float orth_p1p0_length = sqrt(orth_p1p0.x * orth_p1p0.x + orth_p1p0.y * orth_p1p0.y); + float factor_ra = radius / orth_p1p0_length; + + double cos_alpha = (orth_p1p0.x * p1p2.x + orth_p1p0.y * p1p2.y) / (orth_p1p0_length * p1p2_length); + if (cos_alpha < 0.f) + orth_p1p0 = Point(-orth_p1p0.x, -orth_p1p0.y); + + Point p((t_p1p0.x + factor_ra * orth_p1p0.x), (t_p1p0.y + factor_ra * orth_p1p0.y)); + + orth_p1p0 = Point(-orth_p1p0.x, -orth_p1p0.y); + float sa = acos(orth_p1p0.x / orth_p1p0_length); + if (orth_p1p0.y < 0.f) + sa = 2 * M_PI - sa; + + bool anticlockwise = false; + + float factor_p1p2 = tangent / p1p2_length; + Point t_p1p2((p1.x + factor_p1p2 * p1p2.x), (p1.y + factor_p1p2 * p1p2.y)); + Point orth_p1p2((t_p1p2.x - p.x),(t_p1p2.y - p.y)); + float orth_p1p2_length = sqrtf(orth_p1p2.x * orth_p1p2.x + orth_p1p2.y * orth_p1p2.y); + float ea = acos(orth_p1p2.x / orth_p1p2_length); + + if (orth_p1p2.y < 0) ea = 2 * M_PI - ea; + if ((sa > ea) && ((sa - ea) < M_PI)) anticlockwise = true; + if ((sa < ea) && ((ea - sa) > M_PI)) anticlockwise = true; + + cairo_line_to(ctx, t_p1p0.x, t_p1p0.y); + + if (anticlockwise && M_PI * 2 != radius) { + cairo_arc_negative(ctx + , p.x + , p.y + , radius + , sa + , ea); + } else { + cairo_arc(ctx + , p.x + , p.y + , radius + , sa + , ea); + } + + return Undefined(); +} diff --git a/src/CanvasRenderingContext2d.h b/src/CanvasRenderingContext2d.h index 7030c4e..b11409a 100644 --- a/src/CanvasRenderingContext2d.h +++ b/src/CanvasRenderingContext2d.h @@ -84,6 +84,7 @@ class Context2d: public node::ObjectWrap { static Handle ClearRect(const Arguments &args); static Handle Rect(const Arguments &args); static Handle Arc(const Arguments &args); + static Handle ArcTo(const Arguments &args); static Handle GetGlobalCompositeOperation(Local prop, const AccessorInfo &info); static Handle GetGlobalAlpha(Local prop, const AccessorInfo &info); static Handle GetMiterLimit(Local prop, const AccessorInfo &info); diff --git a/src/Point.h b/src/Point.h new file mode 100644 index 0000000..932150c --- /dev/null +++ b/src/Point.h @@ -0,0 +1,19 @@ + + +// +// Point.h +// +// Copyright (c) 2010 LearnBoost +// + +#ifndef __NODE_POINT_H__ +#define __NODE_POINT_H__ + +template +class Point { + public: + T x, y; + Point(T x, T y): x(x), y(y) {} +}; + +#endif /* __NODE_POINT_H__ */ \ No newline at end of file