gum-jsx 1.6.2 → 1.6.4

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.
Files changed (60) hide show
  1. package/README.md +2 -3
  2. package/docs/code/Arc.jsx +2 -2
  3. package/docs/code/Ellipse.jsx +2 -2
  4. package/docs/code/Images.jsx +1 -1
  5. package/docs/code/Line.jsx +1 -1
  6. package/docs/code/Math.jsx +12 -10
  7. package/docs/code/Network.jsx +1 -1
  8. package/docs/code/Points.jsx +4 -1
  9. package/docs/code/Shape.jsx +1 -1
  10. package/docs/code/SymFill.jsx +1 -1
  11. package/docs/code/SymShape.jsx +3 -4
  12. package/docs/code/Tables.jsx +3 -2
  13. package/docs/text/Box.md +1 -1
  14. package/docs/text/Element.md +11 -14
  15. package/docs/text/Gum.md +5 -5
  16. package/docs/text/Math.md +2 -1
  17. package/docs/text/SymFill.md +2 -1
  18. package/docs/text/SymLine.md +2 -1
  19. package/docs/text/SymPoints.md +2 -1
  20. package/docs/text/SymShape.md +2 -1
  21. package/docs/text/SymSpline.md +1 -0
  22. package/gala/code/atomic_orbitals.jsx +1 -2
  23. package/gala/code/complex_plot.jsx +1 -1
  24. package/gala/code/pendulum_physics.jsx +3 -3
  25. package/gala/code/plot_manual.jsx +2 -2
  26. package/gala/code/polygon_slide.jsx +3 -4
  27. package/gala/code/spline_star.jsx +2 -2
  28. package/gala/code/unit_distance.jsx +96 -0
  29. package/gala/text/unit_distance.md +7 -0
  30. package/package.json +1 -1
  31. package/scripts/gum.ts +14 -11
  32. package/scripts/test.ts +0 -69
  33. package/skills/gum-jsx/SKILL.md +12 -15
  34. package/skills/gum-jsx/references/gala/atomic_orbitals.md +1 -2
  35. package/skills/gum-jsx/references/gala/complex_plot.md +1 -1
  36. package/skills/gum-jsx/references/gala/pendulum_physics.md +3 -3
  37. package/skills/gum-jsx/references/gala/plot_manual.md +2 -2
  38. package/skills/gum-jsx/references/gala/polygon_slide.md +3 -4
  39. package/skills/gum-jsx/references/gala/spline_star.md +2 -2
  40. package/skills/gum-jsx/references/geometry.md +6 -6
  41. package/skills/gum-jsx/references/layout.md +5 -2
  42. package/skills/gum-jsx/references/networks.md +1 -1
  43. package/skills/gum-jsx/references/symbolic.md +12 -8
  44. package/skills/gum-jsx/references/utilities.md +18 -14
  45. package/src/elems/core.ts +33 -13
  46. package/src/elems/geometry.ts +37 -14
  47. package/src/elems/layout.ts +5 -7
  48. package/src/elems/math.ts +9 -0
  49. package/src/elems/network.ts +4 -3
  50. package/src/elems/plot.ts +19 -35
  51. package/src/elems/symbolic.ts +30 -24
  52. package/src/eval.ts +13 -3
  53. package/src/gum.ts +9 -9
  54. package/src/lib/const.ts +2 -1
  55. package/src/lib/interp.ts +10 -4
  56. package/src/lib/parse.ts +8 -2
  57. package/src/lib/types.ts +5 -4
  58. package/src/lib/utils.ts +105 -25
  59. package/src/render.ts +7 -27
  60. package/src/types/katex.d.ts +8 -0
package/src/lib/utils.ts CHANGED
@@ -2,7 +2,7 @@
2
2
 
3
3
  import { DEFAULTS as D, d2r, r2d } from './const'
4
4
  import { setSeed, random, uniform, normal, integer } from './rng'
5
- import type { Point, Rect, Limit, RGBA, MNumber, MPoint, Orient, Side, Side0, Angle, Direc, Size, Pair, Grad, Polar, Attrs } from './types'
5
+ import type { Point, Rect, Limit, RGBA, MNumber, MPoint, Orient, Side, Side0, Direc, Size, Pair, Grad, Complex, Vector, Attrs } from './types'
6
6
 
7
7
  //
8
8
  // environment tests
@@ -72,6 +72,10 @@ function ensure_vector<T>(x: T | T[], n: number): T[] {
72
72
  }
73
73
  }
74
74
 
75
+ function ensure_pair<T>(x: T | [T, T]): [T, T] {
76
+ return is_array(x) ? x : [ x, x ]
77
+ }
78
+
75
79
  function ensure_singleton<T>(x: T[] | undefined): T | undefined {
76
80
  if (x == null) return undefined
77
81
  return is_array(x) ? x[0] : x
@@ -433,34 +437,102 @@ function invert(x: number | undefined): number | undefined {
433
437
  // pair arithmetic
434
438
  //
435
439
 
436
- function broadcast_point(p: number | Point): Point {
437
- return is_scalar(p) ? [ p, p ] as Point : p
438
- }
439
-
440
440
  function add2(p0: number | Pair, p1: number | Pair): Pair {
441
- const [ x0, y0 ] = broadcast_point(p0)
442
- const [ x1, y1 ] = broadcast_point(p1)
441
+ const [ x0, y0 ] = ensure_pair(p0)
442
+ const [ x1, y1 ] = ensure_pair(p1)
443
443
  return [ x0 + x1, y0 + y1 ]
444
444
  }
445
445
 
446
446
  function sub2(p0: number | Pair, p1: number | Pair): Pair {
447
- const [ x0, y0 ] = broadcast_point(p0)
448
- const [ x1, y1 ] = broadcast_point(p1)
447
+ const [ x0, y0 ] = ensure_pair(p0)
448
+ const [ x1, y1 ] = ensure_pair(p1)
449
449
  return [ x0 - x1, y0 - y1 ]
450
450
  }
451
451
 
452
452
  function mul2(p0: number | Pair, p1: number | Pair): Pair {
453
- const [ x0, y0 ] = broadcast_point(p0)
454
- const [ x1, y1 ] = broadcast_point(p1)
453
+ const [ x0, y0 ] = ensure_pair(p0)
454
+ const [ x1, y1 ] = ensure_pair(p1)
455
455
  return [ x0 * x1, y0 * y1 ]
456
456
  }
457
457
 
458
458
  function div2(p0: number | Pair, p1: number | Pair): Pair {
459
- const [ x0, y0 ] = broadcast_point(p0)
460
- const [ x1, y1 ] = broadcast_point(p1)
459
+ const [ x0, y0 ] = ensure_pair(p0)
460
+ const [ x1, y1 ] = ensure_pair(p1)
461
461
  return [ x0 / x1, y0 / y1 ]
462
462
  }
463
463
 
464
+ //
465
+ // vector arithmetic
466
+ //
467
+
468
+ function broadcast_op(op: (x: number, y: number) => number, v0: Vector, v1: Vector): Vector {
469
+ return zip(v0, v1).map(([ x0, x1 ]) => op(x0, x1))
470
+ }
471
+
472
+ function addn(v0: Vector, v1: Vector): Vector {
473
+ return broadcast_op((x, y) => x + y, v0, v1)
474
+ }
475
+
476
+ function subn(v0: Vector, v1: Vector): Vector {
477
+ return broadcast_op((x, y) => x - y, v0, v1)
478
+ }
479
+
480
+ function muln(v0: Vector, v1: Vector): Vector {
481
+ return broadcast_op((x, y) => x * y, v0, v1)
482
+ }
483
+
484
+ function divn(v0: Vector, v1: Vector): Vector {
485
+ return broadcast_op((x, y) => x / y, v0, v1)
486
+ }
487
+
488
+ //
489
+ // complex arithmetic
490
+ //
491
+
492
+ function ensure_complex(z: number | Complex): Complex {
493
+ return is_scalar(z) ? [ z, 0 ] as Complex : z
494
+ }
495
+
496
+ function addc(z: number | Complex, w: number | Complex): Complex {
497
+ const [ x, y ] = ensure_complex(z)
498
+ const [ u, v ] = ensure_complex(w)
499
+ return [ x + u, y + v ]
500
+ }
501
+
502
+ function subc(z: number | Complex, w: number | Complex): Complex {
503
+ const [ x, y ] = ensure_complex(z)
504
+ const [ u, v ] = ensure_complex(w)
505
+ return [ x - u, y - v ]
506
+ }
507
+
508
+ function mulc(z: number | Complex, w: number | Complex): Complex {
509
+ const [ x, y ] = ensure_complex(z)
510
+ const [ u, v ] = ensure_complex(w)
511
+ return [ x * u - y * v, x * v + y * u ]
512
+ }
513
+
514
+ function divc(z: number | Complex, w: number | Complex): Complex {
515
+ const [ x, y ] = ensure_complex(z)
516
+ const [ u, v ] = ensure_complex(w)
517
+ const denom = norm([ u, v ], 2)
518
+ return [ (x * u + y * v) / denom, (y * u - x * v) / denom ]
519
+ }
520
+
521
+ function conjc(z: number | Complex): Complex {
522
+ const [ x, y ] = ensure_complex(z)
523
+ return [ x, -y ]
524
+ }
525
+
526
+ function normc(z: number | Complex): number {
527
+ const [ x, y ] = ensure_complex(z)
528
+ return norm([ x, y ], 2)
529
+ }
530
+
531
+ function argc(z: number | Complex): number {
532
+ const [ x, y ] = ensure_complex(z)
533
+ return atan2(y, x)
534
+ }
535
+
464
536
  //
465
537
  // metaposition arithmetic
466
538
  //
@@ -567,7 +639,7 @@ function rect_radial(rect: Rect, absolute: boolean = false): Rect {
567
639
  // TODO: add optimized path for Point/number case
568
640
  function radial_rect(p0: Point | MPoint, r0: number | Size): Rect {
569
641
  const p = ensure_mpoint(p0)
570
- const r = broadcast_point(r0)
642
+ const r = ensure_pair(r0)
571
643
  const pa = sub2m(p, r)
572
644
  const pb = add2m(p, r)
573
645
  return [ ...squeeze_mpoint(pa), ...squeeze_mpoint(pb) ]
@@ -675,21 +747,21 @@ type LimitArgs = {
675
747
  [key in Orient]?: Limit
676
748
  }
677
749
 
678
- function join_limits({ v, h }: LimitArgs = {}): Rect {
750
+ function join_limits({ h, v }: LimitArgs = {}): Rect {
679
751
  const [ vlo, vhi ] = v ?? D.lim
680
752
  const [ hlo, hhi ] = h ?? D.lim
681
753
  return [ hlo, vlo, hhi, vhi ]
682
754
  }
683
755
 
684
- function split_limits(coord: Rect | undefined): { xlim?: Limit, ylim?: Limit } {
756
+ function split_limits(coord: Rect | undefined): { h?: Limit, v?: Limit } {
685
757
  if (coord == null) return {}
686
758
  const [ xlo, ylo, xhi, yhi ] = coord
687
- return { xlim: [ xlo, xhi ], ylim: [ ylo, yhi ] }
759
+ return { h: [ xlo, xhi ], v: [ ylo, yhi ] }
688
760
  }
689
761
 
690
- function resolve_limits(xlim: Limit | undefined, ylim: Limit | undefined, coord: Rect | undefined): { xlim: Limit, ylim: Limit } {
691
- const { xlim: xlim0, ylim: ylim0 } = split_limits(coord)
692
- return { xlim: xlim ?? xlim0!, ylim: ylim ?? ylim0! }
762
+ function resolve_limits(xlim: Limit | undefined, ylim: Limit | undefined, coord: Rect | undefined): { h: Limit | undefined, v: Limit | undefined } {
763
+ const { h: xlim0, v: ylim0 } = split_limits(coord)
764
+ return { h: xlim ?? xlim0, v: ylim ?? ylim0 }
693
765
  }
694
766
 
695
767
  function detect_coords(xvals: number[], yvals: number[], xlim: Limit | undefined, ylim: Limit | undefined): Rect {
@@ -795,15 +867,23 @@ function vector_angle(vector: Point): number {
795
867
  return r2d * Math.atan2(y, x)
796
868
  }
797
869
 
798
- function angle_direc(angle: Angle): Point {
799
- return [ cos(d2r * angle), sin(d2r * angle) ]
870
+ function radian_direc(angle: number): Point {
871
+ return [ cos(angle), sin(angle) ]
872
+ }
873
+
874
+ function angle_direc(angle: number): Point {
875
+ return radian_direc(d2r * angle)
800
876
  }
801
877
 
802
- function polar(angle: number, radius: number | Size = 1, center?: Point): Point {
803
- const offset = mul2(radius, angle_direc(angle))
878
+ function polar(theta: number, radius: number | Size = 1, center?: Point): Point {
879
+ const offset = mul2(radius, radian_direc(theta))
804
880
  return center ? add2(center, offset) : offset
805
881
  }
806
882
 
883
+ function polard(angle: number, radius: number | Size = 1, center?: Point): Point {
884
+ return polar(d2r * angle, radius, center)
885
+ }
886
+
807
887
  function norm_side(side: Side): Side0 {
808
888
  if (side == 'top' || side == 't' || side == 'north' || side == 'n') return 't'
809
889
  if (side == 'bottom' || side == 'b' || side == 'south' || side == 's') return 'b'
@@ -905,4 +985,4 @@ function binary_search(arr: number[], t: number): number {
905
985
  // export
906
986
  //
907
987
 
908
- export { is_browser, is_boolean, is_scalar, is_string, is_number, is_object, is_function, is_array, is_singleton, is_point, ensure_vector, ensure_singleton, ensure_function, check_singleton, check_array, check_string, gzip, zip, reshape, split, concat, squeeze, slice, intersperse, sum, prod, mean, all, any, cumsum, norm, normalize, range, linspace, enumerate, repeat, padvec, meshgrid, lingrid, map_object, filter_object, compress_whitespace, exp, log, log10, sin, cos, tan, cot, abs, pow, sqrt, sign, floor, ceil, round, atan, atan2, isNan, isInf, minimum, maximum, heaviside, heavisign, abs_min, abs_max, min, max, clamp, rescale, sigmoid, logit, smoothstep, identity, invert, setSeed, random, uniform, normal, integer, broadcast_point, add2, sub2, mul2, div2, ensure_number, ensure_point, ensure_mnumber, ensure_mpoint, addm, subm, add2m, sub2m, make_mpoint, squeeze_mnumber, squeeze_mpoint, rect_size, rect_dims, rect_center, rect_radius, rect_aspect, rect_radial, norm_angle, split_limits, vector_angle, angle_direc, polar, side_direc, unit_direc, norm_side, rgba_repr, interp, palette, detect_coords, resolve_limits, join_limits, invert_orient, aspect_invariant, flip_rect, radial_rect, box_rect, rect_box, cbox_rect, rect_cbox, merge_rects, merge_points, merge_limits, merge_values, expand_limits, expand_rect, upright_rect, upright_limits, rounder, remap_rect, resizer, rescaler, rotate_aspect, prefix_split, prefix_join, binary_search }
988
+ export { is_browser, is_boolean, is_scalar, is_string, is_number, is_object, is_function, is_array, is_singleton, is_point, ensure_vector, ensure_pair, ensure_singleton, ensure_function, check_singleton, check_array, check_string, gzip, zip, reshape, split, concat, squeeze, slice, intersperse, sum, prod, mean, all, any, cumsum, norm, normalize, range, linspace, enumerate, repeat, padvec, meshgrid, lingrid, map_object, filter_object, compress_whitespace, exp, log, log10, sin, cos, tan, cot, abs, pow, sqrt, sign, floor, ceil, round, atan, atan2, isNan, isInf, minimum, maximum, heaviside, heavisign, abs_min, abs_max, min, max, clamp, rescale, sigmoid, logit, smoothstep, identity, invert, setSeed, random, uniform, normal, integer, add2, sub2, mul2, div2, addn, subn, muln, divn, addc, subc, mulc, divc, conjc, normc, argc, ensure_number, ensure_point, ensure_mnumber, ensure_mpoint, addm, subm, add2m, sub2m, make_mpoint, squeeze_mnumber, squeeze_mpoint, rect_size, rect_dims, rect_center, rect_radius, rect_aspect, rect_radial, norm_angle, split_limits, vector_angle, angle_direc, polar, polard, side_direc, unit_direc, norm_side, rgba_repr, interp, palette, detect_coords, resolve_limits, join_limits, invert_orient, aspect_invariant, flip_rect, radial_rect, box_rect, rect_box, cbox_rect, rect_cbox, merge_rects, merge_points, merge_limits, merge_values, expand_limits, expand_rect, upright_rect, upright_limits, rounder, remap_rect, resizer, rescaler, rotate_aspect, prefix_split, prefix_join, binary_search }
package/src/render.ts CHANGED
@@ -5,6 +5,8 @@ import { createCanvas, loadImage, registerFont, type ImageData as CanvasImageDat
5
5
  import type { Size } from './lib/types'
6
6
  import { FONT_PATHS } from './fonts/fonts'
7
7
  import { light, regular, bold } from './lib/const'
8
+ import { is_string } from './lib/utils'
9
+ import { fitSize } from './eval'
8
10
 
9
11
  // register bundled fonts so SVG <text> resolves consistently
10
12
  for (const [ family, path ] of Object.entries(FONT_PATHS)) {
@@ -19,8 +21,7 @@ for (const [ family, path ] of Object.entries(FONT_PATHS)) {
19
21
 
20
22
  interface RasterizeBaseArgs {
21
23
  size?: Size
22
- width?: number
23
- height?: number
24
+ rasterSize?: Size
24
25
  background?: string
25
26
  }
26
27
 
@@ -45,32 +46,11 @@ interface FormatImageArgs {
45
46
 
46
47
  async function rasterizeSvg(svg: string | Buffer, args: RasterizePixelArgs): Promise<CanvasImageData>
47
48
  async function rasterizeSvg(svg: string | Buffer, args?: RasterizePngArgs): Promise<Buffer>
48
- async function rasterizeSvg(svg: string | Buffer, { size, width, height, background, pixelData }: RasterizeArgs = {}): Promise<Buffer | CanvasImageData> {
49
- const buf = typeof svg === 'string' ? Buffer.from(svg) : svg
49
+ async function rasterizeSvg(svg: string | Buffer, { size, rasterSize, background, pixelData }: RasterizeArgs = {}): Promise<Buffer | CanvasImageData> {
50
+ const buf = is_string(svg) ? Buffer.from(svg) : svg
50
51
  const img = await loadImage(buf)
51
- const w0 = size?.[0] ?? img.width
52
- const h0 = size?.[1] ?? img.height
53
-
54
- // if both width and height given, scale to fit within (prefer smaller scale)
55
- if (width != null && height != null) {
56
- const scaleW = width / w0
57
- const scaleH = height / h0
58
- if (scaleW < scaleH) height = undefined
59
- else width = undefined
60
- }
61
-
62
- // pick output dimensions; preserve aspect when only one of width/height given
63
- let outW: number, outH: number
64
- if (width != null) {
65
- outW = width
66
- outH = Math.round(width * h0 / w0)
67
- } else if (height != null) {
68
- outH = height
69
- outW = Math.round(height * w0 / h0)
70
- } else {
71
- outW = w0
72
- outH = h0
73
- }
52
+ const imgSize = size ?? [ img.width, img.height ]
53
+ const [ outW, outH ] = fitSize(imgSize, rasterSize)
74
54
 
75
55
  // create canvas
76
56
  const canvas = createCanvas(outW, outH)
@@ -53,6 +53,13 @@ declare module 'katex' {
53
53
  body: TreeNode[]
54
54
  }
55
55
 
56
+ export type TreeFont = {
57
+ type: 'font'
58
+ mode: SymbolMode
59
+ font: string
60
+ body: TreeNode
61
+ }
62
+
56
63
  export type TreeSupSub = {
57
64
  type: 'supsub'
58
65
  base: TreeNode | null
@@ -115,6 +122,7 @@ declare module 'katex' {
115
122
  | TreeOp
116
123
  | TreeKern
117
124
  | TreeText
125
+ | TreeFont
118
126
  | TreeStyling
119
127
  | TreeAccent
120
128
  | TreeSupSub