File size: 8,222 Bytes
e232e39
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
// jigsawR C++ optimizations
// Provides optimized implementations for performance-critical hotspots
// with graceful R fallback when unavailable

#include <Rcpp.h>
#include <cmath>
using namespace Rcpp;

// =============================================================================
// Priority 1: RNG Batch Generation
// =============================================================================

//' Generate batch of deterministic random numbers
//'
//' Uses sine-based RNG matching the JavaScript jigsaw generator.
//' Much faster than calling R's random() function repeatedly.
//'
//' @param seed Starting seed value
//' @param count Number of random values to generate
//' @param min_val Minimum value (default 0.0)
//' @param max_val Maximum value (default 1.0)
//' @return NumericVector of random values
//' @keywords internal
// [[Rcpp::export]]
NumericVector random_batch_cpp(int seed, int count, double min_val = 0.0, double max_val = 1.0) {
  NumericVector result(count);
  double range = max_val - min_val;
  
  for (int i = 0; i < count; i++) {
    double x = std::sin(static_cast<double>(seed + i)) * 10000.0;
    double r = x - std::floor(x);
    result[i] = min_val + r * range;
  }
  
  return result;
}

// =============================================================================
// Priority 2: Bezier Point Interpolation
// =============================================================================

//' Compute cubic Bezier curve points in batch
//'
//' Vectorized implementation of cubic Bezier interpolation.
//' Computes points along the curve defined by P0, CP1, CP2, P1.
//'
//' @param p0 Start point as c(x, y)
//' @param cp1 First control point as c(x, y)
//' @param cp2 Second control point as c(x, y)
//' @param p1 End point as c(x, y)
//' @param n_points Number of points to generate
//' @return NumericMatrix with x and y columns
//' @keywords internal
// [[Rcpp::export]]
NumericMatrix bezier_batch_cpp(NumericVector p0, NumericVector cp1, 
                                NumericVector cp2, NumericVector p1, 
                                int n_points) {
  NumericMatrix result(n_points, 2);
  
  // Pre-extract coordinates
  double p0x = p0[0], p0y = p0[1];
  double cp1x = cp1[0], cp1y = cp1[1];
  double cp2x = cp2[0], cp2y = cp2[1];
  double p1x = p1[0], p1y = p1[1];
  
  // Compute points along the curve
  for (int i = 0; i < n_points; i++) {
    double t = static_cast<double>(i) / static_cast<double>(n_points - 1);
    double one_minus_t = 1.0 - t;
    
    // Precompute powers
    double one_minus_t_sq = one_minus_t * one_minus_t;
    double one_minus_t_cu = one_minus_t_sq * one_minus_t;
    double t_sq = t * t;
    double t_cu = t_sq * t;
    
    // Cubic Bezier: B(t) = (1-t)³P₀ + 3(1-t)²tCP₁ + 3(1-t)t²CP₂ + t³P₁
    double coef1 = one_minus_t_cu;
    double coef2 = 3.0 * one_minus_t_sq * t;
    double coef3 = 3.0 * one_minus_t * t_sq;
    double coef4 = t_cu;
    
    result(i, 0) = coef1 * p0x + coef2 * cp1x + coef3 * cp2x + coef4 * p1x;
    result(i, 1) = coef1 * p0y + coef2 * cp1y + coef3 * cp2y + coef4 * p1y;
  }
  
  return result;
}

// =============================================================================
// Priority 3: SVG Path Translation
// =============================================================================

//' Translate SVG path coordinates by (dx, dy)
//'
//' Character-by-character parsing without regex for better performance.
//' Handles M, L, C, A, and Z commands.
//'
//' @param path_string SVG path d attribute string
//' @param dx X translation
//' @param dy Y translation
//' @return Translated SVG path string
//' @keywords internal
// [[Rcpp::export]]
std::string svg_translate_cpp(std::string path_string, double dx, double dy) {
  // Early return if no translation needed
  if (dx == 0.0 && dy == 0.0) {
    return path_string;
  }
  
  std::string result;
  result.reserve(path_string.size() + 100);
  
  size_t i = 0;
  size_t n = path_string.size();
  
  while (i < n) {
    char c = path_string[i];
    
    // Skip whitespace
    if (c == ' ' || c == '\t' || c == '\n' || c == '\r') {
      result += c;
      i++;
      continue;
    }
    
    // Handle command letters
    if (c == 'M' || c == 'L') {
      // M/L: translate both x and y
      result += c;
      result += ' ';
      i++;
      
      // Skip whitespace
      while (i < n && (path_string[i] == ' ' || path_string[i] == '\t')) i++;
      
      // Parse x coordinate
      size_t start = i;
      while (i < n && (path_string[i] == '-' || path_string[i] == '.' || 
             (path_string[i] >= '0' && path_string[i] <= '9'))) i++;
      double x = std::stod(path_string.substr(start, i - start)) + dx;
      
      // Skip whitespace/comma
      while (i < n && (path_string[i] == ' ' || path_string[i] == ',' || path_string[i] == '\t')) i++;
      
      // Parse y coordinate
      start = i;
      while (i < n && (path_string[i] == '-' || path_string[i] == '.' || 
             (path_string[i] >= '0' && path_string[i] <= '9'))) i++;
      double y = std::stod(path_string.substr(start, i - start)) + dy;
      
      char buf[64];
      std::snprintf(buf, sizeof(buf), "%.2f %.2f", x, y);
      result += buf;
      
    } else if (c == 'C') {
      // C: translate all 3 coordinate pairs
      result += c;
      result += ' ';
      i++;
      
      for (int pair = 0; pair < 3; pair++) {
        // Skip whitespace
        while (i < n && (path_string[i] == ' ' || path_string[i] == ',' || path_string[i] == '\t')) i++;
        
        // Parse x
        size_t start = i;
        while (i < n && (path_string[i] == '-' || path_string[i] == '.' || 
               (path_string[i] >= '0' && path_string[i] <= '9'))) i++;
        double x = std::stod(path_string.substr(start, i - start)) + dx;
        
        // Skip whitespace/comma
        while (i < n && (path_string[i] == ' ' || path_string[i] == ',' || path_string[i] == '\t')) i++;
        
        // Parse y
        start = i;
        while (i < n && (path_string[i] == '-' || path_string[i] == '.' || 
               (path_string[i] >= '0' && path_string[i] <= '9'))) i++;
        double y = std::stod(path_string.substr(start, i - start)) + dy;
        
        char buf[64];
        std::snprintf(buf, sizeof(buf), "%.2f %.2f", x, y);
        result += buf;
        if (pair < 2) result += ' ';
      }
      
    } else if (c == 'A') {
      // A: only translate the endpoint (last 2 numbers), keep radii and flags
      result += c;
      result += ' ';
      i++;
      
      // Skip whitespace
      while (i < n && (path_string[i] == ' ' || path_string[i] == '\t')) i++;
      
      // Copy rx, ry, rotation, large-arc, sweep (5 values unchanged)
      for (int val = 0; val < 5; val++) {
        size_t start = i;
        while (i < n && (path_string[i] == '-' || path_string[i] == '.' || 
               (path_string[i] >= '0' && path_string[i] <= '9'))) i++;
        result += path_string.substr(start, i - start);
        result += ' ';
        
        // Skip whitespace/comma
        while (i < n && (path_string[i] == ' ' || path_string[i] == ',' || path_string[i] == '\t')) i++;
      }
      
      // Parse and translate endpoint x
      size_t start = i;
      while (i < n && (path_string[i] == '-' || path_string[i] == '.' || 
             (path_string[i] >= '0' && path_string[i] <= '9'))) i++;
      double x = std::stod(path_string.substr(start, i - start)) + dx;
      
      // Skip whitespace/comma
      while (i < n && (path_string[i] == ' ' || path_string[i] == ',' || path_string[i] == '\t')) i++;
      
      // Parse and translate endpoint y
      start = i;
      while (i < n && (path_string[i] == '-' || path_string[i] == '.' || 
             (path_string[i] >= '0' && path_string[i] <= '9'))) i++;
      double y = std::stod(path_string.substr(start, i - start)) + dy;
      
      char buf[64];
      std::snprintf(buf, sizeof(buf), "%.2f %.2f", x, y);
      result += buf;
      
    } else if (c == 'Z') {
      // Z: no coordinates, just copy
      result += c;
      i++;
      
    } else {
      // Unknown character, copy as-is
      result += c;
      i++;
    }
  }
  
  return result;
}