blob: 801727a4d1ea6ca670ce57425f591c857aff8317 [file] [log] [blame]
// Copyright 2020 The Fuchsia Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "tests/common/path_sink.h"
#include <math.h>
#include <string.h>
#include "tests/common/arc_parameters.h"
//
//
//
void
PathSink::addArcTo(double cx,
double cy,
double rx,
double ry,
double x_axis_rotation,
double angle,
double angle_delta)
{
const double cos_phi = cos(x_axis_rotation);
const double sin_phi = sin(x_axis_rotation);
// Emit rational quadratic beziers in the transformed space where the
// arc sits on the unit circle, then scale up the coordinates.
const double angle_sweep = M_PI / 2.; // minimize convex hull.
const double angle_incr = (angle_delta > 0) ? angle_sweep : -angle_sweep;
while (angle_delta != 0.)
{
const double theta = angle;
const double sweep = fabs(angle_delta) <= angle_sweep ? angle_delta : angle_incr;
angle += sweep;
angle_delta -= sweep;
// Coordinates of the control point and the end point on the unit circle.
const double half_sweep = sweep * 0.5;
const double w = cos(half_sweep);
double control_x = cos(theta + half_sweep) / w;
double control_y = sin(theta + half_sweep) / w;
double end_x = cos(theta + sweep);
double end_y = sin(theta + sweep);
// Scale them to the ellipse's radii.
control_x *= rx;
control_y *= ry;
end_x *= rx;
end_y *= ry;
// Rotate them + translate them.
double c_x = cx + control_x * cos_phi - control_y * sin_phi;
double c_y = cy + control_x * sin_phi + control_y * cos_phi;
double n_x = cx + end_x * cos_phi - end_y * sin_phi;
double n_y = cy + end_x * sin_phi + end_y * cos_phi;
// The weight is the cosine of the half-sweep.
addRatQuadTo(c_x, c_y, n_x, n_y, w);
}
}
void
PathSink::addSvgArcTo(double x0,
double y0,
double x,
double y,
double rx,
double ry,
double x_axis_rotation_radians,
bool large_arc_flag,
bool sweep_flag)
{
arc_center_parameters_t params = arc_center_parameters_from_endpoint(
(const arc_endpoint_parameters_t){ x0,
y0,
x,
y,
large_arc_flag,
sweep_flag,
rx,
ry,
x_axis_rotation_radians });
addArcTo(params.cx,
params.cy,
params.rx,
params.ry,
params.phi,
params.theta,
params.theta_delta);
}
// This is cos(PI/4), a.k.a. sqrt(2)/2, which happens to be the rational quad
// weight to use to render a quarter-circle arc.
static const double kCos45degrees = 0.7071067811865475244008;
// Add an ellipse path to a given builder. The ellipse is always axis-aligned.
// Note that this adds a full path (i.e. with begin() ... end() calls).
bool
PathSink::addEllipsePath(double center_x, double center_y, double radius_x, double radius_y)
{
begin();
// Implement the ellipse as four rational quadratic beziers. One per quadrant.
// It is possible to use only 3 beziers but this results in a much wider
// convex hull / bounding box.
//
// This always starts on (cx + rx, cy) in counter-clockwise orientation
// (assuming x-rightwards and y-upwards axis).
//
// To get clockwise arcs, negate radius_y or radius_x, but not both.
// Rational weight to turn a rational quad into a circle arc.
const double rat_weight = kCos45degrees;
// clang-format: off
double coords[] = {
// 0: start point
center_x + radius_x,
center_y,
// 2: first quadrant / rational quad
center_x + radius_x,
center_y + radius_y,
center_x,
center_y + radius_y,
rat_weight,
// 7: second quadrant
center_x - radius_x,
center_y + radius_y,
center_x - radius_x,
center_y,
rat_weight,
// 12: third quadrant
center_x - radius_x,
center_y - radius_y,
center_x,
center_y - radius_y,
rat_weight,
// 17: last quadrant
center_x + radius_x,
center_y - radius_y,
center_x + radius_x,
center_y,
rat_weight,
};
// clang-format: on
addItem(MOVE_TO, coords + 0);
addItem(RAT_QUAD_TO, coords + 2);
addItem(RAT_QUAD_TO, coords + 7);
addItem(RAT_QUAD_TO, coords + 12);
addItem(RAT_QUAD_TO, coords + 17);
return end();
}
bool
PathSink::addRectPath(double x, double y, double w, double h)
{
begin();
addMoveTo(x, y);
addLineTo(x + w, y);
addLineTo(x + w, y + h);
addLineTo(x, y + h);
addLineTo(x, y);
return end();
}
bool
PathSink::addRoundedRectPath(double x, double y, double w, double h, double rx, double ry)
{
if (rx == 0. || ry == 0.)
return addRectPath(x, y, w, h);
const double rat_weight = kCos45degrees;
begin();
addMoveTo(x + rx, y);
addLineTo(x + w - rx, y);
addRatQuadTo(x + w, y, x + w, y + ry, rat_weight);
addLineTo(x + w, y + h - ry);
addRatQuadTo(x + w, y + h, x + w - rx, y + h, rat_weight);
addLineTo(x + rx, y + h);
addRatQuadTo(x, y + h, x, y + h - ry, rat_weight);
addLineTo(x, y + ry);
addRatQuadTo(x, y, x + rx, y, rat_weight);
return end();
}