@jwc/jscad-raspberrypi 3.1.0 → 3.2.1
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/.vscode/settings.json +22 -0
- package/README.md +0 -12
- package/dist/examples/active-cooling-fan.jscad +2737 -0
- package/dist/examples/bplus.jscad +350 -334
- package/dist/examples/bplus3.jscad +350 -334
- package/dist/examples/camera-module-v1.jscad +350 -334
- package/dist/examples/camera-module-v2.jscad +350 -334
- package/dist/examples/example.jscad +366 -343
- package/dist/examples/hat-standoff.jscad +350 -334
- package/dist/examples/hat.jscad +350 -334
- package/dist/examples/hq_camera-module.jscad +2737 -0
- package/dist/examples/miniPiTFT.jscad +2737 -0
- package/dist/examples/pi-tft22.jscad +350 -334
- package/dist/examples/pi-tft24.jscad +350 -334
- package/dist/examples/pi4.jscad +2737 -0
- package/dist/examples/pi5.jscad +2747 -0
- package/dist/examples/spacer.jscad +350 -334
- package/dist/index.js +191 -102
- package/dist/v1compat.js +191 -102
- package/examples/active-cooling-fan.jscad +10 -0
- package/examples/example.jscad +16 -9
- package/examples/hq_camera-module.jscad +10 -0
- package/examples/miniPiTFT.jscad +10 -0
- package/examples/pi4.jscad +10 -0
- package/examples/pi5.jscad +20 -0
- package/gulpfile.js +4 -4
- package/package.json +31 -39
- package/src/active-cooling-fan.js +160 -0
- package/src/bplus.js +38 -2
- package/src/index.js +6 -9
package/examples/example.jscad
CHANGED
|
@@ -1,25 +1,32 @@
|
|
|
1
1
|
function main() {
|
|
2
2
|
util.init(CSG);
|
|
3
3
|
|
|
4
|
-
var pi = RaspberryPi.BPlus().align('mb', util.unitCube(), 'xy');
|
|
4
|
+
var pi = RaspberryPi.BPlus(5, 25).align('mb', util.unitCube(), 'xy');
|
|
5
5
|
|
|
6
6
|
/**
|
|
7
7
|
* Add a spacer to support the screen.
|
|
8
8
|
*/
|
|
9
|
-
pi.add(RaspberryPi.Spacer({}, pi.parts.mb), 'spacer');
|
|
9
|
+
// pi.add(RaspberryPi.Spacer({}, pi.parts.mb), 'spacer');
|
|
10
10
|
|
|
11
11
|
/**
|
|
12
12
|
* Add the screen and align it with the pi and the top of the spacer.
|
|
13
13
|
*/
|
|
14
14
|
pi.add(
|
|
15
|
-
RaspberryPi.
|
|
16
|
-
'mb',
|
|
17
|
-
pi.parts.
|
|
18
|
-
'z',
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
'
|
|
15
|
+
RaspberryPi.MiniPiTFT()
|
|
16
|
+
.snap('board', pi.parts.mb, 'x', 'inside-')
|
|
17
|
+
.snap('board', pi.parts.mb, 'y', 'inside+')
|
|
18
|
+
.snap('board', pi.parts.mb, 'z', 'outside-', 15),
|
|
19
|
+
'miniPiTFT',
|
|
20
|
+
true,
|
|
21
|
+
'miniPiTFT'
|
|
22
22
|
);
|
|
23
|
+
|
|
24
|
+
pi.add(RaspberryPi.ActiveCoolingFan()
|
|
25
|
+
.snap('board', pi.parts.mb, 'x', 'inside-')
|
|
26
|
+
.snap('board', pi.parts.mb, 'y', 'inside+')
|
|
27
|
+
.snap('board', pi.parts.cpu, 'z', 'outside-', 1)
|
|
28
|
+
.translate([0, -6, 0]), "activeCoolingFan", true, 'activeCoolingFan');
|
|
29
|
+
|
|
23
30
|
return pi.combineAll();
|
|
24
31
|
}
|
|
25
32
|
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
function main() {
|
|
2
|
+
util.init(CSG, { debug: 'jscadRPi*,jscadUtils:util' });
|
|
3
|
+
|
|
4
|
+
var pi = RaspberryPi.BPlus(5, 25);
|
|
5
|
+
|
|
6
|
+
pi.add(
|
|
7
|
+
RaspberryPi.PiTFT24({}, pi.parts.mb).snap(
|
|
8
|
+
'mb',
|
|
9
|
+
pi.parts.spacer,
|
|
10
|
+
'z',
|
|
11
|
+
'outside-'
|
|
12
|
+
),
|
|
13
|
+
'screen'
|
|
14
|
+
);
|
|
15
|
+
|
|
16
|
+
return pi.combineAll().Center();
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
// include:js
|
|
20
|
+
// endinject
|
package/gulpfile.js
CHANGED
|
@@ -13,8 +13,8 @@ var debug = require("gulp-debug");
|
|
|
13
13
|
var inject = require("gulp-inject");
|
|
14
14
|
var plumber = require("gulp-plumber");
|
|
15
15
|
var terser = require("gulp-terser");
|
|
16
|
-
var rollupResolve = require("rollup
|
|
17
|
-
var rollupBabel = require("rollup
|
|
16
|
+
var rollupResolve = require("@rollup/plugin-node-resolve");
|
|
17
|
+
var rollupBabel = require("@rollup/plugin-babel");
|
|
18
18
|
var jscadFiles = require("gulp-jscad-files");
|
|
19
19
|
var pkg = require("./package.json");
|
|
20
20
|
|
|
@@ -58,7 +58,7 @@ gulp.task("v1compat", function () {
|
|
|
58
58
|
.src("src/v1compat.js")
|
|
59
59
|
.pipe(plumber())
|
|
60
60
|
.pipe(
|
|
61
|
-
inject(gulp.src("dist/index.js").pipe(debug({ title: "injecting:" }))
|
|
61
|
+
inject(gulp.src("dist/index.js")/* .pipe(debug({ title: "injecting:" })) */, {
|
|
62
62
|
relative: true,
|
|
63
63
|
starttag: "// include:compat",
|
|
64
64
|
endtag: "// end:compat",
|
|
@@ -92,7 +92,7 @@ gulp.task("examples", function () {
|
|
|
92
92
|
},
|
|
93
93
|
})
|
|
94
94
|
)
|
|
95
|
-
.pipe(debug({ title: "injecting:" }))
|
|
95
|
+
/* .pipe(debug({ title: "injecting:" })) */,
|
|
96
96
|
{
|
|
97
97
|
// relative: true,
|
|
98
98
|
starttag: "// include:js",
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@jwc/jscad-raspberrypi",
|
|
3
|
-
"version": "3.1
|
|
3
|
+
"version": "3.2.1",
|
|
4
4
|
"description": "jscad parts library for RaspberryPi",
|
|
5
5
|
"main": "src/index.js",
|
|
6
6
|
"scripts": {
|
|
@@ -26,41 +26,10 @@
|
|
|
26
26
|
"url": "git@gitlab.com:johnwebbcole/jscad-raspberrypi.git"
|
|
27
27
|
},
|
|
28
28
|
"license": "ISC",
|
|
29
|
-
"devDependencies": {
|
|
30
|
-
"@babel/core": "^7.11.6",
|
|
31
|
-
"@babel/preset-env": "^7.11.5",
|
|
32
|
-
"@babel/register": "^7.11.5",
|
|
33
|
-
"@jwc/jscad-test-utils": "^1.0.6",
|
|
34
|
-
"@vuepress/plugin-active-header-links": "^1.6.0",
|
|
35
|
-
"@vuepress/plugin-google-analytics": "^1.6.0",
|
|
36
|
-
"ava": "^3.12.1",
|
|
37
|
-
"del": "^6.0.0",
|
|
38
|
-
"eslint": "^7.9.0",
|
|
39
|
-
"eslint-plugin-ava": "^11.0.0",
|
|
40
|
-
"fsevents": "^2.1.3",
|
|
41
|
-
"gulp": "^4.0.2",
|
|
42
|
-
"gulp-debug": "^4.0.0",
|
|
43
|
-
"gulp-eslint": "^6.0.0",
|
|
44
|
-
"gulp-inject": "^5.0.5",
|
|
45
|
-
"gulp-jscad-files": "^4.1.0",
|
|
46
|
-
"gulp-jsdoc-to-markdown": "^1.2.1",
|
|
47
|
-
"gulp-load-plugins": "^2.0.4",
|
|
48
|
-
"gulp-plumber": "^1.2.1",
|
|
49
|
-
"gulp-terser": "^1.4.0",
|
|
50
|
-
"gulp-util": "^3.0.7",
|
|
51
|
-
"gulp-watch": "^5.0.1",
|
|
52
|
-
"looks-same": "^7.2.3",
|
|
53
|
-
"nyc": "^15.1.0",
|
|
54
|
-
"rollup": "^2.28.2",
|
|
55
|
-
"rollup-plugin-babel": "^4.4.0",
|
|
56
|
-
"rollup-plugin-commonjs": "^10.0.2",
|
|
57
|
-
"rollup-plugin-node-resolve": "^5.2.0",
|
|
58
|
-
"vuepress-jsdoc": "^3.2.0"
|
|
59
|
-
},
|
|
60
29
|
"dependencies": {
|
|
61
|
-
"@jscad/csg": "
|
|
62
|
-
"@jscad/scad-api": "
|
|
63
|
-
"@jwc/jscad-utils": "
|
|
30
|
+
"@jscad/csg": "0.7.0",
|
|
31
|
+
"@jscad/scad-api": "0.5.1",
|
|
32
|
+
"@jwc/jscad-utils": "4.9.0"
|
|
64
33
|
},
|
|
65
34
|
"ava": {
|
|
66
35
|
"files": [
|
|
@@ -79,9 +48,32 @@
|
|
|
79
48
|
"@babel/preset-env"
|
|
80
49
|
]
|
|
81
50
|
},
|
|
82
|
-
"
|
|
83
|
-
|
|
84
|
-
"
|
|
85
|
-
"
|
|
51
|
+
"devDependencies": {
|
|
52
|
+
"@babel/core": "7.28.4",
|
|
53
|
+
"@babel/preset-env": "7.28.3",
|
|
54
|
+
"@babel/register": "7.28.3",
|
|
55
|
+
"@jwc/jscad-test-utils": "2.0.0",
|
|
56
|
+
"@rollup/plugin-babel": "6.0.4",
|
|
57
|
+
"@rollup/plugin-commonjs": "28.0.6",
|
|
58
|
+
"@rollup/plugin-node-resolve": "16.0.1",
|
|
59
|
+
"ava": "6.4.1",
|
|
60
|
+
"del": "8.0.1",
|
|
61
|
+
"eslint": "9.36.0",
|
|
62
|
+
"eslint-plugin-ava": "15.1.0",
|
|
63
|
+
"gulp": "5.0.1",
|
|
64
|
+
"gulp-debug": "5.0.1",
|
|
65
|
+
"gulp-eslint": "6.0.0",
|
|
66
|
+
"gulp-inject": "5.0.5",
|
|
67
|
+
"gulp-jscad-files": "4.1.0",
|
|
68
|
+
"gulp-jsdoc-to-markdown": "1.2.2",
|
|
69
|
+
"gulp-load-plugins": "2.0.8",
|
|
70
|
+
"gulp-plumber": "1.2.1",
|
|
71
|
+
"gulp-terser": "2.1.0",
|
|
72
|
+
"gulp-util": "3.0.8",
|
|
73
|
+
"gulp-watch": "5.0.1",
|
|
74
|
+
"looks-same": "10.0.1",
|
|
75
|
+
"nyc": "17.1.0",
|
|
76
|
+
"rollup": "4.52.2",
|
|
77
|
+
"vuepress-jsdoc": "5.0.0"
|
|
86
78
|
}
|
|
87
79
|
}
|
|
@@ -0,0 +1,160 @@
|
|
|
1
|
+
import { Group, parts as Parts } from '@jwc/jscad-utils';
|
|
2
|
+
|
|
3
|
+
function boardButton(name, board, midline) {
|
|
4
|
+
var g = Group(name);
|
|
5
|
+
g.add(
|
|
6
|
+
Parts.Cube([6.5, 6.5, 2])
|
|
7
|
+
.color('silver')
|
|
8
|
+
.snap(board, 'xy', 'inside-')
|
|
9
|
+
.snap(board, 'z', 'outside-')
|
|
10
|
+
.midlineTo('y', midline),
|
|
11
|
+
'base'
|
|
12
|
+
);
|
|
13
|
+
g.add(
|
|
14
|
+
Parts.Cylinder(3, 1)
|
|
15
|
+
.color('black')
|
|
16
|
+
.align(g.parts.base, 'xy')
|
|
17
|
+
.snap(g.parts.base, 'z', 'outside-'),
|
|
18
|
+
'push'
|
|
19
|
+
);
|
|
20
|
+
|
|
21
|
+
return g;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
function buttonCap(name, button) {
|
|
25
|
+
var g = Group(name);
|
|
26
|
+
g.add(
|
|
27
|
+
Parts.Cylinder(4, 3)
|
|
28
|
+
.fillet(1, 'z+')
|
|
29
|
+
// .translate([0, 0, 1])
|
|
30
|
+
.union(
|
|
31
|
+
Parts.Cylinder(6, 0.5).bisect('x', -0.5).parts.negative //.translate([0, 0, 1])
|
|
32
|
+
)
|
|
33
|
+
|
|
34
|
+
.color('blue')
|
|
35
|
+
.align(button, 'xy')
|
|
36
|
+
.snap(button, 'z', 'outside-'),
|
|
37
|
+
'cap'
|
|
38
|
+
);
|
|
39
|
+
|
|
40
|
+
g.add(
|
|
41
|
+
Parts.Cylinder(7, 0.75)
|
|
42
|
+
.bisect('x', -0.5)
|
|
43
|
+
.parts.negative.color('green')
|
|
44
|
+
.union(Parts.Cylinder(4.5, 2).color('yellow').translate([0, 0, 0]))
|
|
45
|
+
.align(g.parts.cap, 'xy')
|
|
46
|
+
.snap(g.parts.cap, 'z', 'inside-'),
|
|
47
|
+
// .color('red'),
|
|
48
|
+
'clearance',
|
|
49
|
+
true
|
|
50
|
+
);
|
|
51
|
+
return g;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
/**
|
|
55
|
+
* Returns an Adafruit miniPiTFT Hat with buttons.
|
|
56
|
+
*
|
|
57
|
+
* Default parts: board,screen,display,button1,button2
|
|
58
|
+
*
|
|
59
|
+
* All parts: board,screen,display,display-clearance,button1,button1-base,button1-push,button2,button2-base,button2-push,buttonCap1,buttonCap1-cap,buttonCap1-clearance,buttonCap2,buttonCap2-cap,buttonCap2-clearance,button-connector,button-connector-clearance,standoff
|
|
60
|
+
*
|
|
61
|
+
* 
|
|
62
|
+
* @function activeCoolingFan
|
|
63
|
+
*/
|
|
64
|
+
export default function activeCoolingFan() {
|
|
65
|
+
var g = Group('ActiveCoolingFan');
|
|
66
|
+
|
|
67
|
+
var board = Parts.Cube([64, 42.5, 1])
|
|
68
|
+
.subtract(Parts.Cube([64 - 58, 31.3, 1])
|
|
69
|
+
.translate([0, 42.5 - 31.3, 0]).color('orange')
|
|
70
|
+
.union(Parts.Cube([28, 42.5 - 31.3, 1])
|
|
71
|
+
.translate([64 - 28, 0, 0]).color('blue')))
|
|
72
|
+
.union(Parts.Cube([19, 40, 9]).translate([64 - 58, 2.5, 0])).color('silver')
|
|
73
|
+
.union(Parts.Cube([30, 30, 9]).translate([(64 - 58) + 19, 42.5 - 31.3, 0]).color('black'))
|
|
74
|
+
;
|
|
75
|
+
|
|
76
|
+
g.add(board, 'board');
|
|
77
|
+
|
|
78
|
+
// g.add(
|
|
79
|
+
// Parts.Cube([31, 18, 2])
|
|
80
|
+
// .color('white')
|
|
81
|
+
// .snap(board, 'x', 'inside+')
|
|
82
|
+
// .snap(board, 'y', 'inside-')
|
|
83
|
+
// .snap(board, 'z', 'outside-'),
|
|
84
|
+
// 'screen'
|
|
85
|
+
// );
|
|
86
|
+
|
|
87
|
+
// g.add(
|
|
88
|
+
// Parts.Cube([util.inch(0.98), util.inch(0.58), 2.01])
|
|
89
|
+
// .color('black')
|
|
90
|
+
// .snap(board, 'x', 'inside-', util.inch(0.34))
|
|
91
|
+
// .snap(board, 'y', 'inside+', -util.inch(0.32))
|
|
92
|
+
// .snap(board, 'z', 'outside-'),
|
|
93
|
+
// 'display'
|
|
94
|
+
// );
|
|
95
|
+
|
|
96
|
+
// g.add(
|
|
97
|
+
// g.parts.display
|
|
98
|
+
// .stretch('z', 5)
|
|
99
|
+
// .chamfer(-5, 'z+')
|
|
100
|
+
// .align(g.parts.display, 'xy')
|
|
101
|
+
// .color('red'),
|
|
102
|
+
// 'display-clearance',
|
|
103
|
+
// true
|
|
104
|
+
// );
|
|
105
|
+
|
|
106
|
+
// g.add(
|
|
107
|
+
// boardButton('button1', board, util.inch(0.53)),
|
|
108
|
+
// 'button1',
|
|
109
|
+
// false,
|
|
110
|
+
// 'button1-'
|
|
111
|
+
// );
|
|
112
|
+
|
|
113
|
+
// g.add(
|
|
114
|
+
// boardButton('button2', board, util.inch(0.18)),
|
|
115
|
+
// 'button2',
|
|
116
|
+
// false,
|
|
117
|
+
// 'button2-'
|
|
118
|
+
// );
|
|
119
|
+
|
|
120
|
+
// g.add(
|
|
121
|
+
// buttonCap('buttonCap1', g.parts.button1),
|
|
122
|
+
// 'buttonCap1',
|
|
123
|
+
// true,
|
|
124
|
+
// 'buttonCap1-'
|
|
125
|
+
// );
|
|
126
|
+
// g.add(
|
|
127
|
+
// buttonCap('buttonCap2', g.parts.button2),
|
|
128
|
+
// 'buttonCap2',
|
|
129
|
+
// true,
|
|
130
|
+
// 'buttonCap2-'
|
|
131
|
+
// );
|
|
132
|
+
|
|
133
|
+
// g.add(
|
|
134
|
+
// Parts.Cube([1, 5, 0.5])
|
|
135
|
+
// .color('blue')
|
|
136
|
+
// .align(g.parts.buttonCap1, 'x')
|
|
137
|
+
// .snap(g.parts.buttonCap1, 'y', 'outside+', 1)
|
|
138
|
+
// .snap(g.parts.buttonCap1, 'z', 'inside-'),
|
|
139
|
+
// 'button-connector',
|
|
140
|
+
// true
|
|
141
|
+
// );
|
|
142
|
+
|
|
143
|
+
// g.add(
|
|
144
|
+
// g.parts['button-connector'].enlarge(0.5, 0.5, 0.5).color('red'),
|
|
145
|
+
// 'button-connector-clearance',
|
|
146
|
+
// true
|
|
147
|
+
// );
|
|
148
|
+
|
|
149
|
+
// g.add(
|
|
150
|
+
// Parts.Tube(7, 3, 7.5)
|
|
151
|
+
// .snap(board, 'x', 'inside-')
|
|
152
|
+
// .snap(board, 'y', 'inside+')
|
|
153
|
+
// .snap(board, 'z', 'outside+')
|
|
154
|
+
// .color('yellow'),
|
|
155
|
+
// 'standoff',
|
|
156
|
+
// true
|
|
157
|
+
// );
|
|
158
|
+
|
|
159
|
+
return g;
|
|
160
|
+
}
|
package/src/bplus.js
CHANGED
|
@@ -199,8 +199,23 @@ function pi4(group, clearance, mb) {
|
|
|
199
199
|
|
|
200
200
|
function pi5(group, clearance, mb) {
|
|
201
201
|
/**
|
|
202
|
-
|
|
203
|
-
|
|
202
|
+
* Model 5
|
|
203
|
+
*/
|
|
204
|
+
|
|
205
|
+
group.add(
|
|
206
|
+
Parts.Cube([12, 12, 3]).color('lightgray')
|
|
207
|
+
.translate([24, 15, 2]),
|
|
208
|
+
'cpu'
|
|
209
|
+
);
|
|
210
|
+
|
|
211
|
+
group.add(
|
|
212
|
+
Parts.Cube([12, 12, clearance]).color('red')
|
|
213
|
+
.translate([24, 15, 2]),
|
|
214
|
+
'clearance-cpu',
|
|
215
|
+
true,
|
|
216
|
+
'clearance'
|
|
217
|
+
);
|
|
218
|
+
|
|
204
219
|
group.add(
|
|
205
220
|
RightSide(Parts.Cube([21.5, 16.5, 13.5]).color('lightgray'), mb)
|
|
206
221
|
.translate([2, 0, 0])
|
|
@@ -386,6 +401,8 @@ function pi5(group, clearance, mb) {
|
|
|
386
401
|
.color('red'),
|
|
387
402
|
'powerled'
|
|
388
403
|
);
|
|
404
|
+
|
|
405
|
+
|
|
389
406
|
}
|
|
390
407
|
|
|
391
408
|
/**
|
|
@@ -574,5 +591,24 @@ export default function BPlus(model = 2, options) {
|
|
|
574
591
|
group.add(group.holes[2], 'hole3', true);
|
|
575
592
|
group.add(group.holes[3], 'hole4', true);
|
|
576
593
|
|
|
594
|
+
/** Add fan mounting holes for PI 5 */
|
|
595
|
+
if (model >= 5) {
|
|
596
|
+
group.holes.push(RPiParts.MountingHole(3.25, 8)
|
|
597
|
+
.snap(mb, 'xy', 'inside-')
|
|
598
|
+
.align(mb, 'z')
|
|
599
|
+
.midlineTo('x', 3.5)
|
|
600
|
+
.midlineTo('y', 6 + 3.5));
|
|
601
|
+
|
|
602
|
+
group.add(group.holes[4], 'fan1hole', true);
|
|
603
|
+
|
|
604
|
+
group.holes.push(RPiParts.MountingHole(3.25, 8)
|
|
605
|
+
.snap(mb, 'xy', 'inside-')
|
|
606
|
+
.align(mb, 'z')
|
|
607
|
+
.midlineTo('x', 3.5 + 58)
|
|
608
|
+
.midlineTo('y', 52.5 - 6));
|
|
609
|
+
|
|
610
|
+
group.add(group.holes[5], 'fan2hole', true);
|
|
611
|
+
}
|
|
612
|
+
|
|
577
613
|
return group;
|
|
578
614
|
}
|
package/src/index.js
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import ActiveCoolingFan from './active-cooling-fan';
|
|
1
2
|
import BPlus from './bplus';
|
|
2
3
|
import CameraModuleV1 from './camera-module-v1';
|
|
3
4
|
import CameraModuleV2 from './camera-module-v2';
|
|
@@ -10,14 +11,10 @@ import PiTFT24 from './pi-tft24';
|
|
|
10
11
|
import Spacer from './spacer';
|
|
11
12
|
|
|
12
13
|
export {
|
|
13
|
-
BPlus,
|
|
14
|
+
ActiveCoolingFan, BPlus,
|
|
14
15
|
CameraModuleV1,
|
|
15
|
-
CameraModuleV2,
|
|
16
|
-
HQCameraModule,
|
|
17
|
-
|
|
18
|
-
HatStandoff,
|
|
19
|
-
PiTFT22,
|
|
20
|
-
PiTFT24,
|
|
21
|
-
MiniPiTFT,
|
|
22
|
-
Spacer,
|
|
16
|
+
CameraModuleV2, Hat,
|
|
17
|
+
HatStandoff, HQCameraModule, MiniPiTFT, PiTFT22,
|
|
18
|
+
PiTFT24, Spacer
|
|
23
19
|
};
|
|
20
|
+
|