@@ -13,6 +13,8 @@ const baseDir = path.resolve(scriptDir, '..');
1313
1414const jsSrcDir = path . resolve ( baseDir , 'src/' ) ;
1515const pySrcDir = path . resolve ( baseDir , '..' , 'pythreejs' ) ;
16+ const cppSrcDir = path . resolve ( baseDir , '..' , 'xthreejs/xthreejs' ) ;
17+ const cmakeSrcDir = path . resolve ( baseDir , '..' , 'xthreejs' ) ;
1618const docSrcDir = path . resolve ( baseDir , '..' , 'docs' , 'source' , 'api' ) ;
1719const templateDir = path . resolve ( scriptDir , 'templates' ) ;
1820
@@ -99,6 +101,9 @@ const jsWrapperTemplate = compileTemplate('js_wrapper');
99101const jsIndexTemplate = compileTemplate ( 'js_index' ) ;
100102const pyWrapperTemplate = compileTemplate ( 'py_wrapper' ) ;
101103const pyTopLevelInitTemplate = compileTemplate ( 'py_top_level_init' ) ;
104+ const cppWrapperTemplate = compileTemplate ( 'cpp_wrapper' ) ;
105+ const headerCppTemplate = compileTemplate ( 'cppheader_wrapper' ) ;
106+ const cmakeListsTemplate = compileTemplate ( 'cmake_wrapper' ) ;
102107const docTemplate = compileTemplate ( 'autodoc' ) ;
103108const docIndexTemplate = compileTemplate ( 'autodoc_index' ) ;
104109
@@ -227,6 +232,10 @@ function relativePathToPythonImportPath(relativePath) {
227232 return result ;
228233}
229234
235+ function camelCaseToUnderscore ( str ) {
236+ return str . replace ( / ( [ a - z A - Z ] ) (? = [ A - Z ] ) / g, '$1_' ) . toLowerCase ( )
237+ }
238+
230239// Execute a function for each match to a glob query
231240//
232241// Parameters:
@@ -1059,6 +1068,322 @@ function createTopLevelPythonModuleFile() {
10591068
10601069}
10611070
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+ }
10621387
10631388function createJavascriptFiles ( ) {
10641389 return mapPromiseFnOverThreeModules ( createJavascriptWrapper )
@@ -1106,13 +1431,37 @@ function createPythonFiles() {
11061431
11071432}
11081433
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+ }
11091457
11101458
11111459function generateFiles ( ) {
11121460
11131461 return Promise . all ( [
11141462 createJavascriptFiles ( ) ,
11151463 createPythonFiles ( ) ,
1464+ //createCppFiles(),
11161465 ] ) ;
11171466
11181467}
0 commit comments