Files
processing4/agg/agg_bezier_arc_svg.java
2006-04-08 00:50:57 +00:00

191 lines
6.0 KiB
Java

import processing.core.*;
/**
* Compute an SVG-style bezier arc.
*
* Computes an elliptical arc from (x1, y1) to (x2, y2). The size and
* orientation of the ellipse are defined by two radii (rx, ry)
* and an x-axis-rotation, which indicates how the ellipse as a whole
* is rotated relative to the current coordinate system. The center
* (cx, cy) of the ellipse is calculated automatically to satisfy the
* constraints imposed by the other parameters.
* large-arc-flag and sweep-flag contribute to the automatic calculations
* and help determine how the arc is drawn.
*/
public class agg_bezier_arc_svg {
public agg_bezier_arc_svg() : m_arc(), m_radii_ok(false) {}
bezier_arc_svg(float x1, float y1,
float rx, float ry,
float angle,
bool large_arc_flag,
bool sweep_flag,
float x2, float y2) {
m_arc = new agg_bezier_arc();
m_radii_ok = false;
init(x1, y1, rx, ry, angle, large_arc_flag, sweep_flag, x2, y2);
}
//--------------------------------------------------------------------
void init(float x1, float y1,
float rx, float ry,
float angle,
bool large_arc_flag,
bool sweep_flag,
float x2, float y2);
public boolean radii_ok() {
return m_radii_ok;
}
public void rewind() {
m_arc.rewind(0);
}
//--------------------------------------------------------------------
int vertex(float x[], float y[], int offset) { // un
return m_arc.vertex(x, y, offset);
}
// Supplemantary functions. num_vertices() actually returns doubled
// number of vertices. That is, for 1 vertex it returns 2.
//--------------------------------------------------------------------
unsigned num_vertices() const { return m_arc.num_vertices(); }
const float* vertices() const { return m_arc.vertices(); }
float* vertices() { return m_arc.vertices(); }
private agg_bezier_arc m_arc;
private boolean m_radii_ok;
}
//--------------------------------------------------------------------
void bezier_arc_svg::init(float x0, float y0,
float rx, float ry,
float angle,
bool large_arc_flag,
bool sweep_flag,
float x2, float y2)
{
m_radii_ok = true;
if(rx < 0.0) rx = -rx;
if(ry < 0.0) ry = -rx;
// Calculate the middle point between
// the current and the final points
//------------------------
float dx2 = (x0 - x2) / 2.0;
float dy2 = (y0 - y2) / 2.0;
float cos_a = cos(angle);
float sin_a = sin(angle);
// Calculate (x1, y1)
//------------------------
float x1 = cos_a * dx2 + sin_a * dy2;
float y1 = -sin_a * dx2 + cos_a * dy2;
// Ensure radii are large enough
//------------------------
float prx = rx * rx;
float pry = ry * ry;
float px1 = x1 * x1;
float py1 = y1 * y1;
// Check that radii are large enough
//------------------------
float radii_check = px1/prx + py1/pry;
if(radii_check > 1.0)
{
rx = sqrt(radii_check) * rx;
ry = sqrt(radii_check) * ry;
prx = rx * rx;
pry = ry * ry;
if(radii_check > 10.0) m_radii_ok = false;
}
// Calculate (cx1, cy1)
//------------------------
float sign = (large_arc_flag == sweep_flag) ? -1.0 : 1.0;
float sq = (prx*pry - prx*py1 - pry*px1) / (prx*py1 + pry*px1);
float coef = sign * sqrt((sq < 0) ? 0 : sq);
float cx1 = coef * ((rx * y1) / ry);
float cy1 = coef * -((ry * x1) / rx);
//
// Calculate (cx, cy) from (cx1, cy1)
//------------------------
float sx2 = (x0 + x2) / 2.0;
float sy2 = (y0 + y2) / 2.0;
float cx = sx2 + (cos_a * cx1 - sin_a * cy1);
float cy = sy2 + (sin_a * cx1 + cos_a * cy1);
// Calculate the start_angle (angle1) and the sweep_angle (dangle)
//------------------------
float ux = (x1 - cx1) / rx;
float uy = (y1 - cy1) / ry;
float vx = (-x1 - cx1) / rx;
float vy = (-y1 - cy1) / ry;
float p, n;
// Calculate the angle start
//------------------------
n = sqrt(ux*ux + uy*uy);
p = ux; // (1 * ux) + (0 * uy)
sign = (uy < 0) ? -1.0 : 1.0;
float v = p / n;
if(v < -1.0) v = -1.0;
if(v > 1.0) v = 1.0;
float start_angle = sign * acos(v);
// Calculate the sweep angle
//------------------------
n = sqrt((ux*ux + uy*uy) * (vx*vx + vy*vy));
p = ux * vx + uy * vy;
sign = (ux * vy - uy * vx < 0) ? -1.0 : 1.0;
v = p / n;
if(v < -1.0) v = -1.0;
if(v > 1.0) v = 1.0;
float sweep_angle = sign * acos(v);
if(!sweep_flag && sweep_angle > 0)
{
sweep_angle -= pi * 2.0;
}
else
if (sweep_flag && sweep_angle < 0)
{
sweep_angle += pi * 2.0;
}
// We can now build and transform the resulting arc
//------------------------
m_arc.init(0.0, 0.0, rx, ry, start_angle, sweep_angle);
trans_affine mtx = trans_affine_rotation(angle);
mtx *= trans_affine_translation(cx, cy);
for(unsigned i = 2; i < m_arc.num_vertices()-2; i += 2)
{
mtx.transform(m_arc.vertices() + i, m_arc.vertices() + i + 1);
}
// We must make sure that the starting and ending points
// exactly coincide with the initial (x0,y0) and (x2,y2)
m_arc.vertices()[0] = x0;
m_arc.vertices()[1] = y0;
if(m_arc.num_vertices() > 2)
{
m_arc.vertices()[m_arc.num_vertices() - 2] = x2;
m_arc.vertices()[m_arc.num_vertices() - 1] = y2;
}
}
}