@@ -13,6 +13,8 @@ const baseDir = path.resolve(scriptDir, '..');
13
13
14
14
const jsSrcDir = path . resolve ( baseDir , 'src/' ) ;
15
15
const pySrcDir = path . resolve ( baseDir , '..' , 'pythreejs' ) ;
16
+ const cppSrcDir = path . resolve ( baseDir , '..' , 'xthreejs/xthreejs' ) ;
17
+ const cmakeSrcDir = path . resolve ( baseDir , '..' , 'xthreejs' ) ;
16
18
const docSrcDir = path . resolve ( baseDir , '..' , 'docs' , 'source' , 'api' ) ;
17
19
const templateDir = path . resolve ( scriptDir , 'templates' ) ;
18
20
@@ -99,6 +101,9 @@ const jsWrapperTemplate = compileTemplate('js_wrapper');
99
101
const jsIndexTemplate = compileTemplate ( 'js_index' ) ;
100
102
const pyWrapperTemplate = compileTemplate ( 'py_wrapper' ) ;
101
103
const pyTopLevelInitTemplate = compileTemplate ( 'py_top_level_init' ) ;
104
+ const cppWrapperTemplate = compileTemplate ( 'cpp_wrapper' ) ;
105
+ const headerCppTemplate = compileTemplate ( 'cppheader_wrapper' ) ;
106
+ const cmakeListsTemplate = compileTemplate ( 'cmake_wrapper' ) ;
102
107
const docTemplate = compileTemplate ( 'autodoc' ) ;
103
108
const docIndexTemplate = compileTemplate ( 'autodoc_index' ) ;
104
109
@@ -227,6 +232,10 @@ function relativePathToPythonImportPath(relativePath) {
227
232
return result ;
228
233
}
229
234
235
+ function camelCaseToUnderscore ( str ) {
236
+ return str . replace ( / ( [ a - z A - Z ] ) (? = [ A - Z ] ) / g, '$1_' ) . toLowerCase ( )
237
+ }
238
+
230
239
// Execute a function for each match to a glob query
231
240
//
232
241
// Parameters:
@@ -1059,6 +1068,322 @@ function createTopLevelPythonModuleFile() {
1059
1068
1060
1069
}
1061
1070
1071
+ //
1072
+ // Cpp wrapper writer
1073
+ //
1074
+
1075
+ class CppWrapper {
1076
+
1077
+ constructor ( modulePath , className ) {
1078
+
1079
+ this . modulePath = modulePath ;
1080
+ this . dirRelativePath = path . dirname ( modulePath ) ;
1081
+ this . destDirAbsolutePath = path . resolve ( cppSrcDir , this . dirRelativePath ) ;
1082
+ this . destDirRelativeToBase = path . relative ( this . destDirAbsolutePath , cppSrcDir ) ;
1083
+
1084
+ this . basename = path . basename ( modulePath , '.js' ) ;
1085
+
1086
+ if ( className ) {
1087
+ this . className = className ;
1088
+ } else {
1089
+ this . className = this . basename . replace ( / \. / g, '_' ) ;
1090
+ const extraDefines = getExtraDefines ( this . className ) ;
1091
+ extraDefines . forEach ( function ( extraClassName ) {
1092
+ createCppWrapper ( modulePath , extraClassName ) ;
1093
+ } ) ;
1094
+ }
1095
+
1096
+ this . cppDestPath = path . resolve ( this . destDirAbsolutePath , this . getUnderscoreRep ( this . className ) + '.hpp' ) ;
1097
+ this . cppAutoDestPath = path . resolve ( this . destDirAbsolutePath , this . getUnderscoreRep ( this . className ) + '_' + AUTOGEN_EXT + '.hpp' ) ;
1098
+
1099
+ this . cppBaseRelativePath = path . relative ( this . destDirAbsolutePath , cppSrcDir ) ;
1100
+ //this.cppBaseRelativePath = relativePathToPythonImportPath(this.cppBaseRelativePath);
1101
+
1102
+ // check if manual file exists
1103
+ this . hasOverride = fse . existsSync ( this . cppDestPath ) ;
1104
+
1105
+ this . isCustom = CUSTOM_CLASSES . indexOf ( modulePath ) !== - 1 ;
1106
+
1107
+ this . hasParameters = false ;
1108
+
1109
+ this . config = getClassConfig ( this . className ) ;
1110
+
1111
+ this . processSuperClass ( ) ;
1112
+ this . processDependencies ( ) ;
1113
+ this . processProperties ( ) ;
1114
+
1115
+ // Template and context
1116
+ this . context = {
1117
+ now : new Date ( ) ,
1118
+ generatorScriptName : path . basename ( __filename ) ,
1119
+ threejs_docs_url : this . docsUrl ,
1120
+ cpp_base_relative_path : this . cppBaseRelativePath ,
1121
+ constructor : {
1122
+ args : this . constructorArgs ,
1123
+ hasParameters : this . hasParameters ,
1124
+ } ,
1125
+
1126
+ className : this . getUnderscoreRep ( this . className , false ) ,
1127
+ xclassName : this . getUnderscoreRep ( this . className ) ,
1128
+ header : 'XTHREE_' + this . getUnderscoreRep ( this . className , false ) . toUpperCase ( ) + '_HPP' ,
1129
+ modelName : this . className + 'Model' ,
1130
+ superClass : this . superClass ,
1131
+ properties : this . properties ,
1132
+ dependencies : this . dependencies ,
1133
+ hasOverride : this . hasOverride ,
1134
+ isCustom : this . isCustom ,
1135
+ } ;
1136
+
1137
+ if ( this . className == 'ShapeGeometry' )
1138
+ {
1139
+ console . log ( this . context ) ;
1140
+ }
1141
+ if ( this . className == 'ParametricGeometry' )
1142
+ {
1143
+ console . log ( this . context ) ;
1144
+ }
1145
+ // Render template
1146
+ this . output = cppWrapperTemplate ( this . context ) ;
1147
+
1148
+ }
1149
+
1150
+ getUnderscoreRep ( className , with_x = true ) {
1151
+ const REPLACE = {
1152
+ 'web_g_l' : 'webgl'
1153
+ } ;
1154
+
1155
+ let class_name ;
1156
+ _ . mapObject ( REPLACE , function ( new_str , old_str ) {
1157
+ class_name = camelCaseToUnderscore ( className ) ;
1158
+ class_name = class_name . replace ( old_str , new_str ) ;
1159
+ } ) ;
1160
+ return ( with_x ) ? 'x' + class_name : class_name ;
1161
+ }
1162
+
1163
+ getRequireInfoFromClassDescriptor ( classDescriptor ) {
1164
+
1165
+ const result = { } ;
1166
+
1167
+ if ( typeof classDescriptor === 'string' ) {
1168
+
1169
+ if ( classDescriptor in classConfigs ) {
1170
+ const config = getClassConfig ( classDescriptor ) ;
1171
+ result . className = classDescriptor ;
1172
+ result . relativePath = config . relativePath ;
1173
+ let res = result . relativePath . split ( "/" ) ;
1174
+ res [ res . length - 1 ] = this . getUnderscoreRep ( res [ res . length - 1 ] ) ;
1175
+ result . relativePath = res . join ( "/" ) ;
1176
+ } else {
1177
+ result . className = path . basename ( classDescriptor , '.js' ) ;
1178
+ result . relativePath = classDescriptor ;
1179
+ }
1180
+
1181
+ } else {
1182
+ throw new Error ( 'invalid classDescriptor: ' + classDescriptor ) ;
1183
+ }
1184
+
1185
+ // get path of dependency relative to module dir
1186
+ if ( result . className == 'Three' ) {
1187
+ result . relativePath = './base/xthree' ;
1188
+ } ;
1189
+ result . absolutePath = path . resolve ( cppSrcDir , result . relativePath ) ;
1190
+
1191
+ if ( ! fse . existsSync ( result . absolutePath + '.hpp' ) ) {
1192
+ result . absolutePath += '_' + AUTOGEN_EXT ;
1193
+ }
1194
+
1195
+ result . requirePath = path . relative ( this . destDirAbsolutePath , result . absolutePath ) ;
1196
+ //result.cppRelativePath = relativePathToPythonImportPath(result.requirePath);
1197
+ result . cppRelativePath = result . requirePath ;
1198
+
1199
+ return result ;
1200
+
1201
+ }
1202
+
1203
+ processSuperClass ( ) {
1204
+
1205
+ this . superClass = this . getRequireInfoFromClassDescriptor ( this . config . superClass ) ;
1206
+
1207
+ if ( this . superClass . className === 'Three' ) {
1208
+ this . superClass . className = 'three_widget' ;
1209
+ }
1210
+ this . superClass . className = this . getUnderscoreRep ( this . superClass . className , false ) ;
1211
+ this . superClass . xclassName = this . getUnderscoreRep ( this . superClass . className ) ;
1212
+ }
1213
+
1214
+ processDependencies ( ) {
1215
+
1216
+ const dependencies = { } ;
1217
+
1218
+ // process explicitly listed dependencies
1219
+ _ . reduce ( this . config . dependencies , function ( result , depName ) {
1220
+
1221
+ result [ depName ] = this . getRequireInfoFromClassDescriptor ( depName ) ;
1222
+ return result ;
1223
+
1224
+ } , dependencies , this ) ;
1225
+
1226
+ // infer dependencies from any properties that reference other Three types
1227
+ _ . reduce ( this . config . properties , function ( result , prop ) {
1228
+
1229
+ if ( prop instanceof Types . ThreeType || prop instanceof Types . InitializedThreeType ||
1230
+ prop instanceof Types . ThreeTypeArray || prop instanceof Types . ThreeTypeDict ) {
1231
+ if ( prop . typeName !== 'this' ) {
1232
+ if ( typeof prop . typeName === 'string' ) {
1233
+ let typeName = prop . typeName || './base/xthree.hpp' ;
1234
+ result [ typeName ] = this . getRequireInfoFromClassDescriptor ( typeName ) ;
1235
+ if ( result [ typeName ] . className === 'Three' ) {
1236
+ result [ typeName ] . className = 'ThreeWidget' ;
1237
+ }
1238
+ } else if ( prop . typeName instanceof Array ) {
1239
+ prop . typeName . forEach ( function ( typeName ) {
1240
+ result [ typeName ] = this . getRequireInfoFromClassDescriptor ( typeName ) ;
1241
+ } , this ) ;
1242
+ }
1243
+ }
1244
+ }
1245
+ return result ;
1246
+
1247
+ } , dependencies , this ) ;
1248
+
1249
+ this . dependencies = dependencies ;
1250
+
1251
+ }
1252
+
1253
+ processProperties ( ) {
1254
+
1255
+ this . properties = _ . mapObject ( this . config . properties , function ( prop , key ) {
1256
+ if ( prop . getCppProperty ( key ) !== 'undefined' ) {
1257
+ return {
1258
+ xproperty : prop . getCppProperty ( key ) ,
1259
+ defaultJson : prop . getCppDefaultValue ( ) ,
1260
+ } ;
1261
+ }
1262
+ } , this ) ;
1263
+
1264
+ }
1265
+
1266
+
1267
+ getOutputFilename ( ) {
1268
+ return this . cppAutoDestPath ;
1269
+ }
1270
+ }
1271
+
1272
+ function createCppWrapper ( modulePath , className ) {
1273
+
1274
+ let wrapper ;
1275
+ try {
1276
+ wrapper = new CppWrapper ( modulePath , className ) ;
1277
+ } catch ( e ) {
1278
+ console . log ( e ) ;
1279
+ console . log ( 'skipping: ' + modulePath + ( className ? ':' + className : '' ) ) ;
1280
+ return Promise . resolve ( false ) ;
1281
+ }
1282
+ let fname = wrapper . getOutputFilename ( ) ;
1283
+ let cppPromise = fse . outputFile ( fname , wrapper . output ) ;
1284
+
1285
+ // Also output documentation for the Python API
1286
+ //let docfname = wrapper.getDocFilename();
1287
+ //console.log(docfname);
1288
+ //let docPromise = Promise.resolve();
1289
+ //let docPromise = fse.outputFile(docfname, wrapper.getDocOutput());
1290
+
1291
+ return Promise . all ( [ cppPromise ] ) ; //, docPromise]);
1292
+ }
1293
+
1294
+ function writeCMakeLists ( ) {
1295
+
1296
+ console . log ( 'Writing CMakeLists...' ) ;
1297
+
1298
+ // Regexp's
1299
+ const RE_AUTOGEN_EXT = / \. a u t o g e n \. h p p $ / ;
1300
+
1301
+ const excludes = [ 'build' , 'CMakeLists.txt' ] ;
1302
+
1303
+ const allFilesSync = ( dir , fileList = [ ] ) => {
1304
+ fse . readdirSync ( dir ) . forEach ( file => {
1305
+ const filePath = path . join ( dir , file )
1306
+
1307
+ const shouldExclude = _ . any ( excludes , function ( testPattern ) {
1308
+ if ( testPattern instanceof RegExp ) {
1309
+ return testPattern . test ( file ) ;
1310
+ } else if ( typeof testPattern === 'string' ) {
1311
+ return testPattern === file ;
1312
+ }
1313
+ } ) ;
1314
+ if ( ! shouldExclude ) {
1315
+ if ( fse . statSync ( filePath ) . isDirectory ( ) ) {
1316
+ allFilesSync ( filePath , fileList ) ;
1317
+ }
1318
+ else {
1319
+ fileList . push ( path . relative ( cmakeSrcDir , filePath ) ) ;
1320
+ }
1321
+ }
1322
+ } )
1323
+ return fileList
1324
+ }
1325
+
1326
+ var fileList = [ ] ;
1327
+ allFilesSync ( cmakeSrcDir , fileList ) ;
1328
+ const context = {
1329
+ hppfiles : fileList
1330
+ } ;
1331
+ const output = cmakeListsTemplate ( context ) ;
1332
+ const outputPath = path . join ( cmakeSrcDir , 'CMakeLists.txt' ) ;
1333
+
1334
+ return fse . outputFile ( outputPath , output ) ;
1335
+ }
1336
+
1337
+ function writeHeaderCppFiles ( ) {
1338
+
1339
+ console . log ( 'Writing HeaderCppFiles...' ) ;
1340
+
1341
+ // Regexp's
1342
+ const RE_AUTOGEN_EXT = / \. a u t o g e n \. h p p $ / ;
1343
+
1344
+ const excludes = [ 'build' , 'CMakeLists.txt' ] ;
1345
+
1346
+ const allFilesSync = ( dir , fileList = [ ] ) => {
1347
+ fse . readdirSync ( dir ) . forEach ( file => {
1348
+ const filePath = path . join ( dir , file )
1349
+
1350
+ const shouldExclude = _ . any ( excludes , function ( testPattern ) {
1351
+ if ( testPattern instanceof RegExp ) {
1352
+ return testPattern . test ( file ) ;
1353
+ } else if ( typeof testPattern === 'string' ) {
1354
+ return testPattern === file ;
1355
+ }
1356
+ } ) ;
1357
+ if ( ! shouldExclude ) {
1358
+ if ( fse . statSync ( filePath ) . isDirectory ( ) ) {
1359
+ allFilesSync ( filePath , fileList ) ;
1360
+ }
1361
+ else {
1362
+ fileList . push ( path . relative ( cppSrcDir , filePath ) ) ;
1363
+ }
1364
+ }
1365
+ } )
1366
+ return fileList
1367
+ }
1368
+
1369
+ fse . readdirSync ( cppSrcDir ) . forEach ( dir => {
1370
+ const filePath = path . join ( cppSrcDir , dir ) ;
1371
+ if ( fse . statSync ( filePath ) . isDirectory ( ) ) {
1372
+ var fileList = [ ] ;
1373
+ allFilesSync ( filePath , fileList ) ;
1374
+ const context = {
1375
+ dir : dir ,
1376
+ header : dir . toUpperCase ( ) ,
1377
+ hppfiles : fileList
1378
+ } ;
1379
+
1380
+ const output = headerCppTemplate ( context ) ;
1381
+ const outputPath = path . join ( cppSrcDir , 'x' + dir + '.hpp' ) ;
1382
+
1383
+ return fse . outputFile ( outputPath , output ) ;
1384
+ }
1385
+ } ) ;
1386
+ }
1062
1387
1063
1388
function createJavascriptFiles ( ) {
1064
1389
return mapPromiseFnOverThreeModules ( createJavascriptWrapper )
@@ -1106,13 +1431,37 @@ function createPythonFiles() {
1106
1431
1107
1432
}
1108
1433
1434
+ function createCppFiles ( ) {
1435
+
1436
+ // Prevent python file generation when outside dir (e.g. npm install in dependent)
1437
+ if ( ! fse . existsSync ( cppSrcDir ) ) {
1438
+ return Promise . resolve ( ) ;
1439
+ }
1440
+
1441
+ return mapPromiseFnOverThreeModules (
1442
+ function ( relativePath ) {
1443
+ return createCppWrapper ( relativePath ) ;
1444
+ } )
1445
+ . then ( function ( ) {
1446
+ return mapPromiseFnOverFileList ( CUSTOM_CLASSES , function ( relativePath ) {
1447
+ return createCppWrapper ( relativePath )
1448
+ } ) ;
1449
+ } )
1450
+ . then ( function ( ) {
1451
+ writeHeaderCppFiles ( ) ;
1452
+ } )
1453
+ . then ( function ( ) {
1454
+ return writeCMakeLists ( ) ;
1455
+ } ) ;
1456
+ }
1109
1457
1110
1458
1111
1459
function generateFiles ( ) {
1112
1460
1113
1461
return Promise . all ( [
1114
1462
createJavascriptFiles ( ) ,
1115
1463
createPythonFiles ( ) ,
1464
+ //createCppFiles(),
1116
1465
] ) ;
1117
1466
1118
1467
}
0 commit comments