Browse Source

Added arcTo(). Closes #11

v1.x
Tj Holowaychuk 14 years ago
parent
commit
ea4b13e7ef
  1. 122
      src/CanvasRenderingContext2d.cc
  2. 1
      src/CanvasRenderingContext2d.h
  3. 19
      src/Point.h

122
src/CanvasRenderingContext2d.cc

@ -9,6 +9,7 @@
#include <string.h>
#include <stdlib.h>
#include "Canvas.h"
#include "Point.h"
#include "Image.h"
#include "ImageData.h"
#include "CanvasRenderingContext2d.h"
@ -97,6 +98,7 @@ Context2d::Initialize(Handle<Object> 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<Value>
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<Context2d>(args.This());
cairo_t *ctx = context->context();
// Current path point
double x, y;
cairo_get_current_point(ctx, &x, &y);
Point<float> p0(x, y);
// Point (x0,y0)
Point<float> p1(args[0]->NumberValue(), args[1]->NumberValue());
// Point (x1,y1)
Point<float> 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<float> p1p0((p0.x - p1.x),(p0.y - p1.y));
Point<float> 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<float> 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<float> t_p1p0((p1.x + factor_p1p0 * p1p0.x), (p1.y + factor_p1p0 * p1p0.y));
Point<float> 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<float>(-orth_p1p0.x, -orth_p1p0.y);
Point<float> p((t_p1p0.x + factor_ra * orth_p1p0.x), (t_p1p0.y + factor_ra * orth_p1p0.y));
orth_p1p0 = Point<float>(-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<float> t_p1p2((p1.x + factor_p1p2 * p1p2.x), (p1.y + factor_p1p2 * p1p2.y));
Point<float> 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();
}

1
src/CanvasRenderingContext2d.h

@ -84,6 +84,7 @@ class Context2d: public node::ObjectWrap {
static Handle<Value> ClearRect(const Arguments &args);
static Handle<Value> Rect(const Arguments &args);
static Handle<Value> Arc(const Arguments &args);
static Handle<Value> ArcTo(const Arguments &args);
static Handle<Value> GetGlobalCompositeOperation(Local<String> prop, const AccessorInfo &info);
static Handle<Value> GetGlobalAlpha(Local<String> prop, const AccessorInfo &info);
static Handle<Value> GetMiterLimit(Local<String> prop, const AccessorInfo &info);

19
src/Point.h

@ -0,0 +1,19 @@
//
// Point.h
//
// Copyright (c) 2010 LearnBoost <tj@learnboost.com>
//
#ifndef __NODE_POINT_H__
#define __NODE_POINT_H__
template <class T>
class Point {
public:
T x, y;
Point(T x, T y): x(x), y(y) {}
};
#endif /* __NODE_POINT_H__ */
Loading…
Cancel
Save