@luma.gl/engine 9.2.6 → 9.3.0-alpha.10
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/animation-loop/animation-loop.d.ts +11 -5
- package/dist/animation-loop/animation-loop.d.ts.map +1 -1
- package/dist/animation-loop/animation-loop.js +83 -47
- package/dist/animation-loop/animation-loop.js.map +1 -1
- package/dist/animation-loop/make-animation-loop.js +7 -1
- package/dist/animation-loop/make-animation-loop.js.map +1 -1
- package/dist/animation-loop/request-animation-frame.d.ts.map +1 -1
- package/dist/animation-loop/request-animation-frame.js +23 -6
- package/dist/animation-loop/request-animation-frame.js.map +1 -1
- package/dist/compute/computation.d.ts +3 -7
- package/dist/compute/computation.d.ts.map +1 -1
- package/dist/compute/computation.js +16 -13
- package/dist/compute/computation.js.map +1 -1
- package/dist/compute/swap.d.ts +2 -0
- package/dist/compute/swap.d.ts.map +1 -1
- package/dist/compute/swap.js +10 -5
- package/dist/compute/swap.js.map +1 -1
- package/dist/dist.dev.js +2639 -1290
- package/dist/dist.min.js +325 -210
- package/dist/dynamic-texture/dynamic-texture.d.ts +102 -0
- package/dist/dynamic-texture/dynamic-texture.d.ts.map +1 -0
- package/dist/dynamic-texture/dynamic-texture.js +556 -0
- package/dist/dynamic-texture/dynamic-texture.js.map +1 -0
- package/dist/dynamic-texture/texture-data.d.ts +144 -0
- package/dist/dynamic-texture/texture-data.d.ts.map +1 -0
- package/dist/dynamic-texture/texture-data.js +208 -0
- package/dist/dynamic-texture/texture-data.js.map +1 -0
- package/dist/geometries/cone-geometry.d.ts +3 -1
- package/dist/geometries/cone-geometry.d.ts.map +1 -1
- package/dist/geometries/cone-geometry.js.map +1 -1
- package/dist/geometries/cylinder-geometry.d.ts +2 -1
- package/dist/geometries/cylinder-geometry.d.ts.map +1 -1
- package/dist/geometries/cylinder-geometry.js.map +1 -1
- package/dist/geometry/gpu-geometry.d.ts.map +1 -1
- package/dist/geometry/gpu-geometry.js +8 -3
- package/dist/geometry/gpu-geometry.js.map +1 -1
- package/dist/index.cjs +2497 -1212
- package/dist/index.cjs.map +4 -4
- package/dist/index.d.ts +20 -6
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +12 -4
- package/dist/index.js.map +1 -1
- package/dist/material/material-factory.d.ts +73 -0
- package/dist/material/material-factory.d.ts.map +1 -0
- package/dist/material/material-factory.js +111 -0
- package/dist/material/material-factory.js.map +1 -0
- package/dist/material/material.d.ts +84 -0
- package/dist/material/material.d.ts.map +1 -0
- package/dist/material/material.js +176 -0
- package/dist/material/material.js.map +1 -0
- package/dist/model/model.d.ts +47 -16
- package/dist/model/model.d.ts.map +1 -1
- package/dist/model/model.js +113 -47
- package/dist/model/model.js.map +1 -1
- package/dist/model/split-uniforms-and-bindings.d.ts +4 -3
- package/dist/model/split-uniforms-and-bindings.d.ts.map +1 -1
- package/dist/model/split-uniforms-and-bindings.js +2 -2
- package/dist/model/split-uniforms-and-bindings.js.map +1 -1
- package/dist/models/billboard-texture-model.d.ts +8 -5
- package/dist/models/billboard-texture-model.d.ts.map +1 -1
- package/dist/models/billboard-texture-model.js +77 -23
- package/dist/models/billboard-texture-model.js.map +1 -1
- package/dist/models/billboard-texture-module.d.ts +1 -1
- package/dist/models/billboard-texture-module.js +1 -1
- package/dist/models/clip-space.js +7 -7
- package/dist/models/directional-light-model.d.ts +7 -0
- package/dist/models/directional-light-model.d.ts.map +1 -0
- package/dist/models/directional-light-model.js +23 -0
- package/dist/models/directional-light-model.js.map +1 -0
- package/dist/models/light-model-utils.d.ts +69 -0
- package/dist/models/light-model-utils.d.ts.map +1 -0
- package/dist/models/light-model-utils.js +395 -0
- package/dist/models/light-model-utils.js.map +1 -0
- package/dist/models/point-light-model.d.ts +7 -0
- package/dist/models/point-light-model.d.ts.map +1 -0
- package/dist/models/point-light-model.js +22 -0
- package/dist/models/point-light-model.js.map +1 -0
- package/dist/models/spot-light-model.d.ts +7 -0
- package/dist/models/spot-light-model.d.ts.map +1 -0
- package/dist/models/spot-light-model.js +23 -0
- package/dist/models/spot-light-model.js.map +1 -0
- package/dist/modules/picking/color-picking.d.ts +5 -9
- package/dist/modules/picking/color-picking.d.ts.map +1 -1
- package/dist/modules/picking/color-picking.js +122 -115
- package/dist/modules/picking/color-picking.js.map +1 -1
- package/dist/modules/picking/index-picking.d.ts +4 -4
- package/dist/modules/picking/index-picking.d.ts.map +1 -1
- package/dist/modules/picking/index-picking.js +36 -16
- package/dist/modules/picking/index-picking.js.map +1 -1
- package/dist/modules/picking/legacy-color-picking.d.ts +26 -0
- package/dist/modules/picking/legacy-color-picking.d.ts.map +1 -0
- package/dist/modules/picking/legacy-color-picking.js +7 -0
- package/dist/modules/picking/legacy-color-picking.js.map +1 -0
- package/dist/modules/picking/picking-manager.d.ts +29 -3
- package/dist/modules/picking/picking-manager.d.ts.map +1 -1
- package/dist/modules/picking/picking-manager.js +188 -41
- package/dist/modules/picking/picking-manager.js.map +1 -1
- package/dist/modules/picking/picking-uniforms.d.ts +13 -12
- package/dist/modules/picking/picking-uniforms.d.ts.map +1 -1
- package/dist/modules/picking/picking-uniforms.js +27 -14
- package/dist/modules/picking/picking-uniforms.js.map +1 -1
- package/dist/modules/picking/picking.d.ts +25 -0
- package/dist/modules/picking/picking.d.ts.map +1 -0
- package/dist/modules/picking/picking.js +18 -0
- package/dist/modules/picking/picking.js.map +1 -0
- package/dist/passes/get-fragment-shader.js +12 -27
- package/dist/passes/get-fragment-shader.js.map +1 -1
- package/dist/passes/shader-pass-renderer.d.ts +5 -7
- package/dist/passes/shader-pass-renderer.d.ts.map +1 -1
- package/dist/passes/shader-pass-renderer.js +16 -42
- package/dist/passes/shader-pass-renderer.js.map +1 -1
- package/dist/scenegraph/group-node.d.ts +5 -0
- package/dist/scenegraph/group-node.d.ts.map +1 -1
- package/dist/scenegraph/group-node.js +12 -0
- package/dist/scenegraph/group-node.js.map +1 -1
- package/dist/scenegraph/model-node.d.ts +2 -2
- package/dist/scenegraph/model-node.d.ts.map +1 -1
- package/dist/scenegraph/model-node.js.map +1 -1
- package/dist/scenegraph/scenegraph-node.d.ts +1 -1
- package/dist/scenegraph/scenegraph-node.d.ts.map +1 -1
- package/dist/scenegraph/scenegraph-node.js +23 -15
- package/dist/scenegraph/scenegraph-node.js.map +1 -1
- package/dist/shader-inputs.d.ts +9 -7
- package/dist/shader-inputs.d.ts.map +1 -1
- package/dist/shader-inputs.js +84 -4
- package/dist/shader-inputs.js.map +1 -1
- package/dist/utils/buffer-layout-order.d.ts.map +1 -1
- package/dist/utils/buffer-layout-order.js +12 -2
- package/dist/utils/buffer-layout-order.js.map +1 -1
- package/dist/utils/shader-module-utils.d.ts +7 -0
- package/dist/utils/shader-module-utils.d.ts.map +1 -0
- package/dist/utils/shader-module-utils.js +46 -0
- package/dist/utils/shader-module-utils.js.map +1 -0
- package/package.json +6 -6
- package/src/animation-loop/animation-loop.ts +89 -50
- package/src/animation-loop/make-animation-loop.ts +13 -5
- package/src/animation-loop/request-animation-frame.ts +32 -6
- package/src/compute/computation.ts +32 -17
- package/src/compute/swap.ts +13 -7
- package/src/dynamic-texture/dynamic-texture.ts +732 -0
- package/src/dynamic-texture/texture-data.ts +336 -0
- package/src/geometries/cone-geometry.ts +6 -1
- package/src/geometries/cylinder-geometry.ts +5 -1
- package/src/geometry/gpu-geometry.ts +8 -3
- package/src/index.ts +38 -8
- package/src/material/material-factory.ts +157 -0
- package/src/material/material.ts +254 -0
- package/src/model/model.ts +158 -67
- package/src/model/split-uniforms-and-bindings.ts +8 -6
- package/src/models/billboard-texture-model.ts +88 -27
- package/src/models/billboard-texture-module.ts +1 -1
- package/src/models/clip-space.ts +7 -7
- package/src/models/directional-light-model.ts +32 -0
- package/src/models/light-model-utils.ts +587 -0
- package/src/models/point-light-model.ts +31 -0
- package/src/models/spot-light-model.ts +32 -0
- package/src/modules/picking/color-picking.ts +123 -122
- package/src/modules/picking/index-picking.ts +36 -16
- package/src/modules/picking/legacy-color-picking.ts +8 -0
- package/src/modules/picking/picking-manager.ts +252 -50
- package/src/modules/picking/picking-uniforms.ts +39 -24
- package/src/modules/picking/picking.ts +22 -0
- package/src/passes/get-fragment-shader.ts +12 -27
- package/src/passes/shader-pass-renderer.ts +25 -48
- package/src/scenegraph/group-node.ts +16 -0
- package/src/scenegraph/model-node.ts +2 -2
- package/src/scenegraph/scenegraph-node.ts +27 -16
- package/src/shader-inputs.ts +165 -15
- package/src/utils/buffer-layout-order.ts +18 -2
- package/src/utils/shader-module-utils.ts +65 -0
- package/dist/async-texture/async-texture.d.ts +0 -166
- package/dist/async-texture/async-texture.d.ts.map +0 -1
- package/dist/async-texture/async-texture.js +0 -386
- package/dist/async-texture/async-texture.js.map +0 -1
- package/dist/factories/pipeline-factory.d.ts +0 -37
- package/dist/factories/pipeline-factory.d.ts.map +0 -1
- package/dist/factories/pipeline-factory.js +0 -181
- package/dist/factories/pipeline-factory.js.map +0 -1
- package/dist/factories/shader-factory.d.ts +0 -22
- package/dist/factories/shader-factory.d.ts.map +0 -1
- package/dist/factories/shader-factory.js +0 -88
- package/dist/factories/shader-factory.js.map +0 -1
- package/src/async-texture/async-texture.ts +0 -551
- package/src/factories/pipeline-factory.ts +0 -224
- package/src/factories/shader-factory.ts +0 -103
- /package/src/{async-texture/texture-setters.ts.disabled → dynamic-texture/texture-data.ts.disabled} +0 -0
|
@@ -3,24 +3,35 @@
|
|
|
3
3
|
// Copyright (c) vis.gl contributors
|
|
4
4
|
|
|
5
5
|
import {Device, Texture} from '@luma.gl/core';
|
|
6
|
-
import {
|
|
7
|
-
import {
|
|
6
|
+
import type {ShaderModule} from '@luma.gl/shadertools';
|
|
7
|
+
import {DynamicTexture} from '../dynamic-texture/dynamic-texture';
|
|
8
|
+
import {ClipSpace, ClipSpaceProps} from './clip-space';
|
|
9
|
+
|
|
10
|
+
const backgroundModule = {
|
|
11
|
+
name: 'background',
|
|
12
|
+
uniformTypes: {
|
|
13
|
+
scale: 'vec2<f32>'
|
|
14
|
+
}
|
|
15
|
+
} as const satisfies ShaderModule<{}, {scale: [number, number]}>;
|
|
8
16
|
|
|
9
17
|
const BACKGROUND_FS_WGSL = /* wgsl */ `\
|
|
10
18
|
@group(0) @binding(0) var backgroundTexture: texture_2d<f32>;
|
|
11
19
|
@group(0) @binding(1) var backgroundTextureSampler: sampler;
|
|
20
|
+
struct backgroundUniforms {
|
|
21
|
+
scale: vec2<f32>,
|
|
22
|
+
};
|
|
23
|
+
@group(0) @binding(2) var<uniform> background: backgroundUniforms;
|
|
12
24
|
|
|
13
|
-
fn billboardTexture_getTextureUV(
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
}
|
|
25
|
+
fn billboardTexture_getTextureUV(uv: vec2<f32>) -> vec2<f32> {
|
|
26
|
+
let scale: vec2<f32> = background.scale;
|
|
27
|
+
var position: vec2<f32> = (uv - vec2<f32>(0.5, 0.5)) / scale + vec2<f32>(0.5, 0.5);
|
|
28
|
+
return position;
|
|
29
|
+
}
|
|
19
30
|
|
|
20
31
|
@fragment
|
|
21
32
|
fn fragmentMain(inputs: FragmentInputs) -> @location(0) vec4<f32> {
|
|
22
|
-
|
|
23
|
-
|
|
33
|
+
let position: vec2<f32> = billboardTexture_getTextureUV(inputs.uv);
|
|
34
|
+
return textureSample(backgroundTexture, backgroundTextureSampler, position);
|
|
24
35
|
}
|
|
25
36
|
`;
|
|
26
37
|
|
|
@@ -29,17 +40,21 @@ const BACKGROUND_FS = /* glsl */ `\
|
|
|
29
40
|
precision highp float;
|
|
30
41
|
|
|
31
42
|
uniform sampler2D backgroundTexture;
|
|
43
|
+
|
|
44
|
+
layout(std140) uniform backgroundUniforms {
|
|
45
|
+
vec2 scale;
|
|
46
|
+
} background;
|
|
47
|
+
|
|
48
|
+
in vec2 coordinate;
|
|
32
49
|
out vec4 fragColor;
|
|
33
50
|
|
|
34
|
-
vec2 billboardTexture_getTextureUV() {
|
|
35
|
-
|
|
36
|
-
vec2 texSize = vec2(float(iTexSize.x), float(iTexSize.y));
|
|
37
|
-
vec2 position = gl_FragCoord.xy / texSize;
|
|
51
|
+
vec2 billboardTexture_getTextureUV(vec2 coord) {
|
|
52
|
+
vec2 position = (coord - 0.5) / background.scale + 0.5;
|
|
38
53
|
return position;
|
|
39
54
|
}
|
|
40
55
|
|
|
41
56
|
void main(void) {
|
|
42
|
-
vec2 position = billboardTexture_getTextureUV();
|
|
57
|
+
vec2 position = billboardTexture_getTextureUV(coordinate);
|
|
43
58
|
fragColor = texture(backgroundTexture, position);
|
|
44
59
|
}
|
|
45
60
|
`;
|
|
@@ -47,11 +62,11 @@ void main(void) {
|
|
|
47
62
|
/**
|
|
48
63
|
* Props for a Model that renders a bitmap into the "background", i.e covering the screen
|
|
49
64
|
*/
|
|
50
|
-
export type BackgroundTextureModelProps = {
|
|
65
|
+
export type BackgroundTextureModelProps = ClipSpaceProps & {
|
|
51
66
|
/** id of this model */
|
|
52
67
|
id?: string;
|
|
53
68
|
/** The texture to render */
|
|
54
|
-
backgroundTexture: Texture |
|
|
69
|
+
backgroundTexture: Texture | DynamicTexture;
|
|
55
70
|
/** If true, the texture is rendered into transparent areas of the screen only, i.e blended in where background alpha is small */
|
|
56
71
|
blend?: boolean;
|
|
57
72
|
};
|
|
@@ -60,22 +75,27 @@ export type BackgroundTextureModelProps = {
|
|
|
60
75
|
* Model that renders a bitmap into the "background", i.e covering the screen
|
|
61
76
|
*/
|
|
62
77
|
export class BackgroundTextureModel extends ClipSpace {
|
|
78
|
+
backgroundTexture: Texture = null!;
|
|
79
|
+
|
|
63
80
|
constructor(device: Device, props: BackgroundTextureModelProps) {
|
|
64
81
|
super(device, {
|
|
82
|
+
...props,
|
|
65
83
|
id: props.id || 'background-texture-model',
|
|
66
84
|
source: BACKGROUND_FS_WGSL,
|
|
67
85
|
fs: BACKGROUND_FS,
|
|
86
|
+
modules: [...(props.modules || []), backgroundModule],
|
|
68
87
|
parameters: {
|
|
69
88
|
depthWriteEnabled: false,
|
|
89
|
+
...(props.parameters || {}),
|
|
70
90
|
...(props.blend
|
|
71
91
|
? {
|
|
72
92
|
blend: true,
|
|
73
93
|
blendColorOperation: 'add',
|
|
74
94
|
blendAlphaOperation: 'add',
|
|
75
|
-
blendColorSrcFactor: 'one',
|
|
76
|
-
blendColorDstFactor: 'one
|
|
77
|
-
blendAlphaSrcFactor: 'one',
|
|
78
|
-
blendAlphaDstFactor: 'one
|
|
95
|
+
blendColorSrcFactor: 'one-minus-dst-alpha',
|
|
96
|
+
blendColorDstFactor: 'one',
|
|
97
|
+
blendAlphaSrcFactor: 'one-minus-dst-alpha',
|
|
98
|
+
blendAlphaDstFactor: 'one'
|
|
79
99
|
}
|
|
80
100
|
: {})
|
|
81
101
|
}
|
|
@@ -84,17 +104,58 @@ export class BackgroundTextureModel extends ClipSpace {
|
|
|
84
104
|
if (!props.backgroundTexture) {
|
|
85
105
|
throw new Error('BackgroundTextureModel requires a backgroundTexture prop');
|
|
86
106
|
}
|
|
87
|
-
this.
|
|
107
|
+
this.setProps(props);
|
|
88
108
|
}
|
|
89
109
|
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
110
|
+
/** Update the background texture */
|
|
111
|
+
setProps(props: Partial<BackgroundTextureModelProps>): void {
|
|
112
|
+
const {backgroundTexture} = props;
|
|
113
|
+
if (backgroundTexture) {
|
|
114
|
+
this.setBindings({backgroundTexture});
|
|
115
|
+
|
|
116
|
+
if (backgroundTexture.isReady) {
|
|
117
|
+
const texture =
|
|
118
|
+
backgroundTexture instanceof DynamicTexture
|
|
119
|
+
? backgroundTexture.texture
|
|
120
|
+
: backgroundTexture;
|
|
121
|
+
this.backgroundTexture = texture;
|
|
122
|
+
this.updateScale(texture);
|
|
123
|
+
} else {
|
|
124
|
+
backgroundTexture.ready.then(texture => {
|
|
125
|
+
this.backgroundTexture = texture;
|
|
126
|
+
this.updateScale(texture);
|
|
127
|
+
});
|
|
128
|
+
}
|
|
129
|
+
}
|
|
94
130
|
}
|
|
95
131
|
|
|
96
132
|
override predraw(): void {
|
|
97
|
-
this.
|
|
133
|
+
// this.updateScale(this.backgroundTexture);
|
|
98
134
|
super.predraw();
|
|
99
135
|
}
|
|
136
|
+
|
|
137
|
+
updateScale(texture: Texture): void {
|
|
138
|
+
if (!texture) {
|
|
139
|
+
// Initial scale to avoid rendering issues before texture is loaded
|
|
140
|
+
this.shaderInputs.setProps({background: {scale: [1, 1]}});
|
|
141
|
+
return;
|
|
142
|
+
}
|
|
143
|
+
const [screenWidth, screenHeight] = this.device.getCanvasContext().getDrawingBufferSize();
|
|
144
|
+
|
|
145
|
+
const textureWidth = texture.width;
|
|
146
|
+
const textureHeight = texture.height;
|
|
147
|
+
|
|
148
|
+
const screenAspect = screenWidth / screenHeight;
|
|
149
|
+
const textureAspect = textureWidth / textureHeight;
|
|
150
|
+
|
|
151
|
+
let scaleX = 1;
|
|
152
|
+
let scaleY = 1;
|
|
153
|
+
if (screenAspect > textureAspect) {
|
|
154
|
+
scaleY = screenAspect / textureAspect;
|
|
155
|
+
} else {
|
|
156
|
+
scaleX = textureAspect / screenAspect;
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
this.shaderInputs.setProps({background: {scale: [scaleX, scaleY]}});
|
|
160
|
+
}
|
|
100
161
|
}
|
package/src/models/clip-space.ts
CHANGED
|
@@ -10,9 +10,9 @@ import {uid} from '../utils/uid';
|
|
|
10
10
|
|
|
11
11
|
const CLIPSPACE_VERTEX_SHADER_WGSL = /* wgsl */ `\
|
|
12
12
|
struct VertexInputs {
|
|
13
|
-
@location(0)
|
|
14
|
-
@location(1)
|
|
15
|
-
@location(2)
|
|
13
|
+
@location(0) clipSpacePositions: vec2<f32>,
|
|
14
|
+
@location(1) texCoords: vec2<f32>,
|
|
15
|
+
@location(2) coordinates: vec2<f32>
|
|
16
16
|
}
|
|
17
17
|
|
|
18
18
|
struct FragmentInputs {
|
|
@@ -25,10 +25,10 @@ struct FragmentInputs {
|
|
|
25
25
|
@vertex
|
|
26
26
|
fn vertexMain(inputs: VertexInputs) -> FragmentInputs {
|
|
27
27
|
var outputs: FragmentInputs;
|
|
28
|
-
outputs.Position = vec4(inputs.
|
|
29
|
-
outputs.position = inputs.
|
|
30
|
-
outputs.coordinate = inputs.
|
|
31
|
-
outputs.uv = inputs.
|
|
28
|
+
outputs.Position = vec4(inputs.clipSpacePositions, 0., 1.);
|
|
29
|
+
outputs.position = inputs.clipSpacePositions;
|
|
30
|
+
outputs.coordinate = inputs.coordinates;
|
|
31
|
+
outputs.uv = inputs.texCoords;
|
|
32
32
|
return outputs;
|
|
33
33
|
}
|
|
34
34
|
`;
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
// luma.gl
|
|
2
|
+
// SPDX-License-Identifier: MIT
|
|
3
|
+
// Copyright (c) vis.gl contributors
|
|
4
|
+
|
|
5
|
+
import {Device} from '@luma.gl/core';
|
|
6
|
+
import {ConeGeometry} from '../geometries/cone-geometry';
|
|
7
|
+
import {
|
|
8
|
+
BaseLightModel,
|
|
9
|
+
buildDirectionalLightInstanceData,
|
|
10
|
+
type DirectionalLightModelProps
|
|
11
|
+
} from './light-model-utils';
|
|
12
|
+
|
|
13
|
+
const DIRECTIONAL_LIGHT_GEOMETRY = new ConeGeometry({
|
|
14
|
+
cap: true,
|
|
15
|
+
nradial: 12,
|
|
16
|
+
nvertical: 1,
|
|
17
|
+
radius: 1
|
|
18
|
+
});
|
|
19
|
+
|
|
20
|
+
export type {DirectionalLightModelProps} from './light-model-utils';
|
|
21
|
+
|
|
22
|
+
export class DirectionalLightModel extends BaseLightModel<DirectionalLightModelProps> {
|
|
23
|
+
constructor(device: Device, props: DirectionalLightModelProps) {
|
|
24
|
+
super(device, props, {
|
|
25
|
+
anchorMode: 'apex',
|
|
26
|
+
buildInstanceData: buildDirectionalLightInstanceData,
|
|
27
|
+
geometry: DIRECTIONAL_LIGHT_GEOMETRY,
|
|
28
|
+
idPrefix: 'directional-light-model',
|
|
29
|
+
sizePropNames: ['directionalLightLength']
|
|
30
|
+
});
|
|
31
|
+
}
|
|
32
|
+
}
|