canvu-react 0.3.32 → 0.3.33

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/tldraw.cjs CHANGED
@@ -228,24 +228,153 @@ function svgNumber(value) {
228
228
  const rounded = Math.round(value * 100) / 100;
229
229
  return Number.isInteger(rounded) ? String(rounded) : String(rounded);
230
230
  }
231
- function approximateEllipsePerimeter(rx, ry) {
232
- if (rx <= 0 || ry <= 0) return 0;
233
- return Math.PI * (3 * (rx + ry) - Math.sqrt((3 * rx + ry) * (rx + 3 * ry)));
234
- }
235
- function architecturalCloudScallopCount(rx, ry, amplitude) {
236
- const perimeter = approximateEllipsePerimeter(rx, ry);
237
- const targetScallopLength = Math.max(10, amplitude * 2);
231
+ function architecturalCloudScallopCount(perimeter, amplitude) {
232
+ const targetScallopLength = Math.max(18, amplitude * 2.45);
238
233
  let count = Math.max(12, Math.round(perimeter / targetScallopLength));
239
234
  if (count % 2 === 1) count += 1;
240
235
  return count;
241
236
  }
242
- function pointOnSuperellipse(centerX, centerY, radiusX, radiusY, theta, exponent) {
243
- const cosTheta = Math.cos(theta);
244
- const sinTheta = Math.sin(theta);
245
- return [
246
- centerX + Math.sign(cosTheta) * radiusX * Math.abs(cosTheta) ** (2 / exponent),
247
- centerY + Math.sign(sinTheta) * radiusY * Math.abs(sinTheta) ** (2 / exponent)
248
- ];
237
+ function roundedRectMetrics(width, height, inset, radius) {
238
+ const left = inset;
239
+ const top = inset;
240
+ const right = width - inset;
241
+ const bottom = height - inset;
242
+ const rectWidth = Math.max(0, right - left);
243
+ const rectHeight = Math.max(0, bottom - top);
244
+ const normalizedRadius = Math.max(
245
+ 0,
246
+ Math.min(radius, rectWidth / 2, rectHeight / 2)
247
+ );
248
+ const centerX = width / 2;
249
+ const topHalfLength = Math.max(0, right - normalizedRadius - centerX);
250
+ const horizontalLength = Math.max(0, rectWidth - normalizedRadius * 2);
251
+ const verticalLength = Math.max(0, rectHeight - normalizedRadius * 2);
252
+ const arcLength = normalizedRadius * (Math.PI / 2);
253
+ return {
254
+ left,
255
+ top,
256
+ right,
257
+ bottom,
258
+ radius: normalizedRadius,
259
+ centerX,
260
+ topHalfLength,
261
+ horizontalLength,
262
+ verticalLength,
263
+ arcLength,
264
+ perimeter: horizontalLength * 2 + verticalLength * 2 + Math.PI * 2 * normalizedRadius
265
+ };
266
+ }
267
+ function pointOnLine(startX, startY, endX, endY, t) {
268
+ return [startX + (endX - startX) * t, startY + (endY - startY) * t];
269
+ }
270
+ function pointOnArc(centerX, centerY, radius, startAngle, endAngle, t) {
271
+ const theta = startAngle + (endAngle - startAngle) * t;
272
+ return [centerX + Math.cos(theta) * radius, centerY + Math.sin(theta) * radius];
273
+ }
274
+ function pointOnRoundedRectPath(metrics, distance) {
275
+ if (metrics.perimeter <= 0) return [metrics.centerX, metrics.top];
276
+ let remaining = (distance % metrics.perimeter + metrics.perimeter) % metrics.perimeter;
277
+ const consume = (length) => {
278
+ if (length <= 1e-9) return null;
279
+ if (remaining <= length) return remaining / length;
280
+ remaining -= length;
281
+ return null;
282
+ };
283
+ let t = consume(metrics.topHalfLength);
284
+ if (t != null) {
285
+ return pointOnLine(
286
+ metrics.centerX,
287
+ metrics.top,
288
+ metrics.right - metrics.radius,
289
+ metrics.top,
290
+ t
291
+ );
292
+ }
293
+ t = consume(metrics.arcLength);
294
+ if (t != null) {
295
+ return pointOnArc(
296
+ metrics.right - metrics.radius,
297
+ metrics.top + metrics.radius,
298
+ metrics.radius,
299
+ -Math.PI / 2,
300
+ 0,
301
+ t
302
+ );
303
+ }
304
+ t = consume(metrics.verticalLength);
305
+ if (t != null) {
306
+ return pointOnLine(
307
+ metrics.right,
308
+ metrics.top + metrics.radius,
309
+ metrics.right,
310
+ metrics.bottom - metrics.radius,
311
+ t
312
+ );
313
+ }
314
+ t = consume(metrics.arcLength);
315
+ if (t != null) {
316
+ return pointOnArc(
317
+ metrics.right - metrics.radius,
318
+ metrics.bottom - metrics.radius,
319
+ metrics.radius,
320
+ 0,
321
+ Math.PI / 2,
322
+ t
323
+ );
324
+ }
325
+ t = consume(metrics.horizontalLength);
326
+ if (t != null) {
327
+ return pointOnLine(
328
+ metrics.right - metrics.radius,
329
+ metrics.bottom,
330
+ metrics.left + metrics.radius,
331
+ metrics.bottom,
332
+ t
333
+ );
334
+ }
335
+ t = consume(metrics.arcLength);
336
+ if (t != null) {
337
+ return pointOnArc(
338
+ metrics.left + metrics.radius,
339
+ metrics.bottom - metrics.radius,
340
+ metrics.radius,
341
+ Math.PI / 2,
342
+ Math.PI,
343
+ t
344
+ );
345
+ }
346
+ t = consume(metrics.verticalLength);
347
+ if (t != null) {
348
+ return pointOnLine(
349
+ metrics.left,
350
+ metrics.bottom - metrics.radius,
351
+ metrics.left,
352
+ metrics.top + metrics.radius,
353
+ t
354
+ );
355
+ }
356
+ t = consume(metrics.arcLength);
357
+ if (t != null) {
358
+ return pointOnArc(
359
+ metrics.left + metrics.radius,
360
+ metrics.top + metrics.radius,
361
+ metrics.radius,
362
+ Math.PI,
363
+ Math.PI * 1.5,
364
+ t
365
+ );
366
+ }
367
+ t = consume(metrics.topHalfLength);
368
+ if (t != null) {
369
+ return pointOnLine(
370
+ metrics.left + metrics.radius,
371
+ metrics.top,
372
+ metrics.centerX,
373
+ metrics.top,
374
+ t
375
+ );
376
+ }
377
+ return [metrics.centerX, metrics.top];
249
378
  }
250
379
  function buildRectSvg(width, height, style = DEFAULT_STROKE_STYLE) {
251
380
  return `<rect width="${width}" height="${height}" fill="none" stroke="${style.stroke}" stroke-width="${style.strokeWidth}" rx="4"${strokeOpacityAttr(style)} />`;
@@ -260,35 +389,34 @@ function buildArchitecturalCloudPathD(width, height, strokeWidth = DEFAULT_STROK
260
389
  const h = Math.max(0, height);
261
390
  if (w <= 0 || h <= 0) return "";
262
391
  const inset = Math.max(0.5, strokeWidth / 2);
263
- const outerRx = Math.max(0, w / 2 - inset);
264
- const outerRy = Math.max(0, h / 2 - inset);
265
- if (outerRx <= 0 || outerRy <= 0) return "";
266
- const baseExponent = 3.6;
392
+ const outerWidth = Math.max(0, w - inset * 2);
393
+ const outerHeight = Math.max(0, h - inset * 2);
394
+ if (outerWidth <= 0 || outerHeight <= 0) return "";
267
395
  const amplitude = Math.min(
268
- outerRx * 0.45,
269
- outerRy * 0.45,
270
- Math.max(2, Math.min(7, Math.min(w, h) * 0.035))
396
+ outerWidth * 0.12,
397
+ outerHeight * 0.12,
398
+ Math.max(5, Math.min(14, Math.min(w, h) * 0.07))
271
399
  );
272
- const innerRx = Math.max(0, outerRx - amplitude);
273
- const innerRy = Math.max(0, outerRy - amplitude);
274
- const scallopCount = architecturalCloudScallopCount(innerRx, innerRy, amplitude);
275
- const angleStep = Math.PI * 2 / scallopCount;
276
- const cx = w / 2;
277
- const cy = h / 2;
278
- const startAngle = -Math.PI / 2;
279
- const pointOnArchitecturalCloud = (theta, radiusX, radiusY) => pointOnSuperellipse(cx, cy, radiusX, radiusY, theta, baseExponent);
280
- const [startX, startY] = pointOnArchitecturalCloud(startAngle, innerRx, innerRy);
400
+ const radius = Math.min(
401
+ outerWidth / 2,
402
+ outerHeight / 2,
403
+ Math.max(amplitude * 2.5, outerHeight * 0.43)
404
+ );
405
+ const outer = roundedRectMetrics(w, h, inset, radius);
406
+ const inner = roundedRectMetrics(
407
+ w,
408
+ h,
409
+ inset + amplitude,
410
+ Math.max(0, radius - amplitude)
411
+ );
412
+ const scallopCount = architecturalCloudScallopCount(outer.perimeter, amplitude);
413
+ const [startX, startY] = pointOnRoundedRectPath(inner, 0);
281
414
  const segments = [`M${svgNumber(startX)} ${svgNumber(startY)}`];
282
415
  for (let index = 0; index < scallopCount; index += 1) {
283
- const segmentStart = startAngle + index * angleStep;
284
- const segmentMid = segmentStart + angleStep / 2;
285
- const segmentEnd = segmentStart + angleStep;
286
- const [controlX, controlY] = pointOnArchitecturalCloud(
287
- segmentMid,
288
- outerRx,
289
- outerRy
290
- );
291
- const [endX, endY] = pointOnArchitecturalCloud(segmentEnd, innerRx, innerRy);
416
+ const controlDistance = (index + 0.5) / scallopCount * outer.perimeter;
417
+ const endDistance = (index + 1) / scallopCount * inner.perimeter;
418
+ const [controlX, controlY] = pointOnRoundedRectPath(outer, controlDistance);
419
+ const [endX, endY] = pointOnRoundedRectPath(inner, endDistance);
292
420
  segments.push(
293
421
  `Q${svgNumber(controlX)} ${svgNumber(controlY)} ${svgNumber(endX)} ${svgNumber(endY)}`
294
422
  );