blob: c1e2697d3755b38c3ac65fd8d458978690266cfe [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 "spinel/ext/geometry/arc.h"
#include <math.h>
#include "spinel/spinel_assert.h"
//
//
//
#ifndef M_PI
#define M_PI 3.14159265358979323846
#endif
//
// For now, just use a single maximum sweep size.
//
// clang-format off
#define SPN_SWEEP_RADIANS (2.0 * M_PI / 3.0) // 120°
#define SPN_SWEEP_COS_2 0.5f // cosf(120° / 2.0)
#define SPN_SWEEP_K 0.5f // 2.0 * 0.5 * 0.5
// clang-format on
//
// Given a valid center parameterization emit zero or more rational
// quads of fixed sweep and one final arc of the remaining sweep.
//
spinel_result_t
spinel_path_builder_arc(spinel_path_builder_t path_builder, spinel_arc_params_t const * arc_params)
{
//
// cursory tests for a degenerate arc
//
// emit nothing
if ((arc_params->x0 == arc_params->x1) && (arc_params->y0 == arc_params->y1))
{
return SPN_SUCCESS;
}
// emit a line
if ((arc_params->theta_delta == 0.0f) || (arc_params->rx == 0.0f) || (arc_params->ry == 0.0f))
{
spinel_path_builder_line_to(path_builder, arc_params->x1, arc_params->y1);
return SPN_SUCCESS;
}
//
// otherwise, emit rationals
//
float const cos_phi = cosf(arc_params->phi);
float const sin_phi = sinf(arc_params->phi);
float theta = arc_params->theta;
float theta_delta = arc_params->theta_delta;
float x0 = arc_params->x0;
float y0 = arc_params->y0;
//
// assume we're going to start with a full sweep
//
float const theta_sweep = (theta_delta > 0.0f) //
? (float)+SPN_SWEEP_RADIANS
: (float)-SPN_SWEEP_RADIANS;
while (true)
{
float const abs_theta_delta = fabsf(theta_delta);
bool const is_final = (abs_theta_delta <= (float)SPN_SWEEP_RADIANS);
float w1;
float k;
float xn;
float yn;
if (is_final)
{
w1 = cosf(theta_delta / 2.0f);
k = 2.0f * w1 * w1;
xn = arc_params->x1;
yn = arc_params->y1;
}
else
{
// clang-format off
theta += theta_sweep;
theta_delta -= theta_sweep;
// clang-format on
w1 = SPN_SWEEP_COS_2;
k = SPN_SWEEP_K;
float const rx_n = arc_params->rx * cosf(theta);
float const ry_n = arc_params->ry * sinf(theta);
xn = rx_n * cos_phi - ry_n * sin_phi + arc_params->cx;
yn = rx_n * sin_phi + ry_n * cos_phi + arc_params->cy;
}
//
// calculate the control point using A. Cantóna method
//
float const xc = (arc_params->cx * (k - 2.0f) + x0 + xn) / k;
float const yc = (arc_params->cy * (k - 2.0f) + y0 + yn) / k;
// emit the rat quad
spinel_result_t err;
// FIXME(allanmac): eventually migrate this to the non-relative path builder
err = spinel_path_builder_rat_quad_to(path_builder, xc, yc, xn, yn, w1);
if (err != SPN_SUCCESS)
{
return err;
}
if (is_final)
{
return SPN_SUCCESS;
}
// otherwise
x0 = xn;
y0 = yn;
}
}
//
//
//