mirror of
https://github.com/google/pebble.git
synced 2025-06-04 01:03:12 +00:00
167 lines
5.5 KiB
C
167 lines
5.5 KiB
C
/*
|
|
* Copyright 2024 Google LLC
|
|
*
|
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
* you may not use this file except in compliance with the License.
|
|
* You may obtain a copy of the License at
|
|
*
|
|
* http://www.apache.org/licenses/LICENSE-2.0
|
|
*
|
|
* Unless required by applicable law or agreed to in writing, software
|
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
* See the License for the specific language governing permissions and
|
|
* limitations under the License.
|
|
*/
|
|
|
|
#include "gpath_builder.h"
|
|
#include "gpath.h"
|
|
#include "applib/applib_malloc.auto.h"
|
|
#include "util/trig.h"
|
|
|
|
#include <string.h>
|
|
|
|
const int fixedpoint_base = 16;
|
|
|
|
// Angle below which we're not going to process with recursion
|
|
int32_t max_angle_tolerance = (TRIG_MAX_ANGLE / 360) * 10;
|
|
|
|
bool recursive_bezier_fixed(GPathBuilder *builder,
|
|
int32_t x1, int32_t y1,
|
|
int32_t x2, int32_t y2,
|
|
int32_t x3, int32_t y3,
|
|
int32_t x4, int32_t y4) {
|
|
// Calculate all the mid-points of the line segments
|
|
int32_t x12 = (x1 + x2) / 2;
|
|
int32_t y12 = (y1 + y2) / 2;
|
|
int32_t x23 = (x2 + x3) / 2;
|
|
int32_t y23 = (y2 + y3) / 2;
|
|
int32_t x34 = (x3 + x4) / 2;
|
|
int32_t y34 = (y3 + y4) / 2;
|
|
int32_t x123 = (x12 + x23) / 2;
|
|
int32_t y123 = (y12 + y23) / 2;
|
|
int32_t x234 = (x23 + x34) / 2;
|
|
int32_t y234 = (y23 + y34) / 2;
|
|
int32_t x1234 = (x123 + x234) / 2;
|
|
int32_t y1234 = (y123 + y234) / 2;
|
|
|
|
// Angle Condition
|
|
int32_t a23 = atan2_lookup((int16_t)((y3 - y2) / fixedpoint_base),
|
|
(int16_t)((x3 - x2) / fixedpoint_base));
|
|
int32_t da1 = abs(a23 - atan2_lookup((int16_t)((y2 - y1) / fixedpoint_base),
|
|
(int16_t)((x2 - x1) / fixedpoint_base)));
|
|
int32_t da2 = abs(atan2_lookup((int16_t)((y4 - y3) / fixedpoint_base),
|
|
(int16_t)((x4 - x3) / fixedpoint_base)) - a23);
|
|
|
|
if (da1 >= TRIG_MAX_ANGLE) {
|
|
da1 = TRIG_MAX_ANGLE - da1;
|
|
}
|
|
|
|
if (da2 >= TRIG_MAX_ANGLE) {
|
|
da2 = TRIG_MAX_ANGLE - da2;
|
|
}
|
|
|
|
if (da1 + da2 < max_angle_tolerance) {
|
|
// Finally we can stop the recursion
|
|
return gpath_builder_line_to_point(builder, GPoint(x1234 / fixedpoint_base,
|
|
y1234 / fixedpoint_base));
|
|
}
|
|
|
|
// Continue subdivision if points are being added successfully
|
|
if (recursive_bezier_fixed(builder, x1, y1, x12, y12, x123, y123, x1234, y1234)
|
|
&& recursive_bezier_fixed(builder, x1234, y1234, x234, y234, x34, y34, x4, y4)) {
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
bool bezier_fixed(GPathBuilder *builder, GPoint p1, GPoint p2, GPoint p3, GPoint p4) {
|
|
// Translate points to fixedpoint realms
|
|
int32_t x1 = p1.x * fixedpoint_base;
|
|
int32_t x2 = p2.x * fixedpoint_base;
|
|
int32_t x3 = p3.x * fixedpoint_base;
|
|
int32_t x4 = p4.x * fixedpoint_base;
|
|
int32_t y1 = p1.y * fixedpoint_base;
|
|
int32_t y2 = p2.y * fixedpoint_base;
|
|
int32_t y3 = p3.y * fixedpoint_base;
|
|
int32_t y4 = p4.y * fixedpoint_base;
|
|
|
|
if (recursive_bezier_fixed(builder, x1, y1, x2, y2, x3, y3, x4, y4)) {
|
|
return gpath_builder_line_to_point(builder, p4);
|
|
}
|
|
return false;
|
|
}
|
|
|
|
GPathBuilder *gpath_builder_create(uint32_t max_points) {
|
|
// Allocate enough memory to store all the points - points are stored contiguously with the
|
|
// GPathBuilder structure
|
|
const size_t required_size = sizeof(GPathBuilder) + max_points * sizeof(GPoint);
|
|
GPathBuilder *result = applib_malloc(required_size);
|
|
|
|
if (!result) {
|
|
return NULL;
|
|
}
|
|
|
|
memset(result, 0, required_size);
|
|
result->max_points = max_points;
|
|
return result;
|
|
}
|
|
|
|
void gpath_builder_destroy(GPathBuilder *builder) {
|
|
applib_free(builder);
|
|
}
|
|
|
|
GPath *gpath_builder_create_path(GPathBuilder *builder) {
|
|
if (builder->num_points <= 1) {
|
|
return NULL;
|
|
}
|
|
|
|
uint32_t num_points = builder->num_points;
|
|
|
|
// handle case where last point == first point => remove last point
|
|
while (num_points > 1
|
|
&& gpoint_equal(&builder->points[0], &builder->points[num_points])) {
|
|
num_points--;
|
|
}
|
|
|
|
// Allocate enough memory for both the GPath structure as well as the array of GPoints.
|
|
// Both will be contiguous in memory.
|
|
const size_t size_of_points = num_points * sizeof(GPoint);
|
|
GPath *result = applib_malloc(sizeof(GPath) + size_of_points);
|
|
|
|
if (!result) {
|
|
return NULL;
|
|
}
|
|
|
|
memset(result, 0, sizeof(GPath));
|
|
result->num_points = num_points;
|
|
// Set the points pointer within the GPath structure to point just after the GPath structure
|
|
// since that is where memory has been allocated for the array.
|
|
result->points = (GPoint*)(result + 1);
|
|
memcpy(result->points, builder->points, size_of_points);
|
|
return result;
|
|
}
|
|
|
|
bool gpath_builder_move_to_point(GPathBuilder *builder, GPoint to_point) {
|
|
if (builder->num_points != 0) {
|
|
return false;
|
|
}
|
|
|
|
return gpath_builder_line_to_point(builder, to_point);
|
|
}
|
|
|
|
bool gpath_builder_line_to_point(GPathBuilder *builder, GPoint to_point) {
|
|
if (builder->num_points >= builder->max_points - 1) {
|
|
return false;
|
|
}
|
|
|
|
builder->points[builder->num_points++] = to_point;
|
|
return true;
|
|
}
|
|
|
|
bool gpath_builder_curve_to_point(GPathBuilder *builder, GPoint to_point,
|
|
GPoint control_point_1, GPoint control_point_2) {
|
|
GPoint from_point = builder->points[builder->num_points-1];
|
|
return bezier_fixed(builder, from_point, control_point_1, control_point_2, to_point);
|
|
}
|