Line data Source code
1 : /* File: draw_svg_path_data.inl; Copyright and License: see below */
2 :
3 : #include "u8/u8_log.h"
4 : #include <math.h>
5 : #include <assert.h>
6 :
7 28 : static inline void draw_svg_path_data_init( draw_svg_path_data_t *this_ )
8 : {
9 28 : (*this_).dummy = 0; /* prevent warnings on uninitialized usage */
10 28 : }
11 :
12 28 : static inline void draw_svg_path_data_destroy( draw_svg_path_data_t *this_ )
13 : {
14 :
15 28 : }
16 :
17 28 : static inline u8_error_t draw_svg_path_data_parse_bounds ( const draw_svg_path_data_t *this_,
18 : utf8stringviewtokenizer_t *tok_iterator,
19 : geometry_rectangle_t *out_view_rect,
20 : u8_error_info_t *out_err_info
21 : )
22 : {
23 28 : const geometry_rectangle_t dummy_target = { .left = 0.0, .top = 0.0, .width = 0.0, .height = 0.0 };
24 : const u8_error_t result
25 28 : = draw_svg_path_data_private_parse( this_,
26 : false, /* draw */
27 : tok_iterator,
28 : out_view_rect,
29 : out_err_info,
30 : &dummy_target,
31 : NULL /* cr */
32 : );
33 28 : return result;
34 : }
35 :
36 0 : static inline u8_error_t draw_svg_path_data_draw ( const draw_svg_path_data_t *this_,
37 : utf8stringviewtokenizer_t *tok_iterator,
38 : const geometry_rectangle_t *in_view_rect,
39 : u8_error_info_t *out_err_info,
40 : const geometry_rectangle_t *target_bounds,
41 : cairo_t *cr
42 : )
43 : {
44 0 : geometry_rectangle_t non_const_view = *in_view_rect;
45 : const u8_error_t result
46 0 : = draw_svg_path_data_private_parse( this_,
47 : true, /* draw */
48 : tok_iterator,
49 : &non_const_view,
50 : out_err_info,
51 : target_bounds,
52 : cr
53 : );
54 0 : return result;
55 : }
56 :
57 16 : static inline double draw_svg_path_data_private_get_angle ( const draw_svg_path_data_t *this_,
58 : double u_x,
59 : double u_y,
60 : double v_x,
61 : double v_y )
62 : {
63 16 : const bool negative = (( u_x * v_y ) - ( u_y * v_x )) < 0.0;
64 16 : const double len_u = sqrt( ( u_x * u_x ) + ( u_y * u_y ) );
65 16 : const double len_v = sqrt( ( v_x * v_x ) + ( v_y * v_y ) );
66 16 : const double abs_angle = acos((( u_x * v_x ) + ( u_y * v_y )) / ( len_u * len_v ));
67 16 : return negative ? ( -abs_angle ) : abs_angle;
68 : }
69 :
70 8 : static inline u8_error_t draw_svg_path_data_private_get_arc_center ( const draw_svg_path_data_t *this_,
71 : double start_x,
72 : double start_y,
73 : double end_x,
74 : double end_y,
75 : bool large_arc,
76 : bool sweep_positive_dir,
77 : double r_x,
78 : double r_y,
79 : double phi,
80 : double *out_center_x,
81 : double *out_center_y,
82 : double *out_start_angle,
83 : double *out_delta_angle )
84 : {
85 8 : U8_TRACE_BEGIN();
86 8 : assert( out_center_x != NULL );
87 8 : assert( out_center_y != NULL );
88 8 : assert( out_start_angle != NULL );
89 8 : assert( out_delta_angle != NULL );
90 8 : u8_error_t result = U8_ERROR_NONE;
91 :
92 : /* see https://www.w3.org/TR/SVG/implnote.html#ArcConversionEndpointToCenter */
93 :
94 : /* see B.2.5, step 2, eq 6.1 */
95 8 : r_x = fabs( r_x );
96 8 : r_y = fabs( r_y );
97 :
98 : /* see B.2.5, step 1 */
99 8 : if (( r_x < 0.000001 )||( r_y < 0.000001 ))
100 : {
101 : /* This is a valid case but cannot be handled by this function */
102 0 : result = U8_ERROR_EDGE_CASE_PARAM;
103 : }
104 :
105 8 : if ( result == U8_ERROR_NONE )
106 : {
107 : /* see B.2.4, step 1, eq 5.1 */
108 8 : const double cos_phi = cos( phi );
109 8 : const double sin_phi = sin( phi );
110 8 : const double half_dx = ( start_x - end_x ) / 2.0;
111 8 : const double half_dy = ( start_y - end_y ) / 2.0;
112 8 : const double rot_half_dx = cos_phi * half_dx + sin_phi * half_dy;
113 8 : const double rot_half_dy = (-sin_phi) * half_dx + cos_phi * half_dy;
114 :
115 : /* see B.2.5, step 3, eq 6.2 */
116 8 : double sqr_r_x = r_x * r_x;
117 8 : double sqr_r_y = r_y * r_y;
118 8 : const double sqr_rot_half_dx = rot_half_dx * rot_half_dx;
119 8 : const double sqr_rot_half_dy = rot_half_dy * rot_half_dy;
120 8 : const double sqr_too_small_factor = ( sqr_rot_half_dx / sqr_r_x ) + ( sqr_rot_half_dy / sqr_r_y );
121 :
122 : /* see B.2.5, step 3, eq 6.3 */
123 8 : if ( sqr_too_small_factor > 1.0 )
124 : {
125 0 : const double too_small_factor = sqrt( sqr_too_small_factor );
126 0 : r_x = too_small_factor * r_x;
127 0 : r_y = too_small_factor * r_y;
128 0 : sqr_r_x = r_x * r_x;
129 0 : sqr_r_y = r_y * r_y;
130 : }
131 :
132 : /* see B.2.4, step 2, eq 5.2 */
133 8 : const double numerator = ( sqr_r_x * sqr_r_y ) - ( sqr_r_x * sqr_rot_half_dy ) - ( sqr_r_y * sqr_rot_half_dx );
134 8 : double denominator = ( sqr_r_x * sqr_rot_half_dy ) + ( sqr_r_y * sqr_rot_half_dx );
135 8 : if ( denominator < 0.000000000001 )
136 : {
137 : /* start and end points are equal */
138 0 : result = U8_ERROR_VALUE_OUT_OF_RANGE;
139 0 : denominator = 0.000000000001;
140 : }
141 8 : const double sqr_factor = numerator / denominator;
142 8 : double rot_c_x = ( r_x * rot_half_dy ) / r_y;
143 8 : double rot_c_y = - ( r_y * rot_half_dx ) / r_x;
144 8 : if ( sqr_factor <= 0.0 )
145 : {
146 : /* case: rounding error and/or just 1 solution; factor is 0.0 */
147 0 : rot_c_x = 0.0;
148 0 : rot_c_y = 0.0;
149 : }
150 : else
151 : {
152 8 : const double factor = sqrt( sqr_factor );
153 8 : if ( large_arc != sweep_positive_dir )
154 : {
155 7 : rot_c_x = factor * rot_c_x;
156 7 : rot_c_y = factor * rot_c_y;
157 : }
158 : else
159 : {
160 1 : rot_c_x = ( -factor ) * rot_c_x;
161 1 : rot_c_y = ( -factor ) * rot_c_y;
162 : }
163 : }
164 :
165 : /* see B.2.4, step 3, eq 5.3 */
166 8 : const double half_way_x = ( start_x + end_x ) / 2.0;
167 8 : const double half_way_y = ( start_y + end_y ) / 2.0;
168 8 : *out_center_x = (( cos_phi * rot_c_x ) + ( (-sin_phi) * rot_c_y )) + half_way_x;
169 8 : *out_center_y = (( sin_phi * rot_c_x ) + ( cos_phi * rot_c_y )) + half_way_y;
170 :
171 : /* see B.2.4, step 4, eq 5.5 */
172 16 : *out_start_angle = draw_svg_path_data_private_get_angle( this_,
173 : 1.0,
174 : 0.0,
175 8 : ( rot_half_dx - rot_c_x ) / r_x,
176 8 : ( rot_half_dy - rot_c_y ) / r_y
177 : );
178 : /* see B.2.4, step 4, eq 5.6 */
179 8 : const double delta_angle = draw_svg_path_data_private_get_angle( this_,
180 8 : ( rot_half_dx - rot_c_x ) / r_x,
181 8 : ( rot_half_dy - rot_c_y ) / r_y,
182 8 : ( (-rot_half_dx) - rot_c_x ) / r_x,
183 8 : ( (-rot_half_dy) - rot_c_y ) / r_y
184 : );
185 : *out_delta_angle
186 8 : = sweep_positive_dir
187 3 : ? ( ( delta_angle < 0.0 ) ? ( delta_angle + ( 2.0 * M_PI ) ) : delta_angle )
188 11 : : ( ( delta_angle > 0.0 ) ? ( delta_angle - ( 2.0 * M_PI ) ) : delta_angle );
189 : }
190 :
191 8 : U8_TRACE_END_ERR(result);
192 8 : return U8_ERROR_NONE;
193 : }
194 :
195 :
196 : /*
197 : Copyright 2023-2024 Andreas Warnke
198 :
199 : Licensed under the Apache License, Version 2.0 (the "License");
200 : you may not use this file except in compliance with the License.
201 : You may obtain a copy of the License at
202 :
203 : http://www.apache.org/licenses/LICENSE-2.0
204 :
205 : Unless required by applicable law or agreed to in writing, software
206 : distributed under the License is distributed on an "AS IS" BASIS,
207 : WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
208 : See the License for the specific language governing permissions and
209 : limitations under the License.
210 : */
|