@luma.gl/shadertools 9.0.19 → 9.0.21

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 (31) hide show
  1. package/dist/dist.dev.js +107 -206
  2. package/dist/dist.min.js +92 -148
  3. package/dist/index.cjs +105 -148
  4. package/dist/index.cjs.map +4 -4
  5. package/dist/index.d.ts +2 -2
  6. package/dist/index.d.ts.map +1 -1
  7. package/dist/modules/lighting/gouraud-material/gouraud-material.d.ts +2 -3
  8. package/dist/modules/lighting/gouraud-material/gouraud-material.d.ts.map +1 -1
  9. package/dist/modules/lighting/gouraud-material/gouraud-material.js +7 -3
  10. package/dist/modules/lighting/lights/lighting-uniforms-glsl.d.ts.map +1 -1
  11. package/dist/modules/lighting/lights/lighting-uniforms-glsl.js +30 -6
  12. package/dist/modules/lighting/lights/lighting-uniforms.d.ts +13 -6
  13. package/dist/modules/lighting/lights/lighting-uniforms.d.ts.map +1 -1
  14. package/dist/modules/lighting/lights/lighting-uniforms.js +41 -33
  15. package/dist/modules/lighting/phong-material/phong-material.d.ts +4 -4
  16. package/dist/modules/lighting/phong-material/phong-material.d.ts.map +1 -1
  17. package/dist/modules/lighting/phong-material/phong-material.js +4 -0
  18. package/dist/modules/lighting/phong-material/phong-shaders-glsl.d.ts +0 -39
  19. package/dist/modules/lighting/phong-material/phong-shaders-glsl.d.ts.map +1 -1
  20. package/dist/modules/lighting/phong-material/phong-shaders-glsl.js +7 -65
  21. package/package.json +2 -2
  22. package/src/index.ts +2 -2
  23. package/src/modules/lighting/gouraud-material/gouraud-material.ts +11 -8
  24. package/src/modules/lighting/lights/lighting-uniforms-glsl.ts +32 -10
  25. package/src/modules/lighting/lights/lighting-uniforms.ts +61 -40
  26. package/src/modules/lighting/phong-material/phong-material.ts +9 -6
  27. package/src/modules/lighting/phong-material/phong-shaders-glsl.ts +8 -89
  28. package/dist/modules/lighting/gouraud-material/gouraud-shaders-glsl.d.ts +0 -42
  29. package/dist/modules/lighting/gouraud-material/gouraud-shaders-glsl.d.ts.map +0 -1
  30. package/dist/modules/lighting/gouraud-material/gouraud-shaders-glsl.js +0 -110
  31. package/src/modules/lighting/gouraud-material/gouraud-shaders-glsl.ts +0 -144
package/dist/index.cjs CHANGED
@@ -353,14 +353,14 @@ ${moduleSource}
353
353
  return this.defines;
354
354
  }
355
355
  // Warn about deprecated uniforms or functions
356
- checkDeprecations(shaderSource, log2) {
356
+ checkDeprecations(shaderSource, log3) {
357
357
  this.deprecations.forEach((def) => {
358
358
  var _a;
359
359
  if ((_a = def.regex) == null ? void 0 : _a.test(shaderSource)) {
360
360
  if (def.deprecated) {
361
- log2.deprecated(def.old, def.new)();
361
+ log3.deprecated(def.old, def.new)();
362
362
  } else {
363
- log2.removed(def.old, def.new)();
363
+ log3.removed(def.old, def.new)();
364
364
  }
365
365
  }
366
366
  });
@@ -684,7 +684,7 @@ function assembleWGSLShader(platformInfo, options) {
684
684
  // defines = {},
685
685
  hookFunctions = [],
686
686
  inject = {},
687
- log: log2
687
+ log: log3
688
688
  } = options;
689
689
  assert(typeof source === "string", "shader source must be a string");
690
690
  const coreSource = source;
@@ -714,8 +714,8 @@ function assembleWGSLShader(platformInfo, options) {
714
714
  }
715
715
  const modulesToInject = platformInfo.type !== "webgpu" ? modules : [];
716
716
  for (const module2 of modulesToInject) {
717
- if (log2) {
718
- module2.checkDeprecations(coreSource, log2);
717
+ if (log3) {
718
+ module2.checkDeprecations(coreSource, log3);
719
719
  }
720
720
  const moduleSource = module2.getModuleSource(stage, "wgsl");
721
721
  assembledSource += moduleSource;
@@ -741,7 +741,7 @@ function assembleWGSLShader(platformInfo, options) {
741
741
  return assembledSource;
742
742
  }
743
743
  function assembleGLSLShader(platformInfo, options) {
744
- const { id, source, stage, language = "glsl", modules, defines = {}, hookFunctions = [], inject = {}, prologue = true, log: log2 } = options;
744
+ const { id, source, stage, language = "glsl", modules, defines = {}, hookFunctions = [], inject = {}, prologue = true, log: log3 } = options;
745
745
  assert(typeof source === "string", "shader source must be a string");
746
746
  const sourceVersion = language === "glsl" ? getShaderInfo(source).version : -1;
747
747
  const targetVersion = platformInfo.shaderLanguageVersion;
@@ -798,8 +798,8 @@ ${getApplicationDefines(allDefines)}
798
798
  }
799
799
  }
800
800
  for (const module2 of modules) {
801
- if (log2) {
802
- module2.checkDeprecations(coreSource, log2);
801
+ if (log3) {
802
+ module2.checkDeprecations(coreSource, log3);
803
803
  }
804
804
  const moduleSource = module2.getModuleSource(stage);
805
805
  assembledSource += moduleSource;
@@ -1527,16 +1527,40 @@ int lightType;
1527
1527
  int directionalLightCount;
1528
1528
  int pointLightCount;
1529
1529
  vec3 ambientColor;
1530
- vec3 lightColor;
1531
- vec3 lightPosition;
1532
- vec3 lightDirection;
1533
- vec3 lightAttenuation;
1530
+ vec3 lightColor0;
1531
+ vec3 lightPosition0;
1532
+ vec3 lightDirection0;
1533
+ vec3 lightAttenuation0;
1534
+ vec3 lightColor1;
1535
+ vec3 lightPosition1;
1536
+ vec3 lightDirection1;
1537
+ vec3 lightAttenuation1;
1538
+ vec3 lightColor2;
1539
+ vec3 lightPosition2;
1540
+ vec3 lightDirection2;
1541
+ vec3 lightAttenuation2;
1534
1542
  } lighting;
1535
1543
  PointLight lighting_getPointLight(int index) {
1536
- return PointLight(lighting.lightColor, lighting.lightPosition, lighting.lightAttenuation);
1544
+ switch (index) {
1545
+ case 0:
1546
+ return PointLight(lighting.lightColor0, lighting.lightPosition0, lighting.lightAttenuation0);
1547
+ case 1:
1548
+ return PointLight(lighting.lightColor1, lighting.lightPosition1, lighting.lightAttenuation1);
1549
+ case 2:
1550
+ default:
1551
+ return PointLight(lighting.lightColor2, lighting.lightPosition2, lighting.lightAttenuation2);
1552
+ }
1537
1553
  }
1538
1554
  DirectionalLight lighting_getDirectionalLight(int index) {
1539
- return DirectionalLight(lighting.lightColor, lighting.lightDirection);
1555
+ switch (index) {
1556
+ case 0:
1557
+ return DirectionalLight(lighting.lightColor0, lighting.lightDirection0);
1558
+ case 1:
1559
+ return DirectionalLight(lighting.lightColor1, lighting.lightDirection1);
1560
+ case 2:
1561
+ default:
1562
+ return DirectionalLight(lighting.lightColor2, lighting.lightDirection2);
1563
+ }
1540
1564
  }
1541
1565
  float getPointLightAttenuation(PointLight pointLight, float distance) {
1542
1566
  return pointLight.attenuation.x
@@ -1546,7 +1570,8 @@ return pointLight.attenuation.x
1546
1570
  `;
1547
1571
 
1548
1572
  // dist/modules/lighting/lights/lighting-uniforms.js
1549
- var MAX_LIGHTS = 5;
1573
+ var import_core2 = require("@luma.gl/core");
1574
+ var MAX_LIGHTS = 3;
1550
1575
  var COLOR_FACTOR = 255;
1551
1576
  var LIGHT_TYPE;
1552
1577
  (function(LIGHT_TYPE2) {
@@ -1566,21 +1591,23 @@ var lighting = {
1566
1591
  uniformTypes: {
1567
1592
  enabled: "i32",
1568
1593
  lightType: "i32",
1569
- // , array: MAX_LIGHTS,
1570
1594
  directionalLightCount: "i32",
1571
- // , array: MAX_LIGHTS,
1572
1595
  pointLightCount: "i32",
1573
- // , array: MAX_LIGHTS,
1574
1596
  ambientLightColor: "vec3<f32>",
1575
- lightColor: "vec3<f32>",
1576
- // , array: MAX_LIGHTS,
1577
- lightPosition: "vec3<f32>",
1578
- // , array: MAX_LIGHTS,
1597
+ // TODO define as arrays once we have appropriate uniformTypes
1598
+ lightColor0: "vec3<f32>",
1599
+ lightPosition0: "vec3<f32>",
1579
1600
  // TODO - could combine direction and attenuation
1580
- lightDirection: "vec3<f32>",
1581
- // , array: MAX_LIGHTS,
1582
- lightAttenuation: "vec3<f32>"
1583
- // , array: MAX_LIGHTS},
1601
+ lightDirection0: "vec3<f32>",
1602
+ lightAttenuation0: "vec3<f32>",
1603
+ lightColor1: "vec3<f32>",
1604
+ lightPosition1: "vec3<f32>",
1605
+ lightDirection1: "vec3<f32>",
1606
+ lightAttenuation1: "vec3<f32>",
1607
+ lightColor2: "vec3<f32>",
1608
+ lightPosition2: "vec3<f32>",
1609
+ lightDirection2: "vec3<f32>",
1610
+ lightAttenuation2: "vec3<f32>"
1584
1611
  },
1585
1612
  defaultUniforms: {
1586
1613
  enabled: 1,
@@ -1588,11 +1615,19 @@ var lighting = {
1588
1615
  directionalLightCount: 0,
1589
1616
  pointLightCount: 0,
1590
1617
  ambientLightColor: [0.1, 0.1, 0.1],
1591
- lightColor: [1, 1, 1],
1592
- lightPosition: [1, 1, 2],
1618
+ lightColor0: [1, 1, 1],
1619
+ lightPosition0: [1, 1, 2],
1593
1620
  // TODO - could combine direction and attenuation
1594
- lightDirection: [1, 1, 1],
1595
- lightAttenuation: [1, 1, 1]
1621
+ lightDirection0: [1, 1, 1],
1622
+ lightAttenuation0: [1, 0, 0],
1623
+ lightColor1: [1, 1, 1],
1624
+ lightPosition1: [1, 1, 2],
1625
+ lightDirection1: [1, 1, 1],
1626
+ lightAttenuation1: [1, 0, 0],
1627
+ lightColor2: [1, 1, 1],
1628
+ lightPosition2: [1, 1, 2],
1629
+ lightDirection2: [1, 1, 1],
1630
+ lightAttenuation2: [1, 0, 0]
1596
1631
  }
1597
1632
  };
1598
1633
  function getUniforms2(props, prevUniforms = {}) {
@@ -1619,29 +1654,27 @@ function getUniforms2(props, prevUniforms = {}) {
1619
1654
  return uniforms;
1620
1655
  }
1621
1656
  function getLightSourceUniforms({ ambientLight, pointLights = [], directionalLights = [] }) {
1622
- const lightSourceUniforms = {
1623
- // lightType: new Array(MAX_LIGHTS).fill(0),
1624
- // lightColor: new Array(MAX_LIGHTS).fill([0, 0, 0]),
1625
- // lightPosition: new Array(MAX_LIGHTS).fill([0, 0, 0]),
1626
- // lightDirection: new Array(MAX_LIGHTS).fill([0, 0, 0]),
1627
- // lightAttenuation: new Array(MAX_LIGHTS).fill([0, 0, 0])
1628
- };
1657
+ const lightSourceUniforms = {};
1629
1658
  lightSourceUniforms.ambientLightColor = convertColor(ambientLight);
1630
1659
  let currentLight = 0;
1631
1660
  for (const pointLight of pointLights) {
1632
1661
  lightSourceUniforms.lightType = LIGHT_TYPE.POINT;
1633
- lightSourceUniforms.lightColor = convertColor(pointLight);
1634
- lightSourceUniforms.lightPosition = pointLight.position;
1635
- lightSourceUniforms.lightAttenuation = [pointLight.attenuation || 1, 0, 0];
1662
+ const i = currentLight;
1663
+ lightSourceUniforms[`lightColor${i}`] = convertColor(pointLight);
1664
+ lightSourceUniforms[`lightPosition${i}`] = pointLight.position;
1665
+ lightSourceUniforms[`lightAttenuation${i}`] = pointLight.attenuation || [1, 0, 0];
1636
1666
  currentLight++;
1637
1667
  }
1638
1668
  for (const directionalLight of directionalLights) {
1639
1669
  lightSourceUniforms.lightType = LIGHT_TYPE.DIRECTIONAL;
1640
- lightSourceUniforms.lightColor = convertColor(directionalLight);
1641
- lightSourceUniforms.lightPosition = directionalLight.position;
1642
- lightSourceUniforms.lightDirection = directionalLight.direction;
1670
+ const i = currentLight;
1671
+ lightSourceUniforms[`lightColor${i}`] = convertColor(directionalLight);
1672
+ lightSourceUniforms[`lightDirection${i}`] = directionalLight.direction;
1643
1673
  currentLight++;
1644
1674
  }
1675
+ if (currentLight > MAX_LIGHTS) {
1676
+ import_core2.log.warn("MAX_LIGHTS exceeded")();
1677
+ }
1645
1678
  lightSourceUniforms.directionalLightCount = directionalLights.length;
1646
1679
  lightSourceUniforms.pointLightCount = pointLights.length;
1647
1680
  return lightSourceUniforms;
@@ -1712,8 +1745,15 @@ function getUniforms3(opts = dirlight.defaultUniforms) {
1712
1745
  return uniforms;
1713
1746
  }
1714
1747
 
1715
- // dist/modules/lighting/gouraud-material/gouraud-shaders-glsl.js
1716
- var GOURAUD_VS = `uniform gouraudMaterialUniforms {
1748
+ // dist/modules/lighting/phong-material/phong-shaders-glsl.js
1749
+ var PHONG_VS = `uniform phongMaterialUniforms {
1750
+ uniform float ambient;
1751
+ uniform float diffuse;
1752
+ uniform float shininess;
1753
+ uniform vec3 specularColor;
1754
+ } material;
1755
+ `;
1756
+ var PHONG_FS = `uniform phongMaterialUniforms {
1717
1757
  uniform float ambient;
1718
1758
  uniform float diffuse;
1719
1759
  uniform float shininess;
@@ -1737,53 +1777,28 @@ return lightColor;
1737
1777
  }
1738
1778
  vec3 view_direction = normalize(cameraPosition - position_worldspace);
1739
1779
  lightColor = material.ambient * surfaceColor * lighting.ambientColor;
1740
- if (lighting.lightType == 0) {
1741
- PointLight pointLight = lighting_getPointLight(0);
1780
+ for (int i = 0; i < lighting.pointLightCount; i++) {
1781
+ PointLight pointLight = lighting_getPointLight(i);
1742
1782
  vec3 light_position_worldspace = pointLight.position;
1743
1783
  vec3 light_direction = normalize(light_position_worldspace - position_worldspace);
1744
- lightColor += lighting_getLightColor(surfaceColor, light_direction, view_direction, normal_worldspace, pointLight.color);
1745
- } else if (lighting.lightType == 1) {
1746
- DirectionalLight directionalLight = lighting_getDirectionalLight(0);
1747
- lightColor += lighting_getLightColor(surfaceColor, -directionalLight.direction, view_direction, normal_worldspace, directionalLight.color);
1748
- }
1749
- return lightColor;
1750
- }
1751
- vec3 lighting_getSpecularLightColor(vec3 cameraPosition, vec3 position_worldspace, vec3 normal_worldspace) {
1752
- vec3 lightColor = vec3(0, 0, 0);
1753
- vec3 surfaceColor = vec3(0, 0, 0);
1754
- if (lighting.enabled == 0) {
1755
- return lightColor;
1784
+ float light_attenuation = getPointLightAttenuation(pointLight, distance(light_position_worldspace, position_worldspace));
1785
+ lightColor += lighting_getLightColor(surfaceColor, light_direction, view_direction, normal_worldspace, pointLight.color / light_attenuation);
1756
1786
  }
1757
- vec3 view_direction = normalize(cameraPosition - position_worldspace);
1758
- switch (lighting.lightType) {
1759
- case 0:
1760
- PointLight pointLight = lighting_getPointLight(0);
1761
- vec3 light_position_worldspace = pointLight.position;
1762
- vec3 light_direction = normalize(light_position_worldspace - position_worldspace);
1763
- lightColor += lighting_getLightColor(surfaceColor, light_direction, view_direction, normal_worldspace, pointLight.color);
1764
- break;
1765
- case 1:
1766
- DirectionalLight directionalLight = lighting_getDirectionalLight(0);
1787
+ int totalLights = min(MAX_LIGHTS, lighting.pointLightCount + lighting.directionalLightCount);
1788
+ for (int i = lighting.pointLightCount; i < totalLights; i++) {
1789
+ DirectionalLight directionalLight = lighting_getDirectionalLight(i);
1767
1790
  lightColor += lighting_getLightColor(surfaceColor, -directionalLight.direction, view_direction, normal_worldspace, directionalLight.color);
1768
- break;
1769
1791
  }
1770
1792
  return lightColor;
1771
1793
  }
1772
1794
  `;
1773
- var GOURAUD_FS = `uniform gouraudMaterialUniforms {
1774
- uniform float ambient;
1775
- uniform float diffuse;
1776
- uniform float shininess;
1777
- uniform vec3 specularColor;
1778
- } material;
1779
- `;
1780
1795
 
1781
1796
  // dist/modules/lighting/gouraud-material/gouraud-material.js
1782
1797
  var gouraudMaterial = {
1783
1798
  name: "gouraudMaterial",
1784
1799
  // Note these are switched between phong and gouraud
1785
- vs: GOURAUD_VS,
1786
- fs: GOURAUD_FS,
1800
+ vs: PHONG_FS.replace("phongMaterial", "gouraudMaterial"),
1801
+ fs: PHONG_VS.replace("phongMaterial", "gouraudMaterial"),
1787
1802
  defines: {
1788
1803
  LIGHTING_VERTEX: 1
1789
1804
  },
@@ -1801,76 +1816,14 @@ var gouraudMaterial = {
1801
1816
  specularColor: [0.15, 0.15, 0.15]
1802
1817
  },
1803
1818
  getUniforms(props) {
1819
+ const uniforms = { ...props };
1820
+ if (uniforms.specularColor) {
1821
+ uniforms.specularColor = uniforms.specularColor.map((x) => x / 255);
1822
+ }
1804
1823
  return { ...gouraudMaterial.defaultUniforms, ...props };
1805
1824
  }
1806
1825
  };
1807
1826
 
1808
- // dist/modules/lighting/phong-material/phong-shaders-glsl.js
1809
- var PHONG_VS = `uniform phongMaterialUniforms {
1810
- uniform float ambient;
1811
- uniform float diffuse;
1812
- uniform float shininess;
1813
- uniform vec3 specularColor;
1814
- } material;
1815
- `;
1816
- var PHONG_FS = `uniform phongMaterialUniforms {
1817
- uniform float ambient;
1818
- uniform float diffuse;
1819
- uniform float shininess;
1820
- uniform vec3 specularColor;
1821
- } material;
1822
- vec3 lighting_getLightColor(vec3 surfaceColor, vec3 light_direction, vec3 view_direction, vec3 normal_worldspace, vec3 color) {
1823
- vec3 halfway_direction = normalize(light_direction + view_direction);
1824
- float lambertian = dot(light_direction, normal_worldspace);
1825
- float specular = 0.0;
1826
- if (lambertian > 0.0) {
1827
- float specular_angle = max(dot(normal_worldspace, halfway_direction), 0.0);
1828
- specular = pow(specular_angle, material.shininess);
1829
- }
1830
- lambertian = max(lambertian, 0.0);
1831
- return (lambertian * material.diffuse * surfaceColor + specular * material.specularColor) * color;
1832
- }
1833
- vec3 lighting_getLightColor(vec3 surfaceColor, vec3 cameraPosition, vec3 position_worldspace, vec3 normal_worldspace) {
1834
- vec3 lightColor = surfaceColor;
1835
- if (lighting.enabled == 0) {
1836
- return lightColor;
1837
- }
1838
- vec3 view_direction = normalize(cameraPosition - position_worldspace);
1839
- lightColor = material.ambient * surfaceColor * lighting.ambientColor;
1840
- if (lighting.lightType == 0) {
1841
- PointLight pointLight = lighting_getPointLight(0);
1842
- vec3 light_position_worldspace = pointLight.position;
1843
- vec3 light_direction = normalize(light_position_worldspace - position_worldspace);
1844
- lightColor += lighting_getLightColor(surfaceColor, light_direction, view_direction, normal_worldspace, pointLight.color);
1845
- } else if (lighting.lightType == 1) {
1846
- DirectionalLight directionalLight = lighting_getDirectionalLight(0);
1847
- lightColor += lighting_getLightColor(surfaceColor, -directionalLight.direction, view_direction, normal_worldspace, directionalLight.color);
1848
- }
1849
- return lightColor;
1850
- }
1851
- vec3 lighting_getSpecularLightColor(vec3 cameraPosition, vec3 position_worldspace, vec3 normal_worldspace) {
1852
- vec3 lightColor = vec3(0, 0, 0);
1853
- vec3 surfaceColor = vec3(0, 0, 0);
1854
- if (lighting.enabled == 0) {
1855
- return lightColor;
1856
- }
1857
- vec3 view_direction = normalize(cameraPosition - position_worldspace);
1858
- switch (lighting.lightType) {
1859
- case 0:
1860
- PointLight pointLight = lighting_getPointLight(0);
1861
- vec3 light_position_worldspace = pointLight.position;
1862
- vec3 light_direction = normalize(light_position_worldspace - position_worldspace);
1863
- lightColor += lighting_getLightColor(surfaceColor, light_direction, view_direction, normal_worldspace, pointLight.color);
1864
- break;
1865
- case 1:
1866
- DirectionalLight directionalLight = lighting_getDirectionalLight(0);
1867
- lightColor += lighting_getLightColor(surfaceColor, -directionalLight.direction, view_direction, normal_worldspace, directionalLight.color);
1868
- break;
1869
- }
1870
- return lightColor;
1871
- }
1872
- `;
1873
-
1874
1827
  // dist/modules/lighting/phong-material/phong-material.js
1875
1828
  var phongMaterial = {
1876
1829
  name: "phongMaterial",
@@ -1894,6 +1847,10 @@ var phongMaterial = {
1894
1847
  specularColor: [0.15, 0.15, 0.15]
1895
1848
  },
1896
1849
  getUniforms(props) {
1850
+ const uniforms = { ...props };
1851
+ if (uniforms.specularColor) {
1852
+ uniforms.specularColor = uniforms.specularColor.map((x) => x / 255);
1853
+ }
1897
1854
  return { ...phongMaterial.defaultUniforms, ...props };
1898
1855
  }
1899
1856
  };
@@ -4341,7 +4298,7 @@ var geometry = {
4341
4298
  };
4342
4299
 
4343
4300
  // dist/modules-webgl1/project/project.js
4344
- var import_core2 = require("@math.gl/core");
4301
+ var import_core3 = require("@math.gl/core");
4345
4302
  var IDENTITY_MATRIX = [1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1];
4346
4303
  var DEFAULT_MODULE_OPTIONS = {
4347
4304
  modelMatrix: IDENTITY_MATRIX,
@@ -4364,7 +4321,7 @@ function getUniforms5(opts = DEFAULT_MODULE_OPTIONS, prevUniforms = {}) {
4364
4321
  uniforms.cameraPositionWorld = opts.cameraPositionWorld;
4365
4322
  }
4366
4323
  if (opts.projectionMatrix !== void 0 || opts.viewMatrix !== void 0) {
4367
- uniforms.viewProjectionMatrix = new import_core2.Matrix4(opts.projectionMatrix).multiplyRight(opts.viewMatrix);
4324
+ uniforms.viewProjectionMatrix = new import_core3.Matrix4(opts.projectionMatrix).multiplyRight(opts.viewMatrix);
4368
4325
  }
4369
4326
  return uniforms;
4370
4327
  }