Embedded libraries for all custom components
Added DPS internal libraries to file structure to improve portability
This commit is contained in:
parent
be9fa5484e
commit
72872b8c9e
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
|
@ -1,7 +1,7 @@
|
|||
ISO-10303-21;
|
||||
HEADER;
|
||||
FILE_DESCRIPTION(('STEP AP214'),'1');
|
||||
FILE_NAME('CR_LHM3H_VIS','2024-01-10T04:05:19',(''),(''),'','','');
|
||||
FILE_NAME('CR_M384A_VIS','2023-11-05T09:11:12',(''),(''),'','','');
|
||||
FILE_SCHEMA(('AUTOMOTIVE_DESIGN'));
|
||||
ENDSEC;
|
||||
DATA;
|
||||
|
@ -229,13 +229,13 @@ DATA;
|
|||
#230=DIRECTION('',(0.,0.,1.));
|
||||
#231=DIRECTION('',(0.,1.,0.));
|
||||
#232=CARTESIAN_POINT('',(0.,0.,0.));
|
||||
#233=CARTESIAN_POINT('',(-1.443,0.0,0.813));
|
||||
#234=CARTESIAN_POINT('',(-1.533,0.0,0.815));
|
||||
#235=CARTESIAN_POINT('',(-1.443,0.0,0.815));
|
||||
#236=CARTESIAN_POINT('',(-1.533,0.0,0.813));
|
||||
#237=CARTESIAN_POINT('',(-1.443,0.0,0.813));
|
||||
#238=CARTESIAN_POINT('',(-1.443,0.0,0.813));
|
||||
#239=CARTESIAN_POINT('',(-1.443,0.0,0.815));
|
||||
#233=CARTESIAN_POINT('',(-1.443,0.0,1.143));
|
||||
#234=CARTESIAN_POINT('',(-1.533,0.0,1.146));
|
||||
#235=CARTESIAN_POINT('',(-1.443,0.0,1.146));
|
||||
#236=CARTESIAN_POINT('',(-1.533,0.0,1.143));
|
||||
#237=CARTESIAN_POINT('',(-1.443,0.0,1.143));
|
||||
#238=CARTESIAN_POINT('',(-1.443,0.0,1.143));
|
||||
#239=CARTESIAN_POINT('',(-1.443,0.0,1.146));
|
||||
#240=STYLED_ITEM('color',(#52),#190);
|
||||
#241=STYLED_ITEM('color',(#52),#191);
|
||||
#242=STYLED_ITEM('color',(#52),#192);
|
||||
|
@ -245,37 +245,37 @@ DATA;
|
|||
#247=ADVANCED_FACE('',(#253),#248,.T.);
|
||||
#248=PLANE('',#249);
|
||||
#249=AXIS2_PLACEMENT_3D('',#250,#251,#252);
|
||||
#250=CARTESIAN_POINT('',(-1.704,-0.993,0.813));
|
||||
#250=CARTESIAN_POINT('',(-1.648,-0.937,1.143));
|
||||
#251=DIRECTION('',(0.0,0.0,1.0));
|
||||
#252=DIRECTION('',(0.,1.,0.));
|
||||
#253=FACE_OUTER_BOUND('',#254,.T.);
|
||||
#254=EDGE_LOOP('',(#255,#265,#275,#285));
|
||||
#258=CARTESIAN_POINT('',(1.704,-0.993,0.813));
|
||||
#258=CARTESIAN_POINT('',(1.648,-0.937,1.143));
|
||||
#257=VERTEX_POINT('',#258);
|
||||
#260=CARTESIAN_POINT('',(-1.704,-0.993,0.813));
|
||||
#260=CARTESIAN_POINT('',(-1.648,-0.937,1.143));
|
||||
#259=VERTEX_POINT('',#260);
|
||||
#256=EDGE_CURVE('',#257,#259,#261,.T.);
|
||||
#261=LINE('',#258,#263);
|
||||
#263=VECTOR('',#264,3.40872929459724);
|
||||
#263=VECTOR('',#264,3.2957942432711);
|
||||
#264=DIRECTION('',(-1.0,0.0,0.0));
|
||||
#255=ORIENTED_EDGE('',*,*,#256,.F.);
|
||||
#268=CARTESIAN_POINT('',(1.704,0.993,0.813));
|
||||
#268=CARTESIAN_POINT('',(1.648,0.937,1.143));
|
||||
#267=VERTEX_POINT('',#268);
|
||||
#266=EDGE_CURVE('',#267,#257,#271,.T.);
|
||||
#271=LINE('',#268,#273);
|
||||
#273=VECTOR('',#274,1.98632929459724);
|
||||
#273=VECTOR('',#274,1.8733942432711);
|
||||
#274=DIRECTION('',(0.0,-1.0,0.0));
|
||||
#265=ORIENTED_EDGE('',*,*,#266,.F.);
|
||||
#278=CARTESIAN_POINT('',(-1.704,0.993,0.813));
|
||||
#278=CARTESIAN_POINT('',(-1.648,0.937,1.143));
|
||||
#277=VERTEX_POINT('',#278);
|
||||
#276=EDGE_CURVE('',#277,#267,#281,.T.);
|
||||
#281=LINE('',#278,#283);
|
||||
#283=VECTOR('',#284,3.40872929459724);
|
||||
#283=VECTOR('',#284,3.2957942432711);
|
||||
#284=DIRECTION('',(1.0,0.0,0.0));
|
||||
#275=ORIENTED_EDGE('',*,*,#276,.F.);
|
||||
#286=EDGE_CURVE('',#259,#277,#291,.T.);
|
||||
#291=LINE('',#260,#293);
|
||||
#293=VECTOR('',#294,1.98632929459724);
|
||||
#293=VECTOR('',#294,1.8733942432711);
|
||||
#294=DIRECTION('',(0.0,1.0,0.0));
|
||||
#285=ORIENTED_EDGE('',*,*,#286,.F.);
|
||||
#295=STYLED_ITEM('',(#43),#296);
|
||||
|
@ -335,13 +335,13 @@ DATA;
|
|||
#353=ORIENTED_EDGE('',*,*,#354,.F.);
|
||||
#364=EDGE_CURVE('',#257,#355,#369,.T.);
|
||||
#369=LINE('',#258,#371);
|
||||
#371=VECTOR('',#372,0.595815387993105);
|
||||
#371=VECTOR('',#372,0.935534688164611);
|
||||
#372=DIRECTION('',(0.166,-0.166,-0.972));
|
||||
#363=ORIENTED_EDGE('',*,*,#364,.F.);
|
||||
#373=ORIENTED_EDGE('',*,*,#256,.T.);
|
||||
#384=EDGE_CURVE('',#357,#259,#389,.T.);
|
||||
#389=LINE('',#358,#391);
|
||||
#391=VECTOR('',#392,0.595815387993105);
|
||||
#391=VECTOR('',#392,0.935534688164611);
|
||||
#392=DIRECTION('',(0.166,0.166,0.972));
|
||||
#383=ORIENTED_EDGE('',*,*,#384,.F.);
|
||||
#393=STYLED_ITEM('',(#43),#394);
|
||||
|
@ -362,7 +362,7 @@ DATA;
|
|||
#402=ORIENTED_EDGE('',*,*,#403,.F.);
|
||||
#413=EDGE_CURVE('',#267,#404,#418,.T.);
|
||||
#418=LINE('',#268,#420);
|
||||
#420=VECTOR('',#421,0.595815387993105);
|
||||
#420=VECTOR('',#421,0.935534688164611);
|
||||
#421=DIRECTION('',(0.166,0.166,-0.972));
|
||||
#412=ORIENTED_EDGE('',*,*,#413,.F.);
|
||||
#422=ORIENTED_EDGE('',*,*,#266,.T.);
|
||||
|
@ -385,7 +385,7 @@ DATA;
|
|||
#451=ORIENTED_EDGE('',*,*,#452,.F.);
|
||||
#462=EDGE_CURVE('',#277,#453,#467,.T.);
|
||||
#467=LINE('',#278,#469);
|
||||
#469=VECTOR('',#470,0.595815387993105);
|
||||
#469=VECTOR('',#470,0.935534688164611);
|
||||
#470=DIRECTION('',(-0.166,0.166,-0.972));
|
||||
#461=ORIENTED_EDGE('',*,*,#462,.F.);
|
||||
#471=ORIENTED_EDGE('',*,*,#276,.T.);
|
||||
|
@ -481,21 +481,21 @@ DATA;
|
|||
#936=ADVANCED_FACE('',(#942),#937,.T.);
|
||||
#937=PLANE('',#938);
|
||||
#938=AXIS2_PLACEMENT_3D('',#939,#940,#941);
|
||||
#939=CARTESIAN_POINT('',(-2.007,-0.673,0.0));
|
||||
#939=CARTESIAN_POINT('',(-1.994,-0.673,0.0));
|
||||
#940=DIRECTION('',(0.0,-1.0,0.0));
|
||||
#941=DIRECTION('',(0.,0.,1.));
|
||||
#942=FACE_OUTER_BOUND('',#943,.T.);
|
||||
#943=EDGE_LOOP('',(#944,#954,#964,#974,#984,#994));
|
||||
#947=CARTESIAN_POINT('',(0.584,-0.673,0.0));
|
||||
#947=CARTESIAN_POINT('',(0.597,-0.673,0.0));
|
||||
#946=VERTEX_POINT('',#947);
|
||||
#949=CARTESIAN_POINT('',(-2.007,-0.673,0.0));
|
||||
#949=CARTESIAN_POINT('',(-1.994,-0.673,0.0));
|
||||
#948=VERTEX_POINT('',#949);
|
||||
#945=EDGE_CURVE('',#946,#948,#950,.T.);
|
||||
#950=LINE('',#947,#952);
|
||||
#952=VECTOR('',#953,2.5908);
|
||||
#953=DIRECTION('',(-1.0,0.0,0.0));
|
||||
#944=ORIENTED_EDGE('',*,*,#945,.F.);
|
||||
#957=CARTESIAN_POINT('',(0.584,-0.673,0.051));
|
||||
#957=CARTESIAN_POINT('',(0.597,-0.673,0.051));
|
||||
#956=VERTEX_POINT('',#957);
|
||||
#955=EDGE_CURVE('',#956,#946,#960,.T.);
|
||||
#960=LINE('',#957,#962);
|
||||
|
@ -506,7 +506,7 @@ DATA;
|
|||
#966=VERTEX_POINT('',#967);
|
||||
#965=EDGE_CURVE('',#966,#956,#970,.T.);
|
||||
#970=LINE('',#967,#972);
|
||||
#972=VECTOR('',#973,2.3876);
|
||||
#972=VECTOR('',#973,2.4003);
|
||||
#973=DIRECTION('',(1.0,0.0,0.0));
|
||||
#964=ORIENTED_EDGE('',*,*,#965,.F.);
|
||||
#977=CARTESIAN_POINT('',(-1.803,-0.673,0.152));
|
||||
|
@ -516,11 +516,11 @@ DATA;
|
|||
#982=VECTOR('',#983,0.1016);
|
||||
#983=DIRECTION('',(0.0,0.0,-1.0));
|
||||
#974=ORIENTED_EDGE('',*,*,#975,.F.);
|
||||
#987=CARTESIAN_POINT('',(-2.007,-0.673,0.152));
|
||||
#987=CARTESIAN_POINT('',(-1.994,-0.673,0.152));
|
||||
#986=VERTEX_POINT('',#987);
|
||||
#985=EDGE_CURVE('',#986,#976,#990,.T.);
|
||||
#990=LINE('',#987,#992);
|
||||
#992=VECTOR('',#993,0.2032);
|
||||
#992=VECTOR('',#993,0.1905);
|
||||
#993=DIRECTION('',(1.0,0.0,0.0));
|
||||
#984=ORIENTED_EDGE('',*,*,#985,.F.);
|
||||
#995=EDGE_CURVE('',#948,#986,#1000,.T.);
|
||||
|
@ -532,21 +532,21 @@ DATA;
|
|||
#1005=ADVANCED_FACE('',(#1011),#1006,.T.);
|
||||
#1006=PLANE('',#1007);
|
||||
#1007=AXIS2_PLACEMENT_3D('',#1008,#1009,#1010);
|
||||
#1008=CARTESIAN_POINT('',(0.584,0.673,0.0));
|
||||
#1008=CARTESIAN_POINT('',(0.597,0.673,0.0));
|
||||
#1009=DIRECTION('',(0.0,1.0,0.0));
|
||||
#1010=DIRECTION('',(0.,0.,1.));
|
||||
#1011=FACE_OUTER_BOUND('',#1012,.T.);
|
||||
#1012=EDGE_LOOP('',(#1013,#1023,#1033,#1043,#1053,#1063));
|
||||
#1016=CARTESIAN_POINT('',(-2.007,0.673,0.0));
|
||||
#1016=CARTESIAN_POINT('',(-1.994,0.673,0.0));
|
||||
#1015=VERTEX_POINT('',#1016);
|
||||
#1018=CARTESIAN_POINT('',(0.584,0.673,0.0));
|
||||
#1018=CARTESIAN_POINT('',(0.597,0.673,0.0));
|
||||
#1017=VERTEX_POINT('',#1018);
|
||||
#1014=EDGE_CURVE('',#1015,#1017,#1019,.T.);
|
||||
#1019=LINE('',#1016,#1021);
|
||||
#1021=VECTOR('',#1022,2.5908);
|
||||
#1022=DIRECTION('',(1.0,0.0,0.0));
|
||||
#1013=ORIENTED_EDGE('',*,*,#1014,.F.);
|
||||
#1026=CARTESIAN_POINT('',(-2.007,0.673,0.152));
|
||||
#1026=CARTESIAN_POINT('',(-1.994,0.673,0.152));
|
||||
#1025=VERTEX_POINT('',#1026);
|
||||
#1024=EDGE_CURVE('',#1025,#1015,#1029,.T.);
|
||||
#1029=LINE('',#1026,#1031);
|
||||
|
@ -557,7 +557,7 @@ DATA;
|
|||
#1035=VERTEX_POINT('',#1036);
|
||||
#1034=EDGE_CURVE('',#1035,#1025,#1039,.T.);
|
||||
#1039=LINE('',#1036,#1041);
|
||||
#1041=VECTOR('',#1042,0.2032);
|
||||
#1041=VECTOR('',#1042,0.1905);
|
||||
#1042=DIRECTION('',(-1.0,0.0,0.0));
|
||||
#1033=ORIENTED_EDGE('',*,*,#1034,.F.);
|
||||
#1046=CARTESIAN_POINT('',(-1.803,0.673,0.051));
|
||||
|
@ -567,11 +567,11 @@ DATA;
|
|||
#1051=VECTOR('',#1052,0.1016);
|
||||
#1052=DIRECTION('',(0.0,0.0,1.0));
|
||||
#1043=ORIENTED_EDGE('',*,*,#1044,.F.);
|
||||
#1056=CARTESIAN_POINT('',(0.584,0.673,0.051));
|
||||
#1056=CARTESIAN_POINT('',(0.597,0.673,0.051));
|
||||
#1055=VERTEX_POINT('',#1056);
|
||||
#1054=EDGE_CURVE('',#1055,#1045,#1059,.T.);
|
||||
#1059=LINE('',#1056,#1061);
|
||||
#1061=VECTOR('',#1062,2.3876);
|
||||
#1061=VECTOR('',#1062,2.4003);
|
||||
#1062=DIRECTION('',(-1.0,0.0,0.0));
|
||||
#1053=ORIENTED_EDGE('',*,*,#1054,.F.);
|
||||
#1064=EDGE_CURVE('',#1017,#1055,#1069,.T.);
|
||||
|
@ -583,7 +583,7 @@ DATA;
|
|||
#1074=ADVANCED_FACE('',(#1080),#1075,.T.);
|
||||
#1075=PLANE('',#1076);
|
||||
#1076=AXIS2_PLACEMENT_3D('',#1077,#1078,#1079);
|
||||
#1077=CARTESIAN_POINT('',(-2.007,-0.673,0.152));
|
||||
#1077=CARTESIAN_POINT('',(-1.994,-0.673,0.152));
|
||||
#1078=DIRECTION('',(0.0,0.0,1.0));
|
||||
#1079=DIRECTION('',(0.,1.,0.));
|
||||
#1080=FACE_OUTER_BOUND('',#1081,.T.);
|
||||
|
@ -625,7 +625,7 @@ DATA;
|
|||
#1172=ADVANCED_FACE('',(#1178),#1173,.T.);
|
||||
#1173=PLANE('',#1174);
|
||||
#1174=AXIS2_PLACEMENT_3D('',#1175,#1176,#1177);
|
||||
#1175=CARTESIAN_POINT('',(-2.007,0.673,0.0));
|
||||
#1175=CARTESIAN_POINT('',(-1.994,0.673,0.0));
|
||||
#1176=DIRECTION('',(-1.0,0.0,0.0));
|
||||
#1177=DIRECTION('',(0.,0.,1.));
|
||||
#1178=FACE_OUTER_BOUND('',#1179,.T.);
|
||||
|
@ -642,7 +642,7 @@ DATA;
|
|||
#1221=ADVANCED_FACE('',(#1227),#1222,.T.);
|
||||
#1222=PLANE('',#1223);
|
||||
#1223=AXIS2_PLACEMENT_3D('',#1224,#1225,#1226);
|
||||
#1224=CARTESIAN_POINT('',(0.584,-0.673,0.0));
|
||||
#1224=CARTESIAN_POINT('',(0.597,-0.673,0.0));
|
||||
#1225=DIRECTION('',(1.0,0.0,0.0));
|
||||
#1226=DIRECTION('',(0.,0.,1.));
|
||||
#1227=FACE_OUTER_BOUND('',#1228,.T.);
|
||||
|
@ -672,7 +672,7 @@ DATA;
|
|||
#1319=ADVANCED_FACE('',(#1325),#1320,.T.);
|
||||
#1320=PLANE('',#1321);
|
||||
#1321=AXIS2_PLACEMENT_3D('',#1322,#1323,#1324);
|
||||
#1322=CARTESIAN_POINT('',(-2.007,0.673,0.0));
|
||||
#1322=CARTESIAN_POINT('',(-1.994,0.673,0.0));
|
||||
#1323=DIRECTION('',(0.0,0.0,-1.0));
|
||||
#1324=DIRECTION('',(0.,1.,0.));
|
||||
#1325=FACE_OUTER_BOUND('',#1326,.T.);
|
||||
|
@ -687,21 +687,21 @@ DATA;
|
|||
#1370=ADVANCED_FACE('',(#1376),#1371,.T.);
|
||||
#1371=PLANE('',#1372);
|
||||
#1372=AXIS2_PLACEMENT_3D('',#1373,#1374,#1375);
|
||||
#1373=CARTESIAN_POINT('',(2.007,0.457,0.0));
|
||||
#1373=CARTESIAN_POINT('',(1.994,0.457,0.0));
|
||||
#1374=DIRECTION('',(0.0,1.0,0.0));
|
||||
#1375=DIRECTION('',(0.,0.,1.));
|
||||
#1376=FACE_OUTER_BOUND('',#1377,.T.);
|
||||
#1377=EDGE_LOOP('',(#1378,#1388,#1398,#1408,#1418,#1428));
|
||||
#1381=CARTESIAN_POINT('',(1.194,0.457,0.0));
|
||||
#1381=CARTESIAN_POINT('',(1.181,0.457,0.0));
|
||||
#1380=VERTEX_POINT('',#1381);
|
||||
#1383=CARTESIAN_POINT('',(2.007,0.457,0.0));
|
||||
#1383=CARTESIAN_POINT('',(1.994,0.457,0.0));
|
||||
#1382=VERTEX_POINT('',#1383);
|
||||
#1379=EDGE_CURVE('',#1380,#1382,#1384,.T.);
|
||||
#1384=LINE('',#1381,#1386);
|
||||
#1386=VECTOR('',#1387,0.8128);
|
||||
#1387=DIRECTION('',(1.0,0.0,0.0));
|
||||
#1378=ORIENTED_EDGE('',*,*,#1379,.F.);
|
||||
#1391=CARTESIAN_POINT('',(1.194,0.457,0.051));
|
||||
#1391=CARTESIAN_POINT('',(1.181,0.457,0.051));
|
||||
#1390=VERTEX_POINT('',#1391);
|
||||
#1389=EDGE_CURVE('',#1390,#1380,#1394,.T.);
|
||||
#1394=LINE('',#1391,#1396);
|
||||
|
@ -712,7 +712,7 @@ DATA;
|
|||
#1400=VERTEX_POINT('',#1401);
|
||||
#1399=EDGE_CURVE('',#1400,#1390,#1404,.T.);
|
||||
#1404=LINE('',#1401,#1406);
|
||||
#1406=VECTOR('',#1407,0.6096);
|
||||
#1406=VECTOR('',#1407,0.6223);
|
||||
#1407=DIRECTION('',(-1.0,0.0,0.0));
|
||||
#1398=ORIENTED_EDGE('',*,*,#1399,.F.);
|
||||
#1411=CARTESIAN_POINT('',(1.803,0.457,0.152));
|
||||
|
@ -722,11 +722,11 @@ DATA;
|
|||
#1416=VECTOR('',#1417,0.1016);
|
||||
#1417=DIRECTION('',(0.0,0.0,-1.0));
|
||||
#1408=ORIENTED_EDGE('',*,*,#1409,.F.);
|
||||
#1421=CARTESIAN_POINT('',(2.007,0.457,0.152));
|
||||
#1421=CARTESIAN_POINT('',(1.994,0.457,0.152));
|
||||
#1420=VERTEX_POINT('',#1421);
|
||||
#1419=EDGE_CURVE('',#1420,#1410,#1424,.T.);
|
||||
#1424=LINE('',#1421,#1426);
|
||||
#1426=VECTOR('',#1427,0.2032);
|
||||
#1426=VECTOR('',#1427,0.1905);
|
||||
#1427=DIRECTION('',(-1.0,0.0,0.0));
|
||||
#1418=ORIENTED_EDGE('',*,*,#1419,.F.);
|
||||
#1429=EDGE_CURVE('',#1382,#1420,#1434,.T.);
|
||||
|
@ -738,21 +738,21 @@ DATA;
|
|||
#1439=ADVANCED_FACE('',(#1445),#1440,.T.);
|
||||
#1440=PLANE('',#1441);
|
||||
#1441=AXIS2_PLACEMENT_3D('',#1442,#1443,#1444);
|
||||
#1442=CARTESIAN_POINT('',(1.194,-0.457,0.0));
|
||||
#1442=CARTESIAN_POINT('',(1.181,-0.457,0.0));
|
||||
#1443=DIRECTION('',(0.0,-1.0,0.0));
|
||||
#1444=DIRECTION('',(0.,0.,1.));
|
||||
#1445=FACE_OUTER_BOUND('',#1446,.T.);
|
||||
#1446=EDGE_LOOP('',(#1447,#1457,#1467,#1477,#1487,#1497));
|
||||
#1450=CARTESIAN_POINT('',(2.007,-0.457,0.0));
|
||||
#1450=CARTESIAN_POINT('',(1.994,-0.457,0.0));
|
||||
#1449=VERTEX_POINT('',#1450);
|
||||
#1452=CARTESIAN_POINT('',(1.194,-0.457,0.0));
|
||||
#1452=CARTESIAN_POINT('',(1.181,-0.457,0.0));
|
||||
#1451=VERTEX_POINT('',#1452);
|
||||
#1448=EDGE_CURVE('',#1449,#1451,#1453,.T.);
|
||||
#1453=LINE('',#1450,#1455);
|
||||
#1455=VECTOR('',#1456,0.8128);
|
||||
#1456=DIRECTION('',(-1.0,0.0,0.0));
|
||||
#1447=ORIENTED_EDGE('',*,*,#1448,.F.);
|
||||
#1460=CARTESIAN_POINT('',(2.007,-0.457,0.152));
|
||||
#1460=CARTESIAN_POINT('',(1.994,-0.457,0.152));
|
||||
#1459=VERTEX_POINT('',#1460);
|
||||
#1458=EDGE_CURVE('',#1459,#1449,#1463,.T.);
|
||||
#1463=LINE('',#1460,#1465);
|
||||
|
@ -763,7 +763,7 @@ DATA;
|
|||
#1469=VERTEX_POINT('',#1470);
|
||||
#1468=EDGE_CURVE('',#1469,#1459,#1473,.T.);
|
||||
#1473=LINE('',#1470,#1475);
|
||||
#1475=VECTOR('',#1476,0.2032);
|
||||
#1475=VECTOR('',#1476,0.1905);
|
||||
#1476=DIRECTION('',(1.0,0.0,0.0));
|
||||
#1467=ORIENTED_EDGE('',*,*,#1468,.F.);
|
||||
#1480=CARTESIAN_POINT('',(1.803,-0.457,0.051));
|
||||
|
@ -773,11 +773,11 @@ DATA;
|
|||
#1485=VECTOR('',#1486,0.1016);
|
||||
#1486=DIRECTION('',(0.0,0.0,1.0));
|
||||
#1477=ORIENTED_EDGE('',*,*,#1478,.F.);
|
||||
#1490=CARTESIAN_POINT('',(1.194,-0.457,0.051));
|
||||
#1490=CARTESIAN_POINT('',(1.181,-0.457,0.051));
|
||||
#1489=VERTEX_POINT('',#1490);
|
||||
#1488=EDGE_CURVE('',#1489,#1479,#1493,.T.);
|
||||
#1493=LINE('',#1490,#1495);
|
||||
#1495=VECTOR('',#1496,0.6096);
|
||||
#1495=VECTOR('',#1496,0.6223);
|
||||
#1496=DIRECTION('',(1.0,0.0,0.0));
|
||||
#1487=ORIENTED_EDGE('',*,*,#1488,.F.);
|
||||
#1498=EDGE_CURVE('',#1451,#1489,#1503,.T.);
|
||||
|
@ -789,7 +789,7 @@ DATA;
|
|||
#1508=ADVANCED_FACE('',(#1514),#1509,.T.);
|
||||
#1509=PLANE('',#1510);
|
||||
#1510=AXIS2_PLACEMENT_3D('',#1511,#1512,#1513);
|
||||
#1511=CARTESIAN_POINT('',(2.007,0.457,0.152));
|
||||
#1511=CARTESIAN_POINT('',(1.994,0.457,0.152));
|
||||
#1512=DIRECTION('',(0.0,0.0,1.0));
|
||||
#1513=DIRECTION('',(0.,1.,0.));
|
||||
#1514=FACE_OUTER_BOUND('',#1515,.T.);
|
||||
|
@ -831,7 +831,7 @@ DATA;
|
|||
#1606=ADVANCED_FACE('',(#1612),#1607,.T.);
|
||||
#1607=PLANE('',#1608);
|
||||
#1608=AXIS2_PLACEMENT_3D('',#1609,#1610,#1611);
|
||||
#1609=CARTESIAN_POINT('',(2.007,-0.457,0.0));
|
||||
#1609=CARTESIAN_POINT('',(1.994,-0.457,0.0));
|
||||
#1610=DIRECTION('',(1.0,0.0,0.0));
|
||||
#1611=DIRECTION('',(0.,0.,1.));
|
||||
#1612=FACE_OUTER_BOUND('',#1613,.T.);
|
||||
|
@ -848,7 +848,7 @@ DATA;
|
|||
#1655=ADVANCED_FACE('',(#1661),#1656,.T.);
|
||||
#1656=PLANE('',#1657);
|
||||
#1657=AXIS2_PLACEMENT_3D('',#1658,#1659,#1660);
|
||||
#1658=CARTESIAN_POINT('',(1.194,0.457,0.0));
|
||||
#1658=CARTESIAN_POINT('',(1.181,0.457,0.0));
|
||||
#1659=DIRECTION('',(-1.0,0.0,0.0));
|
||||
#1660=DIRECTION('',(0.,0.,1.));
|
||||
#1661=FACE_OUTER_BOUND('',#1662,.T.);
|
||||
|
@ -878,7 +878,7 @@ DATA;
|
|||
#1753=ADVANCED_FACE('',(#1759),#1754,.T.);
|
||||
#1754=PLANE('',#1755);
|
||||
#1755=AXIS2_PLACEMENT_3D('',#1756,#1757,#1758);
|
||||
#1756=CARTESIAN_POINT('',(2.007,-0.457,0.0));
|
||||
#1756=CARTESIAN_POINT('',(1.994,-0.457,0.0));
|
||||
#1757=DIRECTION('',(0.0,0.0,-1.0));
|
||||
#1758=DIRECTION('',(0.,1.,0.));
|
||||
#1759=FACE_OUTER_BOUND('',#1760,.T.);
|
|
@ -0,0 +1,825 @@
|
|||
ISO-10303-21;
|
||||
HEADER;
|
||||
FILE_DESCRIPTION (( 'STEP AP214' ),
|
||||
'1' );
|
||||
FILE_NAME ('MSMP package.STEP',
|
||||
'2016-08-19T07:07:45',
|
||||
( 'Vishay' ),
|
||||
( '' ),
|
||||
'SwSTEP 2.0',
|
||||
'SolidWorks 2015',
|
||||
'' );
|
||||
FILE_SCHEMA (( 'AUTOMOTIVE_DESIGN' ));
|
||||
ENDSEC;
|
||||
|
||||
DATA;
|
||||
#1 = ORIENTED_EDGE ( 'NONE', *, *, #448, .F. ) ;
|
||||
#2 = AXIS2_PLACEMENT_3D ( 'NONE', #418, #662, #437 ) ;
|
||||
#3 = ORIENTED_EDGE ( 'NONE', *, *, #229, .F. ) ;
|
||||
#4 = EDGE_CURVE ( 'NONE', #472, #571, #92, .T. ) ;
|
||||
#5 = VECTOR ( 'NONE', #261, 1000.000000000000000 ) ;
|
||||
#6 = LINE ( 'NONE', #32, #480 ) ;
|
||||
#7 =( LENGTH_UNIT ( ) NAMED_UNIT ( * ) SI_UNIT ( .MILLI., .METRE. ) );
|
||||
#8 = PLANE ( 'NONE', #74 ) ;
|
||||
#9 = EDGE_LOOP ( 'NONE', ( #17, #3, #694, #116 ) ) ;
|
||||
#10 =( LENGTH_UNIT ( ) NAMED_UNIT ( * ) SI_UNIT ( .MILLI., .METRE. ) );
|
||||
#11 = LINE ( 'NONE', #377, #158 ) ;
|
||||
#12 = LINE ( 'NONE', #451, #492 ) ;
|
||||
#13 = AXIS2_PLACEMENT_3D ( 'NONE', #105, #604, #224 ) ;
|
||||
#14 = DIRECTION ( 'NONE', ( 0.0000000000000000000, -0.0000000000000000000, -1.000000000000000000 ) ) ;
|
||||
#15 = CARTESIAN_POINT ( 'NONE', ( 0.6302161917152264700, 0.05618686558523977600, 0.8576654041047709900 ) ) ;
|
||||
#16 = ORIENTED_EDGE ( 'NONE', *, *, #606, .T. ) ;
|
||||
#17 = ORIENTED_EDGE ( 'NONE', *, *, #108, .T. ) ;
|
||||
#18 = SURFACE_STYLE_FILL_AREA ( #489 ) ;
|
||||
#19 = ORIENTED_EDGE ( 'NONE', *, *, #4, .T. ) ;
|
||||
#20 = DIRECTION ( 'NONE', ( -0.0000000000000000000, -1.000000000000000000, -0.0000000000000000000 ) ) ;
|
||||
#21 = CARTESIAN_POINT ( 'NONE', ( -1.719783808284775000, 0.05618686558523977600, -0.2023345958952274500 ) ) ;
|
||||
#22 = EDGE_CURVE ( 'NONE', #321, #188, #268, .T. ) ;
|
||||
#23 = PRESENTATION_STYLE_ASSIGNMENT (( #611 ) ) ;
|
||||
#24 = PLANE ( 'NONE', #501 ) ;
|
||||
#25 = DIRECTION ( 'NONE', ( 0.0000000000000000000, 0.0000000000000000000, -1.000000000000000000 ) ) ;
|
||||
#26 = LINE ( 'NONE', #221, #329 ) ;
|
||||
#27 = FILL_AREA_STYLE_COLOUR ( '', #569 ) ;
|
||||
#28 = EDGE_CURVE ( 'NONE', #179, #500, #250, .T. ) ;
|
||||
#29 = SURFACE_SIDE_STYLE ('',( #661 ) ) ;
|
||||
#30 = CARTESIAN_POINT ( 'NONE', ( 0.6170928921863383400, -0.09381313441476020500, 0.8576654041047709900 ) ) ;
|
||||
#31 = ORIENTED_EDGE ( 'NONE', *, *, #290, .T. ) ;
|
||||
#32 = CARTESIAN_POINT ( 'NONE', ( -1.523414816616035900, 0.5861868655852398000, -0.4423345958952280600 ) ) ;
|
||||
#33 =( GEOMETRIC_REPRESENTATION_CONTEXT ( 3 ) GLOBAL_UNCERTAINTY_ASSIGNED_CONTEXT ( ( #704 ) ) GLOBAL_UNIT_ASSIGNED_CONTEXT ( ( #7, #367, #750 ) ) REPRESENTATION_CONTEXT ( 'NONE', 'WORKASPACE' ) );
|
||||
#34 = ADVANCED_FACE ( 'NONE', ( #752 ), #453, .F. ) ;
|
||||
#35 = CARTESIAN_POINT ( 'NONE', ( -1.569783808284774200, 0.5861868655852398000, 0.8112964124360327900 ) ) ;
|
||||
#36 = LINE ( 'NONE', #141, #539 ) ;
|
||||
#37 = PLANE ( 'NONE', #2 ) ;
|
||||
#38 = EDGE_LOOP ( 'NONE', ( #120, #238, #62, #493, #432, #184, #715, #299, #529, #31, #698, #385 ) ) ;
|
||||
#39 = CARTESIAN_POINT ( 'NONE', ( 0.1802161917152239600, -0.09381313441476020500, -0.09233459589523193600 ) ) ;
|
||||
#40 = ORIENTED_EDGE ( 'NONE', *, *, #85, .F. ) ;
|
||||
#41 = UNCERTAINTY_MEASURE_WITH_UNIT (LENGTH_MEASURE( 1.000000000000000100E-005 ), #708, 'distance_accuracy_value', 'NONE');
|
||||
#42 = FACE_OUTER_BOUND ( 'NONE', #277, .T. ) ;
|
||||
#43 = ORIENTED_EDGE ( 'NONE', *, *, #85, .T. ) ;
|
||||
#44 = CARTESIAN_POINT ( 'NONE', ( -1.719783808284775000, 0.05618686558523977600, 0.6176654041047668900 ) ) ;
|
||||
#45 = PRESENTATION_LAYER_ASSIGNMENT ( '', '', ( #762 ) ) ;
|
||||
#46 = DIRECTION ( 'NONE', ( -0.08715574274765836000, 0.9961946980917455500, -5.832412861803903500E-018 ) ) ;
|
||||
#47 = FACE_OUTER_BOUND ( 'NONE', #779, .T. ) ;
|
||||
#48 = EDGE_CURVE ( 'NONE', #571, #559, #487, .T. ) ;
|
||||
#49 = CARTESIAN_POINT ( 'NONE', ( 0.6170928921863383400, -0.09381313441476020500, -0.09233459589523193600 ) ) ;
|
||||
#50 = DIRECTION ( 'NONE', ( 0.08682659386424779200, -0.9924325091389669700, 0.08682659386424779200 ) ) ;
|
||||
#51 = ORIENTED_EDGE ( 'NONE', *, *, #707, .T. ) ;
|
||||
#52 = ADVANCED_FACE ( 'NONE', ( #310 ), #172, .T. ) ;
|
||||
#53 = CARTESIAN_POINT ( 'NONE', ( -1.569783808284774200, -0.09381313441476020500, 0.8445421045758828600 ) ) ;
|
||||
#54 = UNCERTAINTY_MEASURE_WITH_UNIT (LENGTH_MEASURE( 1.000000000000000100E-005 ), #231, 'distance_accuracy_value', 'NONE');
|
||||
#55 = ORIENTED_EDGE ( 'NONE', *, *, #423, .F. ) ;
|
||||
#56 = CARTESIAN_POINT ( 'NONE', ( -1.559983293662088500, -0.05583352914605126800, 0.8478648894820854900 ) ) ;
|
||||
#57 = DIRECTION ( 'NONE', ( -0.0000000000000000000, -0.0000000000000000000, 1.000000000000000000 ) ) ;
|
||||
#58 = DIRECTION ( 'NONE', ( 0.0000000000000000000, 0.0000000000000000000, -1.000000000000000000 ) ) ;
|
||||
#59 = LINE ( 'NONE', #516, #697 ) ;
|
||||
#60 = ORIENTED_EDGE ( 'NONE', *, *, #586, .F. ) ;
|
||||
#61 = STYLED_ITEM ( 'NONE', ( #23 ), #722 ) ;
|
||||
#62 = ORIENTED_EDGE ( 'NONE', *, *, #625, .T. ) ;
|
||||
#63 = AXIS2_PLACEMENT_3D ( 'NONE', #21, #378, #764 ) ;
|
||||
#64 = EDGE_CURVE ( 'NONE', #545, #435, #150, .T. ) ;
|
||||
#65 =( NAMED_UNIT ( * ) PLANE_ANGLE_UNIT ( ) SI_UNIT ( $, .RADIAN. ) );
|
||||
#66 = ORIENTED_EDGE ( 'NONE', *, *, #797, .T. ) ;
|
||||
#67 = FILL_AREA_STYLE_COLOUR ( '', #144 ) ;
|
||||
#68 = EDGE_CURVE ( 'NONE', #421, #321, #460, .T. ) ;
|
||||
#69 = ORIENTED_EDGE ( 'NONE', *, *, #554, .F. ) ;
|
||||
#70 = CARTESIAN_POINT ( 'NONE', ( 0.03021619171522313400, 0.05618686558523977600, -0.09233459589523193600 ) ) ;
|
||||
#71 = COLOUR_RGB ( '',0.5372549019607842900, 0.3490196078431372400, 0.3372549019607843400 ) ;
|
||||
#72 = SURFACE_SIDE_STYLE ('',( #112 ) ) ;
|
||||
#73 = VERTEX_POINT ( 'NONE', #612 ) ;
|
||||
#74 = AXIS2_PLACEMENT_3D ( 'NONE', #127, #457, #392 ) ;
|
||||
#75 = CARTESIAN_POINT ( 'NONE', ( 0.6302161917152264700, 0.05618686558523977600, 0.5076654041047713500 ) ) ;
|
||||
#76 = CARTESIAN_POINT ( 'NONE', ( -1.569783808284774200, 0.05618686558523977600, -0.4423345958952280600 ) ) ;
|
||||
#77 = EDGE_LOOP ( 'NONE', ( #765, #51, #680, #469 ) ) ;
|
||||
#78 = DIRECTION ( 'NONE', ( -0.9961946980917455500, -0.08715574274765836000, 0.0000000000000000000 ) ) ;
|
||||
#79 =( NAMED_UNIT ( * ) SI_UNIT ( $, .STERADIAN. ) SOLID_ANGLE_UNIT ( ) );
|
||||
#80 = LINE ( 'NONE', #191, #401 ) ;
|
||||
#81 = VECTOR ( 'NONE', #718, 1000.000000000000000 ) ;
|
||||
#82 = MECHANICAL_DESIGN_GEOMETRIC_PRESENTATION_REPRESENTATION ( '', ( #762 ), #123 ) ;
|
||||
#83 = CARTESIAN_POINT ( 'NONE', ( 0.6170928921863383400, -0.09381313441476023300, 0.8445421045758828600 ) ) ;
|
||||
#84 = DIRECTION ( 'NONE', ( 0.08715574274765836000, -0.9961946980917455500, 0.0000000000000000000 ) ) ;
|
||||
#85 = EDGE_CURVE ( 'NONE', #685, #441, #619, .T. ) ;
|
||||
#86 = ORIENTED_EDGE ( 'NONE', *, *, #108, .F. ) ;
|
||||
#87 = FILL_AREA_STYLE_COLOUR ( '', #71 ) ;
|
||||
#88 = VECTOR ( 'NONE', #714, 1000.000000000000000 ) ;
|
||||
#89 =( NAMED_UNIT ( * ) PLANE_ANGLE_UNIT ( ) SI_UNIT ( $, .RADIAN. ) );
|
||||
#90 = EDGE_LOOP ( 'NONE', ( #131, #394, #181, #223 ) ) ;
|
||||
#91 = CARTESIAN_POINT ( 'NONE', ( 0.6302161917152264700, 0.05618686558523977600, -0.4423345958952280600 ) ) ;
|
||||
#92 = LINE ( 'NONE', #214, #412 ) ;
|
||||
#93 = FACE_OUTER_BOUND ( 'NONE', #342, .T. ) ;
|
||||
#94 =( LENGTH_UNIT ( ) NAMED_UNIT ( * ) SI_UNIT ( .MILLI., .METRE. ) );
|
||||
#95 = PRESENTATION_STYLE_ASSIGNMENT (( #326 ) ) ;
|
||||
#96 = ORIENTED_EDGE ( 'NONE', *, *, #442, .T. ) ;
|
||||
#97 = FILL_AREA_STYLE ('',( #27 ) ) ;
|
||||
#98 =( NAMED_UNIT ( * ) PLANE_ANGLE_UNIT ( ) SI_UNIT ( $, .RADIAN. ) );
|
||||
#99 = CARTESIAN_POINT ( 'NONE', ( -1.569783808284774200, 0.05618686558523977600, 0.6176654041047668900 ) ) ;
|
||||
#100 =( NAMED_UNIT ( * ) PLANE_ANGLE_UNIT ( ) SI_UNIT ( $, .RADIAN. ) );
|
||||
#101 = COLOUR_RGB ( '',0.5372549019607842900, 0.3490196078431372400, 0.3372549019607843400 ) ;
|
||||
#102 = EDGE_LOOP ( 'NONE', ( #202, #406, #744, #553 ) ) ;
|
||||
#103 = AXIS2_PLACEMENT_3D ( 'NONE', #403, #474, #109 ) ;
|
||||
#104 = UNCERTAINTY_MEASURE_WITH_UNIT (LENGTH_MEASURE( 1.000000000000000100E-005 ), #581, 'distance_accuracy_value', 'NONE');
|
||||
#105 = CARTESIAN_POINT ( 'NONE', ( -1.569783808284774200, 0.05618686558523977600, 0.8576654041047709900 ) ) ;
|
||||
#106 = CARTESIAN_POINT ( 'NONE', ( -1.719783808284775000, 0.05618686558523977600, -0.2023345958952274500 ) ) ;
|
||||
#107 = DIRECTION ( 'NONE', ( 1.000000000000000000, 0.0000000000000000000, 0.0000000000000000000 ) ) ;
|
||||
#108 = EDGE_CURVE ( 'NONE', #500, #73, #778, .T. ) ;
|
||||
#109 = DIRECTION ( 'NONE', ( -0.0000000000000000000, -0.9961946980917455500, -0.08715574274765836000 ) ) ;
|
||||
#110 = VECTOR ( 'NONE', #463, 1000.000000000000100 ) ;
|
||||
#111 = PLANE ( 'NONE', #732 ) ;
|
||||
#112 = SURFACE_STYLE_FILL_AREA ( #509 ) ;
|
||||
#113 = DIRECTION ( 'NONE', ( 0.0000000000000000000, -0.08715574274765836000, 0.9961946980917455500 ) ) ;
|
||||
#114 = VECTOR ( 'NONE', #286, 1000.000000000000000 ) ;
|
||||
#115 = ADVANCED_FACE ( 'NONE', ( #757 ), #111, .T. ) ;
|
||||
#116 = ORIENTED_EDGE ( 'NONE', *, *, #28, .T. ) ;
|
||||
#117 = FILL_AREA_STYLE_COLOUR ( '', #668 ) ;
|
||||
#118 = SURFACE_SIDE_STYLE ('',( #18 ) ) ;
|
||||
#119 = VECTOR ( 'NONE', #404, 1000.000000000000000 ) ;
|
||||
#120 = ORIENTED_EDGE ( 'NONE', *, *, #398, .F. ) ;
|
||||
#121 = ORIENTED_EDGE ( 'NONE', *, *, #531, .F. ) ;
|
||||
#122 = PRESENTATION_STYLE_ASSIGNMENT (( #340 ) ) ;
|
||||
#123 =( GEOMETRIC_REPRESENTATION_CONTEXT ( 3 ) GLOBAL_UNCERTAINTY_ASSIGNED_CONTEXT ( ( #182 ) ) GLOBAL_UNIT_ASSIGNED_CONTEXT ( ( #530, #89, #450 ) ) REPRESENTATION_CONTEXT ( 'NONE', 'WORKASPACE' ) );
|
||||
#124 = EDGE_CURVE ( 'NONE', #372, #338, #300, .T. ) ;
|
||||
#125 = SURFACE_STYLE_USAGE ( .BOTH. , #573 ) ;
|
||||
#126 = LINE ( 'NONE', #608, #613 ) ;
|
||||
#127 = CARTESIAN_POINT ( 'NONE', ( 0.6302161917152264700, 0.05618686558523977600, 0.8576654041047709900 ) ) ;
|
||||
#128 = SHAPE_DEFINITION_REPRESENTATION ( #241, #355 ) ;
|
||||
#129 = CARTESIAN_POINT ( 'NONE', ( -1.569783808284774200, 0.05618686558523977600, -0.4423345958952280600 ) ) ;
|
||||
#130 = VECTOR ( 'NONE', #411, 1000.000000000000000 ) ;
|
||||
#131 = ORIENTED_EDGE ( 'NONE', *, *, #22, .T. ) ;
|
||||
#132 =( NAMED_UNIT ( * ) PLANE_ANGLE_UNIT ( ) SI_UNIT ( $, .RADIAN. ) );
|
||||
#133 = PRESENTATION_LAYER_ASSIGNMENT ( '', '', ( #61 ) ) ;
|
||||
#134 = PLANE ( 'NONE', #140 ) ;
|
||||
#135 = PRESENTATION_STYLE_ASSIGNMENT (( #195 ) ) ;
|
||||
#136 = LINE ( 'NONE', #331, #304 ) ;
|
||||
#137 = ORIENTED_EDGE ( 'NONE', *, *, #713, .T. ) ;
|
||||
#138 =( LENGTH_UNIT ( ) NAMED_UNIT ( * ) SI_UNIT ( .MILLI., .METRE. ) );
|
||||
#139 = ORIENTED_EDGE ( 'NONE', *, *, #707, .F. ) ;
|
||||
#140 = AXIS2_PLACEMENT_3D ( 'NONE', #76, #440, #84 ) ;
|
||||
#141 = CARTESIAN_POINT ( 'NONE', ( 0.6302161917152264700, 0.05618686558523977600, 0.8576654041047709900 ) ) ;
|
||||
#142 = EDGE_CURVE ( 'NONE', #188, #368, #601, .T. ) ;
|
||||
#143 = DIRECTION ( 'NONE', ( 0.0000000000000000000, 0.0000000000000000000, -1.000000000000000000 ) ) ;
|
||||
#144 = COLOUR_RGB ( '',0.7921568627450980000, 0.8196078431372548800, 0.9333333333333333500 ) ;
|
||||
#145 = VECTOR ( 'NONE', #660, 1000.000000000000000 ) ;
|
||||
#146 = SURFACE_STYLE_USAGE ( .BOTH. , #594 ) ;
|
||||
#147 = DIRECTION ( 'NONE', ( -0.08682659386424779200, -0.9924325091389669700, 0.08682659386424779200 ) ) ;
|
||||
#148 =( NAMED_UNIT ( * ) SI_UNIT ( $, .STERADIAN. ) SOLID_ANGLE_UNIT ( ) );
|
||||
#149 = VERTEX_POINT ( 'NONE', #550 ) ;
|
||||
#150 = LINE ( 'NONE', #678, #802 ) ;
|
||||
#151 = ORIENTED_EDGE ( 'NONE', *, *, #142, .F. ) ;
|
||||
#152 = SURFACE_STYLE_USAGE ( .BOTH. , #537 ) ;
|
||||
#153 = SURFACE_SIDE_STYLE ('',( #405 ) ) ;
|
||||
#154 = STYLED_ITEM ( 'NONE', ( #630 ), #355 ) ;
|
||||
#155 = DIRECTION ( 'NONE', ( 1.000000000000000000, -0.0000000000000000000, -0.0000000000000000000 ) ) ;
|
||||
#156 = CARTESIAN_POINT ( 'NONE', ( 0.6302161917152264700, 0.05618686558523977600, 0.8576654041047709900 ) ) ;
|
||||
#157 = ADVANCED_FACE ( 'NONE', ( #419 ), #164, .F. ) ;
|
||||
#158 = VECTOR ( 'NONE', #20, 1000.000000000000000 ) ;
|
||||
#159 = LINE ( 'NONE', #438, #640 ) ;
|
||||
#160 = VERTEX_POINT ( 'NONE', #549 ) ;
|
||||
#161 = DIRECTION ( 'NONE', ( 0.0000000000000000000, 5.854691731421723900E-018, 1.000000000000000000 ) ) ;
|
||||
#162 = ORIENTED_EDGE ( 'NONE', *, *, #766, .T. ) ;
|
||||
#163 = CARTESIAN_POINT ( 'NONE', ( 0.6302161917152264700, 0.05618686558523977600, -0.4423345958952280600 ) ) ;
|
||||
#164 = PLANE ( 'NONE', #375 ) ;
|
||||
#165 = EDGE_LOOP ( 'NONE', ( #602, #562, #741, #175 ) ) ;
|
||||
#166 = CARTESIAN_POINT ( 'NONE', ( 0.6302161917152264700, -0.09381313441476020500, -0.4292112963663399800 ) ) ;
|
||||
#167 = EDGE_CURVE ( 'NONE', #472, #253, #201, .T. ) ;
|
||||
#168 = LINE ( 'NONE', #166, #672 ) ;
|
||||
#169 = SURFACE_SIDE_STYLE ('',( #186 ) ) ;
|
||||
#170 = VECTOR ( 'NONE', #155, 1000.000000000000000 ) ;
|
||||
#171 = DIRECTION ( 'NONE', ( -0.0000000000000000000, -0.0000000000000000000, -1.000000000000000000 ) ) ;
|
||||
#172 = PLANE ( 'NONE', #650 ) ;
|
||||
#173 = ADVANCED_FACE ( 'NONE', ( #42 ), #302, .T. ) ;
|
||||
#174 =( LENGTH_UNIT ( ) NAMED_UNIT ( * ) SI_UNIT ( .MILLI., .METRE. ) );
|
||||
#175 = ORIENTED_EDGE ( 'NONE', *, *, #247, .T. ) ;
|
||||
#176 = FACE_OUTER_BOUND ( 'NONE', #90, .T. ) ;
|
||||
#177 = LINE ( 'NONE', #641, #806 ) ;
|
||||
#178 = FACE_OUTER_BOUND ( 'NONE', #38, .T. ) ;
|
||||
#179 = VERTEX_POINT ( 'NONE', #564 ) ;
|
||||
#180 = CARTESIAN_POINT ( 'NONE', ( -1.569783808284774200, 0.05618686558523977600, -0.4423345958952280600 ) ) ;
|
||||
#181 = ORIENTED_EDGE ( 'NONE', *, *, #265, .F. ) ;
|
||||
#182 = UNCERTAINTY_MEASURE_WITH_UNIT (LENGTH_MEASURE( 1.000000000000000100E-005 ), #530, 'distance_accuracy_value', 'NONE');
|
||||
#183 = VECTOR ( 'NONE', #486, 1000.000000000000000 ) ;
|
||||
#184 = ORIENTED_EDGE ( 'NONE', *, *, #68, .F. ) ;
|
||||
#185 = EDGE_CURVE ( 'NONE', #435, #284, #361, .T. ) ;
|
||||
#186 = SURFACE_STYLE_FILL_AREA ( #552 ) ;
|
||||
#187 = FILL_AREA_STYLE ('',( #67 ) ) ;
|
||||
#188 = VERTEX_POINT ( 'NONE', #359 ) ;
|
||||
#189 = DIRECTION ( 'NONE', ( 1.000000000000000000, 0.0000000000000000000, -0.0000000000000000000 ) ) ;
|
||||
#190 = LINE ( 'NONE', #70, #510 ) ;
|
||||
#191 = CARTESIAN_POINT ( 'NONE', ( 0.6170928921863383400, -0.09381313441476020500, 0.8576654041047709900 ) ) ;
|
||||
#192 = DIRECTION ( 'NONE', ( -0.0000000000000000000, -0.0000000000000000000, -1.000000000000000000 ) ) ;
|
||||
#193 = PRESENTATION_LAYER_ASSIGNMENT ( '', '', ( #154 ) ) ;
|
||||
#194 = LINE ( 'NONE', #712, #316 ) ;
|
||||
#195 = SURFACE_STYLE_USAGE ( .BOTH. , #638 ) ;
|
||||
#196 = ORIENTED_EDGE ( 'NONE', *, *, #327, .F. ) ;
|
||||
#197 = PRODUCT ( 'MSMP package', 'MSMP package', '', ( #308 ) ) ;
|
||||
#198 = EDGE_LOOP ( 'NONE', ( #785, #563, #19, #215 ) ) ;
|
||||
#199 = DIRECTION ( 'NONE', ( 1.000000000000000000, -0.0000000000000000000, -0.0000000000000000000 ) ) ;
|
||||
#200 = CARTESIAN_POINT ( 'NONE', ( -1.719783808284775000, 0.05618686558523977600, 0.6176654041047668900 ) ) ;
|
||||
#201 = LINE ( 'NONE', #358, #521 ) ;
|
||||
#202 = ORIENTED_EDGE ( 'NONE', *, *, #185, .T. ) ;
|
||||
#203 = CARTESIAN_POINT ( 'NONE', ( 0.1802161917152204900, -0.09381313441475436200, 0.5076654041047713500 ) ) ;
|
||||
#204 = SURFACE_SIDE_STYLE ('',( #242 ) ) ;
|
||||
#205 = VECTOR ( 'NONE', #46, 1000.000000000000000 ) ;
|
||||
#206 = STYLED_ITEM ( 'NONE', ( #95 ), #294 ) ;
|
||||
#207 =( NAMED_UNIT ( * ) SI_UNIT ( $, .STERADIAN. ) SOLID_ANGLE_UNIT ( ) );
|
||||
#208 = EDGE_CURVE ( 'NONE', #149, #685, #341, .T. ) ;
|
||||
#209 = DIRECTION ( 'NONE', ( 0.08682659386424779200, 0.9924325091389669700, 0.08682659386424779200 ) ) ;
|
||||
#210 = CARTESIAN_POINT ( 'NONE', ( 0.03021619171521966500, -0.09381313441475436200, -0.09233459589523193600 ) ) ;
|
||||
#211 = ORIENTED_EDGE ( 'NONE', *, *, #64, .F. ) ;
|
||||
#212 = FILL_AREA_STYLE ('',( #387 ) ) ;
|
||||
#213 = PLANE ( 'NONE', #306 ) ;
|
||||
#214 = CARTESIAN_POINT ( 'NONE', ( -1.553198322000227200, -0.1333861101138683600, -0.4257491096106812400 ) ) ;
|
||||
#215 = ORIENTED_EDGE ( 'NONE', *, *, #473, .F. ) ;
|
||||
#216 = CARTESIAN_POINT ( 'NONE', ( -1.569783808284774200, 0.05618686558523978300, 0.6176654041047668900 ) ) ;
|
||||
#217 =( NAMED_UNIT ( * ) PLANE_ANGLE_UNIT ( ) SI_UNIT ( $, .RADIAN. ) );
|
||||
#218 = FACE_OUTER_BOUND ( 'NONE', #663, .T. ) ;
|
||||
#219 = ORIENTED_EDGE ( 'NONE', *, *, #365, .T. ) ;
|
||||
#220 = ADVANCED_FACE ( 'NONE', ( #499 ), #416, .F. ) ;
|
||||
#221 = CARTESIAN_POINT ( 'NONE', ( -1.419783808284780200, 0.05618686558523977600, 0.6176654041047668900 ) ) ;
|
||||
#222 = MECHANICAL_DESIGN_GEOMETRIC_PRESENTATION_REPRESENTATION ( '', ( #691 ), #259 ) ;
|
||||
#223 = ORIENTED_EDGE ( 'NONE', *, *, #320, .F. ) ;
|
||||
#224 = DIRECTION ( 'NONE', ( 0.0000000000000000000, 0.9961946980917455500, -0.08715574274765836000 ) ) ;
|
||||
#225 = CARTESIAN_POINT ( 'NONE', ( 0.7802161917152238300, 0.05618686558523977600, -0.09233459589523193600 ) ) ;
|
||||
#226 = CARTESIAN_POINT ( 'NONE', ( -1.719783808284775000, -0.09381313441476020500, 0.6176654041047668900 ) ) ;
|
||||
#227 = AXIS2_PLACEMENT_3D ( 'NONE', #447, #528, #143 ) ;
|
||||
#228 = FACE_OUTER_BOUND ( 'NONE', #397, .T. ) ;
|
||||
#229 = EDGE_CURVE ( 'NONE', #317, #73, #427, .T. ) ;
|
||||
#230 = EDGE_LOOP ( 'NONE', ( #66, #461, #598, #588 ) ) ;
|
||||
#231 =( LENGTH_UNIT ( ) NAMED_UNIT ( * ) SI_UNIT ( .MILLI., .METRE. ) );
|
||||
#232 = DIRECTION ( 'NONE', ( 1.000000000000000000, 0.0000000000000000000, 0.0000000000000000000 ) ) ;
|
||||
#233 = ORIENTED_EDGE ( 'NONE', *, *, #629, .T. ) ;
|
||||
#234 = ADVANCED_FACE ( 'NONE', ( #266 ), #37, .T. ) ;
|
||||
#235 = CARTESIAN_POINT ( 'NONE', ( -1.569783808284774200, 0.05618686558523977600, 0.8576654041047709900 ) ) ;
|
||||
#236 = SURFACE_STYLE_USAGE ( .BOTH. , #420 ) ;
|
||||
#237 = VECTOR ( 'NONE', #270, 1000.000000000000000 ) ;
|
||||
#238 = ORIENTED_EDGE ( 'NONE', *, *, #498, .T. ) ;
|
||||
#239 = MECHANICAL_DESIGN_GEOMETRIC_PRESENTATION_REPRESENTATION ( '', ( #154 ), #281 ) ;
|
||||
#240 = ORIENTED_EDGE ( 'NONE', *, *, #448, .T. ) ;
|
||||
#241 = PRODUCT_DEFINITION_SHAPE ( 'NONE', 'NONE', #436 ) ;
|
||||
#242 = SURFACE_STYLE_FILL_AREA ( #646 ) ;
|
||||
#243 = LINE ( 'NONE', #216, #81 ) ;
|
||||
#244 = AXIS2_PLACEMENT_3D ( 'NONE', #770, #330, #711 ) ;
|
||||
#245 = APPLICATION_PROTOCOL_DEFINITION ( 'draft international standard', 'automotive_design', 1998, #690 ) ;
|
||||
#246 = ORIENTED_EDGE ( 'NONE', *, *, #290, .F. ) ;
|
||||
#247 = EDGE_CURVE ( 'NONE', #372, #665, #610, .T. ) ;
|
||||
#248 = LINE ( 'NONE', #446, #88 ) ;
|
||||
#249 = ORIENTED_EDGE ( 'NONE', *, *, #247, .F. ) ;
|
||||
#250 = LINE ( 'NONE', #273, #772 ) ;
|
||||
#251 = COLOUR_RGB ( '',0.5372549019607842900, 0.3490196078431372400, 0.3372549019607843400 ) ;
|
||||
#252 = DIRECTION ( 'NONE', ( 0.0000000000000000000, 0.0000000000000000000, 1.000000000000000000 ) ) ;
|
||||
#253 = VERTEX_POINT ( 'NONE', #590 ) ;
|
||||
#254 = CARTESIAN_POINT ( 'NONE', ( -0.4197838082847724100, -0.09381313441475436200, 0.6176654041047668900 ) ) ;
|
||||
#255 = CARTESIAN_POINT ( 'NONE', ( 0.0000000000000000000, 0.0000000000000000000, 0.0000000000000000000 ) ) ;
|
||||
#256 = FILL_AREA_STYLE ('',( #701 ) ) ;
|
||||
#257 = VECTOR ( 'NONE', #793, 1000.000000000000000 ) ;
|
||||
#258 = ORIENTED_EDGE ( 'NONE', *, *, #48, .T. ) ;
|
||||
#259 =( GEOMETRIC_REPRESENTATION_CONTEXT ( 3 ) GLOBAL_UNCERTAINTY_ASSIGNED_CONTEXT ( ( #468 ) ) GLOBAL_UNIT_ASSIGNED_CONTEXT ( ( #174, #681, #683 ) ) REPRESENTATION_CONTEXT ( 'NONE', 'WORKASPACE' ) );
|
||||
#260 = CARTESIAN_POINT ( 'NONE', ( 0.5838472000464882700, 0.5861868655852398000, 0.8112964124360327900 ) ) ;
|
||||
#261 = DIRECTION ( 'NONE', ( 0.0000000000000000000, 0.0000000000000000000, -1.000000000000000000 ) ) ;
|
||||
#262 = STYLED_ITEM ( 'NONE', ( #784 ), #543 ) ;
|
||||
#263 =( NAMED_UNIT ( * ) PLANE_ANGLE_UNIT ( ) SI_UNIT ( $, .RADIAN. ) );
|
||||
#264 = DIRECTION ( 'NONE', ( -1.000000000000000000, -0.0000000000000000000, -0.0000000000000000000 ) ) ;
|
||||
#265 = EDGE_CURVE ( 'NONE', #73, #368, #248, .T. ) ;
|
||||
#266 = FACE_OUTER_BOUND ( 'NONE', #357, .T. ) ;
|
||||
#267 = CARTESIAN_POINT ( 'NONE', ( -1.719783808284778500, -0.09381313441475439000, -0.2023345958952274500 ) ) ;
|
||||
#268 = LINE ( 'NONE', #30, #596 ) ;
|
||||
#269 = FACE_OUTER_BOUND ( 'NONE', #390, .T. ) ;
|
||||
#270 = DIRECTION ( 'NONE', ( -1.000000000000000000, -1.387778780781445700E-017, 0.0000000000000000000 ) ) ;
|
||||
#271 = DIRECTION ( 'NONE', ( -0.0000000000000000000, -0.08715574274765836000, -0.9961946980917455500 ) ) ;
|
||||
#272 = LINE ( 'NONE', #548, #114 ) ;
|
||||
#273 = CARTESIAN_POINT ( 'NONE', ( -1.569783808284774200, 0.05618686558523977600, 0.8576654041047709900 ) ) ;
|
||||
#274 = UNCERTAINTY_MEASURE_WITH_UNIT (LENGTH_MEASURE( 1.000000000000000100E-005 ), #502, 'distance_accuracy_value', 'NONE');
|
||||
#275 = ORIENTED_EDGE ( 'NONE', *, *, #531, .T. ) ;
|
||||
#276 = DIRECTION ( 'NONE', ( 1.000000000000000000, 0.0000000000000000000, 0.0000000000000000000 ) ) ;
|
||||
#277 = EDGE_LOOP ( 'NONE', ( #258, #758, #137, #626, #362, #574 ) ) ;
|
||||
#278 = ADVANCED_FACE ( 'NONE', ( #409 ), #653, .F. ) ;
|
||||
#279 = PRESENTATION_STYLE_ASSIGNMENT (( #236 ) ) ;
|
||||
#280 = ORIENTED_EDGE ( 'NONE', *, *, #407, .F. ) ;
|
||||
#281 =( GEOMETRIC_REPRESENTATION_CONTEXT ( 3 ) GLOBAL_UNCERTAINTY_ASSIGNED_CONTEXT ( ( #274 ) ) GLOBAL_UNIT_ASSIGNED_CONTEXT ( ( #502, #65, #428 ) ) REPRESENTATION_CONTEXT ( 'NONE', 'WORKASPACE' ) );
|
||||
#282 = DIRECTION ( 'NONE', ( 1.000000000000000000, -0.0000000000000000000, -0.0000000000000000000 ) ) ;
|
||||
#283 = CARTESIAN_POINT ( 'NONE', ( -1.556660508755886000, -0.09381313441476020500, -0.4423345958952280600 ) ) ;
|
||||
#284 = VERTEX_POINT ( 'NONE', #314 ) ;
|
||||
#285 = COLOUR_RGB ( '',0.5372549019607842900, 0.3490196078431372400, 0.3372549019607843400 ) ;
|
||||
#286 = DIRECTION ( 'NONE', ( 0.0000000000000000000, 0.0000000000000000000, 1.000000000000000000 ) ) ;
|
||||
#287 = EDGE_LOOP ( 'NONE', ( #632, #151, #533, #196 ) ) ;
|
||||
#288 = PRESENTATION_LAYER_ASSIGNMENT ( '', '', ( #206 ) ) ;
|
||||
#289 = CARTESIAN_POINT ( 'NONE', ( -1.569783808284774200, 0.05618686558523977600, -0.4423345958952280600 ) ) ;
|
||||
#290 = EDGE_CURVE ( 'NONE', #160, #253, #738, .T. ) ;
|
||||
#291 =( NAMED_UNIT ( * ) SI_UNIT ( $, .STERADIAN. ) SOLID_ANGLE_UNIT ( ) );
|
||||
#292 = LINE ( 'NONE', #614, #429 ) ;
|
||||
#293 = CARTESIAN_POINT ( 'NONE', ( -1.569783808284774200, 0.05618686558523977600, 0.8576654041047709900 ) ) ;
|
||||
#294 = ADVANCED_FACE ( 'NONE', ( #577 ), #24, .T. ) ;
|
||||
#295 = VECTOR ( 'NONE', #161, 1000.000000000000000 ) ;
|
||||
#296 = CARTESIAN_POINT ( 'NONE', ( 0.6302161917152264700, 0.05618686558523977600, 0.8576654041047709900 ) ) ;
|
||||
#297 = DIRECTION ( 'NONE', ( 0.0000000000000000000, 0.0000000000000000000, 1.000000000000000000 ) ) ;
|
||||
#298 = MECHANICAL_DESIGN_GEOMETRIC_PRESENTATION_REPRESENTATION ( '', ( #61 ), #332 ) ;
|
||||
#299 = ORIENTED_EDGE ( 'NONE', *, *, #505, .T. ) ;
|
||||
#300 = LINE ( 'NONE', #478, #119 ) ;
|
||||
#301 = ORIENTED_EDGE ( 'NONE', *, *, #523, .T. ) ;
|
||||
#302 = PLANE ( 'NONE', #470 ) ;
|
||||
#303 = COLOUR_RGB ( '',0.5372549019607842900, 0.3490196078431372400, 0.3372549019607843400 ) ;
|
||||
#304 = VECTOR ( 'NONE', #276, 1000.000000000000000 ) ;
|
||||
#305 = ORIENTED_EDGE ( 'NONE', *, *, #481, .T. ) ;
|
||||
#306 = AXIS2_PLACEMENT_3D ( 'NONE', #91, #271, #716 ) ;
|
||||
#307 = STYLED_ITEM ( 'NONE', ( #122 ), #34 ) ;
|
||||
#308 = PRODUCT_CONTEXT ( 'NONE', #628, 'mechanical' ) ;
|
||||
#309 = FACE_OUTER_BOUND ( 'NONE', #9, .T. ) ;
|
||||
#310 = FACE_OUTER_BOUND ( 'NONE', #431, .T. ) ;
|
||||
#311 = VERTEX_POINT ( 'NONE', #648 ) ;
|
||||
#312 = DIRECTION ( 'NONE', ( 0.0000000000000000000, 0.0000000000000000000, 1.000000000000000000 ) ) ;
|
||||
#313 = CARTESIAN_POINT ( 'NONE', ( 0.5838472000464882700, 0.5861868655852398000, 0.8576654041047709900 ) ) ;
|
||||
#314 = CARTESIAN_POINT ( 'NONE', ( -1.719783808284775000, -0.09381313441476020500, 0.6176654041047668900 ) ) ;
|
||||
#315 = LINE ( 'NONE', #318, #465 ) ;
|
||||
#316 = VECTOR ( 'NONE', #388, 1000.000000000000000 ) ;
|
||||
#317 = VERTEX_POINT ( 'NONE', #260 ) ;
|
||||
#318 = CARTESIAN_POINT ( 'NONE', ( -1.719783808284775000, 0.05618686558523977600, 0.6176654041047668900 ) ) ;
|
||||
#319 = UNCERTAINTY_MEASURE_WITH_UNIT (LENGTH_MEASURE( 1.000000000000000100E-005 ), #496, 'distance_accuracy_value', 'NONE');
|
||||
#320 = EDGE_CURVE ( 'NONE', #321, #73, #659, .T. ) ;
|
||||
#321 = VERTEX_POINT ( 'NONE', #83 ) ;
|
||||
#322 = PLANE ( 'NONE', #63 ) ;
|
||||
#323 = PRESENTATION_STYLE_ASSIGNMENT (( #603 ) ) ;
|
||||
#324 = VERTEX_POINT ( 'NONE', #444 ) ;
|
||||
#325 = MECHANICAL_DESIGN_GEOMETRIC_PRESENTATION_REPRESENTATION ( '', ( #206 ), #363 ) ;
|
||||
#326 = SURFACE_STYLE_USAGE ( .BOTH. , #767 ) ;
|
||||
#327 = EDGE_CURVE ( 'NONE', #149, #734, #11, .T. ) ;
|
||||
#328 = LINE ( 'NONE', #730, #205 ) ;
|
||||
#329 = VECTOR ( 'NONE', #282, 1000.000000000000000 ) ;
|
||||
#330 = DIRECTION ( 'NONE', ( 0.0000000000000000000, 0.0000000000000000000, -1.000000000000000000 ) ) ;
|
||||
#331 = CARTESIAN_POINT ( 'NONE', ( 0.03021619171522313400, -0.09381313441476020500, 0.5076654041047713500 ) ) ;
|
||||
#332 =( GEOMETRIC_REPRESENTATION_CONTEXT ( 3 ) GLOBAL_UNCERTAINTY_ASSIGNED_CONTEXT ( ( #54 ) ) GLOBAL_UNIT_ASSIGNED_CONTEXT ( ( #231, #675, #291 ) ) REPRESENTATION_CONTEXT ( 'NONE', 'WORKASPACE' ) );
|
||||
#333 = ORIENTED_EDGE ( 'NONE', *, *, #586, .T. ) ;
|
||||
#334 = DIRECTION ( 'NONE', ( 0.0000000000000000000, 1.000000000000000000, 0.0000000000000000000 ) ) ;
|
||||
#335 =( NAMED_UNIT ( * ) SI_UNIT ( $, .STERADIAN. ) SOLID_ANGLE_UNIT ( ) );
|
||||
#336 = SURFACE_STYLE_FILL_AREA ( #256 ) ;
|
||||
#337 = VECTOR ( 'NONE', #591, 1000.000000000000000 ) ;
|
||||
#338 = VERTEX_POINT ( 'NONE', #584 ) ;
|
||||
#339 = ORIENTED_EDGE ( 'NONE', *, *, #647, .F. ) ;
|
||||
#340 = SURFACE_STYLE_USAGE ( .BOTH. , #153 ) ;
|
||||
#341 = LINE ( 'NONE', #560, #183 ) ;
|
||||
#342 = EDGE_LOOP ( 'NONE', ( #240, #541, #788, #43 ) ) ;
|
||||
#343 = DIRECTION ( 'NONE', ( 0.0000000000000000000, 0.0000000000000000000, 1.000000000000000000 ) ) ;
|
||||
#344 = PRESENTATION_LAYER_ASSIGNMENT ( '', '', ( #262 ) ) ;
|
||||
#345 = COLOUR_RGB ( '',0.5372549019607842900, 0.3490196078431372400, 0.3372549019607843400 ) ;
|
||||
#346 = PRESENTATION_LAYER_ASSIGNMENT ( '', '', ( #307 ) ) ;
|
||||
#347 = FILL_AREA_STYLE_COLOUR ( '', #345 ) ;
|
||||
#348 = CARTESIAN_POINT ( 'NONE', ( 0.6302161917152264700, 0.05618686558523977600, 0.8576654041047709900 ) ) ;
|
||||
#349 = CARTESIAN_POINT ( 'NONE', ( 0.03021619171522313400, -0.09381313441476020500, 0.5076654041047713500 ) ) ;
|
||||
#350 = DIRECTION ( 'NONE', ( -0.08715574274765836000, 0.9961946980917455500, 0.0000000000000000000 ) ) ;
|
||||
#351 = DIRECTION ( 'NONE', ( 0.0000000000000000000, 0.0000000000000000000, 1.000000000000000000 ) ) ;
|
||||
#352 = VERTEX_POINT ( 'NONE', #203 ) ;
|
||||
#353 = ADVANCED_FACE ( 'NONE', ( #512 ), #763, .T. ) ;
|
||||
#354 = LINE ( 'NONE', #129, #536 ) ;
|
||||
#355 = ADVANCED_BREP_SHAPE_REPRESENTATION ( 'MSMP package', ( #543, #395 ), #33 ) ;
|
||||
#356 = LINE ( 'NONE', #525, #684 ) ;
|
||||
#357 = EDGE_LOOP ( 'NONE', ( #366, #804, #514, #162 ) ) ;
|
||||
#358 = CARTESIAN_POINT ( 'NONE', ( -1.556660508755886000, -0.09381313441476020500, -0.4423345958952280600 ) ) ;
|
||||
#359 = CARTESIAN_POINT ( 'NONE', ( 0.6170928921863383400, -0.09381313441476020500, 0.5076654041047713500 ) ) ;
|
||||
#360 = EDGE_CURVE ( 'NONE', #284, #621, #177, .T. ) ;
|
||||
#361 = LINE ( 'NONE', #226, #686 ) ;
|
||||
#362 = ORIENTED_EDGE ( 'NONE', *, *, #593, .F. ) ;
|
||||
#363 =( GEOMETRIC_REPRESENTATION_CONTEXT ( 3 ) GLOBAL_UNCERTAINTY_ASSIGNED_CONTEXT ( ( #41 ) ) GLOBAL_UNIT_ASSIGNED_CONTEXT ( ( #708, #263, #207 ) ) REPRESENTATION_CONTEXT ( 'NONE', 'WORKASPACE' ) );
|
||||
#364 = DIRECTION ( 'NONE', ( 0.0000000000000000000, 0.0000000000000000000, 1.000000000000000000 ) ) ;
|
||||
#365 = EDGE_CURVE ( 'NONE', #545, #443, #315, .T. ) ;
|
||||
#366 = ORIENTED_EDGE ( 'NONE', *, *, #124, .T. ) ;
|
||||
#367 =( NAMED_UNIT ( * ) PLANE_ANGLE_UNIT ( ) SI_UNIT ( $, .RADIAN. ) );
|
||||
#368 = VERTEX_POINT ( 'NONE', #75 ) ;
|
||||
#369 =( NAMED_UNIT ( * ) PLANE_ANGLE_UNIT ( ) SI_UNIT ( $, .RADIAN. ) );
|
||||
#370 = VECTOR ( 'NONE', #761, 1000.000000000000000 ) ;
|
||||
#371 = CARTESIAN_POINT ( 'NONE', ( 0.7802161917152238300, 0.05618686558523977600, -0.09233459589523193600 ) ) ;
|
||||
#372 = VERTEX_POINT ( 'NONE', #515 ) ;
|
||||
#373 = DIRECTION ( 'NONE', ( -0.08682659386424779200, 0.9924325091389669700, 0.08682659386424779200 ) ) ;
|
||||
#374 = ORIENTED_EDGE ( 'NONE', *, *, #625, .F. ) ;
|
||||
#375 = AXIS2_PLACEMENT_3D ( 'NONE', #803, #58, #620 ) ;
|
||||
#376 = CARTESIAN_POINT ( 'NONE', ( 0.7802161917152238300, -0.09381313441476020500, -0.09233459589523193600 ) ) ;
|
||||
#377 = CARTESIAN_POINT ( 'NONE', ( 0.7802161917152238300, 0.05618686558523977600, 0.5076654041047713500 ) ) ;
|
||||
#378 = DIRECTION ( 'NONE', ( 0.0000000000000000000, -1.000000000000000000, 0.0000000000000000000 ) ) ;
|
||||
#379 = COLOUR_RGB ( '',0.5372549019607842900, 0.3490196078431372400, 0.3372549019607843400 ) ;
|
||||
#380 = PRESENTATION_STYLE_ASSIGNMENT (( #152 ) ) ;
|
||||
#381 = MECHANICAL_DESIGN_GEOMETRIC_PRESENTATION_REPRESENTATION ( '', ( #262 ), #425 ) ;
|
||||
#382 = FILL_AREA_STYLE ('',( #87 ) ) ;
|
||||
#383 = PLANE ( 'NONE', #227 ) ;
|
||||
#384 = MECHANICAL_DESIGN_GEOMETRIC_PRESENTATION_REPRESENTATION ( '', ( #307 ), #426 ) ;
|
||||
#385 = ORIENTED_EDGE ( 'NONE', *, *, #688, .F. ) ;
|
||||
#386 = UNCERTAINTY_MEASURE_WITH_UNIT (LENGTH_MEASURE( 1.000000000000000100E-005 ), #600, 'distance_accuracy_value', 'NONE');
|
||||
#387 = FILL_AREA_STYLE_COLOUR ( '', #733 ) ;
|
||||
#388 = DIRECTION ( 'NONE', ( 1.000000000000000000, 1.387778780781445700E-017, 0.0000000000000000000 ) ) ;
|
||||
#389 = FILL_AREA_STYLE ('',( #535 ) ) ;
|
||||
#390 = EDGE_LOOP ( 'NONE', ( #643, #305, #522, #249, #456, #657 ) ) ;
|
||||
#391 = FILL_AREA_STYLE ('',( #595 ) ) ;
|
||||
#392 = DIRECTION ( 'NONE', ( 0.08715574274765836000, 0.9961946980917455500, 0.0000000000000000000 ) ) ;
|
||||
#393 = SURFACE_STYLE_FILL_AREA ( #382 ) ;
|
||||
#394 = ORIENTED_EDGE ( 'NONE', *, *, #142, .T. ) ;
|
||||
#395 = AXIS2_PLACEMENT_3D ( 'NONE', #255, #364, #748 ) ;
|
||||
#396 = ADVANCED_FACE ( 'NONE', ( #639 ), #759, .F. ) ;
|
||||
#397 = EDGE_LOOP ( 'NONE', ( #333, #517, #417, #219 ) ) ;
|
||||
#398 = EDGE_CURVE ( 'NONE', #724, #731, #80, .T. ) ;
|
||||
#399 = DIRECTION ( 'NONE', ( 0.0000000000000000000, 0.0000000000000000000, 1.000000000000000000 ) ) ;
|
||||
#400 = DIRECTION ( 'NONE', ( 1.000000000000000000, -0.0000000000000000000, 0.0000000000000000000 ) ) ;
|
||||
#401 = VECTOR ( 'NONE', #578, 1000.000000000000000 ) ;
|
||||
#402 = SURFACE_STYLE_FILL_AREA ( #97 ) ;
|
||||
#403 = CARTESIAN_POINT ( 'NONE', ( 0.6302161917152264700, 0.05618686558523977600, -0.4423345958952280600 ) ) ;
|
||||
#404 = DIRECTION ( 'NONE', ( -1.000000000000000000, -0.0000000000000000000, -0.0000000000000000000 ) ) ;
|
||||
#405 = SURFACE_STYLE_FILL_AREA ( #389 ) ;
|
||||
#406 = ORIENTED_EDGE ( 'NONE', *, *, #442, .F. ) ;
|
||||
#407 = EDGE_CURVE ( 'NONE', #311, #160, #587, .T. ) ;
|
||||
#408 = DIRECTION ( 'NONE', ( 0.9961946980917455500, -0.08715574274765836000, 0.0000000000000000000 ) ) ;
|
||||
#409 = FACE_OUTER_BOUND ( 'NONE', #287, .T. ) ;
|
||||
#410 = DIRECTION ( 'NONE', ( 0.0000000000000000000, -0.0000000000000000000, 1.000000000000000000 ) ) ;
|
||||
#411 = DIRECTION ( 'NONE', ( 1.000000000000000000, 0.0000000000000000000, 0.0000000000000000000 ) ) ;
|
||||
#412 = VECTOR ( 'NONE', #654, 1000.000000000000100 ) ;
|
||||
#413 = VECTOR ( 'NONE', #192, 1000.000000000000000 ) ;
|
||||
#414 = ADVANCED_FACE ( 'NONE', ( #228 ), #322, .F. ) ;
|
||||
#415 = DIRECTION ( 'NONE', ( 1.000000000000000000, 1.387778780781445700E-017, 0.0000000000000000000 ) ) ;
|
||||
#416 = PLANE ( 'NONE', #725 ) ;
|
||||
#417 = ORIENTED_EDGE ( 'NONE', *, *, #647, .T. ) ;
|
||||
#418 = CARTESIAN_POINT ( 'NONE', ( -1.569783808284774200, 0.5861868655852398000, -0.4423345958952280600 ) ) ;
|
||||
#419 = FACE_OUTER_BOUND ( 'NONE', #703, .T. ) ;
|
||||
#420 = SURFACE_SIDE_STYLE ('',( #743 ) ) ;
|
||||
#421 = VERTEX_POINT ( 'NONE', #634 ) ;
|
||||
#422 = STYLED_ITEM ( 'NONE', ( #323 ), #220 ) ;
|
||||
#423 = EDGE_CURVE ( 'NONE', #324, #665, #503, .T. ) ;
|
||||
#424 = ORIENTED_EDGE ( 'NONE', *, *, #746, .F. ) ;
|
||||
#425 =( GEOMETRIC_REPRESENTATION_CONTEXT ( 3 ) GLOBAL_UNCERTAINTY_ASSIGNED_CONTEXT ( ( #775 ) ) GLOBAL_UNIT_ASSIGNED_CONTEXT ( ( #94, #534, #148 ) ) REPRESENTATION_CONTEXT ( 'NONE', 'WORKASPACE' ) );
|
||||
#426 =( GEOMETRIC_REPRESENTATION_CONTEXT ( 3 ) GLOBAL_UNCERTAINTY_ASSIGNED_CONTEXT ( ( #622 ) ) GLOBAL_UNIT_ASSIGNED_CONTEXT ( ( #459, #98, #538 ) ) REPRESENTATION_CONTEXT ( 'NONE', 'WORKASPACE' ) );
|
||||
#427 = LINE ( 'NONE', #156, #769 ) ;
|
||||
#428 =( NAMED_UNIT ( * ) SI_UNIT ( $, .STERADIAN. ) SOLID_ANGLE_UNIT ( ) );
|
||||
#429 = VECTOR ( 'NONE', #415, 1000.000000000000000 ) ;
|
||||
#430 = LINE ( 'NONE', #727, #776 ) ;
|
||||
#431 = EDGE_LOOP ( 'NONE', ( #693, #507, #754, #86 ) ) ;
|
||||
#432 = ORIENTED_EDGE ( 'NONE', *, *, #22, .F. ) ;
|
||||
#433 = SURFACE_STYLE_USAGE ( .BOTH. , #72 ) ;
|
||||
#434 = VECTOR ( 'NONE', #555, 1000.000000000000000 ) ;
|
||||
#435 = VERTEX_POINT ( 'NONE', #642 ) ;
|
||||
#436 = PRODUCT_DEFINITION ( 'δ֪', '', #491, #494 ) ;
|
||||
#437 = DIRECTION ( 'NONE', ( -1.000000000000000000, 0.0000000000000000000, 0.0000000000000000000 ) ) ;
|
||||
#438 = CARTESIAN_POINT ( 'NONE', ( -1.719783808284775000, -0.09381313441476020500, -0.2023345958952274500 ) ) ;
|
||||
#439 = DIRECTION ( 'NONE', ( 0.08715574274765836000, -0.9961946980917455500, 0.0000000000000000000 ) ) ;
|
||||
#440 = DIRECTION ( 'NONE', ( -0.9961946980917455500, -0.08715574274765836000, 0.0000000000000000000 ) ) ;
|
||||
#441 = VERTEX_POINT ( 'NONE', #376 ) ;
|
||||
#442 = EDGE_CURVE ( 'NONE', #443, #284, #479, .T. ) ;
|
||||
#443 = VERTEX_POINT ( 'NONE', #200 ) ;
|
||||
#444 = CARTESIAN_POINT ( 'NONE', ( 0.6302161917152264700, 0.05618686558523977600, -0.09233459589523193600 ) ) ;
|
||||
#445 = UNCERTAINTY_MEASURE_WITH_UNIT (LENGTH_MEASURE( 1.000000000000000100E-005 ), #138, 'distance_accuracy_value', 'NONE');
|
||||
#446 = CARTESIAN_POINT ( 'NONE', ( 0.6302161917152264700, 0.05618686558523977600, 0.8576654041047709900 ) ) ;
|
||||
#447 = CARTESIAN_POINT ( 'NONE', ( -1.719783808284775000, -0.09381313441476020500, -0.2023345958952274500 ) ) ;
|
||||
#448 = EDGE_CURVE ( 'NONE', #441, #724, #59, .T. ) ;
|
||||
#449 = LINE ( 'NONE', #313, #413 ) ;
|
||||
#450 =( NAMED_UNIT ( * ) SI_UNIT ( $, .STERADIAN. ) SOLID_ANGLE_UNIT ( ) );
|
||||
#451 = CARTESIAN_POINT ( 'NONE', ( 0.6204156770925408600, -0.05583352914605126800, -0.4325340812725425600 ) ) ;
|
||||
#452 = LINE ( 'NONE', #471, #615 ) ;
|
||||
#453 = PLANE ( 'NONE', #572 ) ;
|
||||
#454 = SURFACE_STYLE_USAGE ( .BOTH. , #29 ) ;
|
||||
#455 = AXIS2_PLACEMENT_3D ( 'NONE', #349, #795, #410 ) ;
|
||||
#456 = ORIENTED_EDGE ( 'NONE', *, *, #766, .F. ) ;
|
||||
#457 = DIRECTION ( 'NONE', ( 0.9961946980917455500, -0.08715574274765836000, 0.0000000000000000000 ) ) ;
|
||||
#458 = PLANE ( 'NONE', #742 ) ;
|
||||
#459 =( LENGTH_UNIT ( ) NAMED_UNIT ( * ) SI_UNIT ( .MILLI., .METRE. ) );
|
||||
#460 = LINE ( 'NONE', #53, #130 ) ;
|
||||
#461 = ORIENTED_EDGE ( 'NONE', *, *, #481, .F. ) ;
|
||||
#462 = CARTESIAN_POINT ( 'NONE', ( 0.6302161917152264700, 0.05618686558523976900, -0.09233459589523193600 ) ) ;
|
||||
#463 = DIRECTION ( 'NONE', ( 0.08682659386424779200, -0.9924325091389669700, -0.08682659386424779200 ) ) ;
|
||||
#464 = ADVANCED_FACE ( 'NONE', ( #218 ), #134, .T. ) ;
|
||||
#465 = VECTOR ( 'NONE', #760, 1000.000000000000000 ) ;
|
||||
#466 = PLANE ( 'NONE', #13 ) ;
|
||||
#467 = COLOUR_RGB ( '',0.7921568627450980000, 0.8196078431372548800, 0.9333333333333333500 ) ;
|
||||
#468 = UNCERTAINTY_MEASURE_WITH_UNIT (LENGTH_MEASURE( 1.000000000000000100E-005 ), #174, 'distance_accuracy_value', 'NONE');
|
||||
#469 = ORIENTED_EDGE ( 'NONE', *, *, #4, .F. ) ;
|
||||
#470 = AXIS2_PLACEMENT_3D ( 'NONE', #180, #783, #617 ) ;
|
||||
#471 = CARTESIAN_POINT ( 'NONE', ( -1.719783808284775000, 0.05618686558523977600, -0.2023345958952274500 ) ) ;
|
||||
#472 = VERTEX_POINT ( 'NONE', #709 ) ;
|
||||
#473 = EDGE_CURVE ( 'NONE', #665, #571, #356, .T. ) ;
|
||||
#474 = DIRECTION ( 'NONE', ( 0.0000000000000000000, 0.08715574274765836000, -0.9961946980917455500 ) ) ;
|
||||
#475 = DIRECTION ( 'NONE', ( 0.08715574274765836000, 0.9961946980917455500, 0.0000000000000000000 ) ) ;
|
||||
#476 = STYLED_ITEM ( 'NONE', ( #380 ), #556 ) ;
|
||||
#477 = PLANE ( 'NONE', #455 ) ;
|
||||
#478 = CARTESIAN_POINT ( 'NONE', ( 0.6302161917152264700, 0.5861868655852398000, -0.3959656042264898500 ) ) ;
|
||||
#479 = LINE ( 'NONE', #702, #370 ) ;
|
||||
#480 = VECTOR ( 'NONE', #723, 1000.000000000000000 ) ;
|
||||
#481 = EDGE_CURVE ( 'NONE', #368, #324, #36, .T. ) ;
|
||||
#482 = CARTESIAN_POINT ( 'NONE', ( 0.6170928921863383400, -0.09381313441476023300, -0.4292112963663399800 ) ) ;
|
||||
#483 = UNCERTAINTY_MEASURE_WITH_UNIT (LENGTH_MEASURE( 1.000000000000000100E-005 ), #10, 'distance_accuracy_value', 'NONE');
|
||||
#484 = DIRECTION ( 'NONE', ( -0.0000000000000000000, -1.000000000000000000, -0.0000000000000000000 ) ) ;
|
||||
#485 = ADVANCED_FACE ( 'NONE', ( #178 ), #383, .T. ) ;
|
||||
#486 = DIRECTION ( 'NONE', ( -0.0000000000000000000, -0.0000000000000000000, -1.000000000000000000 ) ) ;
|
||||
#487 = LINE ( 'NONE', #737, #656 ) ;
|
||||
#488 = EDGE_LOOP ( 'NONE', ( #55, #16, #710, #301 ) ) ;
|
||||
#489 = FILL_AREA_STYLE ('',( #117 ) ) ;
|
||||
#490 = VECTOR ( 'NONE', #209, 1000.000000000000100 ) ;
|
||||
#491 = PRODUCT_DEFINITION_FORMATION_WITH_SPECIFIED_SOURCE ( 'ÈκÎ', '', #197, .NOT_KNOWN. ) ;
|
||||
#492 = VECTOR ( 'NONE', #771, 1000.000000000000100 ) ;
|
||||
#493 = ORIENTED_EDGE ( 'NONE', *, *, #746, .T. ) ;
|
||||
#494 = PRODUCT_DEFINITION_CONTEXT ( 'detailed design', #690, 'design' ) ;
|
||||
#495 =( GEOMETRIC_REPRESENTATION_CONTEXT ( 3 ) GLOBAL_UNCERTAINTY_ASSIGNED_CONTEXT ( ( #104 ) ) GLOBAL_UNIT_ASSIGNED_CONTEXT ( ( #581, #132, #511 ) ) REPRESENTATION_CONTEXT ( 'NONE', 'WORKASPACE' ) );
|
||||
#496 =( LENGTH_UNIT ( ) NAMED_UNIT ( * ) SI_UNIT ( .MILLI., .METRE. ) );
|
||||
#497 = ORIENTED_EDGE ( 'NONE', *, *, #360, .T. ) ;
|
||||
#498 = EDGE_CURVE ( 'NONE', #724, #607, #513, .T. ) ;
|
||||
#499 = FACE_OUTER_BOUND ( 'NONE', #102, .T. ) ;
|
||||
#500 = VERTEX_POINT ( 'NONE', #649 ) ;
|
||||
#501 = AXIS2_PLACEMENT_3D ( 'NONE', #524, #699, #14 ) ;
|
||||
#502 =( LENGTH_UNIT ( ) NAMED_UNIT ( * ) SI_UNIT ( .MILLI., .METRE. ) );
|
||||
#503 = LINE ( 'NONE', #15, #5 ) ;
|
||||
#504 = LINE ( 'NONE', #462, #145 ) ;
|
||||
#505 = EDGE_CURVE ( 'NONE', #621, #311, #194, .T. ) ;
|
||||
#506 = DIRECTION ( 'NONE', ( -1.000000000000000000, -0.0000000000000000000, -0.0000000000000000000 ) ) ;
|
||||
#507 = ORIENTED_EDGE ( 'NONE', *, *, #68, .T. ) ;
|
||||
#508 = STYLED_ITEM ( 'NONE', ( #597 ), #781 ) ;
|
||||
#509 = FILL_AREA_STYLE ('',( #580 ) ) ;
|
||||
#510 = VECTOR ( 'NONE', #506, 1000.000000000000000 ) ;
|
||||
#511 =( NAMED_UNIT ( * ) SI_UNIT ( $, .STERADIAN. ) SOLID_ANGLE_UNIT ( ) );
|
||||
#512 = FACE_OUTER_BOUND ( 'NONE', #77, .T. ) ;
|
||||
#513 = LINE ( 'NONE', #210, #337 ) ;
|
||||
#514 = ORIENTED_EDGE ( 'NONE', *, *, #567, .T. ) ;
|
||||
#515 = CARTESIAN_POINT ( 'NONE', ( 0.5838472000464882700, 0.5861868655852398000, -0.3959656042264898500 ) ) ;
|
||||
#516 = CARTESIAN_POINT ( 'NONE', ( 0.03021619171522313400, -0.09381313441476020500, -0.09233459589523193600 ) ) ;
|
||||
#517 = ORIENTED_EDGE ( 'NONE', *, *, #667, .F. ) ;
|
||||
#518 = PRESENTATION_LAYER_ASSIGNMENT ( '', '', ( #422 ) ) ;
|
||||
#519 = CARTESIAN_POINT ( 'NONE', ( -1.569783808284774200, 0.05618686558523977600, -0.4423345958952280600 ) ) ;
|
||||
#520 =( NAMED_UNIT ( * ) PLANE_ANGLE_UNIT ( ) SI_UNIT ( $, .RADIAN. ) );
|
||||
#521 = VECTOR ( 'NONE', #343, 1000.000000000000000 ) ;
|
||||
#522 = ORIENTED_EDGE ( 'NONE', *, *, #423, .T. ) ;
|
||||
#523 = EDGE_CURVE ( 'NONE', #731, #665, #12, .T. ) ;
|
||||
#524 = CARTESIAN_POINT ( 'NONE', ( -1.719783808284775000, -0.09381313441476020500, -0.2023345958952274500 ) ) ;
|
||||
#525 = CARTESIAN_POINT ( 'NONE', ( 0.6302161917152264700, 0.05618686558523977600, -0.4423345958952280600 ) ) ;
|
||||
#526 = VECTOR ( 'NONE', #792, 1000.000000000000000 ) ;
|
||||
#527 = PRESENTATION_LAYER_ASSIGNMENT ( '', '', ( #476 ) ) ;
|
||||
#528 = DIRECTION ( 'NONE', ( 0.0000000000000000000, -1.000000000000000000, 0.0000000000000000000 ) ) ;
|
||||
#529 = ORIENTED_EDGE ( 'NONE', *, *, #407, .T. ) ;
|
||||
#530 =( LENGTH_UNIT ( ) NAMED_UNIT ( * ) SI_UNIT ( .MILLI., .METRE. ) );
|
||||
#531 = EDGE_CURVE ( 'NONE', #253, #435, #159, .T. ) ;
|
||||
#532 = DIRECTION ( 'NONE', ( -1.000000000000000000, -0.0000000000000000000, -0.0000000000000000000 ) ) ;
|
||||
#533 = ORIENTED_EDGE ( 'NONE', *, *, #554, .T. ) ;
|
||||
#534 =( NAMED_UNIT ( * ) PLANE_ANGLE_UNIT ( ) SI_UNIT ( $, .RADIAN. ) );
|
||||
#535 = FILL_AREA_STYLE_COLOUR ( '', #285 ) ;
|
||||
#536 = VECTOR ( 'NONE', #576, 1000.000000000000100 ) ;
|
||||
#537 = SURFACE_SIDE_STYLE ('',( #402 ) ) ;
|
||||
#538 =( NAMED_UNIT ( * ) SI_UNIT ( $, .STERADIAN. ) SOLID_ANGLE_UNIT ( ) );
|
||||
#539 = VECTOR ( 'NONE', #25, 1000.000000000000000 ) ;
|
||||
#540 = ADVANCED_FACE ( 'NONE', ( #807 ), #213, .T. ) ;
|
||||
#541 = ORIENTED_EDGE ( 'NONE', *, *, #606, .F. ) ;
|
||||
#542 = CARTESIAN_POINT ( 'NONE', ( -1.569783808284774200, 0.05618686558523977600, -0.4423345958952280600 ) ) ;
|
||||
#543 = MANIFOLD_SOLID_BREP ( 'Ïû³ýÌØÕ÷1', #618 ) ;
|
||||
#544 = PRODUCT_RELATED_PRODUCT_CATEGORY ( 'part', '', ( #197 ) ) ;
|
||||
#545 = VERTEX_POINT ( 'NONE', #655 ) ;
|
||||
#546 = ORIENTED_EDGE ( 'NONE', *, *, #185, .F. ) ;
|
||||
#547 = EDGE_CURVE ( 'NONE', #565, #621, #243, .T. ) ;
|
||||
#548 = CARTESIAN_POINT ( 'NONE', ( -1.569783808284774200, 0.05618686558523977600, -0.4423345958952280600 ) ) ;
|
||||
#549 = CARTESIAN_POINT ( 'NONE', ( -0.4197838082847724100, -0.09381313441475436200, -0.2023345958952274500 ) ) ;
|
||||
#550 = CARTESIAN_POINT ( 'NONE', ( 0.7802161917152238300, 0.05618686558523977600, 0.5076654041047713500 ) ) ;
|
||||
#551 = STYLED_ITEM ( 'NONE', ( #135 ), #396 ) ;
|
||||
#552 = FILL_AREA_STYLE ('',( #347 ) ) ;
|
||||
#553 = ORIENTED_EDGE ( 'NONE', *, *, #64, .T. ) ;
|
||||
#554 = EDGE_CURVE ( 'NONE', #188, #734, #136, .T. ) ;
|
||||
#555 = DIRECTION ( 'NONE', ( -0.0000000000000000000, -1.000000000000000000, -0.0000000000000000000 ) ) ;
|
||||
#556 = ADVANCED_FACE ( 'NONE', ( #728 ), #458, .T. ) ;
|
||||
#557 = CARTESIAN_POINT ( 'NONE', ( 0.7802161917152238300, -0.09381313441476020500, 0.5076654041047713500 ) ) ;
|
||||
#558 = DIRECTION ( 'NONE', ( 0.0000000000000000000, 0.9961946980917455500, 0.08715574274765836000 ) ) ;
|
||||
#559 = VERTEX_POINT ( 'NONE', #777 ) ;
|
||||
#560 = CARTESIAN_POINT ( 'NONE', ( 0.7802161917152238300, 0.05618686558523977600, -0.09233459589523193600 ) ) ;
|
||||
#561 = AXIS2_PLACEMENT_3D ( 'NONE', #296, #736, #350 ) ;
|
||||
#562 = ORIENTED_EDGE ( 'NONE', *, *, #789, .F. ) ;
|
||||
#563 = ORIENTED_EDGE ( 'NONE', *, *, #688, .T. ) ;
|
||||
#564 = CARTESIAN_POINT ( 'NONE', ( -1.523414816616035900, 0.5861868655852398000, 0.8112964124360327900 ) ) ;
|
||||
#565 = VERTEX_POINT ( 'NONE', #99 ) ;
|
||||
#566 = MECHANICAL_DESIGN_GEOMETRIC_PRESENTATION_REPRESENTATION ( '', ( #422 ), #605 ) ;
|
||||
#567 = EDGE_CURVE ( 'NONE', #179, #317, #673, .T. ) ;
|
||||
#568 = VECTOR ( 'NONE', #696, 1000.000000000000000 ) ;
|
||||
#569 = COLOUR_RGB ( '',0.5372549019607842900, 0.3490196078431372400, 0.3372549019607843400 ) ;
|
||||
#570 = MECHANICAL_DESIGN_GEOMETRIC_PRESENTATION_REPRESENTATION ( '', ( #476 ), #609 ) ;
|
||||
#571 = VERTEX_POINT ( 'NONE', #542 ) ;
|
||||
#572 = AXIS2_PLACEMENT_3D ( 'NONE', #106, #351, #800 ) ;
|
||||
#573 = SURFACE_SIDE_STYLE ('',( #794 ) ) ;
|
||||
#574 = ORIENTED_EDGE ( 'NONE', *, *, #789, .T. ) ;
|
||||
#575 = EDGE_LOOP ( 'NONE', ( #139, #275, #211, #339 ) ) ;
|
||||
#576 = DIRECTION ( 'NONE', ( -0.08682659386424779200, -0.9924325091389669700, -0.08682659386424779200 ) ) ;
|
||||
#577 = FACE_OUTER_BOUND ( 'NONE', #579, .T. ) ;
|
||||
#578 = DIRECTION ( 'NONE', ( 0.0000000000000000000, 0.0000000000000000000, -1.000000000000000000 ) ) ;
|
||||
#579 = EDGE_LOOP ( 'NONE', ( #246, #280, #719, #700, #546, #121 ) ) ;
|
||||
#580 = FILL_AREA_STYLE_COLOUR ( '', #251 ) ;
|
||||
#581 =( LENGTH_UNIT ( ) NAMED_UNIT ( * ) SI_UNIT ( .MILLI., .METRE. ) );
|
||||
#582 = ORIENTED_EDGE ( 'NONE', *, *, #208, .F. ) ;
|
||||
#583 = STYLED_ITEM ( 'NONE', ( #726 ), #414 ) ;
|
||||
#584 = CARTESIAN_POINT ( 'NONE', ( -1.523414816616035900, 0.5861868655852398000, -0.3959656042264898500 ) ) ;
|
||||
#585 = SURFACE_STYLE_USAGE ( .BOTH. , #204 ) ;
|
||||
#586 = EDGE_CURVE ( 'NONE', #443, #565, #26, .T. ) ;
|
||||
#587 = LINE ( 'NONE', #254, #568 ) ;
|
||||
#588 = ORIENTED_EDGE ( 'NONE', *, *, #208, .T. ) ;
|
||||
#589 = AXIS2_PLACEMENT_3D ( 'NONE', #519, #78, #439 ) ;
|
||||
#590 = CARTESIAN_POINT ( 'NONE', ( -1.556660508755886000, -0.09381313441476020500, -0.2023345958952274500 ) ) ;
|
||||
#591 = DIRECTION ( 'NONE', ( -1.000000000000000000, -1.387778780781445700E-017, 0.0000000000000000000 ) ) ;
|
||||
#592 = SURFACE_STYLE_FILL_AREA ( #212 ) ;
|
||||
#593 = EDGE_CURVE ( 'NONE', #338, #179, #6, .T. ) ;
|
||||
#594 = SURFACE_SIDE_STYLE ('',( #592 ) ) ;
|
||||
#595 = FILL_AREA_STYLE_COLOUR ( '', #467 ) ;
|
||||
#596 = VECTOR ( 'NONE', #717, 1000.000000000000000 ) ;
|
||||
#597 = PRESENTATION_STYLE_ASSIGNMENT (( #433 ) ) ;
|
||||
#598 = ORIENTED_EDGE ( 'NONE', *, *, #676, .T. ) ;
|
||||
#599 = PRESENTATION_LAYER_ASSIGNMENT ( '', '', ( #508 ) ) ;
|
||||
#600 =( LENGTH_UNIT ( ) NAMED_UNIT ( * ) SI_UNIT ( .MILLI., .METRE. ) );
|
||||
#601 = LINE ( 'NONE', #671, #257 ) ;
|
||||
#602 = ORIENTED_EDGE ( 'NONE', *, *, #473, .T. ) ;
|
||||
#603 = SURFACE_STYLE_USAGE ( .BOTH. , #118 ) ;
|
||||
#604 = DIRECTION ( 'NONE', ( 0.0000000000000000000, 0.08715574274765836000, 0.9961946980917455500 ) ) ;
|
||||
#605 =( GEOMETRIC_REPRESENTATION_CONTEXT ( 3 ) GLOBAL_UNCERTAINTY_ASSIGNED_CONTEXT ( ( #319 ) ) GLOBAL_UNIT_ASSIGNED_CONTEXT ( ( #496, #100, #335 ) ) REPRESENTATION_CONTEXT ( 'NONE', 'WORKASPACE' ) );
|
||||
#606 = EDGE_CURVE ( 'NONE', #324, #724, #504, .T. ) ;
|
||||
#607 = VERTEX_POINT ( 'NONE', #39 ) ;
|
||||
#608 = CARTESIAN_POINT ( 'NONE', ( 0.7802161917152238300, -0.09381313441476020500, -0.09233459589523193600 ) ) ;
|
||||
#609 =( GEOMETRIC_REPRESENTATION_CONTEXT ( 3 ) GLOBAL_UNCERTAINTY_ASSIGNED_CONTEXT ( ( #386 ) ) GLOBAL_UNIT_ASSIGNED_CONTEXT ( ( #600, #217, #658 ) ) REPRESENTATION_CONTEXT ( 'NONE', 'WORKASPACE' ) );
|
||||
#610 = LINE ( 'NONE', #721, #110 ) ;
|
||||
#611 = SURFACE_STYLE_USAGE ( .BOTH. , #169 ) ;
|
||||
#612 = CARTESIAN_POINT ( 'NONE', ( 0.6302161917152264700, 0.05618686558523977600, 0.8576654041047709900 ) ) ;
|
||||
#613 = VECTOR ( 'NONE', #171, 1000.000000000000000 ) ;
|
||||
#614 = CARTESIAN_POINT ( 'NONE', ( 0.03021619171521966500, -0.09381313441475436200, 0.5076654041047713500 ) ) ;
|
||||
#615 = VECTOR ( 'NONE', #786, 1000.000000000000000 ) ;
|
||||
#616 = ADVANCED_FACE ( 'NONE', ( #309 ), #466, .T. ) ;
|
||||
#617 = DIRECTION ( 'NONE', ( -0.08715574274765836000, -0.9961946980917455500, 0.0000000000000000000 ) ) ;
|
||||
#618 = CLOSED_SHELL ( 'NONE', ( #414, #34, #220, #157, #173, #616, #234, #679, #540, #353, #294, #464, #52, #485, #799, #735, #115, #396, #722, #278, #556, #781 ) ) ;
|
||||
#619 = LINE ( 'NONE', #798, #434 ) ;
|
||||
#620 = DIRECTION ( 'NONE', ( -1.000000000000000000, 0.0000000000000000000, -0.0000000000000000000 ) ) ;
|
||||
#621 = VERTEX_POINT ( 'NONE', #782 ) ;
|
||||
#622 = UNCERTAINTY_MEASURE_WITH_UNIT (LENGTH_MEASURE( 1.000000000000000100E-005 ), #459, 'distance_accuracy_value', 'NONE');
|
||||
#623 = AXIS2_PLACEMENT_3D ( 'NONE', #371, #756, #312 ) ;
|
||||
#624 = ORIENTED_EDGE ( 'NONE', *, *, #327, .T. ) ;
|
||||
#625 = EDGE_CURVE ( 'NONE', #607, #352, #631, .T. ) ;
|
||||
#626 = ORIENTED_EDGE ( 'NONE', *, *, #28, .F. ) ;
|
||||
#627 = PRESENTATION_LAYER_ASSIGNMENT ( '', '', ( #551 ) ) ;
|
||||
#628 = APPLICATION_CONTEXT ( 'automotive_design' ) ;
|
||||
#629 = EDGE_CURVE ( 'NONE', #621, #421, #652, .T. ) ;
|
||||
#630 = PRESENTATION_STYLE_ASSIGNMENT (( #125 ) ) ;
|
||||
#631 = LINE ( 'NONE', #664, #295 ) ;
|
||||
#632 = ORIENTED_EDGE ( 'NONE', *, *, #676, .F. ) ;
|
||||
#633 = MECHANICAL_DESIGN_GEOMETRIC_PRESENTATION_REPRESENTATION ( '', ( #508 ), #682 ) ;
|
||||
#634 = CARTESIAN_POINT ( 'NONE', ( -1.556660508755886000, -0.09381313441476023300, 0.8445421045758828600 ) ) ;
|
||||
#635 = VECTOR ( 'NONE', #232, 1000.000000000000000 ) ;
|
||||
#636 = ORIENTED_EDGE ( 'NONE', *, *, #547, .F. ) ;
|
||||
#637 = PLANE ( 'NONE', #805 ) ;
|
||||
#638 = SURFACE_SIDE_STYLE ('',( #336 ) ) ;
|
||||
#639 = FACE_OUTER_BOUND ( 'NONE', #644, .T. ) ;
|
||||
#640 = VECTOR ( 'NONE', #532, 1000.000000000000000 ) ;
|
||||
#641 = CARTESIAN_POINT ( 'NONE', ( -1.419783808284780200, -0.09381313441476020500, 0.6176654041047668900 ) ) ;
|
||||
#642 = CARTESIAN_POINT ( 'NONE', ( -1.719783808284775000, -0.09381313441476020500, -0.2023345958952274500 ) ) ;
|
||||
#643 = ORIENTED_EDGE ( 'NONE', *, *, #265, .T. ) ;
|
||||
#644 = EDGE_LOOP ( 'NONE', ( #739, #40, #582, #624 ) ) ;
|
||||
#645 = CARTESIAN_POINT ( 'NONE', ( 0.6136307054306795500, -0.1333861101138683600, 0.8410799178202241800 ) ) ;
|
||||
#646 = FILL_AREA_STYLE ('',( #706 ) ) ;
|
||||
#647 = EDGE_CURVE ( 'NONE', #559, #545, #452, .T. ) ;
|
||||
#648 = CARTESIAN_POINT ( 'NONE', ( -0.4197838082847689100, -0.09381313441476020500, 0.6176654041047668900 ) ) ;
|
||||
#649 = CARTESIAN_POINT ( 'NONE', ( -1.569783808284774200, 0.05618686558523977600, 0.8576654041047709900 ) ) ;
|
||||
#650 = AXIS2_PLACEMENT_3D ( 'NONE', #235, #113, #558 ) ;
|
||||
#651 = FACE_OUTER_BOUND ( 'NONE', #165, .T. ) ;
|
||||
#652 = LINE ( 'NONE', #283, #796 ) ;
|
||||
#653 = PLANE ( 'NONE', #244 ) ;
|
||||
#654 = DIRECTION ( 'NONE', ( -0.08682659386424779200, 0.9924325091389669700, -0.08682659386424779200 ) ) ;
|
||||
#655 = CARTESIAN_POINT ( 'NONE', ( -1.719783808284775000, 0.05618686558523977600, -0.2023345958952274500 ) ) ;
|
||||
#656 = VECTOR ( 'NONE', #297, 1000.000000000000000 ) ;
|
||||
#657 = ORIENTED_EDGE ( 'NONE', *, *, #229, .T. ) ;
|
||||
#658 =( NAMED_UNIT ( * ) SI_UNIT ( $, .STERADIAN. ) SOLID_ANGLE_UNIT ( ) );
|
||||
#659 = LINE ( 'NONE', #645, #490 ) ;
|
||||
#660 = DIRECTION ( 'NONE', ( -0.08715574274765836000, -0.9961946980917455500, 5.832412861803903500E-018 ) ) ;
|
||||
#661 = SURFACE_STYLE_FILL_AREA ( #391 ) ;
|
||||
#662 = DIRECTION ( 'NONE', ( 0.0000000000000000000, 1.000000000000000000, 0.0000000000000000000 ) ) ;
|
||||
#663 = EDGE_LOOP ( 'NONE', ( #747, #669, #233, #801 ) ) ;
|
||||
#664 = CARTESIAN_POINT ( 'NONE', ( 0.1802161917152204900, -0.09381313441475436200, -0.09233459589523193600 ) ) ;
|
||||
#665 = VERTEX_POINT ( 'NONE', #163 ) ;
|
||||
#666 = PRESENTATION_LAYER_ASSIGNMENT ( '', '', ( #583 ) ) ;
|
||||
#667 = EDGE_CURVE ( 'NONE', #559, #565, #272, .T. ) ;
|
||||
#668 = COLOUR_RGB ( '',0.5372549019607842900, 0.3490196078431372400, 0.3372549019607843400 ) ;
|
||||
#669 = ORIENTED_EDGE ( 'NONE', *, *, #547, .T. ) ;
|
||||
#670 = DIRECTION ( 'NONE', ( -1.000000000000000000, 0.0000000000000000000, 0.0000000000000000000 ) ) ;
|
||||
#671 = CARTESIAN_POINT ( 'NONE', ( 0.6302161917152264700, 0.05618686558523977600, 0.5076654041047713500 ) ) ;
|
||||
#672 = VECTOR ( 'NONE', #670, 1000.000000000000000 ) ;
|
||||
#673 = LINE ( 'NONE', #35, #170 ) ;
|
||||
#674 = MECHANICAL_DESIGN_GEOMETRIC_PRESENTATION_REPRESENTATION ( '', ( #551 ), #495 ) ;
|
||||
#675 =( NAMED_UNIT ( * ) PLANE_ANGLE_UNIT ( ) SI_UNIT ( $, .RADIAN. ) );
|
||||
#676 = EDGE_CURVE ( 'NONE', #368, #149, #430, .T. ) ;
|
||||
#677 = PLANE ( 'NONE', #561 ) ;
|
||||
#678 = CARTESIAN_POINT ( 'NONE', ( -1.719783808284775000, 0.05618686558523977600, -0.2023345958952274500 ) ) ;
|
||||
#679 = ADVANCED_FACE ( 'NONE', ( #651 ), #790, .T. ) ;
|
||||
#680 = ORIENTED_EDGE ( 'NONE', *, *, #48, .F. ) ;
|
||||
#681 =( NAMED_UNIT ( * ) PLANE_ANGLE_UNIT ( ) SI_UNIT ( $, .RADIAN. ) );
|
||||
#682 =( GEOMETRIC_REPRESENTATION_CONTEXT ( 3 ) GLOBAL_UNCERTAINTY_ASSIGNED_CONTEXT ( ( #483 ) ) GLOBAL_UNIT_ASSIGNED_CONTEXT ( ( #10, #369, #755 ) ) REPRESENTATION_CONTEXT ( 'NONE', 'WORKASPACE' ) );
|
||||
#683 =( NAMED_UNIT ( * ) SI_UNIT ( $, .STERADIAN. ) SOLID_ANGLE_UNIT ( ) );
|
||||
#684 = VECTOR ( 'NONE', #768, 1000.000000000000000 ) ;
|
||||
#685 = VERTEX_POINT ( 'NONE', #225 ) ;
|
||||
#686 = VECTOR ( 'NONE', #57, 1000.000000000000000 ) ;
|
||||
#687 = ORIENTED_EDGE ( 'NONE', *, *, #729, .F. ) ;
|
||||
#688 = EDGE_CURVE ( 'NONE', #731, #472, #168, .T. ) ;
|
||||
#689 = LINE ( 'NONE', #289, #526 ) ;
|
||||
#690 = APPLICATION_CONTEXT ( 'automotive_design' ) ;
|
||||
#691 = STYLED_ITEM ( 'NONE', ( #279 ), #157 ) ;
|
||||
#692 = APPLICATION_PROTOCOL_DEFINITION ( 'draft international standard', 'automotive_design', 1998, #628 ) ;
|
||||
#693 = ORIENTED_EDGE ( 'NONE', *, *, #751, .F. ) ;
|
||||
#694 = ORIENTED_EDGE ( 'NONE', *, *, #567, .F. ) ;
|
||||
#695 = CARTESIAN_POINT ( 'NONE', ( 0.03021619171522313400, 0.05618686558523977600, -0.09233459589523193600 ) ) ;
|
||||
#696 = DIRECTION ( 'NONE', ( 0.0000000000000000000, -5.854691731421723900E-018, -1.000000000000000000 ) ) ;
|
||||
#697 = VECTOR ( 'NONE', #264, 1000.000000000000000 ) ;
|
||||
#698 = ORIENTED_EDGE ( 'NONE', *, *, #167, .F. ) ;
|
||||
#699 = DIRECTION ( 'NONE', ( 0.0000000000000000000, -1.000000000000000000, 0.0000000000000000000 ) ) ;
|
||||
#700 = ORIENTED_EDGE ( 'NONE', *, *, #360, .F. ) ;
|
||||
#701 = FILL_AREA_STYLE_COLOUR ( '', #303 ) ;
|
||||
#702 = CARTESIAN_POINT ( 'NONE', ( -1.719783808284775000, 0.05618686558523977600, 0.6176654041047668900 ) ) ;
|
||||
#703 = EDGE_LOOP ( 'NONE', ( #636, #60, #96, #497 ) ) ;
|
||||
#704 = UNCERTAINTY_MEASURE_WITH_UNIT (LENGTH_MEASURE( 1.000000000000000100E-005 ), #7, 'distance_accuracy_value', 'NONE');
|
||||
#705 = MECHANICAL_DESIGN_GEOMETRIC_PRESENTATION_REPRESENTATION ( '', ( #583 ), #745 ) ;
|
||||
#706 = FILL_AREA_STYLE_COLOUR ( '', #379 ) ;
|
||||
#707 = EDGE_CURVE ( 'NONE', #253, #559, #328, .T. ) ;
|
||||
#708 =( LENGTH_UNIT ( ) NAMED_UNIT ( * ) SI_UNIT ( .MILLI., .METRE. ) );
|
||||
#709 = CARTESIAN_POINT ( 'NONE', ( -1.556660508755886000, -0.09381313441476023300, -0.4292112963663399800 ) ) ;
|
||||
#710 = ORIENTED_EDGE ( 'NONE', *, *, #398, .T. ) ;
|
||||
#711 = DIRECTION ( 'NONE', ( -1.000000000000000000, 0.0000000000000000000, -0.0000000000000000000 ) ) ;
|
||||
#712 = CARTESIAN_POINT ( 'NONE', ( -1.419783808284783800, -0.09381313441475439000, 0.6176654041047668900 ) ) ;
|
||||
#713 = EDGE_CURVE ( 'NONE', #565, #500, #689, .T. ) ;
|
||||
#714 = DIRECTION ( 'NONE', ( 0.0000000000000000000, 0.0000000000000000000, -1.000000000000000000 ) ) ;
|
||||
#715 = ORIENTED_EDGE ( 'NONE', *, *, #629, .F. ) ;
|
||||
#716 = DIRECTION ( 'NONE', ( 0.0000000000000000000, -0.9961946980917455500, 0.08715574274765836000 ) ) ;
|
||||
#717 = DIRECTION ( 'NONE', ( 0.0000000000000000000, 0.0000000000000000000, -1.000000000000000000 ) ) ;
|
||||
#718 = DIRECTION ( 'NONE', ( 0.08715574274765836000, -0.9961946980917455500, 5.832412861803903500E-018 ) ) ;
|
||||
#719 = ORIENTED_EDGE ( 'NONE', *, *, #505, .F. ) ;
|
||||
#720 = CARTESIAN_POINT ( 'NONE', ( 0.03021619171522313400, 0.05618686558523977600, 0.5076654041047713500 ) ) ;
|
||||
#721 = CARTESIAN_POINT ( 'NONE', ( 0.6302161917152264700, 0.05618686558523977600, -0.4423345958952280600 ) ) ;
|
||||
#722 = ADVANCED_FACE ( 'NONE', ( #47 ), #477, .F. ) ;
|
||||
#723 = DIRECTION ( 'NONE', ( -0.0000000000000000000, -0.0000000000000000000, 1.000000000000000000 ) ) ;
|
||||
#724 = VERTEX_POINT ( 'NONE', #49 ) ;
|
||||
#725 = AXIS2_PLACEMENT_3D ( 'NONE', #44, #400, #787 ) ;
|
||||
#726 = PRESENTATION_STYLE_ASSIGNMENT (( #585 ) ) ;
|
||||
#727 = CARTESIAN_POINT ( 'NONE', ( 0.03021619171522313400, 0.05618686558523977600, 0.5076654041047713500 ) ) ;
|
||||
#728 = FACE_OUTER_BOUND ( 'NONE', #230, .T. ) ;
|
||||
#729 = EDGE_CURVE ( 'NONE', #734, #441, #126, .T. ) ;
|
||||
#730 = CARTESIAN_POINT ( 'NONE', ( -1.569783808284774200, 0.05618686558523977600, -0.2023345958952274500 ) ) ;
|
||||
#731 = VERTEX_POINT ( 'NONE', #482 ) ;
|
||||
#732 = AXIS2_PLACEMENT_3D ( 'NONE', #348, #408, #475 ) ;
|
||||
#733 = COLOUR_RGB ( '',0.5372549019607842900, 0.3490196078431372400, 0.3372549019607843400 ) ;
|
||||
#734 = VERTEX_POINT ( 'NONE', #557 ) ;
|
||||
#735 = ADVANCED_FACE ( 'NONE', ( #269 ), #677, .T. ) ;
|
||||
#736 = DIRECTION ( 'NONE', ( 0.9961946980917455500, 0.08715574274765836000, 0.0000000000000000000 ) ) ;
|
||||
#737 = CARTESIAN_POINT ( 'NONE', ( -1.569783808284774200, 0.05618686558523977600, -0.4423345958952280600 ) ) ;
|
||||
#738 = LINE ( 'NONE', #267, #237 ) ;
|
||||
#739 = ORIENTED_EDGE ( 'NONE', *, *, #729, .T. ) ;
|
||||
#740 = FILL_AREA_STYLE ('',( #791 ) ) ;
|
||||
#741 = ORIENTED_EDGE ( 'NONE', *, *, #124, .F. ) ;
|
||||
#742 = AXIS2_PLACEMENT_3D ( 'NONE', #720, #334, #780 ) ;
|
||||
#743 = SURFACE_STYLE_FILL_AREA ( #740 ) ;
|
||||
#744 = ORIENTED_EDGE ( 'NONE', *, *, #365, .F. ) ;
|
||||
#745 =( GEOMETRIC_REPRESENTATION_CONTEXT ( 3 ) GLOBAL_UNCERTAINTY_ASSIGNED_CONTEXT ( ( #445 ) ) GLOBAL_UNIT_ASSIGNED_CONTEXT ( ( #138, #520, #79 ) ) REPRESENTATION_CONTEXT ( 'NONE', 'WORKASPACE' ) );
|
||||
#746 = EDGE_CURVE ( 'NONE', #352, #188, #292, .T. ) ;
|
||||
#747 = ORIENTED_EDGE ( 'NONE', *, *, #713, .F. ) ;
|
||||
#748 = DIRECTION ( 'NONE', ( 1.000000000000000000, 0.0000000000000000000, 0.0000000000000000000 ) ) ;
|
||||
#749 = PRESENTATION_STYLE_ASSIGNMENT (( #146 ) ) ;
|
||||
#750 =( NAMED_UNIT ( * ) SI_UNIT ( $, .STERADIAN. ) SOLID_ANGLE_UNIT ( ) );
|
||||
#751 = EDGE_CURVE ( 'NONE', #421, #500, #774, .T. ) ;
|
||||
#752 = FACE_OUTER_BOUND ( 'NONE', #575, .T. ) ;
|
||||
#753 = VECTOR ( 'NONE', #373, 1000.000000000000100 ) ;
|
||||
#754 = ORIENTED_EDGE ( 'NONE', *, *, #320, .T. ) ;
|
||||
#755 =( NAMED_UNIT ( * ) SI_UNIT ( $, .STERADIAN. ) SOLID_ANGLE_UNIT ( ) );
|
||||
#756 = DIRECTION ( 'NONE', ( -1.000000000000000000, 0.0000000000000000000, 0.0000000000000000000 ) ) ;
|
||||
#757 = FACE_OUTER_BOUND ( 'NONE', #488, .T. ) ;
|
||||
#758 = ORIENTED_EDGE ( 'NONE', *, *, #667, .T. ) ;
|
||||
#759 = PLANE ( 'NONE', #623 ) ;
|
||||
#760 = DIRECTION ( 'NONE', ( -0.0000000000000000000, -0.0000000000000000000, 1.000000000000000000 ) ) ;
|
||||
#761 = DIRECTION ( 'NONE', ( -0.0000000000000000000, -1.000000000000000000, -0.0000000000000000000 ) ) ;
|
||||
#762 = STYLED_ITEM ( 'NONE', ( #749 ), #278 ) ;
|
||||
#763 = PLANE ( 'NONE', #589 ) ;
|
||||
#764 = DIRECTION ( 'NONE', ( 0.0000000000000000000, -0.0000000000000000000, -1.000000000000000000 ) ) ;
|
||||
#765 = ORIENTED_EDGE ( 'NONE', *, *, #167, .T. ) ;
|
||||
#766 = EDGE_CURVE ( 'NONE', #317, #372, #449, .T. ) ;
|
||||
#767 = SURFACE_SIDE_STYLE ('',( #393 ) ) ;
|
||||
#768 = DIRECTION ( 'NONE', ( -1.000000000000000000, 0.0000000000000000000, 0.0000000000000000000 ) ) ;
|
||||
#769 = VECTOR ( 'NONE', #50, 1000.000000000000100 ) ;
|
||||
#770 = CARTESIAN_POINT ( 'NONE', ( 0.03021619171522313400, 0.05618686558523977600, 0.5076654041047713500 ) ) ;
|
||||
#771 = DIRECTION ( 'NONE', ( 0.08682659386424779200, 0.9924325091389669700, -0.08682659386424779200 ) ) ;
|
||||
#772 = VECTOR ( 'NONE', #147, 1000.000000000000100 ) ;
|
||||
#773 = PRESENTATION_LAYER_ASSIGNMENT ( '', '', ( #691 ) ) ;
|
||||
#774 = LINE ( 'NONE', #56, #753 ) ;
|
||||
#775 = UNCERTAINTY_MEASURE_WITH_UNIT (LENGTH_MEASURE( 1.000000000000000100E-005 ), #94, 'distance_accuracy_value', 'NONE');
|
||||
#776 = VECTOR ( 'NONE', #107, 1000.000000000000000 ) ;
|
||||
#777 = CARTESIAN_POINT ( 'NONE', ( -1.569783808284774200, 0.05618686558523977600, -0.2023345958952274500 ) ) ;
|
||||
#778 = LINE ( 'NONE', #293, #635 ) ;
|
||||
#779 = EDGE_LOOP ( 'NONE', ( #424, #374, #808, #1, #687, #69 ) ) ;
|
||||
#780 = DIRECTION ( 'NONE', ( 0.0000000000000000000, -0.0000000000000000000, 1.000000000000000000 ) ) ;
|
||||
#781 = ADVANCED_FACE ( 'NONE', ( #93 ), #637, .F. ) ;
|
||||
#782 = CARTESIAN_POINT ( 'NONE', ( -1.556660508755886000, -0.09381313441476020500, 0.6176654041047668900 ) ) ;
|
||||
#783 = DIRECTION ( 'NONE', ( -0.9961946980917455500, 0.08715574274765836000, 0.0000000000000000000 ) ) ;
|
||||
#784 = PRESENTATION_STYLE_ASSIGNMENT (( #454 ) ) ;
|
||||
#785 = ORIENTED_EDGE ( 'NONE', *, *, #523, .F. ) ;
|
||||
#786 = DIRECTION ( 'NONE', ( -1.000000000000000000, -0.0000000000000000000, -0.0000000000000000000 ) ) ;
|
||||
#787 = DIRECTION ( 'NONE', ( 0.0000000000000000000, 0.0000000000000000000, -1.000000000000000000 ) ) ;
|
||||
#788 = ORIENTED_EDGE ( 'NONE', *, *, #797, .F. ) ;
|
||||
#789 = EDGE_CURVE ( 'NONE', #338, #571, #354, .T. ) ;
|
||||
#790 = PLANE ( 'NONE', #103 ) ;
|
||||
#791 = FILL_AREA_STYLE_COLOUR ( '', #101 ) ;
|
||||
#792 = DIRECTION ( 'NONE', ( 0.0000000000000000000, 0.0000000000000000000, 1.000000000000000000 ) ) ;
|
||||
#793 = DIRECTION ( 'NONE', ( 0.08715574274765836000, 0.9961946980917455500, -5.832412861803903500E-018 ) ) ;
|
||||
#794 = SURFACE_STYLE_FILL_AREA ( #187 ) ;
|
||||
#795 = DIRECTION ( 'NONE', ( 0.0000000000000000000, 1.000000000000000000, 0.0000000000000000000 ) ) ;
|
||||
#796 = VECTOR ( 'NONE', #399, 1000.000000000000000 ) ;
|
||||
#797 = EDGE_CURVE ( 'NONE', #685, #324, #190, .T. ) ;
|
||||
#798 = CARTESIAN_POINT ( 'NONE', ( 0.7802161917152238300, 0.05618686558523977600, -0.09233459589523193600 ) ) ;
|
||||
#799 = ADVANCED_FACE ( 'NONE', ( #176 ), #8, .T. ) ;
|
||||
#800 = DIRECTION ( 'NONE', ( 1.000000000000000000, 0.0000000000000000000, -0.0000000000000000000 ) ) ;
|
||||
#801 = ORIENTED_EDGE ( 'NONE', *, *, #751, .T. ) ;
|
||||
#802 = VECTOR ( 'NONE', #484, 1000.000000000000000 ) ;
|
||||
#803 = CARTESIAN_POINT ( 'NONE', ( -1.419783808284780200, 0.05618686558523977600, 0.6176654041047668900 ) ) ;
|
||||
#804 = ORIENTED_EDGE ( 'NONE', *, *, #593, .T. ) ;
|
||||
#805 = AXIS2_PLACEMENT_3D ( 'NONE', #695, #252, #189 ) ;
|
||||
#806 = VECTOR ( 'NONE', #199, 1000.000000000000000 ) ;
|
||||
#807 = FACE_OUTER_BOUND ( 'NONE', #198, .T. ) ;
|
||||
#808 = ORIENTED_EDGE ( 'NONE', *, *, #498, .F. ) ;
|
||||
ENDSEC;
|
||||
END-ISO-10303-21;
|
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
Binary file not shown.
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
|
@ -1,7 +1,7 @@
|
|||
ISO-10303-21;
|
||||
HEADER;
|
||||
FILE_DESCRIPTION(('STEP AP214'),'1');
|
||||
FILE_NAME('CAP_YX_10X20_RUB','2024-01-10T03:53:25',(''),(''),'','','');
|
||||
FILE_NAME('CAP_YX_10X20_RUB','2023-11-11T07:25:34',(''),(''),'','','');
|
||||
FILE_SCHEMA(('AUTOMOTIVE_DESIGN'));
|
||||
ENDSEC;
|
||||
DATA;
|
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,73 @@
|
|||
@echo off
|
||||
set pathofEDASourceFile=%1
|
||||
set FilePath=%~dp0
|
||||
|
||||
::delete --show-dialog after first start up and setting
|
||||
set option=--show-dialog
|
||||
|
||||
::detect current language of user.
|
||||
reg query "HKCU\Control Panel\Desktop" /v PreferredUILanguages>nul 2>nul&&goto _dosearch1_||goto _dosearch2_
|
||||
|
||||
:_dosearch1_
|
||||
FOR /F "tokens=3" %%a IN (
|
||||
'reg query "HKCU\Control Panel\Desktop" /v PreferredUILanguages ^| find "PreferredUILanguages"'
|
||||
) DO (
|
||||
set language=%%a
|
||||
)
|
||||
set language=%language:~,2%
|
||||
goto _setlanguage_
|
||||
|
||||
:_dosearch2_
|
||||
FOR /F "tokens=3" %%a IN (
|
||||
'reg query "HKLM\SYSTEM\ControlSet001\Control\Nls\Language" /v InstallLanguage ^| find "InstallLanguage"'
|
||||
) DO (
|
||||
set language=%%a
|
||||
)
|
||||
if %language%==0804 (
|
||||
set language=zh
|
||||
)
|
||||
goto _setlanguage_
|
||||
|
||||
:_setlanguage_
|
||||
if %language%==zh (
|
||||
call %FilePath%\i18n\language_zh.bat
|
||||
) else (
|
||||
call %FilePath%\i18n\language_en.bat
|
||||
)
|
||||
|
||||
cls
|
||||
|
||||
echo -------------------------------------------------------------------------------------------------------------------
|
||||
echo -------------------------------------------------------------------------------------------------------------------
|
||||
echo.
|
||||
echo %i18n_thx4using%
|
||||
echo %i18n_gitAddr%
|
||||
echo %i18n_batScar%
|
||||
echo.
|
||||
echo -------------------------------------------------------------------------------------------------------------------
|
||||
echo -------------------------------------------------------------------------------------------------------------------
|
||||
|
||||
set pyFilePath=%FilePath%generate_interactive_bom.py
|
||||
|
||||
:_convert_
|
||||
if not defined pathofEDASourceFile (
|
||||
set /p pathofEDASourceFile=%i18n_draghere%
|
||||
)
|
||||
echo.
|
||||
echo %i18n_converting%
|
||||
echo.
|
||||
python %pyFilePath% %pathofEDASourceFile% %option%
|
||||
set pathofEDASourceFile=
|
||||
|
||||
echo -------------------------------------------------------------------------------------------------------------------
|
||||
echo -------------------------------------------------------------------------------------------------------------------
|
||||
echo.
|
||||
echo %i18n_converted%
|
||||
echo.
|
||||
echo -------------------------------------------------------------------------------------------------------------------
|
||||
echo -------------------------------------------------------------------------------------------------------------------
|
||||
|
||||
|
||||
CHOICE /C YN /N /M "%i18n_again% [ Y/N ]"
|
||||
if errorlevel 2 exit
|
||||
if errorlevel 1 goto _convert_
|
|
@ -0,0 +1,58 @@
|
|||
import os
|
||||
import sys
|
||||
import threading
|
||||
import time
|
||||
|
||||
import wx
|
||||
import wx.aui
|
||||
|
||||
|
||||
def check_for_bom_button():
|
||||
# From Miles McCoo's blog
|
||||
# https://kicad.mmccoo.com/2017/03/05/adding-your-own-command-buttons-to-the-pcbnew-gui/
|
||||
def find_pcbnew_window():
|
||||
windows = wx.GetTopLevelWindows()
|
||||
pcbneww = [w for w in windows if "pcbnew" in w.GetTitle().lower()]
|
||||
if len(pcbneww) != 1:
|
||||
return None
|
||||
return pcbneww[0]
|
||||
|
||||
def callback(_):
|
||||
plugin.Run()
|
||||
|
||||
path = os.path.dirname(__file__)
|
||||
while not wx.GetApp():
|
||||
time.sleep(1)
|
||||
bm = wx.Bitmap(path + '/icon.png', wx.BITMAP_TYPE_PNG)
|
||||
button_wx_item_id = 0
|
||||
|
||||
from pcbnew import ID_H_TOOLBAR
|
||||
while True:
|
||||
time.sleep(1)
|
||||
pcbnew_window = find_pcbnew_window()
|
||||
if not pcbnew_window:
|
||||
continue
|
||||
|
||||
top_tb = pcbnew_window.FindWindowById(ID_H_TOOLBAR)
|
||||
if button_wx_item_id == 0 or not top_tb.FindTool(button_wx_item_id):
|
||||
top_tb.AddSeparator()
|
||||
button_wx_item_id = wx.NewId()
|
||||
top_tb.AddTool(button_wx_item_id, "iBOM", bm,
|
||||
"Generate interactive BOM", wx.ITEM_NORMAL)
|
||||
top_tb.Bind(wx.EVT_TOOL, callback, id=button_wx_item_id)
|
||||
top_tb.Realize()
|
||||
|
||||
|
||||
if (not os.environ.get('INTERACTIVE_HTML_BOM_CLI_MODE', False) and
|
||||
os.path.basename(sys.argv[0]) != 'generate_interactive_bom.py'):
|
||||
from .ecad.kicad import InteractiveHtmlBomPlugin
|
||||
|
||||
plugin = InteractiveHtmlBomPlugin()
|
||||
plugin.register()
|
||||
|
||||
# Add a button the hacky way if plugin button is not supported
|
||||
# in pcbnew, unless this is linux.
|
||||
if not plugin.pcbnew_icon_support and not sys.platform.startswith('linux'):
|
||||
t = threading.Thread(target=check_for_bom_button)
|
||||
t.daemon = True
|
||||
t.start()
|
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
|
@ -0,0 +1,468 @@
|
|||
"""Config object"""
|
||||
|
||||
import argparse
|
||||
import os
|
||||
import re
|
||||
|
||||
from wx import FileConfig
|
||||
|
||||
from .. import dialog
|
||||
|
||||
|
||||
class Config:
|
||||
FILE_NAME_FORMAT_HINT = (
|
||||
'Output file name format supports substitutions:\n'
|
||||
'\n'
|
||||
' %f : original pcb file name without extension.\n'
|
||||
' %p : pcb/project title from pcb metadata.\n'
|
||||
' %c : company from pcb metadata.\n'
|
||||
' %r : revision from pcb metadata.\n'
|
||||
' %d : pcb date from metadata if available, '
|
||||
'file modification date otherwise.\n'
|
||||
' %D : bom generation date.\n'
|
||||
' %T : bom generation time.\n'
|
||||
'\n'
|
||||
'Extension .html will be added automatically.'
|
||||
) # type: str
|
||||
|
||||
# Helper constants
|
||||
bom_view_choices = ['bom-only', 'left-right', 'top-bottom']
|
||||
layer_view_choices = ['F', 'FB', 'B']
|
||||
default_sort_order = [
|
||||
'C', 'R', 'L', 'D', 'U', 'Y', 'X', 'F', 'SW', 'A',
|
||||
'~',
|
||||
'HS', 'CNN', 'J', 'P', 'NT', 'MH',
|
||||
]
|
||||
default_checkboxes = ['Sourced', 'Placed']
|
||||
html_config_fields = [
|
||||
'dark_mode', 'show_pads', 'show_fabrication', 'show_silkscreen',
|
||||
'highlight_pin1', 'redraw_on_drag', 'board_rotation', 'checkboxes',
|
||||
'bom_view', 'layer_view', 'offset_back_rotation',
|
||||
'kicad_text_formatting'
|
||||
]
|
||||
default_show_group_fields = ["Value", "Footprint"]
|
||||
|
||||
# Defaults
|
||||
|
||||
# HTML section
|
||||
dark_mode = False
|
||||
show_pads = True
|
||||
show_fabrication = False
|
||||
show_silkscreen = True
|
||||
highlight_pin1 = False
|
||||
redraw_on_drag = True
|
||||
board_rotation = 0
|
||||
offset_back_rotation = False
|
||||
checkboxes = ','.join(default_checkboxes)
|
||||
bom_view = bom_view_choices[1]
|
||||
layer_view = layer_view_choices[1]
|
||||
compression = True
|
||||
open_browser = True
|
||||
|
||||
# General section
|
||||
bom_dest_dir = 'bom/' # This is relative to pcb file directory
|
||||
bom_name_format = 'ibom'
|
||||
component_sort_order = default_sort_order
|
||||
component_blacklist = []
|
||||
blacklist_virtual = True
|
||||
blacklist_empty_val = False
|
||||
include_tracks = False
|
||||
include_nets = False
|
||||
kicad_text_formatting = True
|
||||
|
||||
# Extra fields section
|
||||
extra_data_file = None
|
||||
netlist_initial_directory = '' # This is relative to pcb file directory
|
||||
show_fields = default_show_group_fields
|
||||
group_fields = default_show_group_fields
|
||||
normalize_field_case = False
|
||||
board_variant_field = ''
|
||||
board_variant_whitelist = []
|
||||
board_variant_blacklist = []
|
||||
dnp_field = ''
|
||||
|
||||
@staticmethod
|
||||
def _split(s):
|
||||
"""Splits string by ',' and drops empty strings from resulting array"""
|
||||
return [a.replace('\\,', ',') for a in re.split(r'(?<!\\),', s) if a]
|
||||
|
||||
@staticmethod
|
||||
def _join(lst):
|
||||
return ','.join([s.replace(',', '\\,') for s in lst])
|
||||
|
||||
def __init__(self, version, local_dir):
|
||||
self.version = version
|
||||
self.local_config_file = os.path.join(local_dir, 'ibom.config.ini')
|
||||
self.global_config_file = os.path.join(
|
||||
os.path.dirname(__file__), '..', 'config.ini')
|
||||
|
||||
def load_from_ini(self):
|
||||
"""Init from config file if it exists."""
|
||||
if os.path.isfile(self.local_config_file):
|
||||
file = self.local_config_file
|
||||
elif os.path.isfile(self.global_config_file):
|
||||
file = self.global_config_file
|
||||
else:
|
||||
return
|
||||
|
||||
f = FileConfig(localFilename=file)
|
||||
|
||||
f.SetPath('/html_defaults')
|
||||
self.dark_mode = f.ReadBool('dark_mode', self.dark_mode)
|
||||
self.show_pads = f.ReadBool('show_pads', self.show_pads)
|
||||
self.show_fabrication = f.ReadBool(
|
||||
'show_fabrication', self.show_fabrication)
|
||||
self.show_silkscreen = f.ReadBool(
|
||||
'show_silkscreen', self.show_silkscreen)
|
||||
self.highlight_pin1 = f.ReadBool('highlight_pin1', self.highlight_pin1)
|
||||
self.redraw_on_drag = f.ReadBool('redraw_on_drag', self.redraw_on_drag)
|
||||
self.board_rotation = f.ReadInt('board_rotation', self.board_rotation)
|
||||
self.offset_back_rotation = f.ReadBool(
|
||||
'offset_back_rotation', self.offset_back_rotation)
|
||||
self.checkboxes = f.Read('checkboxes', self.checkboxes)
|
||||
self.bom_view = f.Read('bom_view', self.bom_view)
|
||||
self.layer_view = f.Read('layer_view', self.layer_view)
|
||||
self.compression = f.ReadBool('compression', self.compression)
|
||||
self.open_browser = f.ReadBool('open_browser', self.open_browser)
|
||||
|
||||
f.SetPath('/general')
|
||||
self.bom_dest_dir = f.Read('bom_dest_dir', self.bom_dest_dir)
|
||||
self.bom_name_format = f.Read('bom_name_format', self.bom_name_format)
|
||||
self.component_sort_order = self._split(f.Read(
|
||||
'component_sort_order',
|
||||
','.join(self.component_sort_order)))
|
||||
self.component_blacklist = self._split(f.Read(
|
||||
'component_blacklist',
|
||||
','.join(self.component_blacklist)))
|
||||
self.blacklist_virtual = f.ReadBool(
|
||||
'blacklist_virtual', self.blacklist_virtual)
|
||||
self.blacklist_empty_val = f.ReadBool(
|
||||
'blacklist_empty_val', self.blacklist_empty_val)
|
||||
self.include_tracks = f.ReadBool('include_tracks', self.include_tracks)
|
||||
self.include_nets = f.ReadBool('include_nets', self.include_nets)
|
||||
|
||||
f.SetPath('/fields')
|
||||
self.show_fields = self._split(f.Read(
|
||||
'show_fields', self._join(self.show_fields)))
|
||||
self.group_fields = self._split(f.Read(
|
||||
'group_fields', self._join(self.group_fields)))
|
||||
self.normalize_field_case = f.ReadBool(
|
||||
'normalize_field_case', self.normalize_field_case)
|
||||
self.board_variant_field = f.Read(
|
||||
'board_variant_field', self.board_variant_field)
|
||||
self.board_variant_whitelist = self._split(f.Read(
|
||||
'board_variant_whitelist',
|
||||
self._join(self.board_variant_whitelist)))
|
||||
self.board_variant_blacklist = self._split(f.Read(
|
||||
'board_variant_blacklist',
|
||||
self._join(self.board_variant_blacklist)))
|
||||
self.dnp_field = f.Read('dnp_field', self.dnp_field)
|
||||
|
||||
def save(self, locally):
|
||||
file = self.local_config_file if locally else self.global_config_file
|
||||
print('Saving to', file)
|
||||
f = FileConfig(localFilename=file)
|
||||
|
||||
f.SetPath('/html_defaults')
|
||||
f.WriteBool('dark_mode', self.dark_mode)
|
||||
f.WriteBool('show_pads', self.show_pads)
|
||||
f.WriteBool('show_fabrication', self.show_fabrication)
|
||||
f.WriteBool('show_silkscreen', self.show_silkscreen)
|
||||
f.WriteBool('highlight_pin1', self.highlight_pin1)
|
||||
f.WriteBool('redraw_on_drag', self.redraw_on_drag)
|
||||
f.WriteInt('board_rotation', self.board_rotation)
|
||||
f.WriteBool('offset_back_rotation', self.offset_back_rotation)
|
||||
f.Write('checkboxes', self.checkboxes)
|
||||
f.Write('bom_view', self.bom_view)
|
||||
f.Write('layer_view', self.layer_view)
|
||||
f.WriteBool('compression', self.compression)
|
||||
f.WriteBool('open_browser', self.open_browser)
|
||||
|
||||
f.SetPath('/general')
|
||||
bom_dest_dir = self.bom_dest_dir
|
||||
if bom_dest_dir.startswith(self.netlist_initial_directory):
|
||||
bom_dest_dir = os.path.relpath(
|
||||
bom_dest_dir, self.netlist_initial_directory)
|
||||
f.Write('bom_dest_dir', bom_dest_dir)
|
||||
f.Write('bom_name_format', self.bom_name_format)
|
||||
f.Write('component_sort_order',
|
||||
','.join(self.component_sort_order))
|
||||
f.Write('component_blacklist',
|
||||
','.join(self.component_blacklist))
|
||||
f.WriteBool('blacklist_virtual', self.blacklist_virtual)
|
||||
f.WriteBool('blacklist_empty_val', self.blacklist_empty_val)
|
||||
f.WriteBool('include_tracks', self.include_tracks)
|
||||
f.WriteBool('include_nets', self.include_nets)
|
||||
|
||||
f.SetPath('/fields')
|
||||
f.Write('show_fields', self._join(self.show_fields))
|
||||
f.Write('group_fields', self._join(self.group_fields))
|
||||
f.WriteBool('normalize_field_case', self.normalize_field_case)
|
||||
f.Write('board_variant_field', self.board_variant_field)
|
||||
f.Write('board_variant_whitelist',
|
||||
self._join(self.board_variant_whitelist))
|
||||
f.Write('board_variant_blacklist',
|
||||
self._join(self.board_variant_blacklist))
|
||||
f.Write('dnp_field', self.dnp_field)
|
||||
f.Flush()
|
||||
|
||||
def set_from_dialog(self, dlg):
|
||||
# type: (dialog.settings_dialog.SettingsDialogPanel) -> None
|
||||
# Html
|
||||
self.dark_mode = dlg.html.darkModeCheckbox.IsChecked()
|
||||
self.show_pads = dlg.html.showPadsCheckbox.IsChecked()
|
||||
self.show_fabrication = dlg.html.showFabricationCheckbox.IsChecked()
|
||||
self.show_silkscreen = dlg.html.showSilkscreenCheckbox.IsChecked()
|
||||
self.highlight_pin1 = dlg.html.highlightPin1Checkbox.IsChecked()
|
||||
self.redraw_on_drag = dlg.html.continuousRedrawCheckbox.IsChecked()
|
||||
self.board_rotation = dlg.html.boardRotationSlider.Value
|
||||
self.offset_back_rotation = \
|
||||
dlg.html.offsetBackRotationCheckbox.IsChecked()
|
||||
self.checkboxes = dlg.html.bomCheckboxesCtrl.Value
|
||||
self.bom_view = self.bom_view_choices[dlg.html.bomDefaultView.Selection]
|
||||
self.layer_view = self.layer_view_choices[
|
||||
dlg.html.layerDefaultView.Selection]
|
||||
self.compression = dlg.html.compressionCheckbox.IsChecked()
|
||||
self.open_browser = dlg.html.openBrowserCheckbox.IsChecked()
|
||||
|
||||
# General
|
||||
self.bom_dest_dir = dlg.general.bomDirPicker.Path
|
||||
self.bom_name_format = dlg.general.fileNameFormatTextControl.Value
|
||||
self.component_sort_order = dlg.general.componentSortOrderBox.GetItems()
|
||||
self.component_blacklist = dlg.general.blacklistBox.GetItems()
|
||||
self.blacklist_virtual = \
|
||||
dlg.general.blacklistVirtualCheckbox.IsChecked()
|
||||
self.blacklist_empty_val = \
|
||||
dlg.general.blacklistEmptyValCheckbox.IsChecked()
|
||||
self.include_tracks = dlg.general.includeTracksCheckbox.IsChecked()
|
||||
self.include_nets = dlg.general.includeNetsCheckbox.IsChecked()
|
||||
|
||||
# Fields
|
||||
self.extra_data_file = dlg.fields.extraDataFilePicker.Path
|
||||
self.show_fields = dlg.fields.GetShowFields()
|
||||
self.group_fields = dlg.fields.GetGroupFields()
|
||||
self.normalize_field_case = dlg.fields.normalizeCaseCheckbox.Value
|
||||
self.board_variant_field = dlg.fields.boardVariantFieldBox.Value
|
||||
if self.board_variant_field == dlg.fields.NONE_STRING:
|
||||
self.board_variant_field = ''
|
||||
self.board_variant_whitelist = list(
|
||||
dlg.fields.boardVariantWhitelist.GetCheckedStrings())
|
||||
self.board_variant_blacklist = list(
|
||||
dlg.fields.boardVariantBlacklist.GetCheckedStrings())
|
||||
self.dnp_field = dlg.fields.dnpFieldBox.Value
|
||||
if self.dnp_field == dlg.fields.NONE_STRING:
|
||||
self.dnp_field = ''
|
||||
|
||||
def transfer_to_dialog(self, dlg):
|
||||
# type: (dialog.settings_dialog.SettingsDialogPanel) -> None
|
||||
# Html
|
||||
dlg.html.darkModeCheckbox.Value = self.dark_mode
|
||||
dlg.html.showPadsCheckbox.Value = self.show_pads
|
||||
dlg.html.showFabricationCheckbox.Value = self.show_fabrication
|
||||
dlg.html.showSilkscreenCheckbox.Value = self.show_silkscreen
|
||||
dlg.html.highlightPin1Checkbox.Value = self.highlight_pin1
|
||||
dlg.html.continuousRedrawCheckbox.value = self.redraw_on_drag
|
||||
dlg.html.boardRotationSlider.Value = self.board_rotation
|
||||
dlg.html.offsetBackRotationCheckbox.Value = self.offset_back_rotation
|
||||
dlg.html.bomCheckboxesCtrl.Value = self.checkboxes
|
||||
dlg.html.bomDefaultView.Selection = self.bom_view_choices.index(
|
||||
self.bom_view)
|
||||
dlg.html.layerDefaultView.Selection = self.layer_view_choices.index(
|
||||
self.layer_view)
|
||||
dlg.html.compressionCheckbox.Value = self.compression
|
||||
dlg.html.openBrowserCheckbox.Value = self.open_browser
|
||||
|
||||
# General
|
||||
import os.path
|
||||
if os.path.isabs(self.bom_dest_dir):
|
||||
dlg.general.bomDirPicker.Path = self.bom_dest_dir
|
||||
else:
|
||||
dlg.general.bomDirPicker.Path = os.path.join(
|
||||
self.netlist_initial_directory, self.bom_dest_dir)
|
||||
dlg.general.fileNameFormatTextControl.Value = self.bom_name_format
|
||||
dlg.general.componentSortOrderBox.SetItems(self.component_sort_order)
|
||||
dlg.general.blacklistBox.SetItems(self.component_blacklist)
|
||||
dlg.general.blacklistVirtualCheckbox.Value = self.blacklist_virtual
|
||||
dlg.general.blacklistEmptyValCheckbox.Value = self.blacklist_empty_val
|
||||
dlg.general.includeTracksCheckbox.Value = self.include_tracks
|
||||
dlg.general.includeNetsCheckbox.Value = self.include_nets
|
||||
|
||||
# Fields
|
||||
dlg.fields.extraDataFilePicker.SetInitialDirectory(
|
||||
self.netlist_initial_directory)
|
||||
|
||||
def safe_set_checked_strings(clb, strings):
|
||||
current = list(clb.GetStrings())
|
||||
if current:
|
||||
present_strings = [s for s in strings if s in current]
|
||||
not_present_strings = [s for s in current if s not in strings]
|
||||
clb.Clear()
|
||||
clb.InsertItems(present_strings + not_present_strings, 0)
|
||||
clb.SetCheckedStrings(present_strings)
|
||||
|
||||
dlg.fields.SetCheckedFields(self.show_fields, self.group_fields)
|
||||
dlg.fields.normalizeCaseCheckbox.Value = self.normalize_field_case
|
||||
dlg.fields.boardVariantFieldBox.Value = self.board_variant_field
|
||||
dlg.fields.OnBoardVariantFieldChange(None)
|
||||
safe_set_checked_strings(dlg.fields.boardVariantWhitelist,
|
||||
self.board_variant_whitelist)
|
||||
safe_set_checked_strings(dlg.fields.boardVariantBlacklist,
|
||||
self.board_variant_blacklist)
|
||||
dlg.fields.dnpFieldBox.Value = self.dnp_field
|
||||
|
||||
dlg.finish_init()
|
||||
|
||||
@classmethod
|
||||
def add_options(cls, parser, version):
|
||||
# type: (argparse.ArgumentParser, str) -> None
|
||||
parser.add_argument('--show-dialog', action='store_true',
|
||||
help='Shows config dialog. All other flags '
|
||||
'will be ignored.')
|
||||
parser.add_argument('--version', action='version', version=version)
|
||||
# Html
|
||||
parser.add_argument('--dark-mode', help='Default to dark mode.',
|
||||
action='store_true')
|
||||
parser.add_argument('--hide-pads',
|
||||
help='Hide footprint pads by default.',
|
||||
action='store_true')
|
||||
parser.add_argument('--show-fabrication',
|
||||
help='Show fabrication layer by default.',
|
||||
action='store_true')
|
||||
parser.add_argument('--hide-silkscreen',
|
||||
help='Hide silkscreen by default.',
|
||||
action='store_true')
|
||||
parser.add_argument('--highlight-pin1',
|
||||
help='Highlight pin1 by default.',
|
||||
action='store_true')
|
||||
parser.add_argument('--no-redraw-on-drag',
|
||||
help='Do not redraw pcb on drag by default.',
|
||||
action='store_true')
|
||||
parser.add_argument('--board-rotation', type=int,
|
||||
default=cls.board_rotation * 5,
|
||||
help='Board rotation in degrees (-180 to 180). '
|
||||
'Will be rounded to multiple of 5.')
|
||||
parser.add_argument('--offset-back-rotation',
|
||||
help='Offset the back of the pcb by 180 degrees',
|
||||
action='store_true')
|
||||
parser.add_argument('--checkboxes',
|
||||
default=cls.checkboxes,
|
||||
help='Comma separated list of checkbox columns.')
|
||||
parser.add_argument('--bom-view', default=cls.bom_view,
|
||||
choices=cls.bom_view_choices,
|
||||
help='Default BOM view.')
|
||||
parser.add_argument('--layer-view', default=cls.layer_view,
|
||||
choices=cls.layer_view_choices,
|
||||
help='Default layer view.')
|
||||
parser.add_argument('--no-compression',
|
||||
help='Disable compression of pcb data.',
|
||||
action='store_true')
|
||||
parser.add_argument('--no-browser', help='Do not launch browser.',
|
||||
action='store_true')
|
||||
|
||||
# General
|
||||
parser.add_argument('--dest-dir', default=cls.bom_dest_dir,
|
||||
help='Destination directory for bom file '
|
||||
'relative to pcb file directory.')
|
||||
parser.add_argument('--name-format', default=cls.bom_name_format,
|
||||
help=cls.FILE_NAME_FORMAT_HINT.replace('%', '%%'))
|
||||
parser.add_argument('--include-tracks', action='store_true',
|
||||
help='Include track/zone information in output. '
|
||||
'F.Cu and B.Cu layers only.')
|
||||
parser.add_argument('--include-nets', action='store_true',
|
||||
help='Include netlist information in output.')
|
||||
parser.add_argument('--sort-order',
|
||||
help='Default sort order for components. '
|
||||
'Must contain "~" once.',
|
||||
default=','.join(cls.component_sort_order))
|
||||
parser.add_argument('--blacklist',
|
||||
default=','.join(cls.component_blacklist),
|
||||
help='List of comma separated blacklisted '
|
||||
'components or prefixes with *. '
|
||||
'E.g. "X1,MH*"')
|
||||
parser.add_argument('--no-blacklist-virtual', action='store_true',
|
||||
help='Do not blacklist virtual components.')
|
||||
parser.add_argument('--blacklist-empty-val', action='store_true',
|
||||
help='Blacklist components with empty value.')
|
||||
|
||||
# Fields section
|
||||
parser.add_argument('--netlist-file',
|
||||
help='(Deprecated) Path to netlist or xml file.')
|
||||
parser.add_argument('--extra-data-file',
|
||||
help='Path to netlist or xml file.')
|
||||
parser.add_argument('--extra-fields',
|
||||
help='Passing --extra-fields "X,Y" is a shortcut '
|
||||
'for --show-fields and --group-fields '
|
||||
'with values "Value,Footprint,X,Y"')
|
||||
parser.add_argument('--show-fields',
|
||||
default=cls._join(cls.show_fields),
|
||||
help='List of fields to show in the BOM.')
|
||||
parser.add_argument('--group-fields',
|
||||
default=cls._join(cls.group_fields),
|
||||
help='Fields that components will be grouped by.')
|
||||
parser.add_argument('--normalize-field-case',
|
||||
help='Normalize extra field name case. E.g. "MPN" '
|
||||
', "mpn" will be considered the same field.',
|
||||
action='store_true')
|
||||
parser.add_argument('--variant-field',
|
||||
help='Name of the extra field that stores board '
|
||||
'variant for component.')
|
||||
parser.add_argument('--variants-whitelist', default='',
|
||||
help='List of board variants to '
|
||||
'include in the BOM.')
|
||||
parser.add_argument('--variants-blacklist', default='',
|
||||
help='List of board variants to '
|
||||
'exclude from the BOM.')
|
||||
parser.add_argument('--dnp-field', default=cls.dnp_field,
|
||||
help='Name of the extra field that indicates '
|
||||
'do not populate status. Components with '
|
||||
'this field not empty will be excluded.')
|
||||
|
||||
def set_from_args(self, args):
|
||||
# type: (argparse.Namespace) -> None
|
||||
import math
|
||||
|
||||
# Html
|
||||
self.dark_mode = args.dark_mode
|
||||
self.show_pads = not args.hide_pads
|
||||
self.show_fabrication = args.show_fabrication
|
||||
self.show_silkscreen = not args.hide_silkscreen
|
||||
self.highlight_pin1 = args.highlight_pin1
|
||||
self.redraw_on_drag = not args.no_redraw_on_drag
|
||||
self.board_rotation = math.fmod(args.board_rotation // 5, 37)
|
||||
self.offset_back_rotation = args.offset_back_rotation
|
||||
self.checkboxes = args.checkboxes
|
||||
self.bom_view = args.bom_view
|
||||
self.layer_view = args.layer_view
|
||||
self.compression = not args.no_compression
|
||||
self.open_browser = not args.no_browser
|
||||
|
||||
# General
|
||||
self.bom_dest_dir = args.dest_dir
|
||||
self.bom_name_format = args.name_format
|
||||
self.component_sort_order = self._split(args.sort_order)
|
||||
self.component_blacklist = self._split(args.blacklist)
|
||||
self.blacklist_virtual = not args.no_blacklist_virtual
|
||||
self.blacklist_empty_val = args.blacklist_empty_val
|
||||
self.include_tracks = args.include_tracks
|
||||
self.include_nets = args.include_nets
|
||||
|
||||
# Fields
|
||||
self.extra_data_file = args.extra_data_file or args.netlist_file
|
||||
if args.extra_fields is not None:
|
||||
self.show_fields = self.default_show_group_fields + \
|
||||
self._split(args.extra_fields)
|
||||
self.group_fields = self.show_fields
|
||||
else:
|
||||
self.show_fields = self._split(args.show_fields)
|
||||
self.group_fields = self._split(args.group_fields)
|
||||
self.normalize_field_case = args.normalize_field_case
|
||||
self.board_variant_field = args.variant_field
|
||||
self.board_variant_whitelist = self._split(args.variants_whitelist)
|
||||
self.board_variant_blacklist = self._split(args.variants_blacklist)
|
||||
self.dnp_field = args.dnp_field
|
||||
|
||||
def get_html_config(self):
|
||||
import json
|
||||
d = {f: getattr(self, f) for f in self.html_config_fields}
|
||||
d["fields"] = self.show_fields
|
||||
return json.dumps(d)
|
|
@ -0,0 +1,52 @@
|
|||
from .newstroke_font import NEWSTROKE_FONT
|
||||
|
||||
|
||||
class FontParser:
|
||||
STROKE_FONT_SCALE = 1.0 / 21.0
|
||||
FONT_OFFSET = -10
|
||||
|
||||
def __init__(self):
|
||||
self.parsed_font = {}
|
||||
|
||||
def parse_font_char(self, chr):
|
||||
lines = []
|
||||
line = []
|
||||
glyph_x = 0
|
||||
index = ord(chr) - ord(' ')
|
||||
if index >= len(NEWSTROKE_FONT):
|
||||
index = ord('?') - ord(' ')
|
||||
glyph_str = NEWSTROKE_FONT[index]
|
||||
for i in range(0, len(glyph_str), 2):
|
||||
coord = glyph_str[i:i + 2]
|
||||
|
||||
# The first two values contain the width of the char
|
||||
if i < 2:
|
||||
glyph_x = (ord(coord[0]) - ord('R')) * self.STROKE_FONT_SCALE
|
||||
glyph_width = (ord(coord[1]) - ord(coord[0])) * self.STROKE_FONT_SCALE
|
||||
elif coord[0] == ' ' and coord[1] == 'R':
|
||||
lines.append(line)
|
||||
line = []
|
||||
else:
|
||||
line.append([
|
||||
(ord(coord[0]) - ord('R')) * self.STROKE_FONT_SCALE - glyph_x,
|
||||
(ord(coord[1]) - ord('R') + self.FONT_OFFSET) * self.STROKE_FONT_SCALE
|
||||
])
|
||||
|
||||
if len(line) > 0:
|
||||
lines.append(line)
|
||||
|
||||
return {
|
||||
'w': glyph_width,
|
||||
'l': lines
|
||||
}
|
||||
|
||||
def parse_font_for_string(self, s):
|
||||
for c in s:
|
||||
if c == '\t' and ' ' not in self.parsed_font:
|
||||
# tabs rely on space char to calculate offset
|
||||
self.parsed_font[' '] = self.parse_font_char(' ')
|
||||
if c not in self.parsed_font and ord(c) >= ord(' '):
|
||||
self.parsed_font[c] = self.parse_font_char(c)
|
||||
|
||||
def get_parsed_font(self):
|
||||
return self.parsed_font
|
|
@ -0,0 +1,357 @@
|
|||
from __future__ import absolute_import
|
||||
|
||||
import io
|
||||
import json
|
||||
import logging
|
||||
import os
|
||||
import re
|
||||
import sys
|
||||
from datetime import datetime
|
||||
|
||||
import wx
|
||||
|
||||
from . import units
|
||||
from .config import Config
|
||||
from ..dialog import SettingsDialog
|
||||
from ..ecad.common import EcadParser, Component
|
||||
from ..errors import ParsingException
|
||||
|
||||
|
||||
class Logger(object):
|
||||
|
||||
def __init__(self, cli=False):
|
||||
self.cli = cli
|
||||
self.logger = logging.getLogger('InteractiveHtmlBom')
|
||||
self.logger.setLevel(logging.INFO)
|
||||
ch = logging.StreamHandler(sys.stdout)
|
||||
ch.setLevel(logging.INFO)
|
||||
formatter = logging.Formatter(
|
||||
"%(asctime)-15s %(levelname)s %(message)s")
|
||||
ch.setFormatter(formatter)
|
||||
self.logger.addHandler(ch)
|
||||
|
||||
def info(self, *args):
|
||||
if self.cli:
|
||||
self.logger.info(*args)
|
||||
|
||||
def error(self, msg):
|
||||
if self.cli:
|
||||
self.logger.error(msg)
|
||||
else:
|
||||
wx.MessageBox(msg)
|
||||
|
||||
def warn(self, msg):
|
||||
if self.cli:
|
||||
self.logger.warning(msg)
|
||||
else:
|
||||
wx.LogWarning(msg)
|
||||
|
||||
|
||||
log = None
|
||||
|
||||
|
||||
def skip_component(m, config):
|
||||
# type: (Component, Config) -> bool
|
||||
# skip blacklisted components
|
||||
ref_prefix = re.findall('^[A-Z]*', m.ref)[0]
|
||||
if m.ref in config.component_blacklist:
|
||||
return True
|
||||
if ref_prefix + '*' in config.component_blacklist:
|
||||
return True
|
||||
|
||||
if config.blacklist_empty_val and m.val in ['', '~']:
|
||||
return True
|
||||
|
||||
# skip virtual components if needed
|
||||
if config.blacklist_virtual and m.attr == 'Virtual':
|
||||
return True
|
||||
|
||||
# skip components with dnp field not empty
|
||||
if config.dnp_field \
|
||||
and config.dnp_field in m.extra_fields \
|
||||
and m.extra_fields[config.dnp_field]:
|
||||
return True
|
||||
|
||||
# skip components with wrong variant field
|
||||
if config.board_variant_field and config.board_variant_whitelist:
|
||||
ref_variant = m.extra_fields.get(config.board_variant_field, '')
|
||||
if ref_variant not in config.board_variant_whitelist:
|
||||
return True
|
||||
|
||||
if config.board_variant_field and config.board_variant_blacklist:
|
||||
ref_variant = m.extra_fields.get(config.board_variant_field, '')
|
||||
if ref_variant and ref_variant in config.board_variant_blacklist:
|
||||
return True
|
||||
|
||||
return False
|
||||
|
||||
|
||||
def generate_bom(pcb_footprints, config):
|
||||
# type: (list, Config) -> dict
|
||||
"""
|
||||
Generate BOM from pcb layout.
|
||||
:param pcb_footprints: list of footprints on the pcb
|
||||
:param config: Config object
|
||||
:return: dict of BOM tables (qty, value, footprint, refs)
|
||||
and dnp components
|
||||
"""
|
||||
|
||||
def convert(text):
|
||||
return int(text) if text.isdigit() else text.lower()
|
||||
|
||||
def alphanum_key(key):
|
||||
return [convert(c)
|
||||
for c in re.split('([0-9]+)', key)]
|
||||
|
||||
def natural_sort(lst):
|
||||
"""
|
||||
Natural sort for strings containing numbers
|
||||
"""
|
||||
|
||||
return sorted(lst, key=lambda r: (alphanum_key(r[0]), r[1]))
|
||||
|
||||
# build grouped part list
|
||||
skipped_components = []
|
||||
part_groups = {}
|
||||
group_by = set(config.group_fields)
|
||||
index_to_fields = {}
|
||||
|
||||
for i, f in enumerate(pcb_footprints):
|
||||
if skip_component(f, config):
|
||||
skipped_components.append(i)
|
||||
continue
|
||||
|
||||
# group part refs by value and footprint
|
||||
fields = []
|
||||
group_key = []
|
||||
|
||||
for field in config.show_fields:
|
||||
if field == "Value":
|
||||
fields.append(f.val)
|
||||
if "Value" in group_by:
|
||||
norm_value, unit = units.componentValue(f.val, f.ref)
|
||||
group_key.append(norm_value)
|
||||
group_key.append(unit)
|
||||
elif field == "Footprint":
|
||||
fields.append(f.footprint)
|
||||
if "Footprint" in group_by:
|
||||
group_key.append(f.footprint)
|
||||
group_key.append(f.attr)
|
||||
else:
|
||||
fields.append(f.extra_fields.get(field, ''))
|
||||
if field in group_by:
|
||||
group_key.append(f.extra_fields.get(field, ''))
|
||||
|
||||
index_to_fields[i] = fields
|
||||
refs = part_groups.setdefault(tuple(group_key), [])
|
||||
refs.append((f.ref, i))
|
||||
|
||||
bom_table = []
|
||||
|
||||
# If some extra fields are just integers then convert the whole column
|
||||
# so that sorting will work naturally
|
||||
for i, field in enumerate(config.show_fields):
|
||||
if field in ["Value", "Footprint"]:
|
||||
continue
|
||||
all_num = True
|
||||
for f in index_to_fields.values():
|
||||
if not f[i].isdigit() and len(f[i].strip()) > 0:
|
||||
all_num = False
|
||||
break
|
||||
if all_num:
|
||||
for f in index_to_fields.values():
|
||||
if f[i].isdigit():
|
||||
f[i] = int(f[i])
|
||||
|
||||
for _, refs in part_groups.items():
|
||||
# Fixup values to normalized string
|
||||
if "Value" in group_by and "Value" in config.show_fields:
|
||||
index = config.show_fields.index("Value")
|
||||
value = index_to_fields[refs[0][1]][index]
|
||||
for ref in refs:
|
||||
index_to_fields[ref[1]][index] = value
|
||||
|
||||
bom_table.append(natural_sort(refs))
|
||||
|
||||
# sort table by reference prefix and quantity
|
||||
def row_sort_key(element):
|
||||
prefix = re.findall('^[^0-9]*', element[0][0])[0]
|
||||
if prefix in config.component_sort_order:
|
||||
ref_ord = config.component_sort_order.index(prefix)
|
||||
else:
|
||||
ref_ord = config.component_sort_order.index('~')
|
||||
return ref_ord, -len(element), alphanum_key(element[0][0])
|
||||
|
||||
if '~' not in config.component_sort_order:
|
||||
config.component_sort_order.append('~')
|
||||
|
||||
bom_table = sorted(bom_table, key=row_sort_key)
|
||||
|
||||
result = {
|
||||
'both': bom_table,
|
||||
'skipped': skipped_components,
|
||||
'fields': index_to_fields
|
||||
}
|
||||
|
||||
for layer in ['F', 'B']:
|
||||
filtered_table = []
|
||||
for row in bom_table:
|
||||
filtered_refs = [ref for ref in row
|
||||
if pcb_footprints[ref[1]].layer == layer]
|
||||
if filtered_refs:
|
||||
filtered_table.append(filtered_refs)
|
||||
|
||||
result[layer] = sorted(filtered_table, key=row_sort_key)
|
||||
|
||||
return result
|
||||
|
||||
|
||||
def open_file(filename):
|
||||
import subprocess
|
||||
try:
|
||||
if sys.platform.startswith('win'):
|
||||
os.startfile(filename)
|
||||
elif sys.platform.startswith('darwin'):
|
||||
subprocess.call(('open', filename))
|
||||
elif sys.platform.startswith('linux'):
|
||||
subprocess.call(('xdg-open', filename))
|
||||
except Exception as e:
|
||||
log.warn('Failed to open browser: {}'.format(e))
|
||||
|
||||
|
||||
def process_substitutions(bom_name_format, pcb_file_name, metadata):
|
||||
# type: (str, str, dict)->str
|
||||
name = bom_name_format.replace('%f', os.path.splitext(pcb_file_name)[0])
|
||||
name = name.replace('%p', metadata['title'])
|
||||
name = name.replace('%c', metadata['company'])
|
||||
name = name.replace('%r', metadata['revision'])
|
||||
name = name.replace('%d', metadata['date'].replace(':', '-'))
|
||||
now = datetime.now()
|
||||
name = name.replace('%D', now.strftime('%Y-%m-%d'))
|
||||
name = name.replace('%T', now.strftime('%H-%M-%S'))
|
||||
# sanitize the name to avoid characters illegal in file systems
|
||||
name = name.replace('\\', '/')
|
||||
name = re.sub(r'[?%*:|"<>]', '_', name)
|
||||
return name + '.html'
|
||||
|
||||
|
||||
def round_floats(o, precision):
|
||||
if isinstance(o, float):
|
||||
return round(o, precision)
|
||||
if isinstance(o, dict):
|
||||
return {k: round_floats(v, precision) for k, v in o.items()}
|
||||
if isinstance(o, (list, tuple)):
|
||||
return [round_floats(x, precision) for x in o]
|
||||
return o
|
||||
|
||||
|
||||
def get_pcbdata_javascript(pcbdata, compression):
|
||||
from .lzstring import LZString
|
||||
|
||||
js = "var pcbdata = {}"
|
||||
pcbdata_str = json.dumps(round_floats(pcbdata, 6))
|
||||
|
||||
if compression:
|
||||
log.info("Compressing pcb data")
|
||||
pcbdata_str = json.dumps(LZString().compress_to_base64(pcbdata_str))
|
||||
js = "var pcbdata = JSON.parse(LZString.decompressFromBase64({}))"
|
||||
|
||||
return js.format(pcbdata_str)
|
||||
|
||||
|
||||
def generate_file(pcb_file_dir, pcb_file_name, pcbdata, config):
|
||||
def get_file_content(file_name):
|
||||
path = os.path.join(os.path.dirname(__file__), "..", "web", file_name)
|
||||
if not os.path.exists(path):
|
||||
return ""
|
||||
with io.open(path, 'r', encoding='utf-8') as f:
|
||||
return f.read()
|
||||
|
||||
if os.path.isabs(config.bom_dest_dir):
|
||||
bom_file_dir = config.bom_dest_dir
|
||||
else:
|
||||
bom_file_dir = os.path.join(pcb_file_dir, config.bom_dest_dir)
|
||||
bom_file_name = process_substitutions(
|
||||
config.bom_name_format, pcb_file_name, pcbdata['metadata'])
|
||||
bom_file_name = os.path.join(bom_file_dir, bom_file_name)
|
||||
bom_file_dir = os.path.dirname(bom_file_name)
|
||||
if not os.path.isdir(bom_file_dir):
|
||||
os.makedirs(bom_file_dir)
|
||||
pcbdata_js = get_pcbdata_javascript(pcbdata, config.compression)
|
||||
log.info("Dumping pcb data")
|
||||
config_js = "var config = " + config.get_html_config()
|
||||
html = get_file_content("ibom.html")
|
||||
html = html.replace('///CSS///', get_file_content('ibom.css'))
|
||||
html = html.replace('///USERCSS///', get_file_content('user.css'))
|
||||
html = html.replace('///SPLITJS///', get_file_content('split.js'))
|
||||
html = html.replace('///LZ-STRING///',
|
||||
get_file_content('lz-string.js')
|
||||
if config.compression else '')
|
||||
html = html.replace('///POINTER_EVENTS_POLYFILL///',
|
||||
get_file_content('pep.js'))
|
||||
html = html.replace('///CONFIG///', config_js)
|
||||
html = html.replace('///UTILJS///', get_file_content('util.js'))
|
||||
html = html.replace('///RENDERJS///', get_file_content('render.js'))
|
||||
html = html.replace('///TABLEUTILJS///', get_file_content('table-util.js'))
|
||||
html = html.replace('///IBOMJS///', get_file_content('ibom.js'))
|
||||
html = html.replace('///USERJS///', get_file_content('user.js'))
|
||||
html = html.replace('///USERHEADER///',
|
||||
get_file_content('userheader.html'))
|
||||
html = html.replace('///USERFOOTER///',
|
||||
get_file_content('userfooter.html'))
|
||||
# Replace pcbdata last for better performance.
|
||||
html = html.replace('///PCBDATA///', pcbdata_js)
|
||||
|
||||
with io.open(bom_file_name, 'wt', encoding='utf-8') as bom:
|
||||
bom.write(html)
|
||||
|
||||
log.info("Created file %s", bom_file_name)
|
||||
return bom_file_name
|
||||
|
||||
|
||||
def main(parser, config, logger):
|
||||
# type: (EcadParser, Config, Logger) -> None
|
||||
global log
|
||||
log = logger
|
||||
pcb_file_name = os.path.basename(parser.file_name)
|
||||
pcb_file_dir = os.path.dirname(parser.file_name)
|
||||
|
||||
pcbdata, components = parser.parse()
|
||||
if not pcbdata and not components:
|
||||
raise ParsingException('Parsing failed.')
|
||||
|
||||
pcbdata["bom"] = generate_bom(components, config)
|
||||
pcbdata["ibom_version"] = config.version
|
||||
|
||||
# build BOM
|
||||
bom_file = generate_file(pcb_file_dir, pcb_file_name, pcbdata, config)
|
||||
|
||||
if config.open_browser:
|
||||
logger.info("Opening file in browser")
|
||||
open_file(bom_file)
|
||||
|
||||
|
||||
def run_with_dialog(parser, config, logger):
|
||||
# type: (EcadParser, Config, Logger) -> None
|
||||
def save_config(dialog_panel, locally=False):
|
||||
config.set_from_dialog(dialog_panel)
|
||||
config.save(locally)
|
||||
|
||||
config.load_from_ini()
|
||||
dlg = SettingsDialog(extra_data_func=parser.parse_extra_data,
|
||||
extra_data_wildcard=parser.extra_data_file_filter(),
|
||||
config_save_func=save_config,
|
||||
file_name_format_hint=config.FILE_NAME_FORMAT_HINT,
|
||||
version=config.version)
|
||||
try:
|
||||
config.netlist_initial_directory = os.path.dirname(parser.file_name)
|
||||
extra_data_file = parser.latest_extra_data(
|
||||
extra_dirs=[config.bom_dest_dir])
|
||||
if extra_data_file is not None:
|
||||
dlg.set_extra_data_path(extra_data_file)
|
||||
config.transfer_to_dialog(dlg.panel)
|
||||
if dlg.ShowModal() == wx.ID_OK:
|
||||
config.set_from_dialog(dlg.panel)
|
||||
main(parser, config, logger)
|
||||
finally:
|
||||
dlg.Destroy()
|
|
@ -0,0 +1,304 @@
|
|||
"""
|
||||
Copyright 2014 Eduard Tomasek
|
||||
This work is free. You can redistribute it and/or modify it under the
|
||||
terms of the Do What The Fuck You Want To Public License, Version 2,
|
||||
as published by Sam Hocevar. See the COPYING file for more details.
|
||||
"""
|
||||
import sys
|
||||
if sys.version_info[0] == 3:
|
||||
unichr = chr
|
||||
|
||||
|
||||
class LZString:
|
||||
|
||||
def __init__(self):
|
||||
self.keyStr = (
|
||||
"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/="
|
||||
)
|
||||
|
||||
@staticmethod
|
||||
def compress(uncompressed):
|
||||
|
||||
if uncompressed is None:
|
||||
return ''
|
||||
|
||||
context_dictionary = {}
|
||||
context_dictionary_to_create = {}
|
||||
context_w = ''
|
||||
context_enlarge_in = 2
|
||||
|
||||
context_dict_size = 3
|
||||
context_num_bits = 2
|
||||
context_data_string = ''
|
||||
context_data_val = 0
|
||||
context_data_position = 0
|
||||
|
||||
uncompressed = uncompressed
|
||||
|
||||
for ii in range(len(uncompressed)):
|
||||
context_c = uncompressed[ii]
|
||||
|
||||
if context_c not in context_dictionary:
|
||||
context_dictionary[context_c] = context_dict_size
|
||||
context_dict_size += 1
|
||||
context_dictionary_to_create[context_c] = True
|
||||
|
||||
context_wc = context_w + context_c
|
||||
|
||||
if context_wc in context_dictionary:
|
||||
context_w = context_wc
|
||||
else:
|
||||
if context_w in context_dictionary_to_create:
|
||||
if ord(context_w[0]) < 256:
|
||||
for _ in range(context_num_bits):
|
||||
context_data_val = (context_data_val << 1)
|
||||
|
||||
if context_data_position == 15:
|
||||
context_data_position = 0
|
||||
context_data_string += unichr(context_data_val)
|
||||
context_data_val = 0
|
||||
else:
|
||||
context_data_position += 1
|
||||
|
||||
value = ord(context_w[0])
|
||||
|
||||
for i in range(8):
|
||||
context_data_val = (
|
||||
(context_data_val << 1) | (value & 1)
|
||||
)
|
||||
|
||||
if context_data_position == 15:
|
||||
context_data_position = 0
|
||||
context_data_string += unichr(context_data_val)
|
||||
context_data_val = 0
|
||||
else:
|
||||
context_data_position += 1
|
||||
|
||||
value = value >> 1
|
||||
else:
|
||||
value = 1
|
||||
|
||||
for i in range(context_num_bits):
|
||||
context_data_val = (context_data_val << 1) | value
|
||||
|
||||
if context_data_position == 15:
|
||||
context_data_position = 0
|
||||
context_data_string += unichr(context_data_val)
|
||||
context_data_val = 0
|
||||
else:
|
||||
context_data_position += 1
|
||||
|
||||
value = 0
|
||||
|
||||
value = ord(context_w[0])
|
||||
|
||||
for i in range(16):
|
||||
context_data_val = (
|
||||
(context_data_val << 1) | (value & 1)
|
||||
)
|
||||
|
||||
if context_data_position == 15:
|
||||
context_data_position = 0
|
||||
context_data_string += unichr(context_data_val)
|
||||
context_data_val = 0
|
||||
else:
|
||||
context_data_position += 1
|
||||
|
||||
value = value >> 1
|
||||
|
||||
context_enlarge_in -= 1
|
||||
|
||||
if context_enlarge_in == 0:
|
||||
context_enlarge_in = pow(2, context_num_bits)
|
||||
context_num_bits += 1
|
||||
|
||||
context_dictionary_to_create.pop(context_w, None)
|
||||
# del context_dictionary_to_create[context_w]
|
||||
else:
|
||||
value = context_dictionary[context_w]
|
||||
|
||||
for i in range(context_num_bits):
|
||||
context_data_val = (
|
||||
(context_data_val << 1) | (value & 1)
|
||||
)
|
||||
|
||||
if context_data_position == 15:
|
||||
context_data_position = 0
|
||||
context_data_string += unichr(context_data_val)
|
||||
context_data_val = 0
|
||||
else:
|
||||
context_data_position += 1
|
||||
|
||||
value = value >> 1
|
||||
|
||||
context_enlarge_in -= 1
|
||||
|
||||
if context_enlarge_in == 0:
|
||||
context_enlarge_in = pow(2, context_num_bits)
|
||||
context_num_bits += 1
|
||||
|
||||
context_dictionary[context_wc] = context_dict_size
|
||||
context_dict_size += 1
|
||||
context_w = context_c
|
||||
if context_w != '':
|
||||
if context_w in context_dictionary_to_create:
|
||||
if ord(context_w[0]) < 256:
|
||||
for i in range(context_num_bits):
|
||||
context_data_val = (context_data_val << 1)
|
||||
|
||||
if context_data_position == 15:
|
||||
context_data_position = 0
|
||||
context_data_string += unichr(context_data_val)
|
||||
context_data_val = 0
|
||||
else:
|
||||
context_data_position += 1
|
||||
|
||||
value = ord(context_w[0])
|
||||
|
||||
for i in range(8):
|
||||
context_data_val = (
|
||||
(context_data_val << 1) | (value & 1)
|
||||
)
|
||||
|
||||
if context_data_position == 15:
|
||||
context_data_position = 0
|
||||
context_data_string += unichr(context_data_val)
|
||||
context_data_val = 0
|
||||
else:
|
||||
context_data_position += 1
|
||||
|
||||
value = value >> 1
|
||||
else:
|
||||
value = 1
|
||||
|
||||
for i in range(context_num_bits):
|
||||
context_data_val = (context_data_val << 1) | value
|
||||
|
||||
if context_data_position == 15:
|
||||
context_data_position = 0
|
||||
context_data_string += unichr(context_data_val)
|
||||
context_data_val = 0
|
||||
else:
|
||||
context_data_position += 1
|
||||
|
||||
value = 0
|
||||
|
||||
value = ord(context_w[0])
|
||||
|
||||
for i in range(16):
|
||||
context_data_val = (
|
||||
(context_data_val << 1) | (value & 1)
|
||||
)
|
||||
|
||||
if context_data_position == 15:
|
||||
context_data_position = 0
|
||||
context_data_string += unichr(context_data_val)
|
||||
context_data_val = 0
|
||||
else:
|
||||
context_data_position += 1
|
||||
|
||||
value = value >> 1
|
||||
|
||||
context_enlarge_in -= 1
|
||||
|
||||
if context_enlarge_in == 0:
|
||||
context_enlarge_in = pow(2, context_num_bits)
|
||||
context_num_bits += 1
|
||||
|
||||
context_dictionary_to_create.pop(context_w, None)
|
||||
# del context_dictionary_to_create[context_w]
|
||||
else:
|
||||
value = context_dictionary[context_w]
|
||||
|
||||
for i in range(context_num_bits):
|
||||
context_data_val = (context_data_val << 1) | (value & 1)
|
||||
|
||||
if context_data_position == 15:
|
||||
context_data_position = 0
|
||||
context_data_string += unichr(context_data_val)
|
||||
context_data_val = 0
|
||||
else:
|
||||
context_data_position += 1
|
||||
|
||||
value = value >> 1
|
||||
|
||||
context_enlarge_in -= 1
|
||||
|
||||
if context_enlarge_in == 0:
|
||||
context_num_bits += 1
|
||||
|
||||
value = 2
|
||||
|
||||
for i in range(context_num_bits):
|
||||
context_data_val = (context_data_val << 1) | (value & 1)
|
||||
|
||||
if context_data_position == 15:
|
||||
context_data_position = 0
|
||||
context_data_string += unichr(context_data_val)
|
||||
context_data_val = 0
|
||||
else:
|
||||
context_data_position += 1
|
||||
|
||||
value = value >> 1
|
||||
|
||||
context_data_val = (context_data_val << 1)
|
||||
while context_data_position != 15:
|
||||
context_data_position += 1
|
||||
context_data_val = (context_data_val << 1)
|
||||
context_data_string += unichr(context_data_val)
|
||||
|
||||
return context_data_string
|
||||
|
||||
def compress_to_base64(self, string):
|
||||
if string is None:
|
||||
return ''
|
||||
|
||||
output = ''
|
||||
|
||||
string = self.compress(string)
|
||||
str_len = len(string)
|
||||
|
||||
for i in range(0, str_len * 2, 3):
|
||||
if (i % 2) == 0:
|
||||
chr1 = ord(string[i // 2]) >> 8
|
||||
chr2 = ord(string[i // 2]) & 255
|
||||
|
||||
if (i / 2) + 1 < str_len:
|
||||
chr3 = ord(string[(i // 2) + 1]) >> 8
|
||||
else:
|
||||
chr3 = None
|
||||
else:
|
||||
chr1 = ord(string[(i - 1) // 2]) & 255
|
||||
if (i + 1) / 2 < str_len:
|
||||
chr2 = ord(string[(i + 1) // 2]) >> 8
|
||||
chr3 = ord(string[(i + 1) // 2]) & 255
|
||||
else:
|
||||
chr2 = None
|
||||
chr3 = None
|
||||
|
||||
# python dont support bit operation with NaN like javascript
|
||||
enc1 = chr1 >> 2
|
||||
enc2 = (
|
||||
((chr1 & 3) << 4) |
|
||||
(chr2 >> 4 if chr2 is not None else 0)
|
||||
)
|
||||
enc3 = (
|
||||
((chr2 & 15 if chr2 is not None else 0) << 2) |
|
||||
(chr3 >> 6 if chr3 is not None else 0)
|
||||
)
|
||||
enc4 = (chr3 if chr3 is not None else 0) & 63
|
||||
|
||||
if chr2 is None:
|
||||
enc3 = 64
|
||||
enc4 = 64
|
||||
elif chr3 is None:
|
||||
enc4 = 64
|
||||
|
||||
output += (
|
||||
self.keyStr[enc1] +
|
||||
self.keyStr[enc2] +
|
||||
self.keyStr[enc3] +
|
||||
self.keyStr[enc4]
|
||||
)
|
||||
|
||||
return output
|
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,192 @@
|
|||
# _*_ coding:utf-8 _*_
|
||||
|
||||
# Stolen from
|
||||
# https://github.com/SchrodingersGat/KiBoM/blob/master/KiBOM/units.py
|
||||
|
||||
"""
|
||||
|
||||
This file contains a set of functions for matching values which may be written
|
||||
in different formats e.g.
|
||||
0.1uF = 100n (different suffix specified, one has missing unit)
|
||||
0R1 = 0.1Ohm (Unit replaces decimal, different units)
|
||||
|
||||
"""
|
||||
|
||||
import re
|
||||
import locale
|
||||
|
||||
current_locale = locale.setlocale(locale.LC_NUMERIC)
|
||||
try:
|
||||
locale.setlocale(locale.LC_NUMERIC, '')
|
||||
except Exception:
|
||||
# sometimes setlocale with empty string doesn't work on OSX
|
||||
pass
|
||||
decimal_separator = locale.localeconv()['decimal_point']
|
||||
locale.setlocale(locale.LC_NUMERIC, current_locale)
|
||||
|
||||
PREFIX_MICRO = [u"μ", u"µ", "u", "micro"] # first is \u03BC second is \u00B5
|
||||
PREFIX_MILLI = ["milli", "m"]
|
||||
PREFIX_NANO = ["nano", "n"]
|
||||
PREFIX_PICO = ["pico", "p"]
|
||||
PREFIX_KILO = ["kilo", "k"]
|
||||
PREFIX_MEGA = ["mega", "meg"]
|
||||
PREFIX_GIGA = ["giga", "g"]
|
||||
|
||||
# All prefices
|
||||
PREFIX_ALL = PREFIX_PICO + PREFIX_NANO + PREFIX_MICRO + \
|
||||
PREFIX_MILLI + PREFIX_KILO + PREFIX_MEGA + PREFIX_GIGA
|
||||
|
||||
# Common methods of expressing component units
|
||||
UNIT_R = ["r", "ohms", "ohm", u"Ω", u"ω"]
|
||||
UNIT_C = ["farad", "f"]
|
||||
UNIT_L = ["henry", "h"]
|
||||
|
||||
UNIT_ALL = UNIT_R + UNIT_C + UNIT_L
|
||||
|
||||
VALUE_REGEX = re.compile(
|
||||
"^([0-9\\.]+)(" + "|".join(PREFIX_ALL) + ")*(" + "|".join(
|
||||
UNIT_ALL) + ")*(\\d*)$")
|
||||
|
||||
REFERENCE_REGEX = re.compile("^(r|rv|c|l)(\\d+)$")
|
||||
|
||||
|
||||
def getUnit(unit):
|
||||
"""
|
||||
Return a simplified version of a units string, for comparison purposes
|
||||
"""
|
||||
if not unit:
|
||||
return None
|
||||
|
||||
unit = unit.lower()
|
||||
|
||||
if unit in UNIT_R:
|
||||
return "R"
|
||||
if unit in UNIT_C:
|
||||
return "F"
|
||||
if unit in UNIT_L:
|
||||
return "H"
|
||||
|
||||
return None
|
||||
|
||||
|
||||
def getPrefix(prefix):
|
||||
"""
|
||||
Return the (numerical) value of a given prefix
|
||||
"""
|
||||
if not prefix:
|
||||
return 1
|
||||
|
||||
prefix = prefix.lower()
|
||||
|
||||
if prefix in PREFIX_PICO:
|
||||
return 1.0e-12
|
||||
if prefix in PREFIX_NANO:
|
||||
return 1.0e-9
|
||||
if prefix in PREFIX_MICRO:
|
||||
return 1.0e-6
|
||||
if prefix in PREFIX_MILLI:
|
||||
return 1.0e-3
|
||||
if prefix in PREFIX_KILO:
|
||||
return 1.0e3
|
||||
if prefix in PREFIX_MEGA:
|
||||
return 1.0e6
|
||||
if prefix in PREFIX_GIGA:
|
||||
return 1.0e9
|
||||
|
||||
return 1
|
||||
|
||||
|
||||
def compMatch(component):
|
||||
"""
|
||||
Return a normalized value and units for a given component value string
|
||||
e.g. compMatch("10R2") returns (1000, R)
|
||||
e.g. compMatch("3.3mOhm") returns (0.0033, R)
|
||||
"""
|
||||
component = component.strip().lower()
|
||||
if decimal_separator == ',':
|
||||
# replace separator with dot
|
||||
component = component.replace(",", ".")
|
||||
else:
|
||||
# remove thousands separator
|
||||
component = component.replace(",", "")
|
||||
|
||||
result = VALUE_REGEX.match(component)
|
||||
|
||||
if not result:
|
||||
return None
|
||||
|
||||
if not len(result.groups()) == 4:
|
||||
return None
|
||||
|
||||
value, prefix, units, post = result.groups()
|
||||
|
||||
# special case where units is in the middle of the string
|
||||
# e.g. "0R05" for 0.05Ohm
|
||||
# in this case, we will NOT have a decimal
|
||||
# we will also have a trailing number
|
||||
|
||||
if post and "." not in value:
|
||||
try:
|
||||
value = float(int(value))
|
||||
postValue = float(int(post)) / (10 ** len(post))
|
||||
value = value * 1.0 + postValue
|
||||
except ValueError:
|
||||
return None
|
||||
|
||||
try:
|
||||
val = float(value)
|
||||
except ValueError:
|
||||
return None
|
||||
|
||||
val = "{0:.15f}".format(val * 1.0 * getPrefix(prefix))
|
||||
|
||||
return (val, getUnit(units))
|
||||
|
||||
|
||||
def componentValue(valString, reference):
|
||||
# type: (str, str) -> tuple
|
||||
result = compMatch(valString)
|
||||
|
||||
if not result:
|
||||
return valString, None # return the same string back with `None` unit
|
||||
|
||||
if not len(result) == 2: # result length is incorrect
|
||||
return valString, None # return the same string back with `None` unit
|
||||
|
||||
if result[1] is None:
|
||||
# try to infer unit from reference
|
||||
match = REFERENCE_REGEX.match(reference.lower())
|
||||
if match and len(match.groups()) == 2:
|
||||
prefix, _ = match.groups()
|
||||
unit = None
|
||||
if prefix in ['r', 'rv']:
|
||||
unit = 'R'
|
||||
if prefix == 'c':
|
||||
unit = 'F'
|
||||
if prefix == 'l':
|
||||
unit = 'H'
|
||||
result = (result[0], unit)
|
||||
|
||||
return result # (val,unit)
|
||||
|
||||
|
||||
def compareValues(c1, c2):
|
||||
r1 = compMatch(c1)
|
||||
r2 = compMatch(c2)
|
||||
|
||||
if not r1 or not r2:
|
||||
return False
|
||||
|
||||
(v1, u1) = r1
|
||||
(v2, u2) = r2
|
||||
|
||||
if v1 == v2:
|
||||
# values match
|
||||
if u1 == u2:
|
||||
return True # units match
|
||||
if not u1:
|
||||
return True # no units for component 1
|
||||
if not u2:
|
||||
return True # no units for component 2
|
||||
|
||||
return False
|
|
@ -0,0 +1 @@
|
|||
from .settings_dialog import SettingsDialog, GeneralSettingsPanel
|
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
After Width: | Height: | Size: 386 B |
Binary file not shown.
After Width: | Height: | Size: 382 B |
Binary file not shown.
After Width: | Height: | Size: 137 B |
Binary file not shown.
After Width: | Height: | Size: 165 B |
Binary file not shown.
After Width: | Height: | Size: 460 B |
|
@ -0,0 +1,574 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
|
||||
###########################################################################
|
||||
## Python code generated with wxFormBuilder (version 3.10.1-0-g8feb16b3)
|
||||
## http://www.wxformbuilder.org/
|
||||
##
|
||||
## PLEASE DO *NOT* EDIT THIS FILE!
|
||||
###########################################################################
|
||||
|
||||
import wx
|
||||
import wx.xrc
|
||||
import wx.grid
|
||||
|
||||
###########################################################################
|
||||
## Class SettingsDialogBase
|
||||
###########################################################################
|
||||
|
||||
class SettingsDialogBase ( wx.Dialog ):
|
||||
|
||||
def __init__( self, parent ):
|
||||
wx.Dialog.__init__ ( self, parent, id = wx.ID_ANY, title = u"InteractiveHtmlBom", pos = wx.DefaultPosition, size = wx.Size( 463,497 ), style = wx.DEFAULT_DIALOG_STYLE|wx.STAY_ON_TOP|wx.BORDER_DEFAULT )
|
||||
|
||||
self.SetSizeHints( wx.DefaultSize, wx.DefaultSize )
|
||||
|
||||
|
||||
self.Centre( wx.BOTH )
|
||||
|
||||
def __del__( self ):
|
||||
pass
|
||||
|
||||
|
||||
###########################################################################
|
||||
## Class SettingsDialogPanel
|
||||
###########################################################################
|
||||
|
||||
class SettingsDialogPanel ( wx.Panel ):
|
||||
|
||||
def __init__( self, parent, id = wx.ID_ANY, pos = wx.DefaultPosition, size = wx.Size( 400,300 ), style = wx.TAB_TRAVERSAL, name = wx.EmptyString ):
|
||||
wx.Panel.__init__ ( self, parent, id = id, pos = pos, size = size, style = style, name = name )
|
||||
|
||||
bSizer20 = wx.BoxSizer( wx.VERTICAL )
|
||||
|
||||
self.notebook = wx.Notebook( self, wx.ID_ANY, wx.DefaultPosition, wx.DefaultSize, wx.NB_TOP|wx.BORDER_DEFAULT )
|
||||
|
||||
bSizer20.Add( self.notebook, 1, wx.EXPAND |wx.ALL, 5 )
|
||||
|
||||
bSizer39 = wx.BoxSizer( wx.HORIZONTAL )
|
||||
|
||||
self.saveSettingsBtn = wx.Button( self, wx.ID_ANY, u"Save current settings...", wx.DefaultPosition, wx.DefaultSize, 0|wx.BORDER_DEFAULT )
|
||||
bSizer39.Add( self.saveSettingsBtn, 0, wx.ALL, 5 )
|
||||
|
||||
|
||||
bSizer39.Add( ( 50, 0), 1, wx.EXPAND, 5 )
|
||||
|
||||
self.generateBomBtn = wx.Button( self, wx.ID_ANY, u"Generate BOM", wx.DefaultPosition, wx.DefaultSize, 0|wx.BORDER_DEFAULT )
|
||||
|
||||
self.generateBomBtn.SetDefault()
|
||||
bSizer39.Add( self.generateBomBtn, 0, wx.ALL, 5 )
|
||||
|
||||
self.cancelBtn = wx.Button( self, wx.ID_CANCEL, u"Cancel", wx.DefaultPosition, wx.DefaultSize, 0|wx.BORDER_DEFAULT )
|
||||
bSizer39.Add( self.cancelBtn, 0, wx.ALL, 5 )
|
||||
|
||||
|
||||
bSizer20.Add( bSizer39, 0, wx.EXPAND, 5 )
|
||||
|
||||
|
||||
self.SetSizer( bSizer20 )
|
||||
self.Layout()
|
||||
|
||||
# Connect Events
|
||||
self.saveSettingsBtn.Bind( wx.EVT_BUTTON, self.OnSave )
|
||||
self.generateBomBtn.Bind( wx.EVT_BUTTON, self.OnGenerateBom )
|
||||
self.cancelBtn.Bind( wx.EVT_BUTTON, self.OnExit )
|
||||
|
||||
def __del__( self ):
|
||||
pass
|
||||
|
||||
|
||||
# Virtual event handlers, override them in your derived class
|
||||
def OnSave( self, event ):
|
||||
event.Skip()
|
||||
|
||||
def OnGenerateBom( self, event ):
|
||||
event.Skip()
|
||||
|
||||
def OnExit( self, event ):
|
||||
event.Skip()
|
||||
|
||||
|
||||
###########################################################################
|
||||
## Class HtmlSettingsPanelBase
|
||||
###########################################################################
|
||||
|
||||
class HtmlSettingsPanelBase ( wx.Panel ):
|
||||
|
||||
def __init__( self, parent, id = wx.ID_ANY, pos = wx.DefaultPosition, size = wx.Size( -1,-1 ), style = wx.TAB_TRAVERSAL, name = wx.EmptyString ):
|
||||
wx.Panel.__init__ ( self, parent, id = id, pos = pos, size = size, style = style, name = name )
|
||||
|
||||
b_sizer = wx.BoxSizer( wx.VERTICAL )
|
||||
|
||||
self.darkModeCheckbox = wx.CheckBox( self, wx.ID_ANY, u"Dark mode", wx.DefaultPosition, wx.DefaultSize, 0 )
|
||||
b_sizer.Add( self.darkModeCheckbox, 0, wx.ALL, 5 )
|
||||
|
||||
self.showPadsCheckbox = wx.CheckBox( self, wx.ID_ANY, u"Show footprint pads", wx.DefaultPosition, wx.DefaultSize, 0 )
|
||||
self.showPadsCheckbox.SetValue(True)
|
||||
b_sizer.Add( self.showPadsCheckbox, 0, wx.ALL, 5 )
|
||||
|
||||
self.showFabricationCheckbox = wx.CheckBox( self, wx.ID_ANY, u"Show fabrication layer", wx.DefaultPosition, wx.DefaultSize, 0 )
|
||||
b_sizer.Add( self.showFabricationCheckbox, 0, wx.ALL, 5 )
|
||||
|
||||
self.showSilkscreenCheckbox = wx.CheckBox( self, wx.ID_ANY, u"Show silkscreen", wx.DefaultPosition, wx.DefaultSize, 0 )
|
||||
self.showSilkscreenCheckbox.SetValue(True)
|
||||
b_sizer.Add( self.showSilkscreenCheckbox, 0, wx.ALL, 5 )
|
||||
|
||||
self.highlightPin1Checkbox = wx.CheckBox( self, wx.ID_ANY, u"Highlight first pin", wx.DefaultPosition, wx.DefaultSize, 0 )
|
||||
b_sizer.Add( self.highlightPin1Checkbox, 0, wx.ALL, 5 )
|
||||
|
||||
self.continuousRedrawCheckbox = wx.CheckBox( self, wx.ID_ANY, u"Continuous redraw on drag", wx.DefaultPosition, wx.DefaultSize, 0 )
|
||||
self.continuousRedrawCheckbox.SetValue(True)
|
||||
b_sizer.Add( self.continuousRedrawCheckbox, 0, wx.ALL, 5 )
|
||||
|
||||
bSizer18 = wx.BoxSizer( wx.VERTICAL )
|
||||
|
||||
bSizer19 = wx.BoxSizer( wx.HORIZONTAL )
|
||||
|
||||
self.m_boardRotationLabel = wx.StaticText( self, wx.ID_ANY, u"Board rotation", wx.DefaultPosition, wx.DefaultSize, 0 )
|
||||
self.m_boardRotationLabel.Wrap( -1 )
|
||||
|
||||
bSizer19.Add( self.m_boardRotationLabel, 0, wx.ALL, 5 )
|
||||
|
||||
|
||||
bSizer19.Add( ( 0, 0), 1, wx.EXPAND, 5 )
|
||||
|
||||
self.rotationDegreeLabel = wx.StaticText( self, wx.ID_ANY, u"0", wx.DefaultPosition, wx.Size( 30,-1 ), wx.ALIGN_RIGHT|wx.ST_NO_AUTORESIZE )
|
||||
self.rotationDegreeLabel.Wrap( -1 )
|
||||
|
||||
bSizer19.Add( self.rotationDegreeLabel, 0, wx.ALL, 5 )
|
||||
|
||||
|
||||
bSizer19.Add( ( 8, 0), 0, 0, 5 )
|
||||
|
||||
|
||||
bSizer18.Add( bSizer19, 1, wx.EXPAND, 5 )
|
||||
|
||||
self.boardRotationSlider = wx.Slider( self, wx.ID_ANY, 0, -36, 36, wx.DefaultPosition, wx.DefaultSize, wx.SL_HORIZONTAL )
|
||||
bSizer18.Add( self.boardRotationSlider, 0, wx.ALL|wx.EXPAND, 5 )
|
||||
|
||||
|
||||
b_sizer.Add( bSizer18, 0, wx.EXPAND, 5 )
|
||||
|
||||
self.offsetBackRotationCheckbox = wx.CheckBox( self, wx.ID_ANY, u"Offset back rotation", wx.DefaultPosition, wx.DefaultSize, 0 )
|
||||
b_sizer.Add( self.offsetBackRotationCheckbox, 0, wx.ALL, 5 )
|
||||
|
||||
sbSizer31 = wx.StaticBoxSizer( wx.StaticBox( self, wx.ID_ANY, u"Checkboxes" ), wx.HORIZONTAL )
|
||||
|
||||
self.bomCheckboxesCtrl = wx.TextCtrl( sbSizer31.GetStaticBox(), wx.ID_ANY, u"Sourced,Placed", wx.DefaultPosition, wx.DefaultSize, 0 )
|
||||
sbSizer31.Add( self.bomCheckboxesCtrl, 1, wx.ALL, 5 )
|
||||
|
||||
|
||||
b_sizer.Add( sbSizer31, 0, wx.ALL|wx.EXPAND, 5 )
|
||||
|
||||
bomDefaultViewChoices = [ u"BOM only", u"BOM left, drawings right", u"BOM top, drawings bottom" ]
|
||||
self.bomDefaultView = wx.RadioBox( self, wx.ID_ANY, u"BOM View", wx.DefaultPosition, wx.DefaultSize, bomDefaultViewChoices, 1, wx.RA_SPECIFY_COLS )
|
||||
self.bomDefaultView.SetSelection( 1 )
|
||||
b_sizer.Add( self.bomDefaultView, 0, wx.ALL|wx.EXPAND, 5 )
|
||||
|
||||
layerDefaultViewChoices = [ u"Front only", u"Front and Back", u"Back only" ]
|
||||
self.layerDefaultView = wx.RadioBox( self, wx.ID_ANY, u"Layer View", wx.DefaultPosition, wx.DefaultSize, layerDefaultViewChoices, 1, wx.RA_SPECIFY_COLS )
|
||||
self.layerDefaultView.SetSelection( 1 )
|
||||
b_sizer.Add( self.layerDefaultView, 0, wx.ALL|wx.EXPAND, 5 )
|
||||
|
||||
sbSizer10 = wx.StaticBoxSizer( wx.StaticBox( self, wx.ID_ANY, u"Miscellaneous" ), wx.VERTICAL )
|
||||
|
||||
self.compressionCheckbox = wx.CheckBox( sbSizer10.GetStaticBox(), wx.ID_ANY, u"Enable compression", wx.DefaultPosition, wx.DefaultSize, 0 )
|
||||
self.compressionCheckbox.SetValue(True)
|
||||
sbSizer10.Add( self.compressionCheckbox, 0, wx.ALL, 5 )
|
||||
|
||||
self.openBrowserCheckbox = wx.CheckBox( sbSizer10.GetStaticBox(), wx.ID_ANY, u"Open browser", wx.DefaultPosition, wx.DefaultSize, 0 )
|
||||
self.openBrowserCheckbox.SetValue(True)
|
||||
sbSizer10.Add( self.openBrowserCheckbox, 0, wx.ALL, 5 )
|
||||
|
||||
|
||||
b_sizer.Add( sbSizer10, 1, wx.EXPAND, 5 )
|
||||
|
||||
|
||||
self.SetSizer( b_sizer )
|
||||
self.Layout()
|
||||
b_sizer.Fit( self )
|
||||
|
||||
# Connect Events
|
||||
self.boardRotationSlider.Bind( wx.EVT_SLIDER, self.OnBoardRotationSlider )
|
||||
|
||||
def __del__( self ):
|
||||
pass
|
||||
|
||||
|
||||
# Virtual event handlers, override them in your derived class
|
||||
def OnBoardRotationSlider( self, event ):
|
||||
event.Skip()
|
||||
|
||||
|
||||
###########################################################################
|
||||
## Class GeneralSettingsPanelBase
|
||||
###########################################################################
|
||||
|
||||
class GeneralSettingsPanelBase ( wx.Panel ):
|
||||
|
||||
def __init__( self, parent, id = wx.ID_ANY, pos = wx.DefaultPosition, size = wx.Size( -1,-1 ), style = wx.TAB_TRAVERSAL, name = wx.EmptyString ):
|
||||
wx.Panel.__init__ ( self, parent, id = id, pos = pos, size = size, style = style, name = name )
|
||||
|
||||
bSizer32 = wx.BoxSizer( wx.VERTICAL )
|
||||
|
||||
sbSizer6 = wx.StaticBoxSizer( wx.StaticBox( self, wx.ID_ANY, u"Bom destination" ), wx.VERTICAL )
|
||||
|
||||
fgSizer1 = wx.FlexGridSizer( 0, 2, 0, 0 )
|
||||
fgSizer1.AddGrowableCol( 1 )
|
||||
fgSizer1.SetFlexibleDirection( wx.BOTH )
|
||||
fgSizer1.SetNonFlexibleGrowMode( wx.FLEX_GROWMODE_SPECIFIED )
|
||||
|
||||
self.m_staticText8 = wx.StaticText( sbSizer6.GetStaticBox(), wx.ID_ANY, u"Directory", wx.DefaultPosition, wx.DefaultSize, 0 )
|
||||
self.m_staticText8.Wrap( -1 )
|
||||
|
||||
fgSizer1.Add( self.m_staticText8, 0, wx.ALIGN_CENTER_VERTICAL|wx.ALL, 5 )
|
||||
|
||||
self.bomDirPicker = wx.DirPickerCtrl( sbSizer6.GetStaticBox(), wx.ID_ANY, wx.EmptyString, u"Select bom folder", wx.DefaultPosition, wx.DefaultSize, wx.DIRP_SMALL|wx.DIRP_USE_TEXTCTRL|wx.BORDER_SIMPLE )
|
||||
fgSizer1.Add( self.bomDirPicker, 1, wx.ALIGN_CENTER_VERTICAL|wx.ALL|wx.EXPAND, 5 )
|
||||
|
||||
self.m_staticText9 = wx.StaticText( sbSizer6.GetStaticBox(), wx.ID_ANY, u"Name format", wx.DefaultPosition, wx.DefaultSize, 0 )
|
||||
self.m_staticText9.Wrap( -1 )
|
||||
|
||||
fgSizer1.Add( self.m_staticText9, 0, wx.ALIGN_CENTER_VERTICAL|wx.ALL, 5 )
|
||||
|
||||
bSizer20 = wx.BoxSizer( wx.HORIZONTAL )
|
||||
|
||||
self.fileNameFormatTextControl = wx.TextCtrl( sbSizer6.GetStaticBox(), wx.ID_ANY, wx.EmptyString, wx.DefaultPosition, wx.DefaultSize, 0 )
|
||||
bSizer20.Add( self.fileNameFormatTextControl, 1, wx.ALIGN_CENTER_VERTICAL|wx.BOTTOM|wx.LEFT|wx.TOP, 5 )
|
||||
|
||||
self.m_btnNameHint = wx.Button( sbSizer6.GetStaticBox(), wx.ID_ANY, wx.EmptyString, wx.DefaultPosition, wx.DefaultSize, wx.BU_EXACTFIT )
|
||||
self.m_btnNameHint.SetMinSize( wx.Size( 30,30 ) )
|
||||
|
||||
bSizer20.Add( self.m_btnNameHint, 0, wx.ALIGN_CENTER_VERTICAL|wx.ALL, 5 )
|
||||
|
||||
|
||||
fgSizer1.Add( bSizer20, 1, wx.EXPAND, 5 )
|
||||
|
||||
|
||||
sbSizer6.Add( fgSizer1, 1, wx.EXPAND, 5 )
|
||||
|
||||
|
||||
bSizer32.Add( sbSizer6, 0, wx.ALL|wx.EXPAND, 5 )
|
||||
|
||||
sbSizer9 = wx.StaticBoxSizer( wx.StaticBox( self, wx.ID_ANY, u"Additional pcb data" ), wx.HORIZONTAL )
|
||||
|
||||
self.includeTracksCheckbox = wx.CheckBox( sbSizer9.GetStaticBox(), wx.ID_ANY, u"Include tracks/zones", wx.DefaultPosition, wx.DefaultSize, 0 )
|
||||
sbSizer9.Add( self.includeTracksCheckbox, 1, wx.ALL, 5 )
|
||||
|
||||
self.includeNetsCheckbox = wx.CheckBox( sbSizer9.GetStaticBox(), wx.ID_ANY, u"Include nets", wx.DefaultPosition, wx.DefaultSize, 0 )
|
||||
sbSizer9.Add( self.includeNetsCheckbox, 1, wx.ALL, 5 )
|
||||
|
||||
|
||||
bSizer32.Add( sbSizer9, 0, wx.ALL|wx.EXPAND, 5 )
|
||||
|
||||
sortingSizer = wx.StaticBoxSizer( wx.StaticBox( self, wx.ID_ANY, u"Component sort order" ), wx.VERTICAL )
|
||||
|
||||
bSizer4 = wx.BoxSizer( wx.HORIZONTAL )
|
||||
|
||||
bSizer6 = wx.BoxSizer( wx.VERTICAL )
|
||||
|
||||
componentSortOrderBoxChoices = []
|
||||
self.componentSortOrderBox = wx.ListBox( sortingSizer.GetStaticBox(), wx.ID_ANY, wx.DefaultPosition, wx.DefaultSize, componentSortOrderBoxChoices, wx.LB_SINGLE|wx.BORDER_SIMPLE )
|
||||
bSizer6.Add( self.componentSortOrderBox, 1, wx.ALL|wx.EXPAND, 5 )
|
||||
|
||||
|
||||
bSizer4.Add( bSizer6, 1, wx.EXPAND, 5 )
|
||||
|
||||
bSizer5 = wx.BoxSizer( wx.VERTICAL )
|
||||
|
||||
self.m_btnSortUp = wx.Button( sortingSizer.GetStaticBox(), wx.ID_ANY, wx.EmptyString, wx.DefaultPosition, wx.DefaultSize, wx.BU_EXACTFIT )
|
||||
self.m_btnSortUp.SetMinSize( wx.Size( 30,30 ) )
|
||||
|
||||
bSizer5.Add( self.m_btnSortUp, 0, wx.ALL, 5 )
|
||||
|
||||
self.m_btnSortDown = wx.Button( sortingSizer.GetStaticBox(), wx.ID_ANY, wx.EmptyString, wx.DefaultPosition, wx.DefaultSize, wx.BU_EXACTFIT )
|
||||
self.m_btnSortDown.SetMinSize( wx.Size( 30,30 ) )
|
||||
|
||||
bSizer5.Add( self.m_btnSortDown, 0, wx.ALL, 5 )
|
||||
|
||||
self.m_btnSortAdd = wx.Button( sortingSizer.GetStaticBox(), wx.ID_ANY, wx.EmptyString, wx.DefaultPosition, wx.DefaultSize, wx.BU_EXACTFIT )
|
||||
self.m_btnSortAdd.SetMinSize( wx.Size( 30,30 ) )
|
||||
|
||||
bSizer5.Add( self.m_btnSortAdd, 0, wx.ALL, 5 )
|
||||
|
||||
self.m_btnSortRemove = wx.Button( sortingSizer.GetStaticBox(), wx.ID_ANY, wx.EmptyString, wx.DefaultPosition, wx.DefaultSize, wx.BU_EXACTFIT )
|
||||
self.m_btnSortRemove.SetMinSize( wx.Size( 30,30 ) )
|
||||
|
||||
bSizer5.Add( self.m_btnSortRemove, 0, wx.ALL, 5 )
|
||||
|
||||
|
||||
bSizer4.Add( bSizer5, 0, 0, 5 )
|
||||
|
||||
|
||||
sortingSizer.Add( bSizer4, 1, wx.EXPAND, 5 )
|
||||
|
||||
|
||||
bSizer32.Add( sortingSizer, 1, wx.ALL|wx.EXPAND, 5 )
|
||||
|
||||
blacklistSizer = wx.StaticBoxSizer( wx.StaticBox( self, wx.ID_ANY, u"Component blacklist" ), wx.VERTICAL )
|
||||
|
||||
bSizer412 = wx.BoxSizer( wx.HORIZONTAL )
|
||||
|
||||
bSizer612 = wx.BoxSizer( wx.VERTICAL )
|
||||
|
||||
blacklistBoxChoices = []
|
||||
self.blacklistBox = wx.ListBox( blacklistSizer.GetStaticBox(), wx.ID_ANY, wx.DefaultPosition, wx.DefaultSize, blacklistBoxChoices, wx.LB_SINGLE|wx.LB_SORT|wx.BORDER_SIMPLE )
|
||||
bSizer612.Add( self.blacklistBox, 1, wx.ALL|wx.EXPAND, 5 )
|
||||
|
||||
|
||||
bSizer412.Add( bSizer612, 1, wx.EXPAND, 5 )
|
||||
|
||||
bSizer512 = wx.BoxSizer( wx.VERTICAL )
|
||||
|
||||
self.m_btnBlacklistAdd = wx.Button( blacklistSizer.GetStaticBox(), wx.ID_ANY, wx.EmptyString, wx.DefaultPosition, wx.DefaultSize, wx.BU_EXACTFIT )
|
||||
self.m_btnBlacklistAdd.SetMinSize( wx.Size( 30,30 ) )
|
||||
|
||||
bSizer512.Add( self.m_btnBlacklistAdd, 0, wx.ALL, 5 )
|
||||
|
||||
self.m_btnBlacklistRemove = wx.Button( blacklistSizer.GetStaticBox(), wx.ID_ANY, wx.EmptyString, wx.DefaultPosition, wx.DefaultSize, wx.BU_EXACTFIT )
|
||||
self.m_btnBlacklistRemove.SetMinSize( wx.Size( 30,30 ) )
|
||||
|
||||
bSizer512.Add( self.m_btnBlacklistRemove, 0, wx.ALL, 5 )
|
||||
|
||||
|
||||
bSizer412.Add( bSizer512, 0, 0, 5 )
|
||||
|
||||
|
||||
blacklistSizer.Add( bSizer412, 1, wx.EXPAND, 5 )
|
||||
|
||||
self.m_staticText1 = wx.StaticText( blacklistSizer.GetStaticBox(), wx.ID_ANY, u"Globs are supported, e.g. MH*", wx.DefaultPosition, wx.DefaultSize, 0 )
|
||||
self.m_staticText1.Wrap( -1 )
|
||||
|
||||
blacklistSizer.Add( self.m_staticText1, 0, wx.ALL, 5 )
|
||||
|
||||
self.blacklistVirtualCheckbox = wx.CheckBox( blacklistSizer.GetStaticBox(), wx.ID_ANY, u"Blacklist virtual components", wx.DefaultPosition, wx.DefaultSize, 0 )
|
||||
self.blacklistVirtualCheckbox.SetValue(True)
|
||||
blacklistSizer.Add( self.blacklistVirtualCheckbox, 0, wx.ALL, 5 )
|
||||
|
||||
self.blacklistEmptyValCheckbox = wx.CheckBox( blacklistSizer.GetStaticBox(), wx.ID_ANY, u"Blacklist components with empty value", wx.DefaultPosition, wx.DefaultSize, 0 )
|
||||
blacklistSizer.Add( self.blacklistEmptyValCheckbox, 0, wx.ALL, 5 )
|
||||
|
||||
|
||||
bSizer32.Add( blacklistSizer, 1, wx.ALL|wx.EXPAND|wx.TOP, 5 )
|
||||
|
||||
|
||||
self.SetSizer( bSizer32 )
|
||||
self.Layout()
|
||||
bSizer32.Fit( self )
|
||||
|
||||
# Connect Events
|
||||
self.Bind( wx.EVT_SIZE, self.OnSize )
|
||||
self.m_btnNameHint.Bind( wx.EVT_BUTTON, self.OnNameFormatHintClick )
|
||||
self.m_btnSortUp.Bind( wx.EVT_BUTTON, self.OnComponentSortOrderUp )
|
||||
self.m_btnSortDown.Bind( wx.EVT_BUTTON, self.OnComponentSortOrderDown )
|
||||
self.m_btnSortAdd.Bind( wx.EVT_BUTTON, self.OnComponentSortOrderAdd )
|
||||
self.m_btnSortRemove.Bind( wx.EVT_BUTTON, self.OnComponentSortOrderRemove )
|
||||
self.m_btnBlacklistAdd.Bind( wx.EVT_BUTTON, self.OnComponentBlacklistAdd )
|
||||
self.m_btnBlacklistRemove.Bind( wx.EVT_BUTTON, self.OnComponentBlacklistRemove )
|
||||
|
||||
def __del__( self ):
|
||||
pass
|
||||
|
||||
|
||||
# Virtual event handlers, override them in your derived class
|
||||
def OnSize( self, event ):
|
||||
event.Skip()
|
||||
|
||||
def OnNameFormatHintClick( self, event ):
|
||||
event.Skip()
|
||||
|
||||
def OnComponentSortOrderUp( self, event ):
|
||||
event.Skip()
|
||||
|
||||
def OnComponentSortOrderDown( self, event ):
|
||||
event.Skip()
|
||||
|
||||
def OnComponentSortOrderAdd( self, event ):
|
||||
event.Skip()
|
||||
|
||||
def OnComponentSortOrderRemove( self, event ):
|
||||
event.Skip()
|
||||
|
||||
def OnComponentBlacklistAdd( self, event ):
|
||||
event.Skip()
|
||||
|
||||
def OnComponentBlacklistRemove( self, event ):
|
||||
event.Skip()
|
||||
|
||||
|
||||
###########################################################################
|
||||
## Class FieldsPanelBase
|
||||
###########################################################################
|
||||
|
||||
class FieldsPanelBase ( wx.Panel ):
|
||||
|
||||
def __init__( self, parent, id = wx.ID_ANY, pos = wx.DefaultPosition, size = wx.Size( -1,-1 ), style = wx.TAB_TRAVERSAL, name = wx.EmptyString ):
|
||||
wx.Panel.__init__ ( self, parent, id = id, pos = pos, size = size, style = style, name = name )
|
||||
|
||||
bSizer42 = wx.BoxSizer( wx.VERTICAL )
|
||||
|
||||
sbSizer7 = wx.StaticBoxSizer( wx.StaticBox( self, wx.ID_ANY, u"Extra data file" ), wx.VERTICAL )
|
||||
|
||||
self.extraDataFilePicker = wx.FilePickerCtrl( sbSizer7.GetStaticBox(), wx.ID_ANY, wx.EmptyString, u"Select a file", u"Netlist and xml files (*.net; *.xml)|*.net;*.xml", wx.DefaultPosition, wx.DefaultSize, wx.FLP_DEFAULT_STYLE|wx.FLP_FILE_MUST_EXIST|wx.FLP_OPEN|wx.FLP_SMALL|wx.FLP_USE_TEXTCTRL|wx.BORDER_SIMPLE )
|
||||
sbSizer7.Add( self.extraDataFilePicker, 0, wx.EXPAND|wx.BOTTOM|wx.RIGHT|wx.LEFT, 5 )
|
||||
|
||||
|
||||
bSizer42.Add( sbSizer7, 0, wx.ALL|wx.EXPAND, 5 )
|
||||
|
||||
fieldsSizer = wx.StaticBoxSizer( wx.StaticBox( self, wx.ID_ANY, u"Fields" ), wx.VERTICAL )
|
||||
|
||||
bSizer4 = wx.BoxSizer( wx.HORIZONTAL )
|
||||
|
||||
self.fieldsGrid = wx.grid.Grid( fieldsSizer.GetStaticBox(), wx.ID_ANY, wx.DefaultPosition, wx.DefaultSize, 0 )
|
||||
|
||||
# Grid
|
||||
self.fieldsGrid.CreateGrid( 2, 3 )
|
||||
self.fieldsGrid.EnableEditing( True )
|
||||
self.fieldsGrid.EnableGridLines( True )
|
||||
self.fieldsGrid.EnableDragGridSize( False )
|
||||
self.fieldsGrid.SetMargins( 0, 0 )
|
||||
|
||||
# Columns
|
||||
self.fieldsGrid.AutoSizeColumns()
|
||||
self.fieldsGrid.EnableDragColMove( False )
|
||||
self.fieldsGrid.EnableDragColSize( True )
|
||||
self.fieldsGrid.SetColLabelValue( 0, u"Show" )
|
||||
self.fieldsGrid.SetColLabelValue( 1, u"Group" )
|
||||
self.fieldsGrid.SetColLabelValue( 2, u"Name" )
|
||||
self.fieldsGrid.SetColLabelSize( 30 )
|
||||
self.fieldsGrid.SetColLabelAlignment( wx.ALIGN_CENTER, wx.ALIGN_CENTER )
|
||||
|
||||
# Rows
|
||||
self.fieldsGrid.EnableDragRowSize( False )
|
||||
self.fieldsGrid.SetRowLabelSize( 0 )
|
||||
self.fieldsGrid.SetRowLabelAlignment( wx.ALIGN_CENTER, wx.ALIGN_CENTER )
|
||||
|
||||
# Label Appearance
|
||||
|
||||
# Cell Defaults
|
||||
self.fieldsGrid.SetDefaultCellAlignment( wx.ALIGN_CENTER, wx.ALIGN_TOP )
|
||||
bSizer4.Add( self.fieldsGrid, 1, wx.ALL|wx.EXPAND, 5 )
|
||||
|
||||
bSizer5 = wx.BoxSizer( wx.VERTICAL )
|
||||
|
||||
self.m_btnUp = wx.Button( fieldsSizer.GetStaticBox(), wx.ID_ANY, wx.EmptyString, wx.DefaultPosition, wx.DefaultSize, wx.BU_EXACTFIT )
|
||||
self.m_btnUp.SetMinSize( wx.Size( 30,30 ) )
|
||||
|
||||
bSizer5.Add( self.m_btnUp, 0, wx.ALL, 5 )
|
||||
|
||||
self.m_btnDown = wx.Button( fieldsSizer.GetStaticBox(), wx.ID_ANY, wx.EmptyString, wx.DefaultPosition, wx.DefaultSize, wx.BU_EXACTFIT )
|
||||
self.m_btnDown.SetMinSize( wx.Size( 30,30 ) )
|
||||
|
||||
bSizer5.Add( self.m_btnDown, 0, wx.ALL, 5 )
|
||||
|
||||
|
||||
bSizer4.Add( bSizer5, 0, 0, 5 )
|
||||
|
||||
|
||||
fieldsSizer.Add( bSizer4, 1, wx.EXPAND, 5 )
|
||||
|
||||
self.normalizeCaseCheckbox = wx.CheckBox( fieldsSizer.GetStaticBox(), wx.ID_ANY, u"Normalize field name case", wx.DefaultPosition, wx.DefaultSize, 0 )
|
||||
fieldsSizer.Add( self.normalizeCaseCheckbox, 0, wx.ALL|wx.EXPAND, 5 )
|
||||
|
||||
|
||||
bSizer42.Add( fieldsSizer, 2, wx.ALL|wx.EXPAND, 5 )
|
||||
|
||||
sbSizer32 = wx.StaticBoxSizer( wx.StaticBox( self, wx.ID_ANY, u"Board variant" ), wx.VERTICAL )
|
||||
|
||||
self.m_staticText5 = wx.StaticText( sbSizer32.GetStaticBox(), wx.ID_ANY, u"Board variant field name", wx.DefaultPosition, wx.DefaultSize, 0 )
|
||||
self.m_staticText5.Wrap( -1 )
|
||||
|
||||
sbSizer32.Add( self.m_staticText5, 0, wx.ALL, 5 )
|
||||
|
||||
boardVariantFieldBoxChoices = []
|
||||
self.boardVariantFieldBox = wx.ComboBox( sbSizer32.GetStaticBox(), wx.ID_ANY, wx.EmptyString, wx.DefaultPosition, wx.DefaultSize, boardVariantFieldBoxChoices, wx.CB_READONLY|wx.CB_SORT|wx.BORDER_SIMPLE )
|
||||
sbSizer32.Add( self.boardVariantFieldBox, 0, wx.ALL|wx.EXPAND, 5 )
|
||||
|
||||
bSizer17 = wx.BoxSizer( wx.HORIZONTAL )
|
||||
|
||||
bSizer18 = wx.BoxSizer( wx.VERTICAL )
|
||||
|
||||
self.m_staticText6 = wx.StaticText( sbSizer32.GetStaticBox(), wx.ID_ANY, u"Whitelist", wx.DefaultPosition, wx.DefaultSize, 0 )
|
||||
self.m_staticText6.Wrap( -1 )
|
||||
|
||||
bSizer18.Add( self.m_staticText6, 0, wx.ALL, 5 )
|
||||
|
||||
boardVariantWhitelistChoices = []
|
||||
self.boardVariantWhitelist = wx.CheckListBox( sbSizer32.GetStaticBox(), wx.ID_ANY, wx.DefaultPosition, wx.DefaultSize, boardVariantWhitelistChoices, 0|wx.BORDER_SIMPLE )
|
||||
bSizer18.Add( self.boardVariantWhitelist, 1, wx.ALL|wx.EXPAND, 5 )
|
||||
|
||||
|
||||
bSizer17.Add( bSizer18, 1, wx.EXPAND, 5 )
|
||||
|
||||
bSizer19 = wx.BoxSizer( wx.VERTICAL )
|
||||
|
||||
self.m_staticText7 = wx.StaticText( sbSizer32.GetStaticBox(), wx.ID_ANY, u"Blacklist", wx.DefaultPosition, wx.DefaultSize, 0 )
|
||||
self.m_staticText7.Wrap( -1 )
|
||||
|
||||
bSizer19.Add( self.m_staticText7, 0, wx.ALL, 5 )
|
||||
|
||||
boardVariantBlacklistChoices = []
|
||||
self.boardVariantBlacklist = wx.CheckListBox( sbSizer32.GetStaticBox(), wx.ID_ANY, wx.DefaultPosition, wx.DefaultSize, boardVariantBlacklistChoices, 0|wx.BORDER_SIMPLE )
|
||||
bSizer19.Add( self.boardVariantBlacklist, 1, wx.ALL|wx.EXPAND, 5 )
|
||||
|
||||
|
||||
bSizer17.Add( bSizer19, 1, wx.EXPAND, 5 )
|
||||
|
||||
|
||||
sbSizer32.Add( bSizer17, 1, wx.EXPAND, 5 )
|
||||
|
||||
|
||||
bSizer42.Add( sbSizer32, 3, wx.ALL|wx.EXPAND, 5 )
|
||||
|
||||
sbSizer8 = wx.StaticBoxSizer( wx.StaticBox( self, wx.ID_ANY, u"DNP field name" ), wx.VERTICAL )
|
||||
|
||||
self.m_staticText4 = wx.StaticText( sbSizer8.GetStaticBox(), wx.ID_ANY, u"Components with this field not empty will be ignored", wx.DefaultPosition, wx.DefaultSize, 0 )
|
||||
self.m_staticText4.Wrap( -1 )
|
||||
|
||||
sbSizer8.Add( self.m_staticText4, 0, wx.ALL, 5 )
|
||||
|
||||
dnpFieldBoxChoices = []
|
||||
self.dnpFieldBox = wx.ComboBox( sbSizer8.GetStaticBox(), wx.ID_ANY, u"-None-", wx.DefaultPosition, wx.DefaultSize, dnpFieldBoxChoices, wx.CB_READONLY|wx.CB_SORT|wx.BORDER_NONE )
|
||||
sbSizer8.Add( self.dnpFieldBox, 0, wx.ALL|wx.EXPAND, 5 )
|
||||
|
||||
|
||||
bSizer42.Add( sbSizer8, 0, wx.ALL|wx.EXPAND, 5 )
|
||||
|
||||
|
||||
self.SetSizer( bSizer42 )
|
||||
self.Layout()
|
||||
bSizer42.Fit( self )
|
||||
|
||||
# Connect Events
|
||||
self.Bind( wx.EVT_SIZE, self.OnSize )
|
||||
self.extraDataFilePicker.Bind( wx.EVT_FILEPICKER_CHANGED, self.OnExtraDataFileChanged )
|
||||
self.fieldsGrid.Bind( wx.grid.EVT_GRID_CELL_LEFT_CLICK, self.OnGridCellClicked )
|
||||
self.m_btnUp.Bind( wx.EVT_BUTTON, self.OnFieldsUp )
|
||||
self.m_btnDown.Bind( wx.EVT_BUTTON, self.OnFieldsDown )
|
||||
self.normalizeCaseCheckbox.Bind( wx.EVT_CHECKBOX, self.OnNetlistFileChanged )
|
||||
self.boardVariantFieldBox.Bind( wx.EVT_COMBOBOX, self.OnBoardVariantFieldChange )
|
||||
|
||||
def __del__( self ):
|
||||
pass
|
||||
|
||||
|
||||
# Virtual event handlers, override them in your derived class
|
||||
def OnSize( self, event ):
|
||||
event.Skip()
|
||||
|
||||
def OnExtraDataFileChanged( self, event ):
|
||||
event.Skip()
|
||||
|
||||
def OnGridCellClicked( self, event ):
|
||||
event.Skip()
|
||||
|
||||
def OnFieldsUp( self, event ):
|
||||
event.Skip()
|
||||
|
||||
def OnFieldsDown( self, event ):
|
||||
event.Skip()
|
||||
|
||||
def OnNetlistFileChanged( self, event ):
|
||||
event.Skip()
|
||||
|
||||
def OnBoardVariantFieldChange( self, event ):
|
||||
event.Skip()
|
|
@ -0,0 +1,390 @@
|
|||
import os
|
||||
import re
|
||||
|
||||
import wx
|
||||
import wx.grid
|
||||
|
||||
from . import dialog_base
|
||||
|
||||
if hasattr(wx, "GetLibraryVersionInfo"):
|
||||
WX_VERSION = wx.GetLibraryVersionInfo() # type: wx.VersionInfo
|
||||
WX_VERSION = (WX_VERSION.Major, WX_VERSION.Minor, WX_VERSION.Micro)
|
||||
else:
|
||||
# old kicad used this (exact version doesnt matter)
|
||||
WX_VERSION = (3, 0, 2)
|
||||
|
||||
|
||||
def pop_error(msg):
|
||||
wx.MessageBox(msg, 'Error', wx.OK | wx.ICON_ERROR)
|
||||
|
||||
|
||||
def get_btn_bitmap(bitmap):
|
||||
path = os.path.join(os.path.dirname(__file__), "bitmaps", bitmap)
|
||||
png = wx.Bitmap(path, wx.BITMAP_TYPE_PNG)
|
||||
|
||||
if WX_VERSION >= (3, 1, 6):
|
||||
return wx.BitmapBundle(png)
|
||||
else:
|
||||
return png
|
||||
|
||||
|
||||
class SettingsDialog(dialog_base.SettingsDialogBase):
|
||||
def __init__(self, extra_data_func, extra_data_wildcard, config_save_func,
|
||||
file_name_format_hint, version):
|
||||
dialog_base.SettingsDialogBase.__init__(self, None)
|
||||
self.panel = SettingsDialogPanel(
|
||||
self, extra_data_func, extra_data_wildcard, config_save_func,
|
||||
file_name_format_hint)
|
||||
best_size = self.panel.BestSize
|
||||
# hack for some gtk themes that incorrectly calculate best size
|
||||
best_size.IncBy(dx=0, dy=30)
|
||||
self.SetClientSize(best_size)
|
||||
self.SetTitle('InteractiveHtmlBom %s' % version)
|
||||
|
||||
# hack for new wxFormBuilder generating code incompatible with old wxPython
|
||||
# noinspection PyMethodOverriding
|
||||
def SetSizeHints(self, sz1, sz2):
|
||||
try:
|
||||
# wxPython 4
|
||||
super(SettingsDialog, self).SetSizeHints(sz1, sz2)
|
||||
except TypeError:
|
||||
# wxPython 3
|
||||
self.SetSizeHintsSz(sz1, sz2)
|
||||
|
||||
def set_extra_data_path(self, extra_data_file):
|
||||
self.panel.fields.extraDataFilePicker.Path = extra_data_file
|
||||
self.panel.fields.OnExtraDataFileChanged(None)
|
||||
|
||||
|
||||
# Implementing settings_dialog
|
||||
class SettingsDialogPanel(dialog_base.SettingsDialogPanel):
|
||||
def __init__(self, parent, extra_data_func, extra_data_wildcard,
|
||||
config_save_func, file_name_format_hint):
|
||||
self.config_save_func = config_save_func
|
||||
dialog_base.SettingsDialogPanel.__init__(self, parent)
|
||||
self.general = GeneralSettingsPanel(self.notebook,
|
||||
file_name_format_hint)
|
||||
self.html = HtmlSettingsPanel(self.notebook)
|
||||
self.fields = FieldsPanel(self.notebook, extra_data_func,
|
||||
extra_data_wildcard)
|
||||
self.notebook.AddPage(self.general, "General")
|
||||
self.notebook.AddPage(self.html, "Html defaults")
|
||||
self.notebook.AddPage(self.fields, "Fields")
|
||||
|
||||
self.save_menu = wx.Menu()
|
||||
self.save_locally = self.save_menu.Append(
|
||||
wx.ID_ANY, u"Locally", wx.EmptyString, wx.ITEM_NORMAL)
|
||||
self.save_globally = self.save_menu.Append(
|
||||
wx.ID_ANY, u"Globally", wx.EmptyString, wx.ITEM_NORMAL)
|
||||
|
||||
self.Bind(
|
||||
wx.EVT_MENU, self.OnSaveLocally, id=self.save_locally.GetId())
|
||||
self.Bind(
|
||||
wx.EVT_MENU, self.OnSaveGlobally, id=self.save_globally.GetId())
|
||||
|
||||
def OnExit(self, event):
|
||||
self.GetParent().EndModal(wx.ID_CANCEL)
|
||||
|
||||
def OnGenerateBom(self, event):
|
||||
self.GetParent().EndModal(wx.ID_OK)
|
||||
|
||||
def finish_init(self):
|
||||
self.html.OnBoardRotationSlider(None)
|
||||
|
||||
def OnSave(self, event):
|
||||
# type: (wx.CommandEvent) -> None
|
||||
pos = wx.Point(0, event.GetEventObject().GetSize().y)
|
||||
self.saveSettingsBtn.PopupMenu(self.save_menu, pos)
|
||||
|
||||
def OnSaveGlobally(self, event):
|
||||
self.config_save_func(self)
|
||||
|
||||
def OnSaveLocally(self, event):
|
||||
self.config_save_func(self, locally=True)
|
||||
|
||||
|
||||
# Implementing HtmlSettingsPanelBase
|
||||
class HtmlSettingsPanel(dialog_base.HtmlSettingsPanelBase):
|
||||
def __init__(self, parent):
|
||||
dialog_base.HtmlSettingsPanelBase.__init__(self, parent)
|
||||
|
||||
# Handlers for HtmlSettingsPanelBase events.
|
||||
def OnBoardRotationSlider(self, event):
|
||||
degrees = self.boardRotationSlider.Value * 5
|
||||
self.rotationDegreeLabel.LabelText = u"{}\u00B0".format(degrees)
|
||||
|
||||
|
||||
# Implementing GeneralSettingsPanelBase
|
||||
class GeneralSettingsPanel(dialog_base.GeneralSettingsPanelBase):
|
||||
|
||||
def __init__(self, parent, file_name_format_hint):
|
||||
dialog_base.GeneralSettingsPanelBase.__init__(self, parent)
|
||||
|
||||
self.file_name_format_hint = file_name_format_hint
|
||||
|
||||
bmp_arrow_up = get_btn_bitmap("btn-arrow-up.png")
|
||||
bmp_arrow_down = get_btn_bitmap("btn-arrow-down.png")
|
||||
bmp_plus = get_btn_bitmap("btn-plus.png")
|
||||
bmp_minus = get_btn_bitmap("btn-minus.png")
|
||||
bmp_question = get_btn_bitmap("btn-question.png")
|
||||
|
||||
self.m_btnSortUp.SetBitmap(bmp_arrow_up)
|
||||
self.m_btnSortDown.SetBitmap(bmp_arrow_down)
|
||||
self.m_btnSortAdd.SetBitmap(bmp_plus)
|
||||
self.m_btnSortRemove.SetBitmap(bmp_minus)
|
||||
self.m_btnNameHint.SetBitmap(bmp_question)
|
||||
self.m_btnBlacklistAdd.SetBitmap(bmp_plus)
|
||||
self.m_btnBlacklistRemove.SetBitmap(bmp_minus)
|
||||
|
||||
self.Layout()
|
||||
|
||||
# Handlers for GeneralSettingsPanelBase events.
|
||||
def OnComponentSortOrderUp(self, event):
|
||||
selection = self.componentSortOrderBox.Selection
|
||||
if selection != wx.NOT_FOUND and selection > 0:
|
||||
item = self.componentSortOrderBox.GetString(selection)
|
||||
self.componentSortOrderBox.Delete(selection)
|
||||
self.componentSortOrderBox.Insert(item, selection - 1)
|
||||
self.componentSortOrderBox.SetSelection(selection - 1)
|
||||
|
||||
def OnComponentSortOrderDown(self, event):
|
||||
selection = self.componentSortOrderBox.Selection
|
||||
size = self.componentSortOrderBox.Count
|
||||
if selection != wx.NOT_FOUND and selection < size - 1:
|
||||
item = self.componentSortOrderBox.GetString(selection)
|
||||
self.componentSortOrderBox.Delete(selection)
|
||||
self.componentSortOrderBox.Insert(item, selection + 1)
|
||||
self.componentSortOrderBox.SetSelection(selection + 1)
|
||||
|
||||
def OnComponentSortOrderAdd(self, event):
|
||||
item = wx.GetTextFromUser(
|
||||
"Characters other than A-Z will be ignored.",
|
||||
"Add sort order item")
|
||||
item = re.sub('[^A-Z]', '', item.upper())
|
||||
if item == '':
|
||||
return
|
||||
found = self.componentSortOrderBox.FindString(item)
|
||||
if found != wx.NOT_FOUND:
|
||||
self.componentSortOrderBox.SetSelection(found)
|
||||
return
|
||||
self.componentSortOrderBox.Append(item)
|
||||
self.componentSortOrderBox.SetSelection(
|
||||
self.componentSortOrderBox.Count - 1)
|
||||
|
||||
def OnComponentSortOrderRemove(self, event):
|
||||
selection = self.componentSortOrderBox.Selection
|
||||
if selection != wx.NOT_FOUND:
|
||||
item = self.componentSortOrderBox.GetString(selection)
|
||||
if item == '~':
|
||||
pop_error("You can not delete '~' item")
|
||||
return
|
||||
self.componentSortOrderBox.Delete(selection)
|
||||
if self.componentSortOrderBox.Count > 0:
|
||||
self.componentSortOrderBox.SetSelection(max(selection - 1, 0))
|
||||
|
||||
def OnComponentBlacklistAdd(self, event):
|
||||
item = wx.GetTextFromUser(
|
||||
"Characters other than A-Z 0-9 and * will be ignored.",
|
||||
"Add blacklist item")
|
||||
item = re.sub('[^A-Z0-9*]', '', item.upper())
|
||||
if item == '':
|
||||
return
|
||||
found = self.blacklistBox.FindString(item)
|
||||
if found != wx.NOT_FOUND:
|
||||
self.blacklistBox.SetSelection(found)
|
||||
return
|
||||
self.blacklistBox.Append(item)
|
||||
self.blacklistBox.SetSelection(self.blacklistBox.Count - 1)
|
||||
|
||||
def OnComponentBlacklistRemove(self, event):
|
||||
selection = self.blacklistBox.Selection
|
||||
if selection != wx.NOT_FOUND:
|
||||
self.blacklistBox.Delete(selection)
|
||||
if self.blacklistBox.Count > 0:
|
||||
self.blacklistBox.SetSelection(max(selection - 1, 0))
|
||||
|
||||
def OnNameFormatHintClick(self, event):
|
||||
wx.MessageBox(self.file_name_format_hint, 'File name format help',
|
||||
style=wx.ICON_NONE | wx.OK)
|
||||
|
||||
def OnSize(self, event):
|
||||
# Trick the listCheckBox best size calculations
|
||||
tmp = self.componentSortOrderBox.GetStrings()
|
||||
self.componentSortOrderBox.SetItems([])
|
||||
self.Layout()
|
||||
self.componentSortOrderBox.SetItems(tmp)
|
||||
|
||||
|
||||
# Implementing FieldsPanelBase
|
||||
class FieldsPanel(dialog_base.FieldsPanelBase):
|
||||
NONE_STRING = '<none>'
|
||||
FIELDS_GRID_COLUMNS = 3
|
||||
|
||||
def __init__(self, parent, extra_data_func, extra_data_wildcard):
|
||||
dialog_base.FieldsPanelBase.__init__(self, parent)
|
||||
self.extra_data_func = extra_data_func
|
||||
self.extra_field_data = None
|
||||
|
||||
self.m_btnUp.SetBitmap(get_btn_bitmap("btn-arrow-up.png"))
|
||||
self.m_btnDown.SetBitmap(get_btn_bitmap("btn-arrow-down.png"))
|
||||
|
||||
self.set_file_picker_wildcard(extra_data_wildcard)
|
||||
self._setFieldsList([])
|
||||
for i in range(2):
|
||||
box = self.GetTextExtent(self.fieldsGrid.GetColLabelValue(i))
|
||||
if hasattr(box, "x"):
|
||||
width = box.x
|
||||
else:
|
||||
width = box[0]
|
||||
width = int(width * 1.1 + 5)
|
||||
self.fieldsGrid.SetColMinimalWidth(i, width)
|
||||
self.fieldsGrid.SetColSize(i, width)
|
||||
|
||||
self.Layout()
|
||||
|
||||
def set_file_picker_wildcard(self, extra_data_wildcard):
|
||||
if extra_data_wildcard is None:
|
||||
self.extraDataFilePicker.Disable()
|
||||
return
|
||||
|
||||
# wxFilePickerCtrl doesn't support changing wildcard at runtime
|
||||
# so we have to replace it
|
||||
picker_parent = self.extraDataFilePicker.GetParent()
|
||||
new_picker = wx.FilePickerCtrl(
|
||||
picker_parent, wx.ID_ANY, wx.EmptyString,
|
||||
u"Select a file",
|
||||
extra_data_wildcard,
|
||||
wx.DefaultPosition, wx.DefaultSize,
|
||||
(wx.FLP_DEFAULT_STYLE | wx.FLP_FILE_MUST_EXIST | wx.FLP_OPEN |
|
||||
wx.FLP_SMALL | wx.FLP_USE_TEXTCTRL | wx.BORDER_SIMPLE))
|
||||
self.GetSizer().Replace(self.extraDataFilePicker, new_picker,
|
||||
recursive=True)
|
||||
self.extraDataFilePicker.Destroy()
|
||||
self.extraDataFilePicker = new_picker
|
||||
self.Layout()
|
||||
|
||||
def _swapRows(self, a, b):
|
||||
for i in range(self.FIELDS_GRID_COLUMNS):
|
||||
va = self.fieldsGrid.GetCellValue(a, i)
|
||||
vb = self.fieldsGrid.GetCellValue(b, i)
|
||||
self.fieldsGrid.SetCellValue(a, i, vb)
|
||||
self.fieldsGrid.SetCellValue(b, i, va)
|
||||
|
||||
# Handlers for FieldsPanelBase events.
|
||||
def OnGridCellClicked(self, event):
|
||||
self.fieldsGrid.ClearSelection()
|
||||
self.fieldsGrid.SelectRow(event.Row)
|
||||
if event.Col < 2:
|
||||
# toggle checkbox
|
||||
val = self.fieldsGrid.GetCellValue(event.Row, event.Col)
|
||||
val = "" if val else "1"
|
||||
self.fieldsGrid.SetCellValue(event.Row, event.Col, val)
|
||||
# group shouldn't be enabled without show
|
||||
if event.Col == 0 and val == "":
|
||||
self.fieldsGrid.SetCellValue(event.Row, 1, val)
|
||||
if event.Col == 1 and val == "1":
|
||||
self.fieldsGrid.SetCellValue(event.Row, 0, val)
|
||||
|
||||
def OnFieldsUp(self, event):
|
||||
selection = self.fieldsGrid.SelectedRows
|
||||
if len(selection) == 1 and selection[0] > 0:
|
||||
self._swapRows(selection[0], selection[0] - 1)
|
||||
self.fieldsGrid.ClearSelection()
|
||||
self.fieldsGrid.SelectRow(selection[0] - 1)
|
||||
|
||||
def OnFieldsDown(self, event):
|
||||
selection = self.fieldsGrid.SelectedRows
|
||||
size = self.fieldsGrid.NumberRows
|
||||
if len(selection) == 1 and selection[0] < size - 1:
|
||||
self._swapRows(selection[0], selection[0] + 1)
|
||||
self.fieldsGrid.ClearSelection()
|
||||
self.fieldsGrid.SelectRow(selection[0] + 1)
|
||||
|
||||
def _setFieldsList(self, fields):
|
||||
if self.fieldsGrid.NumberRows:
|
||||
self.fieldsGrid.DeleteRows(0, self.fieldsGrid.NumberRows)
|
||||
self.fieldsGrid.AppendRows(len(fields))
|
||||
row = 0
|
||||
for f in fields:
|
||||
self.fieldsGrid.SetCellValue(row, 0, "1")
|
||||
self.fieldsGrid.SetCellValue(row, 1, "1")
|
||||
self.fieldsGrid.SetCellRenderer(
|
||||
row, 0, wx.grid.GridCellBoolRenderer())
|
||||
self.fieldsGrid.SetCellRenderer(
|
||||
row, 1, wx.grid.GridCellBoolRenderer())
|
||||
self.fieldsGrid.SetCellValue(row, 2, f)
|
||||
self.fieldsGrid.SetCellAlignment(
|
||||
row, 2, wx.ALIGN_LEFT, wx.ALIGN_TOP)
|
||||
self.fieldsGrid.SetReadOnly(row, 2)
|
||||
row += 1
|
||||
|
||||
def OnExtraDataFileChanged(self, event):
|
||||
extra_data_file = self.extraDataFilePicker.Path
|
||||
if not os.path.isfile(extra_data_file):
|
||||
return
|
||||
|
||||
self.extra_field_data = None
|
||||
try:
|
||||
self.extra_field_data = self.extra_data_func(
|
||||
extra_data_file, self.normalizeCaseCheckbox.Value)
|
||||
except Exception as e:
|
||||
pop_error(
|
||||
"Failed to parse file %s\n\n%s" % (extra_data_file, e))
|
||||
self.extraDataFilePicker.Path = ''
|
||||
|
||||
if self.extra_field_data is not None:
|
||||
field_list = list(self.extra_field_data[0])
|
||||
self._setFieldsList(["Value", "Footprint"] + field_list)
|
||||
field_list.append(self.NONE_STRING)
|
||||
self.boardVariantFieldBox.SetItems(field_list)
|
||||
self.boardVariantFieldBox.SetStringSelection(self.NONE_STRING)
|
||||
self.boardVariantWhitelist.Clear()
|
||||
self.boardVariantBlacklist.Clear()
|
||||
self.dnpFieldBox.SetItems(field_list)
|
||||
self.dnpFieldBox.SetStringSelection(self.NONE_STRING)
|
||||
|
||||
def OnBoardVariantFieldChange(self, event):
|
||||
selection = self.boardVariantFieldBox.Value
|
||||
if not selection or selection == self.NONE_STRING \
|
||||
or self.extra_field_data is None:
|
||||
self.boardVariantWhitelist.Clear()
|
||||
self.boardVariantBlacklist.Clear()
|
||||
return
|
||||
variant_set = set()
|
||||
for _, field_dict in self.extra_field_data[1].items():
|
||||
if selection in field_dict:
|
||||
variant_set.add(field_dict[selection])
|
||||
self.boardVariantWhitelist.SetItems(list(variant_set))
|
||||
self.boardVariantBlacklist.SetItems(list(variant_set))
|
||||
|
||||
def OnSize(self, event):
|
||||
self.Layout()
|
||||
g = self.fieldsGrid
|
||||
g.SetColSize(
|
||||
2, g.GetClientSize().x - g.GetColSize(0) - g.GetColSize(1) - 30)
|
||||
|
||||
def GetShowFields(self):
|
||||
result = []
|
||||
for row in range(self.fieldsGrid.NumberRows):
|
||||
if self.fieldsGrid.GetCellValue(row, 0) == "1":
|
||||
result.append(self.fieldsGrid.GetCellValue(row, 2))
|
||||
return result
|
||||
|
||||
def GetGroupFields(self):
|
||||
result = []
|
||||
for row in range(self.fieldsGrid.NumberRows):
|
||||
if self.fieldsGrid.GetCellValue(row, 1) == "1":
|
||||
result.append(self.fieldsGrid.GetCellValue(row, 2))
|
||||
return result
|
||||
|
||||
def SetCheckedFields(self, show, group):
|
||||
group = [s for s in group if s in show]
|
||||
current = []
|
||||
for row in range(self.fieldsGrid.NumberRows):
|
||||
current.append(self.fieldsGrid.GetCellValue(row, 2))
|
||||
new = [s for s in current if s not in show]
|
||||
self._setFieldsList(show + new)
|
||||
for row in range(self.fieldsGrid.NumberRows):
|
||||
field = self.fieldsGrid.GetCellValue(row, 2)
|
||||
self.fieldsGrid.SetCellValue(row, 0, "1" if field in show else "")
|
||||
self.fieldsGrid.SetCellValue(row, 1, "1" if field in group else "")
|
|
@ -0,0 +1,41 @@
|
|||
import os
|
||||
|
||||
|
||||
def get_parser_by_extension(file_name, config, logger):
|
||||
ext = os.path.splitext(file_name)[1]
|
||||
if ext == '.kicad_pcb':
|
||||
return get_kicad_parser(file_name, config, logger)
|
||||
elif ext == '.json':
|
||||
""".json file may be from EasyEDA or a generic json format"""
|
||||
import io
|
||||
import json
|
||||
with io.open(file_name, 'r', encoding='utf-8') as f:
|
||||
obj = json.load(f)
|
||||
if 'pcbdata' in obj:
|
||||
return get_generic_json_parser(file_name, config, logger)
|
||||
else:
|
||||
return get_easyeda_parser(file_name, config, logger)
|
||||
elif ext in ['.fbrd', '.brd']:
|
||||
return get_fusion_eagle_parser(file_name, config, logger)
|
||||
else:
|
||||
return None
|
||||
|
||||
|
||||
def get_kicad_parser(file_name, config, logger, board=None):
|
||||
from .kicad import PcbnewParser
|
||||
return PcbnewParser(file_name, config, logger, board)
|
||||
|
||||
|
||||
def get_easyeda_parser(file_name, config, logger):
|
||||
from .easyeda import EasyEdaParser
|
||||
return EasyEdaParser(file_name, config, logger)
|
||||
|
||||
|
||||
def get_generic_json_parser(file_name, config, logger):
|
||||
from .genericjson import GenericJsonParser
|
||||
return GenericJsonParser(file_name, config, logger)
|
||||
|
||||
|
||||
def get_fusion_eagle_parser(file_name, config, logger):
|
||||
from .fusion_eagle import FusionEagleParser
|
||||
return FusionEagleParser(file_name, config, logger)
|
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
|
@ -0,0 +1,248 @@
|
|||
import math
|
||||
|
||||
from .svgpath import parse_path
|
||||
|
||||
|
||||
class EcadParser(object):
|
||||
|
||||
def __init__(self, file_name, config, logger):
|
||||
"""
|
||||
:param file_name: path to file that should be parsed.
|
||||
:param config: Config instance
|
||||
:param logger: logging object.
|
||||
"""
|
||||
self.file_name = file_name
|
||||
self.config = config
|
||||
self.logger = logger
|
||||
|
||||
def parse(self):
|
||||
"""
|
||||
Abstract method that should be overridden in implementations.
|
||||
Performs all the parsing and returns a tuple of
|
||||
(pcbdata, components)
|
||||
pcbdata is described in DATAFORMAT.md
|
||||
components is list of Component objects
|
||||
:return:
|
||||
"""
|
||||
pass
|
||||
|
||||
@staticmethod
|
||||
def normalize_field_names(data):
|
||||
field_map = {f.lower(): f for f in reversed(data[0])}
|
||||
|
||||
def remap(ref_fields):
|
||||
return {field_map[f.lower()]: v for (f, v) in
|
||||
sorted(ref_fields.items(), reverse=True)}
|
||||
|
||||
field_data = {r: remap(d) for (r, d) in data[1].items()}
|
||||
return field_map.values(), field_data
|
||||
|
||||
def get_extra_field_data(self, file_name):
|
||||
"""
|
||||
Abstract method that may be overridden in implementations that support
|
||||
extra field data.
|
||||
:return: tuple of the format
|
||||
(
|
||||
[field_name1, field_name2,... ],
|
||||
{
|
||||
ref1: {
|
||||
field_name1: field_value1,
|
||||
field_name2: field_value2,
|
||||
...
|
||||
],
|
||||
ref2: ...
|
||||
}
|
||||
)
|
||||
"""
|
||||
return [], {}
|
||||
|
||||
def parse_extra_data(self, file_name, normalize_case):
|
||||
"""
|
||||
Parses the file and returns extra field data.
|
||||
:param file_name: path to file containing extra data
|
||||
:param normalize_case: if true, normalize case so that
|
||||
"mpn", "Mpn", "MPN" fields are combined
|
||||
:return:
|
||||
"""
|
||||
data = self.get_extra_field_data(file_name)
|
||||
if normalize_case:
|
||||
data = self.normalize_field_names(data)
|
||||
return sorted(data[0]), data[1]
|
||||
|
||||
def latest_extra_data(self, extra_dirs=None):
|
||||
"""
|
||||
Abstract method that may be overridden in implementations that support
|
||||
extra field data.
|
||||
:param extra_dirs: List of extra directories to search.
|
||||
:return: File name of most recent file with extra field data.
|
||||
"""
|
||||
return None
|
||||
|
||||
def extra_data_file_filter(self):
|
||||
"""
|
||||
Abstract method that may be overridden in implementations that support
|
||||
extra field data.
|
||||
:return: File open dialog filter string, eg:
|
||||
"Netlist and xml files (*.net; *.xml)|*.net;*.xml"
|
||||
"""
|
||||
return None
|
||||
|
||||
def add_drawing_bounding_box(self, drawing, bbox):
|
||||
# type: (dict, BoundingBox) -> None
|
||||
|
||||
def add_segment():
|
||||
bbox.add_segment(drawing['start'][0], drawing['start'][1],
|
||||
drawing['end'][0], drawing['end'][1],
|
||||
drawing['width'] / 2)
|
||||
|
||||
def add_circle():
|
||||
bbox.add_circle(drawing['start'][0], drawing['start'][1],
|
||||
drawing['radius'] + drawing['width'] / 2)
|
||||
|
||||
def add_svgpath():
|
||||
width = drawing.get('width', 0)
|
||||
bbox.add_svgpath(drawing['svgpath'], width, self.logger)
|
||||
|
||||
def add_polygon():
|
||||
if 'polygons' not in drawing:
|
||||
add_svgpath()
|
||||
return
|
||||
polygon = drawing['polygons'][0]
|
||||
for point in polygon:
|
||||
bbox.add_point(point[0], point[1])
|
||||
|
||||
def add_arc():
|
||||
if 'svgpath' in drawing:
|
||||
add_svgpath()
|
||||
else:
|
||||
width = drawing.get('width', 0)
|
||||
xc, yc = drawing['start'][:2]
|
||||
a1 = drawing['startangle']
|
||||
a2 = drawing['endangle']
|
||||
r = drawing['radius']
|
||||
x1 = xc + math.cos(math.radians(a1))
|
||||
y1 = xc + math.sin(math.radians(a1))
|
||||
x2 = xc + math.cos(math.radians(a2))
|
||||
y2 = xc + math.sin(math.radians(a2))
|
||||
da = a2 - a1 if a2 > a1 else a2 + 360 - a1
|
||||
la = 1 if da > 180 else 0
|
||||
svgpath = 'M %s %s A %s %s 0 %s 1 %s %s' % \
|
||||
(x1, y1, r, r, la, x2, y2)
|
||||
bbox.add_svgpath(svgpath, width, self.logger)
|
||||
|
||||
{
|
||||
'segment': add_segment,
|
||||
'rect': add_segment, # bbox of a rect and segment are the same
|
||||
'circle': add_circle,
|
||||
'arc': add_arc,
|
||||
'polygon': add_polygon,
|
||||
'text': lambda: None, # text is not really needed for bounding box
|
||||
}.get(drawing['type'])()
|
||||
|
||||
|
||||
class Component(object):
|
||||
"""Simple data object to store component data needed for bom table."""
|
||||
|
||||
def __init__(self, ref, val, footprint, layer, attr=None, extra_fields={}):
|
||||
self.ref = ref
|
||||
self.val = val
|
||||
self.footprint = footprint
|
||||
self.layer = layer
|
||||
self.attr = attr
|
||||
self.extra_fields = extra_fields
|
||||
|
||||
|
||||
class BoundingBox(object):
|
||||
"""Geometry util to calculate and combine bounding box of simple shapes."""
|
||||
|
||||
def __init__(self):
|
||||
self._x0 = None
|
||||
self._y0 = None
|
||||
self._x1 = None
|
||||
self._y1 = None
|
||||
|
||||
def to_dict(self):
|
||||
# type: () -> dict
|
||||
return {
|
||||
"minx": self._x0,
|
||||
"miny": self._y0,
|
||||
"maxx": self._x1,
|
||||
"maxy": self._y1,
|
||||
}
|
||||
|
||||
def to_component_dict(self):
|
||||
# type: () -> dict
|
||||
return {
|
||||
"pos": [self._x0, self._y0],
|
||||
"relpos": [0, 0],
|
||||
"size": [self._x1 - self._x0, self._y1 - self._y0],
|
||||
"angle": 0,
|
||||
}
|
||||
|
||||
def add(self, other):
|
||||
"""Add another bounding box.
|
||||
:type other: BoundingBox
|
||||
"""
|
||||
if other._x0 is not None:
|
||||
self.add_point(other._x0, other._y0)
|
||||
self.add_point(other._x1, other._y1)
|
||||
return self
|
||||
|
||||
@staticmethod
|
||||
def _rotate(x, y, rx, ry, angle):
|
||||
sin = math.sin(math.radians(angle))
|
||||
cos = math.cos(math.radians(angle))
|
||||
new_x = rx + (x - rx) * cos - (y - ry) * sin
|
||||
new_y = ry + (x - rx) * sin + (y - ry) * cos
|
||||
return new_x, new_y
|
||||
|
||||
def add_point(self, x, y, rx=0, ry=0, angle=0):
|
||||
x, y = self._rotate(x, y, rx, ry, angle)
|
||||
if self._x0 is None:
|
||||
self._x0 = x
|
||||
self._y0 = y
|
||||
self._x1 = x
|
||||
self._y1 = y
|
||||
else:
|
||||
self._x0 = min(self._x0, x)
|
||||
self._y0 = min(self._y0, y)
|
||||
self._x1 = max(self._x1, x)
|
||||
self._y1 = max(self._y1, y)
|
||||
return self
|
||||
|
||||
def add_segment(self, x0, y0, x1, y1, r):
|
||||
self.add_circle(x0, y0, r)
|
||||
self.add_circle(x1, y1, r)
|
||||
return self
|
||||
|
||||
def add_rectangle(self, x, y, w, h, angle=0):
|
||||
self.add_point(x - w / 2, y - h / 2, x, y, angle)
|
||||
self.add_point(x + w / 2, y - h / 2, x, y, angle)
|
||||
self.add_point(x - w / 2, y + h / 2, x, y, angle)
|
||||
self.add_point(x + w / 2, y + h / 2, x, y, angle)
|
||||
return self
|
||||
|
||||
def add_circle(self, x, y, r):
|
||||
self.add_point(x - r, y)
|
||||
self.add_point(x, y - r)
|
||||
self.add_point(x + r, y)
|
||||
self.add_point(x, y + r)
|
||||
return self
|
||||
|
||||
def add_svgpath(self, svgpath, width, logger):
|
||||
w = width / 2
|
||||
for segment in parse_path(svgpath, logger):
|
||||
x0, x1, y0, y1 = segment.bbox()
|
||||
self.add_point(x0 - w, y0 - w)
|
||||
self.add_point(x1 + w, y1 + w)
|
||||
|
||||
def pad(self, amount):
|
||||
"""Add small padding to the box."""
|
||||
if self._x0 is not None:
|
||||
self._x0 -= amount
|
||||
self._y0 -= amount
|
||||
self._x1 += amount
|
||||
self._y1 += amount
|
||||
|
||||
def initialized(self):
|
||||
return self._x0 is not None
|
|
@ -0,0 +1,462 @@
|
|||
import io
|
||||
import sys
|
||||
|
||||
from .common import EcadParser, Component, BoundingBox
|
||||
|
||||
|
||||
if sys.version_info >= (3, 0):
|
||||
string_types = str
|
||||
else:
|
||||
string_types = basestring # noqa F821: ignore undefined
|
||||
|
||||
|
||||
class EasyEdaParser(EcadParser):
|
||||
TOP_COPPER_LAYER = 1
|
||||
BOT_COPPER_LAYER = 2
|
||||
TOP_SILK_LAYER = 3
|
||||
BOT_SILK_LAYER = 4
|
||||
BOARD_OUTLINE_LAYER = 10
|
||||
TOP_ASSEMBLY_LAYER = 13
|
||||
BOT_ASSEMBLY_LAYER = 14
|
||||
ALL_LAYERS = 11
|
||||
|
||||
def get_easyeda_pcb(self):
|
||||
import json
|
||||
with io.open(self.file_name, 'r', encoding='utf-8') as f:
|
||||
return json.load(f)
|
||||
|
||||
@staticmethod
|
||||
def tilda_split(s):
|
||||
# type: (str) -> list
|
||||
return s.split('~')
|
||||
|
||||
@staticmethod
|
||||
def sharp_split(s):
|
||||
# type: (str) -> list
|
||||
return s.split('#@$')
|
||||
|
||||
def _verify(self, pcb):
|
||||
"""Spot check the pcb object."""
|
||||
if 'head' not in pcb:
|
||||
self.logger.error('No head attribute.')
|
||||
return False
|
||||
head = pcb['head']
|
||||
if len(head) < 2:
|
||||
self.logger.error('Incorrect head attribute ' + pcb['head'])
|
||||
return False
|
||||
if head['docType'] != '3':
|
||||
self.logger.error('Incorrect document type: ' + head['docType'])
|
||||
return False
|
||||
if 'canvas' not in pcb:
|
||||
self.logger.error('No canvas attribute.')
|
||||
return False
|
||||
canvas = self.tilda_split(pcb['canvas'])
|
||||
if len(canvas) < 18:
|
||||
self.logger.error('Incorrect canvas attribute ' + pcb['canvas'])
|
||||
return False
|
||||
self.logger.info('EasyEDA editor version ' + head['editorVersion'])
|
||||
return True
|
||||
|
||||
@staticmethod
|
||||
def normalize(v):
|
||||
if isinstance(v, string_types):
|
||||
v = float(v)
|
||||
return v
|
||||
|
||||
def parse_track(self, shape):
|
||||
shape = self.tilda_split(shape)
|
||||
assert len(shape) >= 5, 'Invalid track ' + str(shape)
|
||||
width = self.normalize(shape[0])
|
||||
layer = int(shape[1])
|
||||
points = [self.normalize(v) for v in shape[3].split(' ')]
|
||||
|
||||
points_xy = [[points[i], points[i + 1]] for i in
|
||||
range(0, len(points), 2)]
|
||||
segments = [(points_xy[i], points_xy[i + 1]) for i in
|
||||
range(len(points_xy) - 1)]
|
||||
segments_json = []
|
||||
for segment in segments:
|
||||
segments_json.append({
|
||||
"type": "segment",
|
||||
"start": segment[0],
|
||||
"end": segment[1],
|
||||
"width": width,
|
||||
})
|
||||
|
||||
return layer, segments_json
|
||||
|
||||
def parse_rect(self, shape):
|
||||
shape = self.tilda_split(shape)
|
||||
assert len(shape) >= 9, 'Invalid rect ' + str(shape)
|
||||
x = self.normalize(shape[0])
|
||||
y = self.normalize(shape[1])
|
||||
width = self.normalize(shape[2])
|
||||
height = self.normalize(shape[3])
|
||||
layer = int(shape[4])
|
||||
fill = shape[8]
|
||||
|
||||
if fill == "none":
|
||||
thickness = self.normalize(shape[7])
|
||||
return layer, [{
|
||||
"type": "rect",
|
||||
"start": [x, y],
|
||||
"end": [x + width, y + height],
|
||||
"width": thickness,
|
||||
}]
|
||||
else:
|
||||
return layer, [{
|
||||
"type": "polygon",
|
||||
"pos": [x, y],
|
||||
"angle": 0,
|
||||
"polygons": [
|
||||
[[0, 0], [width, 0], [width, height], [0, height]]
|
||||
]
|
||||
}]
|
||||
|
||||
def parse_circle(self, shape):
|
||||
shape = self.tilda_split(shape)
|
||||
assert len(shape) >= 6, 'Invalid circle ' + str(shape)
|
||||
cx = self.normalize(shape[0])
|
||||
cy = self.normalize(shape[1])
|
||||
r = self.normalize(shape[2])
|
||||
width = self.normalize(shape[3])
|
||||
layer = int(shape[4])
|
||||
|
||||
return layer, [{
|
||||
"type": "circle",
|
||||
"start": [cx, cy],
|
||||
"radius": r,
|
||||
"width": width
|
||||
}]
|
||||
|
||||
def parse_solid_region(self, shape):
|
||||
shape = self.tilda_split(shape)
|
||||
assert len(shape) >= 5, 'Invalid solid region ' + str(shape)
|
||||
layer = int(shape[0])
|
||||
svgpath = shape[2]
|
||||
|
||||
return layer, [{
|
||||
"type": "polygon",
|
||||
"svgpath": svgpath,
|
||||
}]
|
||||
|
||||
def parse_text(self, shape):
|
||||
shape = self.tilda_split(shape)
|
||||
assert len(shape) >= 12, 'Invalid text ' + str(shape)
|
||||
text_type = shape[0]
|
||||
stroke_width = self.normalize(shape[3])
|
||||
layer = int(shape[6])
|
||||
text = shape[9]
|
||||
svgpath = shape[10]
|
||||
hide = shape[11]
|
||||
|
||||
return layer, [{
|
||||
"type": "text",
|
||||
"text": text,
|
||||
"thickness": stroke_width,
|
||||
"attr": [],
|
||||
"svgpath": svgpath,
|
||||
"hide": hide,
|
||||
"text_type": text_type,
|
||||
}]
|
||||
|
||||
def parse_arc(self, shape):
|
||||
shape = self.tilda_split(shape)
|
||||
assert len(shape) >= 6, 'Invalid arc ' + str(shape)
|
||||
width = self.normalize(shape[0])
|
||||
layer = int(shape[1])
|
||||
svgpath = shape[3]
|
||||
|
||||
return layer, [{
|
||||
"type": "arc",
|
||||
"svgpath": svgpath,
|
||||
"width": width
|
||||
}]
|
||||
|
||||
def parse_hole(self, shape):
|
||||
shape = self.tilda_split(shape)
|
||||
assert len(shape) >= 4, 'Invalid hole ' + str(shape)
|
||||
cx = self.normalize(shape[0])
|
||||
cy = self.normalize(shape[1])
|
||||
radius = self.normalize(shape[2])
|
||||
|
||||
return self.BOARD_OUTLINE_LAYER, [{
|
||||
"type": "circle",
|
||||
"start": [cx, cy],
|
||||
"radius": radius,
|
||||
"width": 0.1, # 1 mil
|
||||
}]
|
||||
|
||||
def parse_pad(self, shape):
|
||||
shape = self.tilda_split(shape)
|
||||
assert len(shape) >= 15, 'Invalid pad ' + str(shape)
|
||||
pad_shape = shape[0]
|
||||
x = self.normalize(shape[1])
|
||||
y = self.normalize(shape[2])
|
||||
width = self.normalize(shape[3])
|
||||
height = self.normalize(shape[4])
|
||||
layer = int(shape[5])
|
||||
number = shape[7]
|
||||
hole_radius = self.normalize(shape[8])
|
||||
if shape[9]:
|
||||
points = [self.normalize(v) for v in shape[9].split(' ')]
|
||||
else:
|
||||
points = []
|
||||
angle = int(shape[10])
|
||||
hole_length = self.normalize(shape[12]) if shape[12] else 0
|
||||
|
||||
pad_layers = {
|
||||
self.TOP_COPPER_LAYER: ['F'],
|
||||
self.BOT_COPPER_LAYER: ['B'],
|
||||
self.ALL_LAYERS: ['F', 'B']
|
||||
}.get(layer)
|
||||
pad_shape = {
|
||||
"ELLIPSE": "circle",
|
||||
"RECT": "rect",
|
||||
"OVAL": "oval",
|
||||
"POLYGON": "custom",
|
||||
}.get(pad_shape)
|
||||
pad_type = "smd" if len(pad_layers) == 1 else "th"
|
||||
|
||||
json = {
|
||||
"layers": pad_layers,
|
||||
"pos": [x, y],
|
||||
"size": [width, height],
|
||||
"angle": angle,
|
||||
"shape": pad_shape,
|
||||
"type": pad_type,
|
||||
}
|
||||
if number == '1':
|
||||
json['pin1'] = 1
|
||||
if pad_shape == "custom":
|
||||
polygon = [(points[i], points[i + 1]) for i in
|
||||
range(0, len(points), 2)]
|
||||
# translate coordinates to be relative to footprint
|
||||
polygon = [(p[0] - x, p[1] - y) for p in polygon]
|
||||
json["polygons"] = [polygon]
|
||||
json["angle"] = 0
|
||||
if pad_type == "th":
|
||||
if hole_length > 1e-6:
|
||||
json["drillshape"] = "oblong"
|
||||
json["drillsize"] = [hole_radius * 2, hole_length]
|
||||
else:
|
||||
json["drillshape"] = "circle"
|
||||
json["drillsize"] = [hole_radius * 2, hole_radius * 2]
|
||||
|
||||
return layer, [{
|
||||
"type": "pad",
|
||||
"pad": json,
|
||||
}]
|
||||
|
||||
@staticmethod
|
||||
def add_pad_bounding_box(pad, bbox):
|
||||
# type: (dict, BoundingBox) -> None
|
||||
|
||||
def add_circle():
|
||||
bbox.add_circle(pad['pos'][0], pad['pos'][1], pad['size'][0] / 2)
|
||||
|
||||
def add_rect():
|
||||
bbox.add_rectangle(pad['pos'][0], pad['pos'][1],
|
||||
pad['size'][0], pad['size'][1],
|
||||
pad['angle'])
|
||||
|
||||
def add_custom():
|
||||
x = pad['pos'][0]
|
||||
y = pad['pos'][1]
|
||||
polygon = pad['polygons'][0]
|
||||
for point in polygon:
|
||||
bbox.add_point(x + point[0], y + point[1])
|
||||
|
||||
{
|
||||
'circle': add_circle,
|
||||
'rect': add_rect,
|
||||
'oval': add_rect,
|
||||
'custom': add_custom,
|
||||
}.get(pad['shape'])()
|
||||
|
||||
def parse_lib(self, shape):
|
||||
parts = self.sharp_split(shape)
|
||||
head = self.tilda_split(parts[0])
|
||||
inner_shapes, _, _ = self.parse_shapes(parts[1:])
|
||||
x = self.normalize(head[0])
|
||||
y = self.normalize(head[1])
|
||||
attr = head[2]
|
||||
fp_layer = int(head[6])
|
||||
|
||||
attr = attr.split('`')
|
||||
if len(attr) % 2 != 0:
|
||||
attr.pop()
|
||||
attr = {attr[i]: attr[i + 1] for i in range(0, len(attr), 2)}
|
||||
fp_layer = 'F' if fp_layer == self.TOP_COPPER_LAYER else 'B'
|
||||
val = '??'
|
||||
ref = '??'
|
||||
footprint = attr.get('package', '??')
|
||||
|
||||
pads = []
|
||||
copper_drawings = []
|
||||
extra_drawings = []
|
||||
bbox = BoundingBox()
|
||||
for layer, shapes in inner_shapes.items():
|
||||
for s in shapes:
|
||||
if s["type"] == "pad":
|
||||
pads.append(s["pad"])
|
||||
continue
|
||||
if s["type"] == "text":
|
||||
if s["text_type"] == "N":
|
||||
val = s["text"]
|
||||
if s["text_type"] == "P":
|
||||
ref = s["text"]
|
||||
del s["text_type"]
|
||||
if s["hide"]:
|
||||
continue
|
||||
if layer in [self.TOP_COPPER_LAYER, self.BOT_COPPER_LAYER]:
|
||||
copper_drawings.append({
|
||||
"layer": (
|
||||
'F' if layer == self.TOP_COPPER_LAYER else 'B'),
|
||||
"drawing": s,
|
||||
})
|
||||
elif layer in [self.TOP_SILK_LAYER,
|
||||
self.BOT_SILK_LAYER,
|
||||
self.TOP_ASSEMBLY_LAYER,
|
||||
self.BOT_ASSEMBLY_LAYER,
|
||||
self.BOARD_OUTLINE_LAYER]:
|
||||
extra_drawings.append((layer, s))
|
||||
|
||||
for pad in pads:
|
||||
self.add_pad_bounding_box(pad, bbox)
|
||||
for drawing in copper_drawings:
|
||||
self.add_drawing_bounding_box(drawing['drawing'], bbox)
|
||||
for _, drawing in extra_drawings:
|
||||
self.add_drawing_bounding_box(drawing, bbox)
|
||||
bbox.pad(0.5) # pad by 5 mil
|
||||
if not bbox.initialized():
|
||||
# if bounding box is not calculated yet
|
||||
# set it to 100x100 mil square
|
||||
bbox.add_rectangle(x, y, 10, 10, 0)
|
||||
|
||||
footprint_json = {
|
||||
"ref": ref,
|
||||
"center": [x, y],
|
||||
"bbox": bbox.to_component_dict(),
|
||||
"pads": pads,
|
||||
"drawings": copper_drawings,
|
||||
"layer": fp_layer,
|
||||
}
|
||||
|
||||
component = Component(ref, val, footprint, fp_layer)
|
||||
|
||||
return fp_layer, component, footprint_json, extra_drawings
|
||||
|
||||
def parse_shapes(self, shapes):
|
||||
drawings = {}
|
||||
footprints = []
|
||||
components = []
|
||||
|
||||
for shape_str in shapes:
|
||||
shape = shape_str.split('~', 1)
|
||||
parse_func = {
|
||||
'TRACK': self.parse_track,
|
||||
'RECT': self.parse_rect,
|
||||
'CIRCLE': self.parse_circle,
|
||||
'SOLIDREGION': self.parse_solid_region,
|
||||
'TEXT': self.parse_text,
|
||||
'ARC': self.parse_arc,
|
||||
'PAD': self.parse_pad,
|
||||
'HOLE': self.parse_hole,
|
||||
}.get(shape[0], None)
|
||||
if parse_func:
|
||||
layer, json_list = parse_func(shape[1])
|
||||
drawings.setdefault(layer, []).extend(json_list)
|
||||
if shape[0] == 'LIB':
|
||||
layer, component, json, extras = self.parse_lib(shape[1])
|
||||
for drawing_layer, drawing in extras:
|
||||
drawings.setdefault(drawing_layer, []).append(drawing)
|
||||
footprints.append(json)
|
||||
components.append(component)
|
||||
|
||||
return drawings, footprints, components
|
||||
|
||||
def get_metadata(self, pcb):
|
||||
if hasattr(pcb, 'metadata'):
|
||||
return pcb.metadata
|
||||
else:
|
||||
import os
|
||||
from datetime import datetime
|
||||
pcb_file_name = os.path.basename(self.file_name)
|
||||
title = os.path.splitext(pcb_file_name)[0]
|
||||
file_mtime = os.path.getmtime(self.file_name)
|
||||
file_date = datetime.fromtimestamp(file_mtime).strftime(
|
||||
'%Y-%m-%d %H:%M:%S')
|
||||
return {
|
||||
"title": title,
|
||||
"revision": "",
|
||||
"company": "",
|
||||
"date": file_date,
|
||||
}
|
||||
|
||||
def parse(self):
|
||||
pcb = self.get_easyeda_pcb()
|
||||
if not self._verify(pcb):
|
||||
self.logger.error(
|
||||
'File ' + self.file_name +
|
||||
' does not appear to be valid EasyEDA json file.')
|
||||
return None, None
|
||||
|
||||
drawings, footprints, components = self.parse_shapes(pcb['shape'])
|
||||
|
||||
board_outline_bbox = BoundingBox()
|
||||
for drawing in drawings.get(self.BOARD_OUTLINE_LAYER, []):
|
||||
self.add_drawing_bounding_box(drawing, board_outline_bbox)
|
||||
if board_outline_bbox.initialized():
|
||||
bbox = board_outline_bbox.to_dict()
|
||||
else:
|
||||
# if nothing is drawn on outline layer then rely on EasyEDA bbox
|
||||
x = self.normalize(pcb['BBox']['x'])
|
||||
y = self.normalize(pcb['BBox']['y'])
|
||||
bbox = {
|
||||
"minx": x,
|
||||
"miny": y,
|
||||
"maxx": x + self.normalize(pcb['BBox']['width']),
|
||||
"maxy": y + self.normalize(pcb['BBox']['height'])
|
||||
}
|
||||
|
||||
pcbdata = {
|
||||
"edges_bbox": bbox,
|
||||
"edges": drawings.get(self.BOARD_OUTLINE_LAYER, []),
|
||||
"drawings": {
|
||||
"silkscreen": {
|
||||
'F': drawings.get(self.TOP_SILK_LAYER, []),
|
||||
'B': drawings.get(self.BOT_SILK_LAYER, []),
|
||||
},
|
||||
"fabrication": {
|
||||
'F': drawings.get(self.TOP_ASSEMBLY_LAYER, []),
|
||||
'B': drawings.get(self.BOT_ASSEMBLY_LAYER, []),
|
||||
},
|
||||
},
|
||||
"footprints": footprints,
|
||||
"metadata": self.get_metadata(pcb),
|
||||
"bom": {},
|
||||
"font_data": {}
|
||||
}
|
||||
|
||||
if self.config.include_tracks:
|
||||
def filter_tracks(drawing_list, drawing_type, keys):
|
||||
result = []
|
||||
for d in drawing_list:
|
||||
if d["type"] == drawing_type:
|
||||
r = {}
|
||||
for key in keys:
|
||||
r[key] = d[key]
|
||||
result.append(r)
|
||||
return result
|
||||
|
||||
pcbdata["tracks"] = {
|
||||
'F': filter_tracks(drawings.get(self.TOP_COPPER_LAYER, []),
|
||||
"segment", ["start", "end", "width"]),
|
||||
'B': filter_tracks(drawings.get(self.BOT_COPPER_LAYER, []),
|
||||
"segment", ["start", "end", "width"]),
|
||||
}
|
||||
# zones are not supported
|
||||
pcbdata["zones"] = {'F': [], 'B': []}
|
||||
|
||||
return pcbdata, components
|
|
@ -0,0 +1,862 @@
|
|||
import io
|
||||
import math
|
||||
import os
|
||||
import string
|
||||
import zipfile
|
||||
from datetime import datetime
|
||||
from xml.etree import ElementTree
|
||||
|
||||
from .common import EcadParser, Component, BoundingBox
|
||||
from .svgpath import Arc
|
||||
from ..core.fontparser import FontParser
|
||||
|
||||
|
||||
class FusionEagleParser(EcadParser):
|
||||
TOP_COPPER_LAYER = '1'
|
||||
BOT_COPPER_LAYER = '16'
|
||||
TOP_PLACE_LAYER = '21'
|
||||
BOT_PLACE_LAYER = '22'
|
||||
TOP_NAMES_LAYER = '25'
|
||||
BOT_NAMES_LAYER = '26'
|
||||
DIMENSION_LAYER = '20'
|
||||
TOP_DOCU_LAYER = '51'
|
||||
BOT_DOCU_LAYER = '52'
|
||||
|
||||
def __init__(self, file_name, config, logger):
|
||||
super(FusionEagleParser, self).__init__(file_name, config, logger)
|
||||
self.config = config
|
||||
self.font_parser = FontParser()
|
||||
self.min_via_w = 1e-3
|
||||
self.pcbdata = {
|
||||
'drawings': {
|
||||
'silkscreen': {
|
||||
'F': [],
|
||||
'B': []
|
||||
},
|
||||
'fabrication': {
|
||||
'F': [],
|
||||
'B': []
|
||||
}
|
||||
},
|
||||
'edges': [],
|
||||
'footprints': [],
|
||||
'font_data': {}
|
||||
}
|
||||
self.components = []
|
||||
|
||||
def _parse_pad_nets(self, signals):
|
||||
elements = {}
|
||||
|
||||
for signal in signals.iter('signal'):
|
||||
net = signal.attrib['name']
|
||||
for c in signal.iter('contactref'):
|
||||
e = c.attrib['element']
|
||||
if e not in elements:
|
||||
elements[e] = {}
|
||||
elements[e][c.attrib['pad']] = net
|
||||
|
||||
self.elements_pad_nets = elements
|
||||
|
||||
@staticmethod
|
||||
def _radian(ux, uy, vx, vy):
|
||||
dot = ux * vx + uy * vy
|
||||
mod = math.sqrt((ux * ux + uy * uy) * (vx * vx + vy * vy))
|
||||
rad = math.acos(dot / mod)
|
||||
if ux * vy - uy * vx < 0.0:
|
||||
rad = -rad
|
||||
return rad
|
||||
|
||||
def _curve_to_svgparams(self, el, x=0, y=0, angle=0):
|
||||
_x1 = float(el.attrib['x1'])
|
||||
_x2 = float(el.attrib['x2'])
|
||||
_y1 = -float(el.attrib['y1'])
|
||||
_y2 = -float(el.attrib['y2'])
|
||||
|
||||
dx1, dy1 = self._rotate(_x1, _y1, -angle)
|
||||
dx2, dy2 = self._rotate(_x2, _y2, -angle)
|
||||
|
||||
x1, y1 = x + dx1, -y + dy1
|
||||
x2, y2 = x + dx2, -y + dy2
|
||||
|
||||
chord = math.sqrt((x2 - x1) ** 2 + (y2 - y1) ** 2)
|
||||
theta = float(el.attrib['curve'])
|
||||
r = abs(0.5 * chord / math.sin(math.radians(theta) / 2))
|
||||
la = 0 if abs(theta) < 180 else 1
|
||||
sw = 0 if theta > 0 else 1
|
||||
return {
|
||||
'x1': x1,
|
||||
'y1': y1,
|
||||
'r': r,
|
||||
'la': la,
|
||||
'sw': sw,
|
||||
'x2': x2,
|
||||
'y2': y2
|
||||
}
|
||||
|
||||
def _curve_to_svgpath(self, el, x=0, y=0, angle=0):
|
||||
p = self._curve_to_svgparams(el, x, y, angle)
|
||||
return 'M {x1} {y1} A {r} {r} 0 {la} {sw} {x2} {y2}'.format(**p)
|
||||
|
||||
@staticmethod
|
||||
class Rot:
|
||||
def __init__(self, rot_string):
|
||||
if not rot_string:
|
||||
self.mirrored = False
|
||||
self.spin = False
|
||||
self.angle = 0
|
||||
return
|
||||
self.mirrored = 'M' in rot_string
|
||||
self.spin = 'S' in rot_string
|
||||
self.angle = float(''.join(d for d in rot_string
|
||||
if d in string.digits + '.'))
|
||||
|
||||
def __str__(self):
|
||||
return "Mirrored: {0}, Spin: {1}, Angle: {2}".format(self.mirrored,
|
||||
self.spin,
|
||||
self.angle)
|
||||
|
||||
def _rectangle_vertices(self, el):
|
||||
# Note: Eagle specifies a rectangle using opposing corners
|
||||
# (x1, y1) = lower-left and (x2, y2) = upper-right) and *optionally*
|
||||
# a rotation angle. The size of the rectangle is defined by the
|
||||
# corners irrespective of rotation angle, and then it is rotated
|
||||
# about its own center point.
|
||||
_x1 = float(el.attrib['x1'])
|
||||
_x2 = float(el.attrib['x2'])
|
||||
_y1 = -float(el.attrib['y1'])
|
||||
_y2 = -float(el.attrib['y2'])
|
||||
|
||||
# Center of rectangle
|
||||
xc = (_x1 + _x2) / 2
|
||||
yc = (_y1 + _y2) / 2
|
||||
|
||||
# Vertices of rectangle relative to its center, un-rotated
|
||||
_dv_c = [
|
||||
(_x1 - xc, _y1 - yc),
|
||||
(_x2 - xc, _y1 - yc),
|
||||
(_x2 - xc, _y2 - yc),
|
||||
(_x1 - xc, _y2 - yc)
|
||||
]
|
||||
|
||||
elr = self.Rot(el.get('rot'))
|
||||
|
||||
# Rotate the rectangle about its center
|
||||
dv_c = [self._rotate(_x, _y, -elr.angle, elr.mirrored)
|
||||
for (_x, _y) in _dv_c]
|
||||
|
||||
# Map vertices to position relative to component origin, un-rotated
|
||||
return [(_x + xc, _y + yc) for (_x, _y) in dv_c]
|
||||
|
||||
def _add_drawing(self, el):
|
||||
layer_dest = {
|
||||
self.DIMENSION_LAYER: self.pcbdata['edges'],
|
||||
self.TOP_PLACE_LAYER: self.pcbdata['drawings']['silkscreen']['F'],
|
||||
self.BOT_PLACE_LAYER: self.pcbdata['drawings']['silkscreen']['B'],
|
||||
self.TOP_NAMES_LAYER: self.pcbdata['drawings']['silkscreen']['F'],
|
||||
self.BOT_NAMES_LAYER: self.pcbdata['drawings']['silkscreen']['B']
|
||||
}
|
||||
if ("layer" in el.attrib) and (el.attrib['layer'] in layer_dest):
|
||||
dwg = None
|
||||
if el.tag == 'wire':
|
||||
dwg = {'width': float(el.attrib['width'])}
|
||||
|
||||
if 'curve' in el.attrib:
|
||||
dwg['type'] = 'arc'
|
||||
dwg['svgpath'] = self._curve_to_svgpath(el)
|
||||
else:
|
||||
dwg['type'] = 'segment'
|
||||
dwg['start'] = [
|
||||
float(el.attrib['x1']),
|
||||
-float(el.attrib['y1'])
|
||||
]
|
||||
dwg['end'] = [
|
||||
float(el.attrib['x2']),
|
||||
-float(el.attrib['y2'])
|
||||
]
|
||||
|
||||
elif el.tag == 'text':
|
||||
# Text is not currently supported (except refdes)
|
||||
# due to lack of Eagle font data
|
||||
pass
|
||||
|
||||
elif el.tag == 'circle':
|
||||
dwg = {
|
||||
'type': 'circle',
|
||||
'start': [float(el.attrib['x']), -float(el.attrib['y'])],
|
||||
'radius': float(el.attrib['radius']),
|
||||
'width': float(el.attrib['width'])
|
||||
}
|
||||
|
||||
elif el.tag in ['polygonshape', 'polygon']:
|
||||
dwg = {
|
||||
'type': 'polygon',
|
||||
'pos': [0, 0],
|
||||
'angle': 0,
|
||||
'polygons': []
|
||||
}
|
||||
segs = el if el.tag == 'polygon' \
|
||||
else el.find('polygonoutlinesegments')
|
||||
polygon = FusionEagleParser._segments_to_polygon(segs)
|
||||
dwg['polygons'].append(polygon)
|
||||
|
||||
elif el.tag == 'rectangle':
|
||||
vertices = self._rectangle_vertices(el)
|
||||
dwg = {
|
||||
'type': 'polygon',
|
||||
'pos': [0, 0],
|
||||
'angle': 0,
|
||||
'polygons': [[list(v) for v in vertices]]
|
||||
}
|
||||
|
||||
if dwg:
|
||||
layer_dest[el.attrib['layer']].append(dwg)
|
||||
|
||||
def _add_track(self, el, net):
|
||||
if el.tag == 'via' or (
|
||||
el.tag == 'wire' and el.attrib['layer'] in
|
||||
[self.TOP_COPPER_LAYER, self.BOT_COPPER_LAYER]):
|
||||
trk = {}
|
||||
if self.config.include_nets:
|
||||
trk['net'] = net
|
||||
|
||||
if el.tag == 'wire':
|
||||
dest = self.pcbdata['tracks']['F'] \
|
||||
if el.attrib['layer'] == self.TOP_COPPER_LAYER\
|
||||
else self.pcbdata['tracks']['B']
|
||||
|
||||
if 'curve' in el.attrib:
|
||||
trk['width'] = float(el.attrib['width'])
|
||||
# Get SVG parameters for the curve
|
||||
p = self._curve_to_svgparams(el)
|
||||
start = complex(p['x1'], p['y1'])
|
||||
end = complex(p['x2'], p['y2'])
|
||||
radius = complex(p['r'], p['r'])
|
||||
large_arc = bool(p['la'])
|
||||
sweep = bool(p['sw'])
|
||||
# Pass SVG parameters to get center parameters
|
||||
arc = Arc(start, radius, 0, large_arc, sweep, end)
|
||||
# Create arc track from center parameters
|
||||
trk['center'] = [arc.center.real, arc.center.imag]
|
||||
trk['radius'] = radius.real
|
||||
if arc.delta < 0:
|
||||
trk['startangle'] = arc.theta + arc.delta
|
||||
trk['endangle'] = arc.theta
|
||||
else:
|
||||
trk['startangle'] = arc.theta
|
||||
trk['endangle'] = arc.theta + arc.delta
|
||||
dest.append(trk)
|
||||
else:
|
||||
trk['start'] = [
|
||||
float(el.attrib['x1']),
|
||||
-float(el.attrib['y1'])
|
||||
]
|
||||
trk['end'] = [
|
||||
float(el.attrib['x2']),
|
||||
-float(el.attrib['y2'])
|
||||
]
|
||||
trk['width'] = float(el.attrib['width'])
|
||||
dest.append(trk)
|
||||
|
||||
elif el.tag == 'via':
|
||||
trk['start'] = [float(el.attrib['x']), -float(el.attrib['y'])]
|
||||
trk['end'] = trk['start']
|
||||
trk['width'] = float(el.attrib['drill']) + 2 * self.min_via_w \
|
||||
if 'diameter' not in el.attrib else float(
|
||||
el.attrib['diameter'])
|
||||
self.pcbdata['tracks']['F'].append(trk)
|
||||
self.pcbdata['tracks']['B'].append(trk)
|
||||
|
||||
def _calculate_footprint_bbox(self, package, x, y, angle, mirrored):
|
||||
_angle = angle if not mirrored else -angle
|
||||
layers = [
|
||||
self.TOP_PLACE_LAYER,
|
||||
self.BOT_PLACE_LAYER,
|
||||
self.TOP_DOCU_LAYER,
|
||||
self.BOT_DOCU_LAYER
|
||||
]
|
||||
xmax, ymax = -float('inf'), -float('inf')
|
||||
xmin, ymin = float('inf'), float('inf')
|
||||
|
||||
for el in package.iter('wire'):
|
||||
if el.tag == 'wire' and el.attrib['layer'] in layers:
|
||||
xmax = max(xmax,
|
||||
max(float(el.attrib['x1']), float(el.attrib['x2'])))
|
||||
ymax = max(ymax,
|
||||
max(float(el.attrib['y1']), float(el.attrib['y2'])))
|
||||
xmin = min(xmin,
|
||||
min(float(el.attrib['x1']), float(el.attrib['x2'])))
|
||||
ymin = min(ymin,
|
||||
min(float(el.attrib['y1']), float(el.attrib['y2'])))
|
||||
|
||||
for el in package.iter():
|
||||
if el.tag in ['smd', 'pad']:
|
||||
elx, ely = float(el.attrib['x']), float(el.attrib['y'])
|
||||
if el.tag == 'smd':
|
||||
dx, dy = abs(float(el.attrib['dx'])) / 2, abs(
|
||||
float(el.attrib['dy'])) / 2
|
||||
else:
|
||||
d = el.get('diameter')
|
||||
if d is None:
|
||||
diameter = float(el.get('drill')) + 2 * self.min_via_w
|
||||
else:
|
||||
diameter = float(d)
|
||||
dx, dy = diameter / 2, diameter / 2
|
||||
xmax, ymax = max(xmax, elx + dx), max(ymax, ely + dy)
|
||||
xmin, ymin = min(xmin, elx - dx), min(ymin, ely - dy)
|
||||
|
||||
if not math.isinf(xmin):
|
||||
if mirrored:
|
||||
xmin, xmax = -xmax, -xmin
|
||||
dx, dy = self._rotate(xmin, ymax, _angle)
|
||||
sx = abs(xmax - xmin)
|
||||
sy = abs(ymax - ymin)
|
||||
else:
|
||||
dx, dy = 0, 0
|
||||
sx, sy = 0, 0
|
||||
|
||||
return {
|
||||
'pos': [x + dx, -y - dy],
|
||||
'angle': _angle,
|
||||
'relpos': [0, 0],
|
||||
'size': [sx, sy]
|
||||
}
|
||||
|
||||
def _footprint_pads(self, package, x, y, angle, mirrored, refdes):
|
||||
pads = []
|
||||
element_pad_nets = self.elements_pad_nets.get(refdes)
|
||||
pin1_allocated = False
|
||||
for el in package.iter():
|
||||
if el.tag == 'pad':
|
||||
elx = float(el.attrib['x'])
|
||||
ely = -float(el.attrib['y'])
|
||||
drill = float(el.attrib['drill'])
|
||||
|
||||
dx, dy = self._rotate(elx, ely, -angle, mirrored)
|
||||
|
||||
diameter = drill + 2 * self.min_via_w \
|
||||
if 'diameter' not in el.attrib \
|
||||
else float(el.attrib['diameter'])
|
||||
|
||||
pr = self.Rot(el.get('rot'))
|
||||
if mirrored ^ pr.mirrored:
|
||||
pad_angle = -angle - pr.angle
|
||||
else:
|
||||
pad_angle = angle + pr.angle
|
||||
|
||||
pad = {
|
||||
'layers': ['F', 'B'],
|
||||
'pos': [x + dx, -y + dy],
|
||||
'angle': pad_angle,
|
||||
'type': 'th',
|
||||
'drillshape': 'circle',
|
||||
'drillsize': [
|
||||
drill,
|
||||
drill
|
||||
]
|
||||
}
|
||||
|
||||
if el.get('name') in ['1', 'A', 'A1', 'P1', 'PAD1'] and \
|
||||
not pin1_allocated:
|
||||
pad['pin1'] = 1
|
||||
pin1_allocated = True
|
||||
|
||||
if 'shape' not in el.attrib or el.attrib['shape'] == 'round':
|
||||
pad['shape'] = 'circle'
|
||||
pad['size'] = [diameter, diameter]
|
||||
elif el.attrib['shape'] == 'square':
|
||||
pad['shape'] = 'rect'
|
||||
pad['size'] = [diameter, diameter]
|
||||
elif el.attrib['shape'] == 'octagon':
|
||||
pad['shape'] = 'chamfrect'
|
||||
pad['size'] = [diameter, diameter]
|
||||
pad['radius'] = 0
|
||||
pad['chamfpos'] = 0b1111 # all corners
|
||||
pad['chamfratio'] = 0.333
|
||||
elif el.attrib['shape'] == 'long':
|
||||
pad['shape'] = 'roundrect'
|
||||
pad['radius'] = diameter / 2
|
||||
pad['size'] = [2 * diameter, diameter]
|
||||
elif el.attrib['shape'] == 'offset':
|
||||
pad['shape'] = 'roundrect'
|
||||
pad['radius'] = diameter / 2
|
||||
pad['size'] = [2 * diameter, diameter]
|
||||
pad['offset'] = [diameter / 2, 0]
|
||||
elif el.attrib['shape'] == 'slot':
|
||||
pad['shape'] = 'roundrect'
|
||||
pad['radius'] = diameter / 2
|
||||
slot_length = float(el.attrib['slotLength'])
|
||||
pad['size'] = [slot_length + diameter / 2, diameter]
|
||||
pad['drillshape'] = 'oblong'
|
||||
pad['drillsize'] = [slot_length, drill]
|
||||
else:
|
||||
self.logger.info(
|
||||
"Unsupported footprint pad shape %s, skipping",
|
||||
el.attrib['shape'])
|
||||
|
||||
if self.config.include_nets and element_pad_nets is not None:
|
||||
net = element_pad_nets.get(el.attrib['name'])
|
||||
if net is not None:
|
||||
pad['net'] = net
|
||||
|
||||
pads.append(pad)
|
||||
|
||||
elif el.tag == 'smd':
|
||||
layer = el.attrib['layer']
|
||||
if layer == '1' and not mirrored or \
|
||||
layer == '16' and mirrored:
|
||||
layers = ['F']
|
||||
elif layer == '1' and mirrored or \
|
||||
layer == '16' and not mirrored:
|
||||
layers = ['B']
|
||||
else:
|
||||
self.logger.error('Unable to determine layer for '
|
||||
'{0} pad {1}'.format(refdes,
|
||||
el.attrib['name']))
|
||||
layers = None
|
||||
|
||||
if layers is not None:
|
||||
elx = float(el.attrib['x'])
|
||||
ely = -float(el.attrib['y'])
|
||||
|
||||
dx, dy = self._rotate(elx, ely, -angle, mirrored)
|
||||
|
||||
pr = self.Rot(el.get('rot'))
|
||||
if mirrored ^ pr.mirrored:
|
||||
pad_angle = -angle - pr.angle
|
||||
else:
|
||||
pad_angle = angle + pr.angle
|
||||
|
||||
pad = {'layers': layers,
|
||||
'pos': [x + dx, -y + dy],
|
||||
'size': [
|
||||
float(el.attrib['dx']),
|
||||
float(el.attrib['dy'])
|
||||
],
|
||||
'angle': pad_angle,
|
||||
'type': 'smd',
|
||||
}
|
||||
|
||||
if el.get('name') in ['1', 'A', 'A1', 'P1', 'PAD1'] and \
|
||||
not pin1_allocated:
|
||||
pad['pin1'] = 1
|
||||
pin1_allocated = True
|
||||
|
||||
if 'roundness' not in el.attrib:
|
||||
pad['shape'] = 'rect'
|
||||
else:
|
||||
pad['shape'] = 'roundrect'
|
||||
pad['radius'] = (float(el.attrib['roundness']) / 100) \
|
||||
* float(el.attrib['dy']) / 2
|
||||
|
||||
if self.config.include_nets and \
|
||||
element_pad_nets is not None:
|
||||
net = element_pad_nets.get(el.attrib['name'])
|
||||
if net is not None:
|
||||
pad['net'] = net
|
||||
|
||||
pads.append(pad)
|
||||
return pads
|
||||
|
||||
@staticmethod
|
||||
def _rotate(x, y, angle, mirrored=False):
|
||||
sin = math.sin(math.radians(angle))
|
||||
cos = math.cos(math.radians(angle))
|
||||
xr = x * cos - y * sin
|
||||
yr = y * cos + x * sin
|
||||
if mirrored:
|
||||
return -xr, yr
|
||||
else:
|
||||
return xr, yr
|
||||
|
||||
def _add_silk_fab(self, el, x, y, angle, mirrored, populate):
|
||||
if el.tag == 'hole':
|
||||
dwg_layer = self.pcbdata['edges']
|
||||
elif el.attrib['layer'] in [self.TOP_PLACE_LAYER, self.BOT_PLACE_LAYER]:
|
||||
dwg_layer = self.pcbdata['drawings']['silkscreen']
|
||||
top = el.attrib['layer'] == self.TOP_PLACE_LAYER
|
||||
elif el.attrib['layer'] in [self.TOP_DOCU_LAYER, self.BOT_DOCU_LAYER]:
|
||||
if not populate:
|
||||
return
|
||||
dwg_layer = self.pcbdata['drawings']['fabrication']
|
||||
top = el.attrib['layer'] == self.TOP_DOCU_LAYER
|
||||
else:
|
||||
return
|
||||
|
||||
dwg = None
|
||||
|
||||
if el.tag == 'wire':
|
||||
_dx1 = float(el.attrib['x1'])
|
||||
_dx2 = float(el.attrib['x2'])
|
||||
_dy1 = -float(el.attrib['y1'])
|
||||
_dy2 = -float(el.attrib['y2'])
|
||||
|
||||
dx1, dy1 = self._rotate(_dx1, _dy1, -angle, mirrored)
|
||||
dx2, dy2 = self._rotate(_dx2, _dy2, -angle, mirrored)
|
||||
|
||||
x1, y1 = x + dx1, -y + dy1
|
||||
x2, y2 = x + dx2, -y + dy2
|
||||
|
||||
if el.get('curve'):
|
||||
dwg = {
|
||||
'type': 'arc',
|
||||
'width': float(el.attrib['width']),
|
||||
'svgpath': self._curve_to_svgpath(el, x, y, angle)
|
||||
}
|
||||
else:
|
||||
dwg = {
|
||||
'type': 'segment',
|
||||
'start': [x1, y1],
|
||||
'end': [x2, y2],
|
||||
'width': float(el.attrib['width'])
|
||||
}
|
||||
|
||||
elif el.tag == 'rectangle':
|
||||
_dv = self._rectangle_vertices(el)
|
||||
|
||||
# Rotate rectangle about component origin based on component angle
|
||||
dv = [self._rotate(_x, _y, -angle, mirrored) for (_x, _y) in _dv]
|
||||
|
||||
# Map vertices back to absolute coordinates
|
||||
v = [(x + _x, -y + _y) for (_x, _y) in dv]
|
||||
|
||||
dwg = {
|
||||
'type': 'polygon',
|
||||
'filled': 1,
|
||||
'pos': [0, 0],
|
||||
'polygons': [v]
|
||||
}
|
||||
|
||||
elif el.tag in ['circle', 'hole']:
|
||||
_x = float(el.attrib['x'])
|
||||
_y = -float(el.attrib['y'])
|
||||
dxc, dyc = self._rotate(_x, _y, -angle, mirrored)
|
||||
xc, yc = x + dxc, -y + dyc
|
||||
|
||||
if el.tag == 'circle':
|
||||
radius = float(el.attrib['radius'])
|
||||
width = float(el.attrib['width'])
|
||||
else:
|
||||
radius = float(el.attrib['drill']) / 2
|
||||
width = 0
|
||||
|
||||
dwg = {
|
||||
'type': 'circle',
|
||||
'start': [xc, yc],
|
||||
'radius': radius,
|
||||
'width': width
|
||||
}
|
||||
|
||||
elif el.tag in ['polygonshape', 'polygon']:
|
||||
segs = el if el.tag == 'polygon' \
|
||||
else el.find('polygonoutlinesegments')
|
||||
|
||||
dv = self._segments_to_polygon(segs, angle, mirrored)
|
||||
|
||||
polygon = [[x + v[0], -y + v[1]] for v in dv]
|
||||
|
||||
dwg = {
|
||||
'type': 'polygon',
|
||||
'filled': 1,
|
||||
'pos': [0, 0],
|
||||
'polygons': [polygon]
|
||||
}
|
||||
|
||||
if dwg is not None:
|
||||
if el.tag == 'hole':
|
||||
dwg_layer.append(dwg)
|
||||
else:
|
||||
bot = not top
|
||||
|
||||
# Note that in Eagle terminology, 'mirrored' essentially means
|
||||
# 'flipped' (i.e. to the opposite side of the board)
|
||||
if (mirrored and bot) or (not mirrored and top):
|
||||
dwg_layer['F'].append(dwg)
|
||||
elif (mirrored and top) or (not mirrored and bot):
|
||||
dwg_layer['B'].append(dwg)
|
||||
|
||||
def _process_footprint(self, package, x, y, angle, mirrored, populate):
|
||||
for el in package.iter():
|
||||
if el.tag in ['wire', 'rectangle', 'circle', 'hole',
|
||||
'polygonshape', 'polygon', 'hole']:
|
||||
self._add_silk_fab(el, x, y, angle, mirrored, populate)
|
||||
|
||||
def _element_refdes_to_silk(self, el):
|
||||
for attr in el.iter('attribute'):
|
||||
if attr.attrib['name'] == 'NAME':
|
||||
attrx = float(attr.attrib['x'])
|
||||
attry = -float(attr.attrib['y'])
|
||||
xpos = attrx
|
||||
ypos = attry
|
||||
elr = self.Rot(el.get('rot'))
|
||||
tr = self.Rot(attr.get('rot'))
|
||||
text = el.attrib['name']
|
||||
|
||||
angle = tr.angle
|
||||
mirrored = tr.mirrored
|
||||
spin = elr.spin ^ tr.spin
|
||||
if mirrored:
|
||||
angle = -angle
|
||||
|
||||
if 'align' not in attr.attrib:
|
||||
justify = [-1, 1]
|
||||
elif attr.attrib['align'] == 'center':
|
||||
justify = [0, 0]
|
||||
else:
|
||||
j = attr.attrib['align'].split('-')
|
||||
alignments = {
|
||||
'bottom': 1,
|
||||
'center': 0,
|
||||
'top': -1,
|
||||
'left': -1,
|
||||
'right': 1
|
||||
}
|
||||
justify = [alignments[ss] for ss in j[::-1]]
|
||||
if (90 < angle < 270 and not spin) or \
|
||||
(-90 >= angle >= -270 and not spin):
|
||||
angle += 180
|
||||
justify = [-j for j in justify]
|
||||
|
||||
size = float(attr.attrib['size'])
|
||||
ratio = float(attr.get('ratio', '8')) / 100
|
||||
dwg = {
|
||||
'type': 'text',
|
||||
'text': text,
|
||||
'pos': [xpos, ypos],
|
||||
'height': size,
|
||||
'width': size,
|
||||
'justify': justify,
|
||||
'thickness': size * ratio,
|
||||
'attr': [] if not mirrored else ['mirrored'],
|
||||
'angle': angle
|
||||
}
|
||||
|
||||
self.font_parser.parse_font_for_string(text)
|
||||
if mirrored:
|
||||
self.pcbdata['drawings']['silkscreen']['B'].append(dwg)
|
||||
else:
|
||||
self.pcbdata['drawings']['silkscreen']['F'].append(dwg)
|
||||
|
||||
@staticmethod
|
||||
def _segments_to_polygon(segs, angle=0, mirrored=False):
|
||||
polygon = []
|
||||
for vertex in segs.iter('vertex'):
|
||||
_x, _y = float(vertex.attrib['x']), -float(vertex.attrib['y'])
|
||||
x, y = FusionEagleParser._rotate(_x, _y, -angle, mirrored)
|
||||
polygon.append([x, y])
|
||||
return polygon
|
||||
|
||||
def _add_zone(self, poly, net):
|
||||
layer = poly.attrib['layer']
|
||||
if layer == self.TOP_COPPER_LAYER:
|
||||
dest = self.pcbdata['zones']['F']
|
||||
elif layer == self.BOT_COPPER_LAYER:
|
||||
dest = self.pcbdata['zones']['B']
|
||||
else:
|
||||
return
|
||||
|
||||
if poly.tag == 'polygonpour':
|
||||
segs = poly.find('polygonfilldetails').find('polygonshape') \
|
||||
.find('polygonoutlinesegments')
|
||||
else:
|
||||
segs = poly
|
||||
|
||||
zone = {'polygons': []}
|
||||
zone['polygons'].append(self._segments_to_polygon(segs))
|
||||
if self.config.include_nets:
|
||||
zone['net'] = net
|
||||
dest.append(zone)
|
||||
|
||||
def _add_parsed_font_data(self):
|
||||
for (c, wl) in self.font_parser.get_parsed_font().items():
|
||||
if c not in self.pcbdata['font_data']:
|
||||
self.pcbdata['font_data'][c] = wl
|
||||
|
||||
def parse(self):
|
||||
ext = os.path.splitext(self.file_name)[1]
|
||||
|
||||
if ext.lower() == '.fbrd':
|
||||
with zipfile.ZipFile(self.file_name) as myzip:
|
||||
brdfilename = [fname for fname in myzip.namelist() if
|
||||
os.path.splitext(fname)[1] == '.brd']
|
||||
with myzip.open(brdfilename[0]) as brdfile:
|
||||
return self._parse(brdfile)
|
||||
|
||||
elif ext.lower() == '.brd':
|
||||
with io.open(self.file_name, 'r', encoding='utf-8') as brdfile:
|
||||
return self._parse(brdfile)
|
||||
|
||||
def _parse(self, brdfile):
|
||||
try:
|
||||
brdxml = ElementTree.parse(brdfile)
|
||||
except ElementTree.ParseError as err:
|
||||
self.logger.error("Exception occurred trying to parse {0}, message:"
|
||||
" {1}"
|
||||
.format(brdfile.name, err.msg))
|
||||
return None, None
|
||||
if brdxml is None:
|
||||
self.logger.error("No data was able to be parsed from {0}"
|
||||
.format(brdfile.name))
|
||||
return None, None
|
||||
|
||||
# Pick out key sections
|
||||
root = brdxml.getroot()
|
||||
board = root.find('drawing').find('board')
|
||||
plain = board.find('plain')
|
||||
elements = board.find('elements')
|
||||
signals = board.find('signals')
|
||||
|
||||
# Build library mapping elements' pads to nets
|
||||
self._parse_pad_nets(signals)
|
||||
|
||||
# Determine minimum via annular ring from board design rules
|
||||
# (Needed in order to calculate through-hole pad diameters correctly)
|
||||
mv = [el.attrib['value'] for el in root.iter('param') if
|
||||
el.attrib['name'] == 'rlMinViaOuter']
|
||||
if len(mv) == 0:
|
||||
self.logger.warning("rlMinViaOuter not found, defaulting to 0")
|
||||
self.min_via_w = 0
|
||||
else:
|
||||
if len(mv) > 1:
|
||||
self.logger.warning("Multiple rlMinViaOuter found, using first "
|
||||
"occurrence")
|
||||
mv = mv[0]
|
||||
mv_val = float(''.join(d for d in mv if d in string.digits + '.'))
|
||||
mv_units = (''.join(d for d in mv if d in string.ascii_lowercase))
|
||||
|
||||
if mv_units == 'mm':
|
||||
self.min_via_w = mv_val
|
||||
elif mv_units == 'mil':
|
||||
self.min_via_w = mv_val * 0.0254
|
||||
else:
|
||||
self.logger.error("Unsupported units %s on rlMinViaOuter",
|
||||
mv_units)
|
||||
|
||||
# Edges & silkscreen (partial)
|
||||
for el in plain.iter():
|
||||
self._add_drawing(el)
|
||||
# identify board bounding box based on edges
|
||||
board_outline_bbox = BoundingBox()
|
||||
|
||||
for drawing in self.pcbdata['edges']:
|
||||
self.add_drawing_bounding_box(drawing, board_outline_bbox)
|
||||
if board_outline_bbox.initialized():
|
||||
self.pcbdata['edges_bbox'] = board_outline_bbox.to_dict()
|
||||
|
||||
# Signals --> nets
|
||||
if self.config.include_nets:
|
||||
self.pcbdata['nets'] = []
|
||||
for signal in signals.iter('signal'):
|
||||
self.pcbdata['nets'].append(signal.attrib['name'])
|
||||
|
||||
# Signals --> tracks, zones
|
||||
if self.config.include_tracks:
|
||||
self.pcbdata['tracks'] = {'F': [], 'B': []}
|
||||
self.pcbdata['zones'] = {'F': [], 'B': []}
|
||||
for signal in signals.iter('signal'):
|
||||
for wire in signal.iter('wire'):
|
||||
self._add_track(wire, signal.attrib['name'])
|
||||
for via in signal.iter('via'):
|
||||
self._add_track(via, signal.attrib['name'])
|
||||
for poly in signal.iter('polygonpour'):
|
||||
self._add_zone(poly, signal.attrib['name'])
|
||||
for poly in signal.iter('polygon'):
|
||||
self._add_zone(poly, signal.attrib['name'])
|
||||
|
||||
# Elements --> components, footprints, silkscreen
|
||||
for el in elements.iter('element'):
|
||||
populate = el.get('populate') != 'no'
|
||||
elr = self.Rot(el.get('rot'))
|
||||
layer = 'B' if elr.mirrored else 'F'
|
||||
extra_fields = {}
|
||||
|
||||
for a in el.iter('attribute'):
|
||||
if 'value' in a.attrib:
|
||||
extra_fields[a.attrib['name']] = a.attrib['value']
|
||||
|
||||
comp = Component(ref=el.attrib['name'],
|
||||
val='' if 'value' not in el.attrib else el.attrib[
|
||||
'value'],
|
||||
footprint=el.attrib['package'],
|
||||
layer=layer,
|
||||
attr=None,
|
||||
extra_fields=extra_fields)
|
||||
|
||||
# For component, get footprint data
|
||||
libs = [lib for lib in board.find('libraries').findall('library')
|
||||
if lib.attrib['name'] == el.attrib['library']]
|
||||
packages = []
|
||||
for lib in libs:
|
||||
p = [pac for pac in lib.find('packages').findall('package')
|
||||
if pac.attrib['name'] == el.attrib['package']]
|
||||
packages.extend(p)
|
||||
if not packages:
|
||||
self.logger.error("Package {0} in library {1} not found in "
|
||||
"source file {2} for element {3}"
|
||||
.format(el.attrib['package'],
|
||||
el.attrib['library'],
|
||||
brdfile.name,
|
||||
el.attrib['name']))
|
||||
return None, None
|
||||
else:
|
||||
package = packages[0]
|
||||
if len(packages) > 1:
|
||||
self.logger.warn("Multiple packages found for package {0}"
|
||||
" in library {1}, using first instance "
|
||||
"found".format(el.attrib['package'],
|
||||
el.attrib['library']))
|
||||
|
||||
elx = float(el.attrib['x'])
|
||||
ely = float(el.attrib['y'])
|
||||
refdes = el.attrib['name']
|
||||
footprint = {
|
||||
'ref': refdes,
|
||||
'center': [elx, ely],
|
||||
'pads': [],
|
||||
'drawings': [],
|
||||
'layer': layer
|
||||
}
|
||||
|
||||
elr = self.Rot(el.get('rot'))
|
||||
footprint['pads'] = self._footprint_pads(package, elx, ely,
|
||||
elr.angle, elr.mirrored,
|
||||
refdes)
|
||||
footprint['bbox'] = self._calculate_footprint_bbox(package, elx,
|
||||
ely, elr.angle,
|
||||
elr.mirrored)
|
||||
self.pcbdata['footprints'].append(footprint)
|
||||
|
||||
# Add silkscreen for component footprint & refdes
|
||||
self._process_footprint(package, elx, ely, elr.angle, elr.mirrored,
|
||||
populate)
|
||||
self._element_refdes_to_silk(el)
|
||||
|
||||
if populate:
|
||||
self.components.append(comp)
|
||||
|
||||
self._add_parsed_font_data()
|
||||
|
||||
# Fabrication & metadata
|
||||
company = [a.attrib['value'] for a in root.iter('attribute') if
|
||||
a.attrib['name'] == 'COMPANY']
|
||||
company = '' if not company else company[0]
|
||||
rev = [a.attrib['value'] for a in root.iter('attribute') if
|
||||
a.attrib['name'] == 'REVISION']
|
||||
rev = '' if not rev else rev[0]
|
||||
|
||||
if not rev:
|
||||
rev = ''
|
||||
|
||||
title = os.path.basename(self.file_name)
|
||||
|
||||
variant = [a.attrib['name'] for a in root.iter('variantdef') if
|
||||
a.get('current') == 'yes']
|
||||
variant = None if not variant else variant[0]
|
||||
if variant:
|
||||
title = "{0}, Variant: {1}".format(title, variant)
|
||||
|
||||
date = datetime.fromtimestamp(
|
||||
os.path.getmtime(self.file_name)).strftime('%Y-%m-%d %H:%M:%S')
|
||||
self.pcbdata['metadata'] = {'title': title, 'revision': rev,
|
||||
'company': company, 'date': date}
|
||||
|
||||
return self.pcbdata, self.components
|
|
@ -0,0 +1,163 @@
|
|||
import io
|
||||
import json
|
||||
import os.path
|
||||
from jsonschema import validate, ValidationError
|
||||
|
||||
from .common import EcadParser, Component, BoundingBox
|
||||
from ..core.fontparser import FontParser
|
||||
from ..errors import ParsingException
|
||||
|
||||
|
||||
class GenericJsonParser(EcadParser):
|
||||
COMPATIBLE_SPEC_VERSIONS = [1]
|
||||
|
||||
def extra_data_file_filter(self):
|
||||
return "Json file ({f})|{f}".format(f=os.path.basename(self.file_name))
|
||||
|
||||
def latest_extra_data(self, extra_dirs=None):
|
||||
return self.file_name
|
||||
|
||||
def get_extra_field_data(self, file_name):
|
||||
if os.path.abspath(file_name) != os.path.abspath(self.file_name):
|
||||
return None
|
||||
|
||||
_, components = self._parse()
|
||||
field_set = set()
|
||||
comp_dict = {}
|
||||
|
||||
for c in components:
|
||||
ref_fields = comp_dict.setdefault(c.ref, {})
|
||||
|
||||
for k, v in c.extra_fields.items():
|
||||
field_set.add(k)
|
||||
ref_fields[k] = v
|
||||
|
||||
return list(field_set), comp_dict
|
||||
|
||||
def get_generic_json_pcb(self):
|
||||
with io.open(self.file_name, 'r', encoding='utf-8') as f:
|
||||
pcb = json.load(f)
|
||||
|
||||
if 'spec_version' not in pcb:
|
||||
raise ValidationError("'spec_version' is a required property")
|
||||
|
||||
if pcb['spec_version'] not in self.COMPATIBLE_SPEC_VERSIONS:
|
||||
raise ValidationError("Unsupported spec_version ({})"
|
||||
.format(pcb['spec_version']))
|
||||
|
||||
schema_dir = os.path.join(os.path.dirname(__file__), 'schema')
|
||||
schema_file_name = os.path.join(schema_dir,
|
||||
'genericjsonpcbdata_v{}.schema'.format(
|
||||
pcb['spec_version']))
|
||||
|
||||
with io.open(schema_file_name, 'r', encoding='utf-8') as f:
|
||||
schema = json.load(f)
|
||||
|
||||
validate(instance=pcb, schema=schema)
|
||||
|
||||
return pcb
|
||||
|
||||
def _verify(self, pcb):
|
||||
"""Spot check the pcb object."""
|
||||
|
||||
if len(pcb['pcbdata']['footprints']) != len(pcb['components']):
|
||||
self.logger.error("Length of components list doesn't match"
|
||||
" length of footprints list.")
|
||||
return False
|
||||
|
||||
return True
|
||||
|
||||
@staticmethod
|
||||
def _texts(pcbdata):
|
||||
for layer in pcbdata['drawings'].values():
|
||||
for side in layer.values():
|
||||
for dwg in side:
|
||||
if 'text' in dwg:
|
||||
yield dwg
|
||||
|
||||
@staticmethod
|
||||
def _remove_control_codes(s):
|
||||
import unicodedata
|
||||
return ''.join(c for c in s if unicodedata.category(c)[0] != "C")
|
||||
|
||||
def _parse_font_data(self, pcbdata):
|
||||
font_parser = FontParser()
|
||||
for dwg in self._texts(pcbdata):
|
||||
if 'svgpath' not in dwg:
|
||||
dwg['text'] = self._remove_control_codes(dwg['text'])
|
||||
font_parser.parse_font_for_string(dwg['text'])
|
||||
|
||||
if font_parser.get_parsed_font():
|
||||
pcbdata['font_data'] = font_parser.get_parsed_font()
|
||||
|
||||
def _check_font_data(self, pcbdata):
|
||||
mc = set()
|
||||
for dwg in self._texts(pcbdata):
|
||||
dwg['text'] = self._remove_control_codes(dwg['text'])
|
||||
mc.update({c for c in dwg['text'] if 'svgpath' not in dwg and
|
||||
c not in pcbdata['font_data']})
|
||||
|
||||
if mc:
|
||||
s = ''.join(mc)
|
||||
self.logger.error('Provided font_data is missing character(s)'
|
||||
f' "{s}" that are present in text drawing'
|
||||
' objects')
|
||||
return False
|
||||
else:
|
||||
return True
|
||||
|
||||
def _parse(self):
|
||||
try:
|
||||
pcb = self.get_generic_json_pcb()
|
||||
except ValidationError as e:
|
||||
self.logger.error('File {f} does not comply with json schema. {m}'
|
||||
.format(f=self.file_name, m=e.message))
|
||||
return None, None
|
||||
|
||||
if not self._verify(pcb):
|
||||
self.logger.error('File {} does not appear to be valid generic'
|
||||
' InteractiveHtmlBom json file.'
|
||||
.format(self.file_name))
|
||||
return None, None
|
||||
|
||||
pcbdata = pcb['pcbdata']
|
||||
components = [Component(**c) for c in pcb['components']]
|
||||
|
||||
if 'font_data' in pcbdata:
|
||||
if not self._check_font_data(pcbdata):
|
||||
raise ParsingException(f'Failed parsing {self.file_name}')
|
||||
else:
|
||||
self._parse_font_data(pcbdata)
|
||||
if 'font_data' in pcbdata:
|
||||
self.logger.info('No font_data provided in JSON, using '
|
||||
'newstroke font')
|
||||
|
||||
self.logger.info('Successfully parsed {}'.format(self.file_name))
|
||||
|
||||
return pcbdata, components
|
||||
|
||||
def parse(self):
|
||||
pcbdata, components = self._parse()
|
||||
|
||||
# override board bounding box based on edges
|
||||
board_outline_bbox = BoundingBox()
|
||||
for drawing in pcbdata['edges']:
|
||||
self.add_drawing_bounding_box(drawing, board_outline_bbox)
|
||||
if board_outline_bbox.initialized():
|
||||
pcbdata['edges_bbox'] = board_outline_bbox.to_dict()
|
||||
|
||||
extra_fields = set(self.config.show_fields)
|
||||
extra_fields.discard("Footprint")
|
||||
extra_fields.discard("Value")
|
||||
if self.config.dnp_field:
|
||||
extra_fields.add(self.config.dnp_field)
|
||||
if self.config.board_variant_field:
|
||||
extra_fields.add(self.config.board_variant_field)
|
||||
if extra_fields:
|
||||
for c in components:
|
||||
c.extra_fields = {
|
||||
f: c.extra_fields.get(f, "") for f in extra_fields}
|
||||
|
||||
self.config.kicad_text_formatting = False
|
||||
|
||||
return pcbdata, components
|
|
@ -0,0 +1,813 @@
|
|||
import os
|
||||
from datetime import datetime
|
||||
|
||||
import pcbnew
|
||||
|
||||
from .common import EcadParser, Component
|
||||
from .kicad_extra import find_latest_schematic_data, parse_schematic_data
|
||||
from .svgpath import create_path
|
||||
from ..core import ibom
|
||||
from ..core.config import Config
|
||||
from ..core.fontparser import FontParser
|
||||
|
||||
|
||||
class PcbnewParser(EcadParser):
|
||||
|
||||
def __init__(self, file_name, config, logger, board=None):
|
||||
super(PcbnewParser, self).__init__(file_name, config, logger)
|
||||
self.board = board
|
||||
if self.board is None:
|
||||
self.board = pcbnew.LoadBoard(self.file_name) # type: pcbnew.BOARD
|
||||
if hasattr(self.board, 'GetModules'):
|
||||
self.footprints = list(self.board.GetModules())
|
||||
else:
|
||||
self.footprints = list(self.board.GetFootprints())
|
||||
self.font_parser = FontParser()
|
||||
|
||||
def get_extra_field_data(self, file_name):
|
||||
if os.path.abspath(file_name) == os.path.abspath(self.file_name):
|
||||
return self.parse_extra_data_from_pcb()
|
||||
if os.path.splitext(file_name)[1] == '.kicad_pcb':
|
||||
return None
|
||||
return parse_schematic_data(file_name)
|
||||
|
||||
def parse_extra_data_from_pcb(self):
|
||||
field_set = set()
|
||||
comp_dict = {}
|
||||
|
||||
for f in self.footprints: # type: pcbnew.FOOTPRINT
|
||||
props = f.GetProperties()
|
||||
ref = f.GetReference()
|
||||
ref_fields = comp_dict.setdefault(ref, {})
|
||||
|
||||
for k, v in props.items():
|
||||
field_set.add(k)
|
||||
ref_fields[k] = v
|
||||
|
||||
return list(field_set), comp_dict
|
||||
|
||||
def latest_extra_data(self, extra_dirs=None):
|
||||
base_name = os.path.splitext(os.path.basename(self.file_name))[0]
|
||||
extra_dirs.append(self.board.GetPlotOptions().GetOutputDirectory())
|
||||
file_dir_name = os.path.dirname(self.file_name)
|
||||
directories = [file_dir_name]
|
||||
for dir in extra_dirs:
|
||||
if not os.path.isabs(dir):
|
||||
dir = os.path.join(file_dir_name, dir)
|
||||
if os.path.exists(dir):
|
||||
directories.append(dir)
|
||||
return find_latest_schematic_data(base_name, directories)
|
||||
|
||||
def extra_data_file_filter(self):
|
||||
if hasattr(self.board, 'GetModules'):
|
||||
return "Netlist and xml files (*.net; *.xml)|*.net;*.xml"
|
||||
else:
|
||||
return ("Netlist, xml and pcb files (*.net; *.xml; *.kicad_pcb)|"
|
||||
"*.net;*.xml;*.kicad_pcb")
|
||||
|
||||
@staticmethod
|
||||
def normalize(point):
|
||||
return [point.x * 1e-6, point.y * 1e-6]
|
||||
|
||||
@staticmethod
|
||||
def normalize_angle(angle):
|
||||
if isinstance(angle, int) or isinstance(angle, float):
|
||||
return angle * 0.1
|
||||
else:
|
||||
return angle.AsDegrees()
|
||||
|
||||
def get_arc_angles(self, d):
|
||||
# type: (pcbnew.PCB_SHAPE) -> tuple
|
||||
a1 = self.normalize_angle(d.GetArcAngleStart())
|
||||
if hasattr(d, "GetAngle"):
|
||||
a2 = a1 + self.normalize_angle(d.GetAngle())
|
||||
else:
|
||||
a2 = a1 + self.normalize_angle(d.GetArcAngle())
|
||||
if a2 < a1:
|
||||
a1, a2 = a2, a1
|
||||
return round(a1, 2), round(a2, 2)
|
||||
|
||||
def parse_shape(self, d):
|
||||
# type: (pcbnew.PCB_SHAPE) -> dict or None
|
||||
shape = {
|
||||
pcbnew.S_SEGMENT: "segment",
|
||||
pcbnew.S_CIRCLE: "circle",
|
||||
pcbnew.S_ARC: "arc",
|
||||
pcbnew.S_POLYGON: "polygon",
|
||||
pcbnew.S_CURVE: "curve",
|
||||
pcbnew.S_RECT: "rect",
|
||||
}.get(d.GetShape(), "")
|
||||
if shape == "":
|
||||
self.logger.info("Unsupported shape %s, skipping", d.GetShape())
|
||||
return None
|
||||
start = self.normalize(d.GetStart())
|
||||
end = self.normalize(d.GetEnd())
|
||||
if shape == "segment":
|
||||
return {
|
||||
"type": shape,
|
||||
"start": start,
|
||||
"end": end,
|
||||
"width": d.GetWidth() * 1e-6
|
||||
}
|
||||
|
||||
if shape == "rect":
|
||||
if hasattr(d, "GetRectCorners"):
|
||||
points = list(map(self.normalize, d.GetRectCorners()))
|
||||
else:
|
||||
points = [
|
||||
start,
|
||||
[end[0], start[1]],
|
||||
end,
|
||||
[start[0], end[1]]
|
||||
]
|
||||
shape_dict = {
|
||||
"type": "polygon",
|
||||
"pos": [0, 0],
|
||||
"angle": 0,
|
||||
"polygons": [points],
|
||||
"width": d.GetWidth() * 1e-6,
|
||||
"filled": 0
|
||||
}
|
||||
if hasattr(d, "IsFilled") and d.IsFilled():
|
||||
shape_dict["filled"] = 1
|
||||
return shape_dict
|
||||
|
||||
if shape == "circle":
|
||||
shape_dict = {
|
||||
"type": shape,
|
||||
"start": start,
|
||||
"radius": d.GetRadius() * 1e-6,
|
||||
"width": d.GetWidth() * 1e-6
|
||||
}
|
||||
if hasattr(d, "IsFilled") and d.IsFilled():
|
||||
shape_dict["filled"] = 1
|
||||
return shape_dict
|
||||
|
||||
if shape == "arc":
|
||||
a1, a2 = self.get_arc_angles(d)
|
||||
if hasattr(d, "GetCenter"):
|
||||
start = self.normalize(d.GetCenter())
|
||||
return {
|
||||
"type": shape,
|
||||
"start": start,
|
||||
"radius": d.GetRadius() * 1e-6,
|
||||
"startangle": a1,
|
||||
"endangle": a2,
|
||||
"width": d.GetWidth() * 1e-6
|
||||
}
|
||||
|
||||
if shape == "polygon":
|
||||
if hasattr(d, "GetPolyShape"):
|
||||
polygons = self.parse_poly_set(d.GetPolyShape())
|
||||
else:
|
||||
self.logger.info(
|
||||
"Polygons not supported for KiCad 4, skipping")
|
||||
return None
|
||||
angle = 0
|
||||
if hasattr(d, 'GetParentModule'):
|
||||
parent_footprint = d.GetParentModule()
|
||||
else:
|
||||
parent_footprint = d.GetParentFootprint()
|
||||
if parent_footprint is not None:
|
||||
angle = self.normalize_angle(parent_footprint.GetOrientation())
|
||||
shape_dict = {
|
||||
"type": shape,
|
||||
"pos": start,
|
||||
"angle": angle,
|
||||
"polygons": polygons
|
||||
}
|
||||
if hasattr(d, "IsFilled") and not d.IsFilled():
|
||||
shape_dict["filled"] = 0
|
||||
shape_dict["width"] = d.GetWidth() * 1e-6
|
||||
return shape_dict
|
||||
if shape == "curve":
|
||||
if hasattr(d, "GetBezierC1"):
|
||||
c1 = self.normalize(d.GetBezierC1())
|
||||
c2 = self.normalize(d.GetBezierC2())
|
||||
else:
|
||||
c1 = self.normalize(d.GetBezControl1())
|
||||
c2 = self.normalize(d.GetBezControl2())
|
||||
return {
|
||||
"type": shape,
|
||||
"start": start,
|
||||
"cpa": c1,
|
||||
"cpb": c2,
|
||||
"end": end,
|
||||
"width": d.GetWidth() * 1e-6
|
||||
}
|
||||
|
||||
def parse_line_chain(self, shape):
|
||||
# type: (pcbnew.SHAPE_LINE_CHAIN) -> list
|
||||
result = []
|
||||
if not hasattr(shape, "PointCount"):
|
||||
self.logger.warn("No PointCount method on outline object. "
|
||||
"Unpatched kicad version?")
|
||||
return result
|
||||
|
||||
for point_index in range(shape.PointCount()):
|
||||
result.append(
|
||||
self.normalize(shape.CPoint(point_index)))
|
||||
|
||||
return result
|
||||
|
||||
def parse_poly_set(self, poly):
|
||||
# type: (pcbnew.SHAPE_POLY_SET) -> list
|
||||
result = []
|
||||
|
||||
for i in range(poly.OutlineCount()):
|
||||
result.append(self.parse_line_chain(poly.Outline(i)))
|
||||
|
||||
return result
|
||||
|
||||
def parse_text(self, d):
|
||||
# type: (pcbnew.PCB_TEXT) -> dict
|
||||
if not d.IsVisible() and d.GetClass() not in ["PTEXT", "PCB_TEXT"]:
|
||||
return None
|
||||
pos = self.normalize(d.GetPosition())
|
||||
if hasattr(d, "GetTextThickness"):
|
||||
thickness = d.GetTextThickness() * 1e-6
|
||||
else:
|
||||
thickness = d.GetThickness() * 1e-6
|
||||
if hasattr(d, 'TransformToSegmentList'):
|
||||
segments = [self.normalize(p) for p in d.TransformToSegmentList()]
|
||||
lines = []
|
||||
for i in range(0, len(segments), 2):
|
||||
if i == 0 or segments[i - 1] != segments[i]:
|
||||
lines.append([segments[i]])
|
||||
lines[-1].append(segments[i + 1])
|
||||
return {
|
||||
"thickness": thickness,
|
||||
"svgpath": create_path(lines)
|
||||
}
|
||||
elif hasattr(d, 'GetEffectiveTextShape'):
|
||||
shape = d.GetEffectiveTextShape(
|
||||
aTriangulate=False) # type: pcbnew.SHAPE_COMPOUND
|
||||
segments = []
|
||||
polygons = []
|
||||
for s in shape.GetSubshapes():
|
||||
if s.Type() == pcbnew.SH_LINE_CHAIN:
|
||||
polygons.append(self.parse_line_chain(s))
|
||||
elif s.Type() == pcbnew.SH_SEGMENT:
|
||||
seg = s.GetSeg()
|
||||
segments.append(
|
||||
[self.normalize(seg.A), self.normalize(seg.B)])
|
||||
else:
|
||||
self.logger.warn(
|
||||
"Unsupported subshape in text: %s" % s.Type())
|
||||
if segments:
|
||||
return {
|
||||
"thickness": thickness,
|
||||
"svgpath": create_path(segments)
|
||||
}
|
||||
else:
|
||||
return {
|
||||
"polygons": polygons
|
||||
}
|
||||
|
||||
if d.GetClass() == "MTEXT":
|
||||
angle = self.normalize_angle(d.GetDrawRotation())
|
||||
else:
|
||||
if hasattr(d, "GetTextAngle"):
|
||||
angle = self.normalize_angle(d.GetTextAngle())
|
||||
else:
|
||||
angle = self.normalize_angle(d.GetOrientation())
|
||||
if hasattr(d, "GetTextHeight"):
|
||||
height = d.GetTextHeight() * 1e-6
|
||||
width = d.GetTextWidth() * 1e-6
|
||||
else:
|
||||
height = d.GetHeight() * 1e-6
|
||||
width = d.GetWidth() * 1e-6
|
||||
if hasattr(d, "GetShownText"):
|
||||
text = d.GetShownText()
|
||||
else:
|
||||
text = d.GetText()
|
||||
self.font_parser.parse_font_for_string(text)
|
||||
attributes = []
|
||||
if d.IsMirrored():
|
||||
attributes.append("mirrored")
|
||||
if d.IsItalic():
|
||||
attributes.append("italic")
|
||||
if d.IsBold():
|
||||
attributes.append("bold")
|
||||
|
||||
return {
|
||||
"pos": pos,
|
||||
"text": text,
|
||||
"height": height,
|
||||
"width": width,
|
||||
"justify": [d.GetHorizJustify(), d.GetVertJustify()],
|
||||
"thickness": thickness,
|
||||
"attr": attributes,
|
||||
"angle": angle
|
||||
}
|
||||
|
||||
def parse_dimension(self, d):
|
||||
# type: (pcbnew.PCB_DIMENSION_BASE) -> dict
|
||||
segments = []
|
||||
circles = []
|
||||
for s in d.GetShapes():
|
||||
s = s.Cast()
|
||||
if s.Type() == pcbnew.SH_SEGMENT:
|
||||
seg = s.GetSeg()
|
||||
segments.append(
|
||||
[self.normalize(seg.A), self.normalize(seg.B)])
|
||||
elif s.Type() == pcbnew.SH_CIRCLE:
|
||||
circles.append(
|
||||
[self.normalize(s.GetCenter()), s.GetRadius() * 1e-6])
|
||||
else:
|
||||
self.logger.info(
|
||||
"Unsupported shape type in dimension object: %s", s.Type())
|
||||
|
||||
svgpath = create_path(segments, circles=circles)
|
||||
|
||||
return {
|
||||
"thickness": d.GetLineThickness() * 1e-6,
|
||||
"svgpath": svgpath
|
||||
}
|
||||
|
||||
def parse_drawing(self, d):
|
||||
# type: (pcbnew.BOARD_ITEM) -> list
|
||||
result = []
|
||||
s = None
|
||||
if d.GetClass() in ["DRAWSEGMENT", "MGRAPHIC", "PCB_SHAPE"]:
|
||||
s = self.parse_shape(d)
|
||||
elif d.GetClass() in ["PTEXT", "MTEXT", "FP_TEXT", "PCB_TEXT"]:
|
||||
s = self.parse_text(d)
|
||||
elif (d.GetClass().startswith("PCB_DIM")
|
||||
and hasattr(pcbnew, "VECTOR_SHAPEPTR")):
|
||||
result.append(self.parse_dimension(d))
|
||||
if hasattr(d, "Text"):
|
||||
s = self.parse_text(d.Text())
|
||||
else:
|
||||
s = self.parse_text(d)
|
||||
else:
|
||||
self.logger.info("Unsupported drawing class %s, skipping",
|
||||
d.GetClass())
|
||||
if s:
|
||||
result.append(s)
|
||||
return result
|
||||
|
||||
def parse_edges(self, pcb):
|
||||
edges = []
|
||||
drawings = list(pcb.GetDrawings())
|
||||
bbox = None
|
||||
for f in self.footprints:
|
||||
for g in f.GraphicalItems():
|
||||
drawings.append(g)
|
||||
for d in drawings:
|
||||
if d.GetLayer() == pcbnew.Edge_Cuts:
|
||||
for parsed_drawing in self.parse_drawing(d):
|
||||
edges.append(parsed_drawing)
|
||||
if bbox is None:
|
||||
bbox = d.GetBoundingBox()
|
||||
else:
|
||||
bbox.Merge(d.GetBoundingBox())
|
||||
if bbox:
|
||||
bbox.Normalize()
|
||||
return edges, bbox
|
||||
|
||||
def parse_drawings_on_layers(self, drawings, f_layer, b_layer):
|
||||
front = []
|
||||
back = []
|
||||
|
||||
for d in drawings:
|
||||
if d[1].GetLayer() not in [f_layer, b_layer]:
|
||||
continue
|
||||
for drawing in self.parse_drawing(d[1]):
|
||||
if d[0] in ["ref", "val"]:
|
||||
drawing[d[0]] = 1
|
||||
if d[1].GetLayer() == f_layer:
|
||||
front.append(drawing)
|
||||
else:
|
||||
back.append(drawing)
|
||||
|
||||
return {
|
||||
"F": front,
|
||||
"B": back
|
||||
}
|
||||
|
||||
def get_all_drawings(self):
|
||||
drawings = [(d.GetClass(), d) for d in list(self.board.GetDrawings())]
|
||||
for f in self.footprints:
|
||||
drawings.append(("ref", f.Reference()))
|
||||
drawings.append(("val", f.Value()))
|
||||
for d in f.GraphicalItems():
|
||||
drawings.append((d.GetClass(), d))
|
||||
return drawings
|
||||
|
||||
def parse_pad(self, pad):
|
||||
# type: (pcbnew.PAD) -> dict or None
|
||||
layers_set = list(pad.GetLayerSet().Seq())
|
||||
layers = []
|
||||
if pcbnew.F_Cu in layers_set:
|
||||
layers.append("F")
|
||||
if pcbnew.B_Cu in layers_set:
|
||||
layers.append("B")
|
||||
pos = self.normalize(pad.GetPosition())
|
||||
size = self.normalize(pad.GetSize())
|
||||
angle = self.normalize_angle(pad.GetOrientation())
|
||||
shape_lookup = {
|
||||
pcbnew.PAD_SHAPE_RECT: "rect",
|
||||
pcbnew.PAD_SHAPE_OVAL: "oval",
|
||||
pcbnew.PAD_SHAPE_CIRCLE: "circle",
|
||||
}
|
||||
if hasattr(pcbnew, "PAD_SHAPE_TRAPEZOID"):
|
||||
shape_lookup[pcbnew.PAD_SHAPE_TRAPEZOID] = "trapezoid"
|
||||
if hasattr(pcbnew, "PAD_SHAPE_ROUNDRECT"):
|
||||
shape_lookup[pcbnew.PAD_SHAPE_ROUNDRECT] = "roundrect"
|
||||
if hasattr(pcbnew, "PAD_SHAPE_CUSTOM"):
|
||||
shape_lookup[pcbnew.PAD_SHAPE_CUSTOM] = "custom"
|
||||
if hasattr(pcbnew, "PAD_SHAPE_CHAMFERED_RECT"):
|
||||
shape_lookup[pcbnew.PAD_SHAPE_CHAMFERED_RECT] = "chamfrect"
|
||||
shape = shape_lookup.get(pad.GetShape(), "")
|
||||
if shape == "":
|
||||
self.logger.info("Unsupported pad shape %s, skipping.",
|
||||
pad.GetShape())
|
||||
return None
|
||||
pad_dict = {
|
||||
"layers": layers,
|
||||
"pos": pos,
|
||||
"size": size,
|
||||
"angle": angle,
|
||||
"shape": shape
|
||||
}
|
||||
if shape == "custom":
|
||||
polygon_set = pad.GetCustomShapeAsPolygon()
|
||||
if polygon_set.HasHoles():
|
||||
self.logger.warn('Detected holes in custom pad polygons')
|
||||
pad_dict["polygons"] = self.parse_poly_set(polygon_set)
|
||||
if shape == "trapezoid":
|
||||
# treat trapezoid as custom shape
|
||||
pad_dict["shape"] = "custom"
|
||||
delta = self.normalize(pad.GetDelta())
|
||||
pad_dict["polygons"] = [[
|
||||
[size[0] / 2 + delta[1] / 2, size[1] / 2 - delta[0] / 2],
|
||||
[-size[0] / 2 - delta[1] / 2, size[1] / 2 + delta[0] / 2],
|
||||
[-size[0] / 2 + delta[1] / 2, -size[1] / 2 - delta[0] / 2],
|
||||
[size[0] / 2 - delta[1] / 2, -size[1] / 2 + delta[0] / 2],
|
||||
]]
|
||||
|
||||
if shape in ["roundrect", "chamfrect"]:
|
||||
pad_dict["radius"] = pad.GetRoundRectCornerRadius() * 1e-6
|
||||
if shape == "chamfrect":
|
||||
pad_dict["chamfpos"] = pad.GetChamferPositions()
|
||||
pad_dict["chamfratio"] = pad.GetChamferRectRatio()
|
||||
if hasattr(pcbnew, 'PAD_ATTRIB_PTH'):
|
||||
through_hole_attributes = [pcbnew.PAD_ATTRIB_PTH,
|
||||
pcbnew.PAD_ATTRIB_NPTH]
|
||||
else:
|
||||
through_hole_attributes = [pcbnew.PAD_ATTRIB_STANDARD,
|
||||
pcbnew.PAD_ATTRIB_HOLE_NOT_PLATED]
|
||||
if pad.GetAttribute() in through_hole_attributes:
|
||||
pad_dict["type"] = "th"
|
||||
pad_dict["drillshape"] = {
|
||||
pcbnew.PAD_DRILL_SHAPE_CIRCLE: "circle",
|
||||
pcbnew.PAD_DRILL_SHAPE_OBLONG: "oblong"
|
||||
}.get(pad.GetDrillShape())
|
||||
pad_dict["drillsize"] = self.normalize(pad.GetDrillSize())
|
||||
else:
|
||||
pad_dict["type"] = "smd"
|
||||
if hasattr(pad, "GetOffset"):
|
||||
pad_dict["offset"] = self.normalize(pad.GetOffset())
|
||||
if self.config.include_nets:
|
||||
pad_dict["net"] = pad.GetNetname()
|
||||
|
||||
return pad_dict
|
||||
|
||||
def parse_footprints(self):
|
||||
# type: () -> list
|
||||
footprints = []
|
||||
for f in self.footprints: # type: pcbnew.FOOTPRINT
|
||||
ref = f.GetReference()
|
||||
|
||||
# bounding box
|
||||
if hasattr(pcbnew, 'MODULE'):
|
||||
f_copy = pcbnew.MODULE(f)
|
||||
else:
|
||||
f_copy = pcbnew.FOOTPRINT(f)
|
||||
try:
|
||||
f_copy.SetOrientation(0)
|
||||
except TypeError:
|
||||
f_copy.SetOrientation(
|
||||
pcbnew.EDA_ANGLE(0, pcbnew.TENTHS_OF_A_DEGREE_T))
|
||||
pos = f_copy.GetPosition()
|
||||
pos.x = pos.y = 0
|
||||
f_copy.SetPosition(pos)
|
||||
if hasattr(f_copy, 'GetFootprintRect'):
|
||||
footprint_rect = f_copy.GetFootprintRect()
|
||||
else:
|
||||
footprint_rect = f_copy.GetBoundingBox(False, False)
|
||||
bbox = {
|
||||
"pos": self.normalize(f.GetPosition()),
|
||||
"relpos": self.normalize(footprint_rect.GetPosition()),
|
||||
"size": self.normalize(footprint_rect.GetSize()),
|
||||
"angle": self.normalize_angle(f.GetOrientation()),
|
||||
}
|
||||
|
||||
# graphical drawings
|
||||
drawings = []
|
||||
for d in f.GraphicalItems():
|
||||
# we only care about copper ones, silkscreen is taken care of
|
||||
if d.GetLayer() not in [pcbnew.F_Cu, pcbnew.B_Cu]:
|
||||
continue
|
||||
for drawing in self.parse_drawing(d):
|
||||
drawings.append({
|
||||
"layer": "F" if d.GetLayer() == pcbnew.F_Cu else "B",
|
||||
"drawing": drawing,
|
||||
})
|
||||
|
||||
# footprint pads
|
||||
pads = []
|
||||
for p in f.Pads():
|
||||
pad_dict = self.parse_pad(p)
|
||||
if pad_dict is not None:
|
||||
pads.append((p.GetPadName(), pad_dict))
|
||||
|
||||
if pads:
|
||||
# Try to guess first pin name.
|
||||
pads = sorted(pads, key=lambda el: el[0])
|
||||
pin1_pads = [p for p in pads if p[0] in
|
||||
['1', 'A', 'A1', 'P1', 'PAD1']]
|
||||
if pin1_pads:
|
||||
pin1_pad_name = pin1_pads[0][0]
|
||||
else:
|
||||
# No pads have common first pin name,
|
||||
# pick lexicographically smallest.
|
||||
pin1_pad_name = pads[0][0]
|
||||
for pad_name, pad_dict in pads:
|
||||
if pad_name == pin1_pad_name:
|
||||
pad_dict['pin1'] = 1
|
||||
|
||||
pads = [p[1] for p in pads]
|
||||
|
||||
# add footprint
|
||||
footprints.append({
|
||||
"ref": ref,
|
||||
"bbox": bbox,
|
||||
"pads": pads,
|
||||
"drawings": drawings,
|
||||
"layer": {
|
||||
pcbnew.F_Cu: "F",
|
||||
pcbnew.B_Cu: "B"
|
||||
}.get(f.GetLayer())
|
||||
})
|
||||
|
||||
return footprints
|
||||
|
||||
def parse_tracks(self, tracks):
|
||||
result = {pcbnew.F_Cu: [], pcbnew.B_Cu: []}
|
||||
for track in tracks:
|
||||
if track.GetClass() in ["VIA", "PCB_VIA"]:
|
||||
track_dict = {
|
||||
"start": self.normalize(track.GetStart()),
|
||||
"end": self.normalize(track.GetEnd()),
|
||||
"width": track.GetWidth() * 1e-6,
|
||||
"net": track.GetNetname(),
|
||||
}
|
||||
for layer in [pcbnew.F_Cu, pcbnew.B_Cu]:
|
||||
if track.IsOnLayer(layer):
|
||||
result[layer].append(track_dict)
|
||||
else:
|
||||
if track.GetLayer() in [pcbnew.F_Cu, pcbnew.B_Cu]:
|
||||
if track.GetClass() in ["ARC", "PCB_ARC"]:
|
||||
a1, a2 = self.get_arc_angles(track)
|
||||
track_dict = {
|
||||
"center": self.normalize(track.GetCenter()),
|
||||
"startangle": a1,
|
||||
"endangle": a2,
|
||||
"radius": track.GetRadius() * 1e-6,
|
||||
"width": track.GetWidth() * 1e-6,
|
||||
}
|
||||
else:
|
||||
track_dict = {
|
||||
"start": self.normalize(track.GetStart()),
|
||||
"end": self.normalize(track.GetEnd()),
|
||||
"width": track.GetWidth() * 1e-6,
|
||||
}
|
||||
if self.config.include_nets:
|
||||
track_dict["net"] = track.GetNetname()
|
||||
result[track.GetLayer()].append(track_dict)
|
||||
|
||||
return {
|
||||
'F': result.get(pcbnew.F_Cu),
|
||||
'B': result.get(pcbnew.B_Cu)
|
||||
}
|
||||
|
||||
def parse_zones(self, zones):
|
||||
result = {pcbnew.F_Cu: [], pcbnew.B_Cu: []}
|
||||
for zone in zones: # type: pcbnew.ZONE
|
||||
if (not zone.IsFilled() or
|
||||
hasattr(zone, 'GetIsKeepout') and zone.GetIsKeepout() or
|
||||
hasattr(zone, 'GetIsRuleArea') and zone.GetIsRuleArea()):
|
||||
continue
|
||||
layers = [layer for layer in list(zone.GetLayerSet().Seq())
|
||||
if layer in [pcbnew.F_Cu, pcbnew.B_Cu]]
|
||||
for layer in layers:
|
||||
try:
|
||||
# kicad 5.1 and earlier
|
||||
poly_set = zone.GetFilledPolysList()
|
||||
except TypeError:
|
||||
poly_set = zone.GetFilledPolysList(layer)
|
||||
width = zone.GetMinThickness() * 1e-6
|
||||
if (hasattr(zone, 'GetFilledPolysUseThickness') and
|
||||
not zone.GetFilledPolysUseThickness()):
|
||||
width = 0
|
||||
zone_dict = {
|
||||
"polygons": self.parse_poly_set(poly_set),
|
||||
"width": width,
|
||||
}
|
||||
if self.config.include_nets:
|
||||
zone_dict["net"] = zone.GetNetname()
|
||||
result[layer].append(zone_dict)
|
||||
|
||||
return {
|
||||
'F': result.get(pcbnew.F_Cu),
|
||||
'B': result.get(pcbnew.B_Cu)
|
||||
}
|
||||
|
||||
@staticmethod
|
||||
def parse_netlist(net_info):
|
||||
# type: (pcbnew.NETINFO_LIST) -> list
|
||||
nets = net_info.NetsByName().asdict().keys()
|
||||
nets = sorted([str(s) for s in nets])
|
||||
return nets
|
||||
|
||||
@staticmethod
|
||||
def footprint_to_component(footprint, extra_fields):
|
||||
try:
|
||||
footprint_name = str(footprint.GetFPID().GetFootprintName())
|
||||
except AttributeError:
|
||||
footprint_name = str(footprint.GetFPID().GetLibItemName())
|
||||
|
||||
attr = 'Normal'
|
||||
if hasattr(pcbnew, 'FP_EXCLUDE_FROM_BOM'):
|
||||
if footprint.GetAttributes() & pcbnew.FP_EXCLUDE_FROM_BOM:
|
||||
attr = 'Virtual'
|
||||
elif hasattr(pcbnew, 'MOD_VIRTUAL'):
|
||||
if footprint.GetAttributes() == pcbnew.MOD_VIRTUAL:
|
||||
attr = 'Virtual'
|
||||
layer = {
|
||||
pcbnew.F_Cu: 'F',
|
||||
pcbnew.B_Cu: 'B',
|
||||
}.get(footprint.GetLayer())
|
||||
|
||||
return Component(footprint.GetReference(),
|
||||
footprint.GetValue(),
|
||||
footprint_name,
|
||||
layer,
|
||||
attr,
|
||||
extra_fields)
|
||||
|
||||
def parse(self):
|
||||
from ..errors import ParsingException
|
||||
|
||||
# Get extra field data from netlist
|
||||
field_set = set(self.config.show_fields)
|
||||
field_set.discard("Value")
|
||||
field_set.discard("Footprint")
|
||||
need_extra_fields = (field_set or
|
||||
self.config.board_variant_whitelist or
|
||||
self.config.board_variant_blacklist or
|
||||
self.config.dnp_field)
|
||||
|
||||
if not self.config.extra_data_file and need_extra_fields:
|
||||
self.logger.warn('Ignoring extra fields related config parameters '
|
||||
'since no netlist/xml file was specified.')
|
||||
need_extra_fields = False
|
||||
|
||||
extra_field_data = None
|
||||
if (self.config.extra_data_file and
|
||||
os.path.isfile(self.config.extra_data_file)):
|
||||
extra_field_data = self.parse_extra_data(
|
||||
self.config.extra_data_file, self.config.normalize_field_case)
|
||||
|
||||
if extra_field_data is None and need_extra_fields:
|
||||
raise ParsingException(
|
||||
'Failed parsing %s' % self.config.extra_data_file)
|
||||
|
||||
extra_field_data = extra_field_data[1] if extra_field_data else None
|
||||
|
||||
title_block = self.board.GetTitleBlock()
|
||||
title = title_block.GetTitle()
|
||||
revision = title_block.GetRevision()
|
||||
company = title_block.GetCompany()
|
||||
file_date = title_block.GetDate()
|
||||
if (hasattr(self.board, "GetProject") and
|
||||
hasattr(pcbnew, "ExpandTextVars")):
|
||||
project = self.board.GetProject()
|
||||
title = pcbnew.ExpandTextVars(title, project)
|
||||
revision = pcbnew.ExpandTextVars(revision, project)
|
||||
company = pcbnew.ExpandTextVars(company, project)
|
||||
file_date = pcbnew.ExpandTextVars(file_date, project)
|
||||
|
||||
if not file_date:
|
||||
file_mtime = os.path.getmtime(self.file_name)
|
||||
file_date = datetime.fromtimestamp(file_mtime).strftime(
|
||||
'%Y-%m-%d %H:%M:%S')
|
||||
pcb_file_name = os.path.basename(self.file_name)
|
||||
if not title:
|
||||
# remove .kicad_pcb extension
|
||||
title = os.path.splitext(pcb_file_name)[0]
|
||||
edges, bbox = self.parse_edges(self.board)
|
||||
if bbox is None:
|
||||
self.logger.error('Please draw pcb outline on the edges '
|
||||
'layer on sheet or any footprint before '
|
||||
'generating BOM.')
|
||||
return None, None
|
||||
bbox = {
|
||||
"minx": bbox.GetPosition().x * 1e-6,
|
||||
"miny": bbox.GetPosition().y * 1e-6,
|
||||
"maxx": bbox.GetRight() * 1e-6,
|
||||
"maxy": bbox.GetBottom() * 1e-6,
|
||||
}
|
||||
|
||||
drawings = self.get_all_drawings()
|
||||
|
||||
pcbdata = {
|
||||
"edges_bbox": bbox,
|
||||
"edges": edges,
|
||||
"drawings": {
|
||||
"silkscreen": self.parse_drawings_on_layers(
|
||||
drawings, pcbnew.F_SilkS, pcbnew.B_SilkS),
|
||||
"fabrication": self.parse_drawings_on_layers(
|
||||
drawings, pcbnew.F_Fab, pcbnew.B_Fab),
|
||||
},
|
||||
"footprints": self.parse_footprints(),
|
||||
"metadata": {
|
||||
"title": title,
|
||||
"revision": revision,
|
||||
"company": company,
|
||||
"date": file_date,
|
||||
},
|
||||
"bom": {},
|
||||
"font_data": self.font_parser.get_parsed_font()
|
||||
}
|
||||
if self.config.include_tracks:
|
||||
pcbdata["tracks"] = self.parse_tracks(self.board.GetTracks())
|
||||
if hasattr(self.board, "Zones"):
|
||||
pcbdata["zones"] = self.parse_zones(self.board.Zones())
|
||||
else:
|
||||
self.logger.info("Zones not supported for KiCad 4, skipping")
|
||||
pcbdata["zones"] = {'F': [], 'B': []}
|
||||
if self.config.include_nets and hasattr(self.board, "GetNetInfo"):
|
||||
pcbdata["nets"] = self.parse_netlist(self.board.GetNetInfo())
|
||||
|
||||
warning_shown = False
|
||||
if extra_field_data and need_extra_fields:
|
||||
e = []
|
||||
for f in self.footprints:
|
||||
e.append(extra_field_data.get(f.GetReference(), {}))
|
||||
if f.GetReference() not in extra_field_data:
|
||||
# Some components are on pcb but not in schematic data.
|
||||
# Show a warning about possibly outdated netlist/xml file.
|
||||
self.logger.warn(
|
||||
'Component %s is missing from schematic data.'
|
||||
% f.GetReference())
|
||||
warning_shown = True
|
||||
else:
|
||||
e = [{}] * len(self.footprints)
|
||||
|
||||
if warning_shown:
|
||||
self.logger.warn('Netlist/xml file is likely out of date.')
|
||||
|
||||
components = [self.footprint_to_component(f, ee)
|
||||
for (f, ee) in zip(self.footprints, e)]
|
||||
|
||||
return pcbdata, components
|
||||
|
||||
|
||||
class InteractiveHtmlBomPlugin(pcbnew.ActionPlugin, object):
|
||||
|
||||
def __init__(self):
|
||||
super(InteractiveHtmlBomPlugin, self).__init__()
|
||||
self.name = "Generate Interactive HTML BOM"
|
||||
self.category = "Read PCB"
|
||||
self.pcbnew_icon_support = hasattr(self, "show_toolbar_button")
|
||||
self.show_toolbar_button = True
|
||||
icon_dir = os.path.dirname(os.path.dirname(__file__))
|
||||
self.icon_file_name = os.path.join(icon_dir, 'icon.png')
|
||||
self.description = "Generate interactive HTML page with BOM " \
|
||||
"table and pcb drawing."
|
||||
|
||||
def defaults(self):
|
||||
pass
|
||||
|
||||
def Run(self):
|
||||
from ..version import version
|
||||
from ..errors import ParsingException
|
||||
|
||||
logger = ibom.Logger()
|
||||
board = pcbnew.GetBoard()
|
||||
pcb_file_name = board.GetFileName()
|
||||
|
||||
if not pcb_file_name:
|
||||
logger.error('Please save the board file before generating BOM.')
|
||||
return
|
||||
|
||||
config = Config(version, os.path.dirname(pcb_file_name))
|
||||
parser = PcbnewParser(pcb_file_name, config, logger, board)
|
||||
|
||||
try:
|
||||
ibom.run_with_dialog(parser, config, logger)
|
||||
except ParsingException as e:
|
||||
logger.error(str(e))
|
|
@ -0,0 +1,59 @@
|
|||
import os
|
||||
import pcbnew
|
||||
|
||||
from .xmlparser import XmlParser
|
||||
from .netlistparser import NetlistParser
|
||||
|
||||
PARSERS = {
|
||||
'.xml': XmlParser,
|
||||
'.net': NetlistParser,
|
||||
}
|
||||
|
||||
|
||||
if hasattr(pcbnew, 'FOOTPRINT'):
|
||||
PARSERS['.kicad_pcb'] = None
|
||||
|
||||
|
||||
def parse_schematic_data(file_name):
|
||||
if not os.path.isfile(file_name):
|
||||
return None
|
||||
extension = os.path.splitext(file_name)[1]
|
||||
if extension not in PARSERS:
|
||||
return None
|
||||
else:
|
||||
parser_cls = PARSERS[extension]
|
||||
if parser_cls is None:
|
||||
return None
|
||||
parser = parser_cls(file_name)
|
||||
return parser.get_extra_field_data()
|
||||
|
||||
|
||||
def find_latest_schematic_data(base_name, directories):
|
||||
"""
|
||||
:param base_name: base name of pcb file
|
||||
:param directories: list of directories to search
|
||||
:return: last modified parsable file path or None if not found
|
||||
"""
|
||||
files = []
|
||||
for d in directories:
|
||||
files.extend(_find_in_dir(d))
|
||||
# sort by decreasing modification time
|
||||
files = sorted(files, reverse=True)
|
||||
if files:
|
||||
# try to find first (last modified) file that has name matching pcb file
|
||||
for _, f in files:
|
||||
if os.path.splitext(os.path.basename(f))[0] == base_name:
|
||||
return f
|
||||
# if no such file is found just return last modified
|
||||
return files[0][1]
|
||||
else:
|
||||
return None
|
||||
|
||||
|
||||
def _find_in_dir(dir):
|
||||
_, _, files = next(os.walk(dir), (None, None, []))
|
||||
# filter out files that we can not parse
|
||||
files = [f for f in files if os.path.splitext(f)[1] in PARSERS.keys()]
|
||||
files = [os.path.join(dir, f) for f in files]
|
||||
# get their modification time and sort in descending order
|
||||
return [(os.path.getmtime(f), f) for f in files]
|
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
|
@ -0,0 +1,52 @@
|
|||
import io
|
||||
|
||||
from .parser_base import ParserBase
|
||||
from .sexpressions import parse_sexpression
|
||||
|
||||
|
||||
class NetlistParser(ParserBase):
|
||||
def get_extra_field_data(self):
|
||||
with io.open(self.file_name, 'r', encoding='utf-8') as f:
|
||||
sexpression = parse_sexpression(f.read())
|
||||
components = None
|
||||
for s in sexpression:
|
||||
if s[0] == 'components':
|
||||
components = s[1:]
|
||||
if components is None:
|
||||
return None
|
||||
field_set = set()
|
||||
comp_dict = {}
|
||||
for c in components:
|
||||
ref = None
|
||||
fields = None
|
||||
datasheet = None
|
||||
libsource = None
|
||||
for f in c[1:]:
|
||||
if f[0] == 'ref':
|
||||
ref = f[1]
|
||||
if f[0] == 'fields':
|
||||
fields = f[1:]
|
||||
if f[0] == 'datasheet':
|
||||
datasheet = f[1]
|
||||
if f[0] == 'libsource':
|
||||
libsource = f[1:]
|
||||
if ref is None:
|
||||
return None
|
||||
ref_fields = comp_dict.setdefault(ref, {})
|
||||
if datasheet and datasheet != '~':
|
||||
field_set.add('Datasheet')
|
||||
ref_fields['Datasheet'] = datasheet
|
||||
if libsource is not None:
|
||||
for lib_field in libsource:
|
||||
if lib_field[0] == 'description':
|
||||
field_set.add('Description')
|
||||
ref_fields['Description'] = lib_field[1]
|
||||
if fields is None:
|
||||
continue
|
||||
for f in fields:
|
||||
if len(f) > 1:
|
||||
field_set.add(f[1][1])
|
||||
if len(f) > 2:
|
||||
ref_fields[f[1][1]] = f[2]
|
||||
|
||||
return list(field_set), comp_dict
|
|
@ -0,0 +1,26 @@
|
|||
class ParserBase:
|
||||
|
||||
def __init__(self, file_name):
|
||||
"""
|
||||
:param file_name: path to file that should be parsed.
|
||||
"""
|
||||
self.file_name = file_name
|
||||
|
||||
def get_extra_field_data(self):
|
||||
# type: () -> tuple
|
||||
"""
|
||||
Parses the file and returns extra field data.
|
||||
:return: tuple of the format
|
||||
(
|
||||
[field_name1, field_name2,... ],
|
||||
{
|
||||
ref1: {
|
||||
field_name1: field_value1,
|
||||
field_name2: field_value2,
|
||||
...
|
||||
],
|
||||
ref2: ...
|
||||
}
|
||||
)
|
||||
"""
|
||||
pass
|
|
@ -0,0 +1,32 @@
|
|||
import re
|
||||
|
||||
term_regex = r'''(?mx)
|
||||
\s*(?:
|
||||
(?P<open>\()|
|
||||
(?P<close>\))|
|
||||
(?P<sq>"(?:\\\\|\\"|[^"])*")|
|
||||
(?P<s>[^(^)\s]+)
|
||||
)'''
|
||||
pattern = re.compile(term_regex)
|
||||
|
||||
|
||||
def parse_sexpression(sexpression):
|
||||
stack = []
|
||||
out = []
|
||||
for terms in pattern.finditer(sexpression):
|
||||
term, value = [(t, v) for t, v in terms.groupdict().items() if v][0]
|
||||
if term == 'open':
|
||||
stack.append(out)
|
||||
out = []
|
||||
elif term == 'close':
|
||||
assert stack, "Trouble with nesting of brackets"
|
||||
tmp, out = out, stack.pop(-1)
|
||||
out.append(tmp)
|
||||
elif term == 'sq':
|
||||
out.append(value[1:-1].replace('\\\\', '\\').replace('\\"', '"'))
|
||||
elif term == 's':
|
||||
out.append(value)
|
||||
else:
|
||||
raise NotImplementedError("Error: %s, %s" % (term, value))
|
||||
assert not stack, "Trouble with nesting of brackets"
|
||||
return out[0]
|
|
@ -0,0 +1,38 @@
|
|||
from xml.dom import minidom
|
||||
|
||||
from .parser_base import ParserBase
|
||||
|
||||
|
||||
class XmlParser(ParserBase):
|
||||
@staticmethod
|
||||
def get_text(nodelist):
|
||||
rc = []
|
||||
for node in nodelist:
|
||||
if node.nodeType == node.TEXT_NODE:
|
||||
rc.append(node.data)
|
||||
return ''.join(rc)
|
||||
|
||||
def get_extra_field_data(self):
|
||||
xml = minidom.parse(self.file_name)
|
||||
components = xml.getElementsByTagName('comp')
|
||||
field_set = set()
|
||||
comp_dict = {}
|
||||
for c in components:
|
||||
ref_fields = comp_dict.setdefault(c.attributes['ref'].value, {})
|
||||
datasheet = c.getElementsByTagName('datasheet')
|
||||
if datasheet:
|
||||
datasheet = self.get_text(datasheet[0].childNodes)
|
||||
if datasheet != '~':
|
||||
field_set.add('Datasheet')
|
||||
ref_fields['Datasheet'] = datasheet
|
||||
libsource = c.getElementsByTagName('libsource')
|
||||
if libsource and libsource[0].hasAttribute('description'):
|
||||
field_set.add('Description')
|
||||
attr = libsource[0].attributes['description']
|
||||
ref_fields['Description'] = attr.value
|
||||
for f in c.getElementsByTagName('field'):
|
||||
name = f.attributes['name'].value
|
||||
field_set.add(name)
|
||||
ref_fields[name] = self.get_text(f.childNodes)
|
||||
|
||||
return list(field_set), comp_dict
|
|
@ -0,0 +1,637 @@
|
|||
{
|
||||
"$schema": "http://json-schema.org/draft-06/schema#",
|
||||
"$ref": "#/definitions/GenericJSONPCBData",
|
||||
"definitions": {
|
||||
"GenericJSONPCBData": {
|
||||
"type": "object",
|
||||
"additionalProperties": false,
|
||||
"properties": {
|
||||
"spec_version": {
|
||||
"type": "integer"
|
||||
},
|
||||
"pcbdata": {
|
||||
"$ref": "#/definitions/Pcbdata"
|
||||
},
|
||||
"components": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"$ref": "#/definitions/Component"
|
||||
}
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"spec_version",
|
||||
"pcbdata",
|
||||
"components"
|
||||
],
|
||||
"title": "GenericJSONPCBData"
|
||||
},
|
||||
"Component": {
|
||||
"type": "object",
|
||||
"additionalProperties": false,
|
||||
"properties": {
|
||||
"attr": {
|
||||
"type": "string"
|
||||
},
|
||||
"footprint": {
|
||||
"type": "string"
|
||||
},
|
||||
"layer": {
|
||||
"$ref": "#/definitions/Layer"
|
||||
},
|
||||
"ref": {
|
||||
"type": "string"
|
||||
},
|
||||
"val": {
|
||||
"type": "string"
|
||||
},
|
||||
"extra_fields": {
|
||||
"$ref": "#/definitions/ExtraData"
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"footprint",
|
||||
"layer",
|
||||
"ref",
|
||||
"val"
|
||||
],
|
||||
"title": "Component"
|
||||
},
|
||||
"Pcbdata": {
|
||||
"type": "object",
|
||||
"additionalProperties": false,
|
||||
"properties": {
|
||||
"edges_bbox": {
|
||||
"$ref": "#/definitions/EdgesBbox"
|
||||
},
|
||||
"edges": {
|
||||
"$ref": "#/definitions/DrawingArray"
|
||||
},
|
||||
"drawings": {
|
||||
"$ref": "#/definitions/LayerDrawings"
|
||||
},
|
||||
"footprints": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"$ref": "#/definitions/Footprint"
|
||||
}
|
||||
},
|
||||
"metadata": {
|
||||
"$ref": "#/definitions/Metadata"
|
||||
},
|
||||
"tracks": {
|
||||
"$ref": "#/definitions/Tracks"
|
||||
},
|
||||
"zones": {
|
||||
"$ref": "#/definitions/Zones"
|
||||
},
|
||||
"nets": {
|
||||
"type": "array",
|
||||
"items": { "type": "string" }
|
||||
},
|
||||
"font_data": {
|
||||
"$ref": "#/definitions/FontData"
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"edges_bbox",
|
||||
"edges",
|
||||
"drawings",
|
||||
"footprints",
|
||||
"metadata"
|
||||
],
|
||||
"dependencies": {
|
||||
"tracks": { "required": ["zones"] },
|
||||
"zones": { "required": ["tracks"] }
|
||||
},
|
||||
"title": "Pcbdata"
|
||||
},
|
||||
"EdgesBbox": {
|
||||
"type": "object",
|
||||
"additionalProperties": false,
|
||||
"properties": {
|
||||
"minx": {
|
||||
"type": "number"
|
||||
},
|
||||
"miny": {
|
||||
"type": "number"
|
||||
},
|
||||
"maxx": {
|
||||
"type": "number"
|
||||
},
|
||||
"maxy": {
|
||||
"type": "number"
|
||||
}
|
||||
},
|
||||
"required": ["minx", "miny", "maxx", "maxy"],
|
||||
"title": "EdgesBbox"
|
||||
},
|
||||
"DrawingSet": {
|
||||
"type": "object",
|
||||
"additionalProperties": false,
|
||||
"properties": {
|
||||
"F": {
|
||||
"$ref": "#/definitions/DrawingArray"
|
||||
},
|
||||
"B": {
|
||||
"$ref": "#/definitions/DrawingArray"
|
||||
}
|
||||
},
|
||||
"required": ["F", "B"],
|
||||
"title": "DrawingSet"
|
||||
},
|
||||
"Footprint": {
|
||||
"type": "object",
|
||||
"additionalProperties": false,
|
||||
"properties": {
|
||||
"ref": {
|
||||
"type": "string"
|
||||
},
|
||||
"center": {
|
||||
"$ref": "#/definitions/Coordinates"
|
||||
},
|
||||
"bbox": {
|
||||
"$ref": "#/definitions/Bbox"
|
||||
},
|
||||
"pads": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"$ref": "#/definitions/Pad"
|
||||
}
|
||||
},
|
||||
"drawings": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "object",
|
||||
"additionalProperties": false,
|
||||
"properties": {
|
||||
"layer": { "$ref": "#/definitions/Layer" },
|
||||
"drawing": { "$ref": "#/definitions/Drawing" }
|
||||
},
|
||||
"required": [ "layer", "drawing" ]
|
||||
}
|
||||
},
|
||||
"layer": {
|
||||
"$ref": "#/definitions/Layer"
|
||||
}
|
||||
},
|
||||
"required": ["ref", "center", "bbox", "pads", "drawings", "layer"],
|
||||
"title": "Footprint"
|
||||
},
|
||||
"Bbox": {
|
||||
"type": "object",
|
||||
"additionalProperties": false,
|
||||
"properties": {
|
||||
"pos": {
|
||||
"$ref": "#/definitions/Coordinates"
|
||||
},
|
||||
"relpos": {
|
||||
"$ref": "#/definitions/Coordinates"
|
||||
},
|
||||
"size": {
|
||||
"$ref": "#/definitions/Coordinates"
|
||||
},
|
||||
"angle": {
|
||||
"type": "number"
|
||||
}
|
||||
},
|
||||
"required": ["pos", "relpos", "size", "angle"],
|
||||
"title": "Bbox"
|
||||
},
|
||||
"Pad": {
|
||||
"type": "object",
|
||||
"additionalProperties": false,
|
||||
"properties": {
|
||||
"layers": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"$ref": "#/definitions/Layer"
|
||||
},
|
||||
"minItems": 1,
|
||||
"maxItems": 2
|
||||
},
|
||||
"pos": {
|
||||
"$ref": "#/definitions/Coordinates"
|
||||
},
|
||||
"size": {
|
||||
"$ref": "#/definitions/Coordinates"
|
||||
},
|
||||
"angle": {
|
||||
"type": "number"
|
||||
},
|
||||
"shape": {
|
||||
"$ref": "#/definitions/Shape"
|
||||
},
|
||||
"svgpath": { "type": "string" },
|
||||
"polygons": { "$ref": "#/definitions/Polygons" },
|
||||
"radius": { "type": "number" },
|
||||
"chamfpos": { "type": "integer" },
|
||||
"chamfratio": { "type": "number" },
|
||||
"type": {
|
||||
"$ref": "#/definitions/PadType"
|
||||
},
|
||||
"pin1": {
|
||||
"type": "integer", "const": 1
|
||||
},
|
||||
"drillshape": {
|
||||
"$ref": "#/definitions/Drillshape"
|
||||
},
|
||||
"drillsize": {
|
||||
"$ref": "#/definitions/Coordinates"
|
||||
},
|
||||
"offset": {
|
||||
"$ref": "#/definitions/Coordinates"
|
||||
},
|
||||
"net": { "type": "string" }
|
||||
},
|
||||
"required": [
|
||||
"layers",
|
||||
"pos",
|
||||
"size",
|
||||
"shape",
|
||||
"type"
|
||||
],
|
||||
"allOf": [
|
||||
{
|
||||
"if": {
|
||||
"properties": { "shape": { "const": "custom" } }
|
||||
},
|
||||
"then": {
|
||||
"anyOf": [
|
||||
{ "required": [ "svgpath" ] },
|
||||
{ "required": [ "pos", "angle", "polygons" ] }
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"if": {
|
||||
"properties": { "shape": { "const": "roundrect" } }
|
||||
},
|
||||
"then": {
|
||||
"required": [ "radius" ]
|
||||
}
|
||||
},
|
||||
{
|
||||
"if": {
|
||||
"properties": { "shape": { "const": "chamfrect" } }
|
||||
},
|
||||
"then": {
|
||||
"required": [ "radius", "chamfpos", "chamfratio" ]
|
||||
}
|
||||
},
|
||||
{
|
||||
"if": {
|
||||
"properties": { "type": { "const": "th" } }
|
||||
},
|
||||
"then": {
|
||||
"required": [ "drillshape", "drillsize" ]
|
||||
}
|
||||
}
|
||||
],
|
||||
"title": "Pad"
|
||||
},
|
||||
"Metadata": {
|
||||
"type": "object",
|
||||
"additionalProperties": false,
|
||||
"properties": {
|
||||
"title": {
|
||||
"type": "string"
|
||||
},
|
||||
"revision": {
|
||||
"type": "string"
|
||||
},
|
||||
"company": {
|
||||
"type": "string"
|
||||
},
|
||||
"date": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"required": ["title", "revision", "company", "date"],
|
||||
"title": "Metadata"
|
||||
},
|
||||
"LayerDrawings": {
|
||||
"type": "object",
|
||||
"items": {
|
||||
"silkscreen": {
|
||||
"$ref": "#/definitions/DrawingSet"
|
||||
},
|
||||
"fabrication": {
|
||||
"$ref": "#/definitions/DrawingSet"
|
||||
}
|
||||
}
|
||||
},
|
||||
"DrawingArray": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"$ref": "#/definitions/Drawing"
|
||||
}
|
||||
},
|
||||
"Drawing": {
|
||||
"type": "object",
|
||||
"oneOf": [
|
||||
{ "$ref": "#/definitions/DrawingSegment" },
|
||||
{ "$ref": "#/definitions/DrawingRect" },
|
||||
{ "$ref": "#/definitions/DrawingCircle" },
|
||||
{ "$ref": "#/definitions/DrawingArc" },
|
||||
{ "$ref": "#/definitions/DrawingCurve" },
|
||||
{ "$ref": "#/definitions/DrawingPolygon" },
|
||||
{ "$ref": "#/definitions/DrawingText" }
|
||||
]
|
||||
},
|
||||
"DrawingSegment": {
|
||||
"type": "object",
|
||||
"additionalProperties": false,
|
||||
"properties": {
|
||||
"type": { "type": "string", "const": "segment" },
|
||||
"start": { "$ref": "#/definitions/Coordinates" },
|
||||
"end": { "$ref": "#/definitions/Coordinates" },
|
||||
"width": { "type": "number" }
|
||||
},
|
||||
"required": ["type", "start", "end", "width"],
|
||||
"title": "DrawingSegment"
|
||||
},
|
||||
"DrawingRect": {
|
||||
"type": "object",
|
||||
"additionalProperties": false,
|
||||
"properties": {
|
||||
"type": { "const": "rect" },
|
||||
"start": { "$ref": "#/definitions/Coordinates" },
|
||||
"end": { "$ref": "#/definitions/Coordinates" },
|
||||
"width": { "type": "number" }
|
||||
},
|
||||
"required": ["type", "start", "end", "width"],
|
||||
"title": "DrawingRect"
|
||||
},
|
||||
"DrawingCircle": {
|
||||
"type": "object",
|
||||
"additionalProperties": false,
|
||||
"properties": {
|
||||
"type": { "const": "circle" },
|
||||
"start": { "$ref": "#/definitions/Coordinates" },
|
||||
"radius": { "type": "number" },
|
||||
"filled": { "type": "integer" },
|
||||
"width": { "type": "number" }
|
||||
},
|
||||
"required": ["type", "start", "radius", "width"],
|
||||
"title": "DrawingCircle"
|
||||
},
|
||||
"DrawingArc": {
|
||||
"type": "object",
|
||||
"additionalProperties": false,
|
||||
"properties": {
|
||||
"type": { "const": "arc" },
|
||||
"width": { "type": "number" },
|
||||
"svgpath": { "type": "string" },
|
||||
"start": { "$ref": "#/definitions/Coordinates" },
|
||||
"radius": { "type": "number" },
|
||||
"startangle": { "type": "number" },
|
||||
"endangle": { "type": "number" }
|
||||
},
|
||||
"required": [
|
||||
"type",
|
||||
"width"
|
||||
],
|
||||
"anyOf": [
|
||||
{ "required": ["svgpath"] },
|
||||
{ "required": ["start", "radius", "startangle", "endangle"] }
|
||||
],
|
||||
"title": "DrawingArc"
|
||||
},
|
||||
"DrawingCurve": {
|
||||
"type": "object",
|
||||
"additionalProperties": false,
|
||||
"properties": {
|
||||
"type": { "const": "curve" },
|
||||
"start": { "$ref": "#/definitions/Coordinates" },
|
||||
"end": { "$ref": "#/definitions/Coordinates" },
|
||||
"cpa": { "$ref": "#/definitions/Coordinates" },
|
||||
"cpb": { "$ref": "#/definitions/Coordinates" },
|
||||
"width": { "type": "number" }
|
||||
},
|
||||
"required": ["type", "start", "end", "cpa", "cpb", "width"],
|
||||
"title": "DrawingCurve"
|
||||
},
|
||||
"DrawingPolygon": {
|
||||
"type": "object",
|
||||
"additionalProperties": false,
|
||||
"properties": {
|
||||
"type": { "const": "polygon" },
|
||||
"filled": { "type": "integer" },
|
||||
"width": { "type": "number" },
|
||||
"svgpath": { "type": "string" },
|
||||
"pos": { "$ref": "#/definitions/Coordinates" },
|
||||
"angle": { "type": "number" },
|
||||
"polygons": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "array",
|
||||
"items": { "$ref": "#/definitions/Coordinates" }
|
||||
}
|
||||
}
|
||||
},
|
||||
"required": ["type"],
|
||||
"anyOf": [
|
||||
{ "required": ["svgpath"] },
|
||||
{ "required": ["pos", "angle", "polygons"] }
|
||||
],
|
||||
"title": "DrawingPolygon"
|
||||
},
|
||||
"DrawingText": {
|
||||
"type": "object",
|
||||
"additionalProperties": false,
|
||||
"properties": {
|
||||
"svgpath": { "type": "string" },
|
||||
"thickness": { "type": "number" },
|
||||
"ref": { "type": "integer" , "const": 1 },
|
||||
"val": { "type": "integer" , "const": 1 }
|
||||
},
|
||||
"required": [
|
||||
"svgpath",
|
||||
"thickness"
|
||||
],
|
||||
"title": "DrawingText"
|
||||
},
|
||||
"Coordinates": {
|
||||
"type": "array",
|
||||
"items": { "type": "number" },
|
||||
"minItems": 2,
|
||||
"maxItems": 2
|
||||
},
|
||||
"Drillshape": {
|
||||
"type": "string",
|
||||
"enum": [
|
||||
"circle",
|
||||
"oblong"
|
||||
],
|
||||
"title": "Drillshape"
|
||||
},
|
||||
"Layer": {
|
||||
"type": "string",
|
||||
"enum": [
|
||||
"B",
|
||||
"F"
|
||||
],
|
||||
"title": "Layer"
|
||||
},
|
||||
"Shape": {
|
||||
"type": "string",
|
||||
"enum": [
|
||||
"rect",
|
||||
"circle",
|
||||
"oval",
|
||||
"roundrect",
|
||||
"chamfrect",
|
||||
"custom"
|
||||
],
|
||||
"title": "Shape"
|
||||
},
|
||||
"PadType": {
|
||||
"type": "string",
|
||||
"enum": [
|
||||
"smd",
|
||||
"th"
|
||||
],
|
||||
"title": "PadType"
|
||||
},
|
||||
"Tracks": {
|
||||
"type": "object",
|
||||
"additionalProperties": false,
|
||||
"properties": {
|
||||
"F": {
|
||||
"type": "array",
|
||||
"items": { "$ref": "#/definitions/Track" }
|
||||
},
|
||||
"B": {
|
||||
"type": "array",
|
||||
"items": { "$ref": "#/definitions/Track" }
|
||||
}
|
||||
},
|
||||
"required": [ "F", "B" ],
|
||||
"title": "Tracks"
|
||||
},
|
||||
"Track": {
|
||||
"type": "object",
|
||||
"oneOf":[
|
||||
{
|
||||
"additionalProperties": false,
|
||||
"properties": {
|
||||
"start": { "$ref": "#/definitions/Coordinates" },
|
||||
"end": { "$ref": "#/definitions/Coordinates" },
|
||||
"width": { "type": "number" },
|
||||
"net": { "type": "string" }
|
||||
},
|
||||
"required": [
|
||||
"start",
|
||||
"end",
|
||||
"width"
|
||||
]
|
||||
},
|
||||
{
|
||||
"additionalProperties": false,
|
||||
"properties": {
|
||||
"center": { "$ref": "#/definitions/Coordinates" },
|
||||
"startangle": { "type": "number" },
|
||||
"endangle": { "type": "number" },
|
||||
"radius": { "type": "number" },
|
||||
"width": { "type": "number" },
|
||||
"net": { "type": "string" }
|
||||
},
|
||||
"required": [
|
||||
"center",
|
||||
"startangle",
|
||||
"endangle",
|
||||
"radius",
|
||||
"width"
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
"Zones": {
|
||||
"type": "object",
|
||||
"additionalProperties": false,
|
||||
"properties": {
|
||||
"F": {
|
||||
"type": "array",
|
||||
"items": { "$ref": "#/definitions/Zone" }
|
||||
},
|
||||
"B": {
|
||||
"type": "array",
|
||||
"items": { "$ref": "#/definitions/Zone" }
|
||||
}
|
||||
},
|
||||
"required": [ "F", "B" ],
|
||||
"title": "Zones"
|
||||
},
|
||||
"Zone": {
|
||||
"type": "object",
|
||||
"additionalProperties": false,
|
||||
"properties": {
|
||||
"svgpath": { "type": "string" },
|
||||
"polygons": {
|
||||
"$ref": "#/definitions/Polygons"
|
||||
},
|
||||
"net": { "type": "string" },
|
||||
"fillrule": {
|
||||
"type": "string",
|
||||
"enum": [
|
||||
"nonzero",
|
||||
"evenodd"
|
||||
]
|
||||
}
|
||||
},
|
||||
"anyOf": [
|
||||
{ "required": [ "svgpath" ] },
|
||||
{ "required": [ "polygons" ] }
|
||||
],
|
||||
"title": "Zone"
|
||||
},
|
||||
"Polygons": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"$ref": "#/definitions/Coordinates"
|
||||
}
|
||||
}
|
||||
},
|
||||
"PolyLineArray": {
|
||||
"$ref": "#/definitions/Polygons"
|
||||
},
|
||||
"ReferenceSet": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "array",
|
||||
"items": [
|
||||
{ "type": "string" },
|
||||
{ "type": "integer" }
|
||||
],
|
||||
"additionalItems": false
|
||||
}
|
||||
},
|
||||
"ExtraData": {
|
||||
"type": "object",
|
||||
"additionalProperties": true,
|
||||
"properties": {
|
||||
},
|
||||
"title": "ExtraData"
|
||||
},
|
||||
"FontData": {
|
||||
"type": "object",
|
||||
"patternProperties": {
|
||||
"^.$" : {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"w": { "type": "number" },
|
||||
"l": { "$ref": "#/definitions/PolyLineArray" }
|
||||
},
|
||||
"additionalProperties" : false,
|
||||
"required": [
|
||||
"w",
|
||||
"l"
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,538 @@
|
|||
"""This submodule contains very stripped down bare bones version of
|
||||
svgpathtools module:
|
||||
https://github.com/mathandy/svgpathtools
|
||||
|
||||
All external dependencies are removed. This code can parse path strings with
|
||||
segments and arcs, calculate bounding box and that's about it. This is all
|
||||
that is needed in ibom parsers at the moment.
|
||||
"""
|
||||
|
||||
# External dependencies
|
||||
from __future__ import division, absolute_import, print_function
|
||||
|
||||
import re
|
||||
from cmath import exp
|
||||
from math import sqrt, cos, sin, acos, degrees, radians, pi
|
||||
|
||||
|
||||
def clip(a, a_min, a_max):
|
||||
return min(a_max, max(a_min, a))
|
||||
|
||||
|
||||
class Line(object):
|
||||
def __init__(self, start, end):
|
||||
self.start = start
|
||||
self.end = end
|
||||
|
||||
def __repr__(self):
|
||||
return 'Line(start=%s, end=%s)' % (self.start, self.end)
|
||||
|
||||
def __eq__(self, other):
|
||||
if not isinstance(other, Line):
|
||||
return NotImplemented
|
||||
return self.start == other.start and self.end == other.end
|
||||
|
||||
def __ne__(self, other):
|
||||
if not isinstance(other, Line):
|
||||
return NotImplemented
|
||||
return not self == other
|
||||
|
||||
def __len__(self):
|
||||
return 2
|
||||
|
||||
def bbox(self):
|
||||
"""returns the bounding box for the segment in the form
|
||||
(xmin, xmax, ymin, ymax)."""
|
||||
xmin = min(self.start.real, self.end.real)
|
||||
xmax = max(self.start.real, self.end.real)
|
||||
ymin = min(self.start.imag, self.end.imag)
|
||||
ymax = max(self.start.imag, self.end.imag)
|
||||
return xmin, xmax, ymin, ymax
|
||||
|
||||
|
||||
class Arc(object):
|
||||
def __init__(self, start, radius, rotation, large_arc, sweep, end,
|
||||
autoscale_radius=True):
|
||||
"""
|
||||
This should be thought of as a part of an ellipse connecting two
|
||||
points on that ellipse, start and end.
|
||||
Parameters
|
||||
----------
|
||||
start : complex
|
||||
The start point of the curve. Note: `start` and `end` cannot be the
|
||||
same. To make a full ellipse or circle, use two `Arc` objects.
|
||||
radius : complex
|
||||
rx + 1j*ry, where rx and ry are the radii of the ellipse (also
|
||||
known as its semi-major and semi-minor axes, or vice-versa or if
|
||||
rx < ry).
|
||||
Note: If rx = 0 or ry = 0 then this arc is treated as a
|
||||
straight line segment joining the endpoints.
|
||||
Note: If rx or ry has a negative sign, the sign is dropped; the
|
||||
absolute value is used instead.
|
||||
Note: If no such ellipse exists, the radius will be scaled so
|
||||
that one does (unless autoscale_radius is set to False).
|
||||
rotation : float
|
||||
This is the CCW angle (in degrees) from the positive x-axis of the
|
||||
current coordinate system to the x-axis of the ellipse.
|
||||
large_arc : bool
|
||||
Given two points on an ellipse, there are two elliptical arcs
|
||||
connecting those points, the first going the short way around the
|
||||
ellipse, and the second going the long way around the ellipse. If
|
||||
`large_arc == False`, the shorter elliptical arc will be used. If
|
||||
`large_arc == True`, then longer elliptical will be used.
|
||||
In other words, `large_arc` should be 0 for arcs spanning less than
|
||||
or equal to 180 degrees and 1 for arcs spanning greater than 180
|
||||
degrees.
|
||||
sweep : bool
|
||||
For any acceptable parameters `start`, `end`, `rotation`, and
|
||||
`radius`, there are two ellipses with the given major and minor
|
||||
axes (radii) which connect `start` and `end`. One which connects
|
||||
them in a CCW fashion and one which connected them in a CW
|
||||
fashion. If `sweep == True`, the CCW ellipse will be used. If
|
||||
`sweep == False`, the CW ellipse will be used. See note on curve
|
||||
orientation below.
|
||||
end : complex
|
||||
The end point of the curve. Note: `start` and `end` cannot be the
|
||||
same. To make a full ellipse or circle, use two `Arc` objects.
|
||||
autoscale_radius : bool
|
||||
If `autoscale_radius == True`, then will also scale `self.radius`
|
||||
in the case that no ellipse exists with the input parameters
|
||||
(see inline comments for further explanation).
|
||||
|
||||
Derived Parameters/Attributes
|
||||
-----------------------------
|
||||
self.theta : float
|
||||
This is the phase (in degrees) of self.u1transform(self.start).
|
||||
It is $\\theta_1$ in the official documentation and ranges from
|
||||
-180 to 180.
|
||||
self.delta : float
|
||||
This is the angular distance (in degrees) between the start and
|
||||
end of the arc after the arc has been sent to the unit circle
|
||||
through self.u1transform().
|
||||
It is $\\Delta\\theta$ in the official documentation and ranges
|
||||
from -360 to 360; being positive when the arc travels CCW and
|
||||
negative otherwise (i.e. is positive/negative when
|
||||
sweep == True/False).
|
||||
self.center : complex
|
||||
This is the center of the arc's ellipse.
|
||||
self.phi : float
|
||||
The arc's rotation in radians, i.e. `radians(self.rotation)`.
|
||||
self.rot_matrix : complex
|
||||
Equal to `exp(1j * self.phi)` which is also equal to
|
||||
`cos(self.phi) + 1j*sin(self.phi)`.
|
||||
|
||||
|
||||
Note on curve orientation (CW vs CCW)
|
||||
-------------------------------------
|
||||
The notions of clockwise (CW) and counter-clockwise (CCW) are reversed
|
||||
in some sense when viewing SVGs (as the y coordinate starts at the top
|
||||
of the image and increases towards the bottom).
|
||||
"""
|
||||
assert start != end
|
||||
assert radius.real != 0 and radius.imag != 0
|
||||
|
||||
self.start = start
|
||||
self.radius = abs(radius.real) + 1j * abs(radius.imag)
|
||||
self.rotation = rotation
|
||||
self.large_arc = bool(large_arc)
|
||||
self.sweep = bool(sweep)
|
||||
self.end = end
|
||||
self.autoscale_radius = autoscale_radius
|
||||
|
||||
# Convenience parameters
|
||||
self.phi = radians(self.rotation)
|
||||
self.rot_matrix = exp(1j * self.phi)
|
||||
|
||||
# Derive derived parameters
|
||||
self._parameterize()
|
||||
|
||||
def __repr__(self):
|
||||
params = (self.start, self.radius, self.rotation,
|
||||
self.large_arc, self.sweep, self.end)
|
||||
return ("Arc(start={}, radius={}, rotation={}, "
|
||||
"large_arc={}, sweep={}, end={})".format(*params))
|
||||
|
||||
def __eq__(self, other):
|
||||
if not isinstance(other, Arc):
|
||||
return NotImplemented
|
||||
return self.start == other.start and self.end == other.end \
|
||||
and self.radius == other.radius \
|
||||
and self.rotation == other.rotation \
|
||||
and self.large_arc == other.large_arc and self.sweep == other.sweep
|
||||
|
||||
def __ne__(self, other):
|
||||
if not isinstance(other, Arc):
|
||||
return NotImplemented
|
||||
return not self == other
|
||||
|
||||
def _parameterize(self):
|
||||
# See http://www.w3.org/TR/SVG/implnote.html#ArcImplementationNotes
|
||||
# my notation roughly follows theirs
|
||||
rx = self.radius.real
|
||||
ry = self.radius.imag
|
||||
rx_sqd = rx * rx
|
||||
ry_sqd = ry * ry
|
||||
|
||||
# Transform z-> z' = x' + 1j*y'
|
||||
# = self.rot_matrix**(-1)*(z - (end+start)/2)
|
||||
# coordinates. This translates the ellipse so that the midpoint
|
||||
# between self.end and self.start lies on the origin and rotates
|
||||
# the ellipse so that the its axes align with the xy-coordinate axes.
|
||||
# Note: This sends self.end to -self.start
|
||||
zp1 = (1 / self.rot_matrix) * (self.start - self.end) / 2
|
||||
x1p, y1p = zp1.real, zp1.imag
|
||||
x1p_sqd = x1p * x1p
|
||||
y1p_sqd = y1p * y1p
|
||||
|
||||
# Correct out of range radii
|
||||
# Note: an ellipse going through start and end with radius and phi
|
||||
# exists if and only if radius_check is true
|
||||
radius_check = (x1p_sqd / rx_sqd) + (y1p_sqd / ry_sqd)
|
||||
if radius_check > 1:
|
||||
if self.autoscale_radius:
|
||||
rx *= sqrt(radius_check)
|
||||
ry *= sqrt(radius_check)
|
||||
self.radius = rx + 1j * ry
|
||||
rx_sqd = rx * rx
|
||||
ry_sqd = ry * ry
|
||||
else:
|
||||
raise ValueError("No such elliptic arc exists.")
|
||||
|
||||
# Compute c'=(c_x', c_y'), the center of the ellipse in (x', y') coords
|
||||
# Noting that, in our new coord system, (x_2', y_2') = (-x_1', -x_2')
|
||||
# and our ellipse is cut out by of the plane by the algebraic equation
|
||||
# (x'-c_x')**2 / r_x**2 + (y'-c_y')**2 / r_y**2 = 1,
|
||||
# we can find c' by solving the system of two quadratics given by
|
||||
# plugging our transformed endpoints (x_1', y_1') and (x_2', y_2')
|
||||
tmp = rx_sqd * y1p_sqd + ry_sqd * x1p_sqd
|
||||
radicand = (rx_sqd * ry_sqd - tmp) / tmp
|
||||
try:
|
||||
radical = sqrt(radicand)
|
||||
except ValueError:
|
||||
radical = 0
|
||||
if self.large_arc == self.sweep:
|
||||
cp = -radical * (rx * y1p / ry - 1j * ry * x1p / rx)
|
||||
else:
|
||||
cp = radical * (rx * y1p / ry - 1j * ry * x1p / rx)
|
||||
|
||||
# The center in (x,y) coordinates is easy to find knowing c'
|
||||
self.center = exp(1j * self.phi) * cp + (self.start + self.end) / 2
|
||||
|
||||
# Now we do a second transformation, from (x', y') to (u_x, u_y)
|
||||
# coordinates, which is a translation moving the center of the
|
||||
# ellipse to the origin and a dilation stretching the ellipse to be
|
||||
# the unit circle
|
||||
u1 = (x1p - cp.real) / rx + 1j * (y1p - cp.imag) / ry
|
||||
u2 = (-x1p - cp.real) / rx + 1j * (-y1p - cp.imag) / ry
|
||||
|
||||
# clip in case of floating point error
|
||||
u1 = clip(u1.real, -1, 1) + 1j * clip(u1.imag, -1, 1)
|
||||
u2 = clip(u2.real, -1, 1) + 1j * clip(u2.imag, -1, 1)
|
||||
|
||||
# Now compute theta and delta (we'll define them as we go)
|
||||
# delta is the angular distance of the arc (w.r.t the circle)
|
||||
# theta is the angle between the positive x'-axis and the start point
|
||||
# on the circle
|
||||
if u1.imag > 0:
|
||||
self.theta = degrees(acos(u1.real))
|
||||
elif u1.imag < 0:
|
||||
self.theta = -degrees(acos(u1.real))
|
||||
else:
|
||||
if u1.real > 0: # start is on pos u_x axis
|
||||
self.theta = 0
|
||||
else: # start is on neg u_x axis
|
||||
# Note: This behavior disagrees with behavior documented in
|
||||
# http://www.w3.org/TR/SVG/implnote.html#ArcImplementationNotes
|
||||
# where theta is set to 0 in this case.
|
||||
self.theta = 180
|
||||
|
||||
det_uv = u1.real * u2.imag - u1.imag * u2.real
|
||||
|
||||
acosand = u1.real * u2.real + u1.imag * u2.imag
|
||||
acosand = clip(acosand.real, -1, 1) + clip(acosand.imag, -1, 1)
|
||||
|
||||
if det_uv > 0:
|
||||
self.delta = degrees(acos(acosand))
|
||||
elif det_uv < 0:
|
||||
self.delta = -degrees(acos(acosand))
|
||||
else:
|
||||
if u1.real * u2.real + u1.imag * u2.imag > 0:
|
||||
# u1 == u2
|
||||
self.delta = 0
|
||||
else:
|
||||
# u1 == -u2
|
||||
# Note: This behavior disagrees with behavior documented in
|
||||
# http://www.w3.org/TR/SVG/implnote.html#ArcImplementationNotes
|
||||
# where delta is set to 0 in this case.
|
||||
self.delta = 180
|
||||
|
||||
if not self.sweep and self.delta >= 0:
|
||||
self.delta -= 360
|
||||
elif self.large_arc and self.delta <= 0:
|
||||
self.delta += 360
|
||||
|
||||
def point(self, t):
|
||||
if t == 0:
|
||||
return self.start
|
||||
if t == 1:
|
||||
return self.end
|
||||
angle = radians(self.theta + t * self.delta)
|
||||
cosphi = self.rot_matrix.real
|
||||
sinphi = self.rot_matrix.imag
|
||||
rx = self.radius.real
|
||||
ry = self.radius.imag
|
||||
|
||||
# z = self.rot_matrix*(rx*cos(angle) + 1j*ry*sin(angle)) + self.center
|
||||
x = rx * cosphi * cos(angle) - ry * sinphi * sin(
|
||||
angle) + self.center.real
|
||||
y = rx * sinphi * cos(angle) + ry * cosphi * sin(
|
||||
angle) + self.center.imag
|
||||
return complex(x, y)
|
||||
|
||||
def bbox(self):
|
||||
"""returns a bounding box for the segment in the form
|
||||
(xmin, xmax, ymin, ymax)."""
|
||||
# a(t) = radians(self.theta + self.delta*t)
|
||||
# = (2*pi/360)*(self.theta + self.delta*t)
|
||||
# x'=0: ~~~~~~~~~
|
||||
# -rx*cos(phi)*sin(a(t)) = ry*sin(phi)*cos(a(t))
|
||||
# -(rx/ry)*cot(phi)*tan(a(t)) = 1
|
||||
# a(t) = arctan(-(ry/rx)tan(phi)) + pi*k === atan_x
|
||||
# y'=0: ~~~~~~~~~~
|
||||
# rx*sin(phi)*sin(a(t)) = ry*cos(phi)*cos(a(t))
|
||||
# (rx/ry)*tan(phi)*tan(a(t)) = 1
|
||||
# a(t) = arctan((ry/rx)*cot(phi))
|
||||
# atanres = arctan((ry/rx)*cot(phi)) === atan_y
|
||||
# ~~~~~~~~
|
||||
# (2*pi/360)*(self.theta + self.delta*t) = atanres + pi*k
|
||||
# Therefore, for both x' and y', we have...
|
||||
# t = ((atan_{x/y} + pi*k)*(360/(2*pi)) - self.theta)/self.delta
|
||||
# for all k s.t. 0 < t < 1
|
||||
from math import atan, tan
|
||||
|
||||
if cos(self.phi) == 0:
|
||||
atan_x = pi / 2
|
||||
atan_y = 0
|
||||
elif sin(self.phi) == 0:
|
||||
atan_x = 0
|
||||
atan_y = pi / 2
|
||||
else:
|
||||
rx, ry = self.radius.real, self.radius.imag
|
||||
atan_x = atan(-(ry / rx) * tan(self.phi))
|
||||
atan_y = atan((ry / rx) / tan(self.phi))
|
||||
|
||||
def angle_inv(ang, q): # inverse of angle from Arc.derivative()
|
||||
return ((ang + pi * q) * (360 / (2 * pi)) -
|
||||
self.theta) / self.delta
|
||||
|
||||
xtrema = [self.start.real, self.end.real]
|
||||
ytrema = [self.start.imag, self.end.imag]
|
||||
|
||||
for k in range(-4, 5):
|
||||
tx = angle_inv(atan_x, k)
|
||||
ty = angle_inv(atan_y, k)
|
||||
if 0 <= tx <= 1:
|
||||
xtrema.append(self.point(tx).real)
|
||||
if 0 <= ty <= 1:
|
||||
ytrema.append(self.point(ty).imag)
|
||||
return min(xtrema), max(xtrema), min(ytrema), max(ytrema)
|
||||
|
||||
|
||||
COMMANDS = set('MmZzLlHhVvCcSsQqTtAa')
|
||||
UPPERCASE = set('MZLHVCSQTA')
|
||||
|
||||
COMMAND_RE = re.compile("([MmZzLlHhVvCcSsQqTtAa])")
|
||||
FLOAT_RE = re.compile(r"[-+]?[0-9]*\.?[0-9]+(?:[eE][-+]?[0-9]+)?")
|
||||
|
||||
|
||||
def _tokenize_path(path_def):
|
||||
for x in COMMAND_RE.split(path_def):
|
||||
if x in COMMANDS:
|
||||
yield x
|
||||
for token in FLOAT_RE.findall(x):
|
||||
yield token
|
||||
|
||||
|
||||
def parse_path(pathdef, logger, current_pos=0j):
|
||||
# In the SVG specs, initial movetos are absolute, even if
|
||||
# specified as 'm'. This is the default behavior here as well.
|
||||
# But if you pass in a current_pos variable, the initial moveto
|
||||
# will be relative to that current_pos. This is useful.
|
||||
elements = list(_tokenize_path(pathdef))
|
||||
# Reverse for easy use of .pop()
|
||||
elements.reverse()
|
||||
absolute = False
|
||||
|
||||
segments = []
|
||||
|
||||
start_pos = None
|
||||
command = None
|
||||
|
||||
while elements:
|
||||
|
||||
if elements[-1] in COMMANDS:
|
||||
# New command.
|
||||
command = elements.pop()
|
||||
absolute = command in UPPERCASE
|
||||
command = command.upper()
|
||||
else:
|
||||
# If this element starts with numbers, it is an implicit command
|
||||
# and we don't change the command. Check that it's allowed:
|
||||
if command is None:
|
||||
raise ValueError(
|
||||
"Unallowed implicit command in %s, position %s" % (
|
||||
pathdef, len(pathdef.split()) - len(elements)))
|
||||
|
||||
if command == 'M':
|
||||
# Moveto command.
|
||||
x = elements.pop()
|
||||
y = elements.pop()
|
||||
pos = float(x) + float(y) * 1j
|
||||
if absolute:
|
||||
current_pos = pos
|
||||
else:
|
||||
current_pos += pos
|
||||
|
||||
# when M is called, reset start_pos
|
||||
# This behavior of Z is defined in svg spec:
|
||||
# http://www.w3.org/TR/SVG/paths.html#PathDataClosePathCommand
|
||||
start_pos = current_pos
|
||||
|
||||
# Implicit moveto commands are treated as lineto commands.
|
||||
# So we set command to lineto here, in case there are
|
||||
# further implicit commands after this moveto.
|
||||
command = 'L'
|
||||
|
||||
elif command == 'Z':
|
||||
# Close path
|
||||
if not (current_pos == start_pos):
|
||||
segments.append(Line(current_pos, start_pos))
|
||||
current_pos = start_pos
|
||||
command = None
|
||||
|
||||
elif command == 'L':
|
||||
x = elements.pop()
|
||||
y = elements.pop()
|
||||
pos = float(x) + float(y) * 1j
|
||||
if not absolute:
|
||||
pos += current_pos
|
||||
segments.append(Line(current_pos, pos))
|
||||
current_pos = pos
|
||||
|
||||
elif command == 'H':
|
||||
x = elements.pop()
|
||||
pos = float(x) + current_pos.imag * 1j
|
||||
if not absolute:
|
||||
pos += current_pos.real
|
||||
segments.append(Line(current_pos, pos))
|
||||
current_pos = pos
|
||||
|
||||
elif command == 'V':
|
||||
y = elements.pop()
|
||||
pos = current_pos.real + float(y) * 1j
|
||||
if not absolute:
|
||||
pos += current_pos.imag * 1j
|
||||
segments.append(Line(current_pos, pos))
|
||||
current_pos = pos
|
||||
|
||||
elif command == 'C':
|
||||
logger.warn('Encountered Cubic Bezier segment. '
|
||||
'It is currently not supported and will be replaced '
|
||||
'by a line segment.')
|
||||
for i in range(4):
|
||||
# ignore control points
|
||||
elements.pop()
|
||||
end = float(elements.pop()) + float(elements.pop()) * 1j
|
||||
|
||||
if not absolute:
|
||||
end += current_pos
|
||||
|
||||
segments.append(Line(current_pos, end))
|
||||
current_pos = end
|
||||
|
||||
elif command == 'S':
|
||||
logger.warn('Encountered Quadratic Bezier segment. '
|
||||
'It is currently not supported and will be replaced '
|
||||
'by a line segment.')
|
||||
for i in range(2):
|
||||
# ignore control points
|
||||
elements.pop()
|
||||
end = float(elements.pop()) + float(elements.pop()) * 1j
|
||||
|
||||
if not absolute:
|
||||
end += current_pos
|
||||
|
||||
segments.append(Line(current_pos, end))
|
||||
current_pos = end
|
||||
|
||||
elif command == 'Q':
|
||||
logger.warn('Encountered Quadratic Bezier segment. '
|
||||
'It is currently not supported and will be replaced '
|
||||
'by a line segment.')
|
||||
for i in range(2):
|
||||
# ignore control points
|
||||
elements.pop()
|
||||
end = float(elements.pop()) + float(elements.pop()) * 1j
|
||||
|
||||
if not absolute:
|
||||
end += current_pos
|
||||
|
||||
segments.append(Line(current_pos, end))
|
||||
current_pos = end
|
||||
|
||||
elif command == 'T':
|
||||
logger.warn('Encountered Quadratic Bezier segment. '
|
||||
'It is currently not supported and will be replaced '
|
||||
'by a line segment.')
|
||||
|
||||
end = float(elements.pop()) + float(elements.pop()) * 1j
|
||||
|
||||
if not absolute:
|
||||
end += current_pos
|
||||
|
||||
segments.append(Line(current_pos, end))
|
||||
current_pos = end
|
||||
|
||||
elif command == 'A':
|
||||
radius = float(elements.pop()) + float(elements.pop()) * 1j
|
||||
rotation = float(elements.pop())
|
||||
arc = float(elements.pop())
|
||||
sweep = float(elements.pop())
|
||||
end = float(elements.pop()) + float(elements.pop()) * 1j
|
||||
|
||||
if not absolute:
|
||||
end += current_pos
|
||||
|
||||
segments.append(
|
||||
Arc(current_pos, radius, rotation, arc, sweep, end))
|
||||
current_pos = end
|
||||
|
||||
return segments
|
||||
|
||||
|
||||
def create_path(lines, circles=[]):
|
||||
"""Returns a path d-string."""
|
||||
|
||||
def limit_digits(val):
|
||||
return format(val, '.6f').rstrip('0').replace(',', '.').rstrip('.')
|
||||
|
||||
def different_points(a, b):
|
||||
return abs(a[0] - b[0]) > 1e-6 or abs(a[1] - b[1]) > 1e-6
|
||||
|
||||
parts = []
|
||||
|
||||
for i, line in enumerate(lines):
|
||||
if i == 0 or different_points(lines[i - 1][-1], line[0]):
|
||||
parts.append('M{},{}'.format(*map(limit_digits, line[0])))
|
||||
for point in line[1:]:
|
||||
parts.append('L{},{}'.format(*map(limit_digits, point)))
|
||||
|
||||
for circle in circles:
|
||||
cx, cy, r = circle[0][0], circle[0][1], circle[1]
|
||||
parts.append('M{},{}'.format(limit_digits(cx - r), limit_digits(cy)))
|
||||
parts.append('a {},{} 0 1,0 {},0'.format(
|
||||
*map(limit_digits, [r, r, r + r])))
|
||||
parts.append('a {},{} 0 1,0 -{},0'.format(
|
||||
*map(limit_digits, [r, r, r + r])))
|
||||
|
||||
return ''.join(parts)
|
|
@ -0,0 +1,16 @@
|
|||
import sys
|
||||
|
||||
|
||||
class ExitCodes():
|
||||
ERROR_PARSE = 3
|
||||
ERROR_FILE_NOT_FOUND = 4
|
||||
ERROR_NO_DISPLAY = 5
|
||||
|
||||
|
||||
class ParsingException(Exception):
|
||||
pass
|
||||
|
||||
|
||||
def exit_error(logger, code, err):
|
||||
logger.error(err)
|
||||
sys.exit(code)
|
|
@ -0,0 +1,84 @@
|
|||
#!/usr/bin/python3
|
||||
from __future__ import absolute_import
|
||||
|
||||
import argparse
|
||||
import os
|
||||
import sys
|
||||
# Add ../ to the path
|
||||
# Works if this script is executed without installing the module
|
||||
script_dir = os.path.dirname(os.path.abspath(os.path.realpath(__file__)))
|
||||
sys.path.insert(0, os.path.dirname(script_dir))
|
||||
# Pretend we are part of a module
|
||||
# Avoids: ImportError: attempted relative import with no known parent package
|
||||
__package__ = os.path.basename(script_dir)
|
||||
__import__(__package__)
|
||||
|
||||
|
||||
# python 2 and 3 compatibility hack
|
||||
def to_utf(s):
|
||||
if isinstance(s, bytes):
|
||||
return s.decode('utf-8')
|
||||
else:
|
||||
return s
|
||||
|
||||
|
||||
def main():
|
||||
create_wx_app = 'INTERACTIVE_HTML_BOM_NO_DISPLAY' not in os.environ
|
||||
|
||||
import wx
|
||||
|
||||
if create_wx_app:
|
||||
app = wx.App()
|
||||
if hasattr(wx, "APP_ASSERT_SUPPRESS"):
|
||||
app.SetAssertMode(wx.APP_ASSERT_SUPPRESS)
|
||||
elif hasattr(wx, "DisableAsserts"):
|
||||
wx.DisableAsserts()
|
||||
|
||||
from .core import ibom
|
||||
from .core.config import Config
|
||||
from .ecad import get_parser_by_extension
|
||||
from .version import version
|
||||
from .errors import (ExitCodes, ParsingException, exit_error)
|
||||
|
||||
parser = argparse.ArgumentParser(
|
||||
description='KiCad InteractiveHtmlBom plugin CLI.',
|
||||
formatter_class=argparse.ArgumentDefaultsHelpFormatter)
|
||||
parser.add_argument('file',
|
||||
type=lambda s: to_utf(s),
|
||||
help="KiCad PCB file")
|
||||
|
||||
Config.add_options(parser, version)
|
||||
args = parser.parse_args()
|
||||
logger = ibom.Logger(cli=True)
|
||||
|
||||
if not os.path.isfile(args.file):
|
||||
exit_error(logger, ExitCodes.ERROR_FILE_NOT_FOUND,
|
||||
"File %s does not exist." % args.file)
|
||||
|
||||
print("Loading %s" % args.file)
|
||||
|
||||
config = Config(version, os.path.dirname(os.path.abspath(args.file)))
|
||||
|
||||
parser = get_parser_by_extension(
|
||||
os.path.abspath(args.file), config, logger)
|
||||
|
||||
if args.show_dialog:
|
||||
if not create_wx_app:
|
||||
exit_error(logger, ExitCodes.ERROR_NO_DISPLAY,
|
||||
"Can not show dialog when "
|
||||
"INTERACTIVE_HTML_BOM_NO_DISPLAY is set.")
|
||||
try:
|
||||
ibom.run_with_dialog(parser, config, logger)
|
||||
except ParsingException as e:
|
||||
exit_error(logger, ExitCodes.ERROR_PARSE, e)
|
||||
else:
|
||||
config.set_from_args(args)
|
||||
try:
|
||||
ibom.main(parser, config, logger)
|
||||
except ParsingException as e:
|
||||
exit_error(logger, ExitCodes.ERROR_PARSE, str(e))
|
||||
return 0
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
|
@ -0,0 +1,12 @@
|
|||
::start up echo
|
||||
set i18n_gitAddr= https://github.com/openscopeproject/InteractiveHtmlBom
|
||||
set i18n_batScar= Bat file by Scarrrr0725
|
||||
set i18n_thx4using= Thank You For Using Generate Interactive Bom
|
||||
|
||||
::convert
|
||||
set i18n_draghere=Please Drag the EasyEDA PCB source file here :
|
||||
set i18n_converting=Converting . . . . . .
|
||||
|
||||
::converted
|
||||
set i18n_again=Do you want to convert another file ?
|
||||
set i18n_converted= EDA source file is converted to bom successfully!
|
|
@ -0,0 +1,17 @@
|
|||
::This file needs to be in 'UTF-8 encoding' AND 'Windows CR LF' to work.
|
||||
|
||||
::set active code page as UTF-8/65001
|
||||
set PYTHONIOENCODING=utf-8
|
||||
chcp 65001
|
||||
::start up echo
|
||||
set i18n_gitAddr= https://github.com/openscopeproject/InteractiveHtmlBom
|
||||
set i18n_batScar= Bat 文件: Scarrrr0725/XiaoMingXD
|
||||
set i18n_thx4using= 感谢使用 Generate Interactive Bom
|
||||
|
||||
::convert
|
||||
set i18n_draghere=请将您的 EDA PCB 源文件拖移至此:
|
||||
set i18n_converting=导出中 . . . . . ."
|
||||
|
||||
::converted
|
||||
set i18n_again=请问是否转换其他文件?
|
||||
set i18n_converted= 您的 EDA 源文件已成功导出 Bom!
|
Binary file not shown.
After Width: | Height: | Size: 820 B |
|
@ -0,0 +1,29 @@
|
|||
# Update this when new version is tagged.
|
||||
import os
|
||||
import subprocess
|
||||
|
||||
|
||||
LAST_TAG = 'v2.6.0'
|
||||
|
||||
|
||||
def _get_git_version():
|
||||
plugin_path = os.path.realpath(os.path.dirname(__file__))
|
||||
try:
|
||||
git_version = subprocess.check_output(
|
||||
['git', 'describe', '--tags', '--abbrev=4', '--dirty=-*'],
|
||||
stderr=subprocess.DEVNULL,
|
||||
cwd=plugin_path)
|
||||
if isinstance(git_version, bytes):
|
||||
return git_version.decode('utf-8').rstrip()
|
||||
else:
|
||||
return git_version.rstrip()
|
||||
except subprocess.CalledProcessError:
|
||||
# print('Git version check failed: ' + str(e))
|
||||
pass
|
||||
except Exception:
|
||||
# print('Git process cannot be launched: ' + str(e))
|
||||
pass
|
||||
return None
|
||||
|
||||
|
||||
version = _get_git_version() or LAST_TAG
|
|
@ -0,0 +1,805 @@
|
|||
:root {
|
||||
--pcb-edge-color: black;
|
||||
--pad-color: #878787;
|
||||
--pad-hole-color: #CCCCCC;
|
||||
--pad-color-highlight: #D04040;
|
||||
--pad-color-highlight-both: #D0D040;
|
||||
--pad-color-highlight-marked: #44a344;
|
||||
--pin1-outline-color: #ffb629;
|
||||
--pin1-outline-color-highlight: #ffb629;
|
||||
--pin1-outline-color-highlight-both: #fcbb39;
|
||||
--pin1-outline-color-highlight-marked: #fdbe41;
|
||||
--silkscreen-edge-color: #aa4;
|
||||
--silkscreen-polygon-color: #4aa;
|
||||
--silkscreen-text-color: #4aa;
|
||||
--fabrication-edge-color: #907651;
|
||||
--fabrication-polygon-color: #907651;
|
||||
--fabrication-text-color: #a27c24;
|
||||
--track-color: #def5f1;
|
||||
--track-color-highlight: #D04040;
|
||||
--zone-color: #def5f1;
|
||||
--zone-color-highlight: #d0404080;
|
||||
}
|
||||
|
||||
html,
|
||||
body {
|
||||
margin: 0px;
|
||||
height: 100%;
|
||||
font-family: Verdana, sans-serif;
|
||||
}
|
||||
|
||||
.dark.topmostdiv {
|
||||
--pcb-edge-color: #eee;
|
||||
--pad-color: #808080;
|
||||
--pin1-outline-color: #ffa800;
|
||||
--pin1-outline-color-highlight: #ccff00;
|
||||
--track-color: #42524f;
|
||||
--zone-color: #42524f;
|
||||
background-color: #252c30;
|
||||
color: #eee;
|
||||
}
|
||||
|
||||
button {
|
||||
background-color: #eee;
|
||||
border: 1px solid #888;
|
||||
color: black;
|
||||
height: 44px;
|
||||
width: 44px;
|
||||
text-align: center;
|
||||
text-decoration: none;
|
||||
display: inline-block;
|
||||
font-size: 14px;
|
||||
font-weight: bolder;
|
||||
}
|
||||
|
||||
.dark button {
|
||||
/* This will be inverted */
|
||||
background-color: #c3b7b5;
|
||||
}
|
||||
|
||||
button.depressed {
|
||||
background-color: #0a0;
|
||||
color: white;
|
||||
}
|
||||
|
||||
.dark button.depressed {
|
||||
/* This will be inverted */
|
||||
background-color: #b3b;
|
||||
}
|
||||
|
||||
button:focus {
|
||||
outline: 0;
|
||||
}
|
||||
|
||||
button#tb-btn {
|
||||
background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 8.47 8.47'%3E%3Crect transform='translate(0 -288.53)' ry='1.17' y='288.8' x='.27' height='7.94' width='7.94' fill='%23f9f9f9'/%3E%3Cg transform='translate(0 -288.53)'%3E%3Crect width='7.94' height='7.94' x='.27' y='288.8' ry='1.17' fill='none' stroke='%23000' stroke-width='.4' stroke-linejoin='round'/%3E%3Cpath d='M1.32 290.12h5.82M1.32 291.45h5.82' fill='none' stroke='%23000' stroke-width='.4'/%3E%3Cpath d='M4.37 292.5v4.23M.26 292.63H8.2' fill='none' stroke='%23000' stroke-width='.3'/%3E%3Ctext font-weight='700' font-size='3.17' font-family='sans-serif'%3E%3Ctspan x='1.35' y='295.73'%3EF%3C/tspan%3E%3Ctspan x='5.03' y='295.68'%3EB%3C/tspan%3E%3C/text%3E%3C/g%3E%3C/svg%3E%0A");
|
||||
}
|
||||
|
||||
button#lr-btn {
|
||||
background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 8.47 8.47'%3E%3Crect transform='translate(0 -288.53)' ry='1.17' y='288.8' x='.27' height='7.94' width='7.94' fill='%23f9f9f9'/%3E%3Cg transform='translate(0 -288.53)'%3E%3Crect width='7.94' height='7.94' x='.27' y='288.8' ry='1.17' fill='none' stroke='%23000' stroke-width='.4' stroke-linejoin='round'/%3E%3Cpath d='M1.06 290.12H3.7m-2.64 1.33H3.7m-2.64 1.32H3.7m-2.64 1.3H3.7m-2.64 1.33H3.7' fill='none' stroke='%23000' stroke-width='.4'/%3E%3Cpath d='M4.37 288.8v7.94m0-4.11h3.96' fill='none' stroke='%23000' stroke-width='.3'/%3E%3Ctext font-weight='700' font-size='3.17' font-family='sans-serif'%3E%3Ctspan x='5.11' y='291.96'%3EF%3C/tspan%3E%3Ctspan x='5.03' y='295.68'%3EB%3C/tspan%3E%3C/text%3E%3C/g%3E%3C/svg%3E%0A");
|
||||
}
|
||||
|
||||
button#bom-btn {
|
||||
background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 8.47 8.47'%3E%3Crect transform='translate(0 -288.53)' ry='1.17' y='288.8' x='.27' height='7.94' width='7.94' fill='%23f9f9f9'/%3E%3Cg transform='translate(0 -288.53)' fill='none' stroke='%23000' stroke-width='.4'%3E%3Crect width='7.94' height='7.94' x='.27' y='288.8' ry='1.17' stroke-linejoin='round'/%3E%3Cpath d='M1.59 290.12h5.29M1.59 291.45h5.33M1.59 292.75h5.33M1.59 294.09h5.33M1.59 295.41h5.33'/%3E%3C/g%3E%3C/svg%3E");
|
||||
}
|
||||
|
||||
button#bom-grouped-btn {
|
||||
background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='32' height='32'%3E%3Cg stroke='%23000' stroke-linejoin='round' class='layer'%3E%3Crect width='29' height='29' x='1.5' y='1.5' stroke-width='2' fill='%23fff' rx='5' ry='5'/%3E%3Cpath stroke-linecap='square' stroke-width='2' d='M6 10h4m4 0h5m4 0h3M6.1 22h3m3.9 0h5m4 0h4m-16-8h4m4 0h4'/%3E%3Cpath stroke-linecap='null' d='M5 17.5h22M5 26.6h22M5 5.5h22'/%3E%3C/g%3E%3C/svg%3E");
|
||||
}
|
||||
|
||||
button#bom-ungrouped-btn {
|
||||
background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='32' height='32'%3E%3Cg stroke='%23000' stroke-linejoin='round' class='layer'%3E%3Crect width='29' height='29' x='1.5' y='1.5' stroke-width='2' fill='%23fff' rx='5' ry='5'/%3E%3Cpath stroke-linecap='square' stroke-width='2' d='M6 10h4m-4 8h3m-3 8h4'/%3E%3Cpath stroke-linecap='null' d='M5 13.5h22m-22 8h22M5 5.5h22'/%3E%3C/g%3E%3C/svg%3E");
|
||||
}
|
||||
|
||||
button#bom-netlist-btn {
|
||||
background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='32' height='32'%3E%3Cg fill='none' stroke='%23000' class='layer'%3E%3Crect width='29' height='29' x='1.5' y='1.5' stroke-width='2' fill='%23fff' rx='5' ry='5'/%3E%3Cpath stroke-width='2' d='M6 26l6-6v-8m13.8-6.3l-6 6v8'/%3E%3Ccircle cx='11.8' cy='9.5' r='2.8' stroke-width='2'/%3E%3Ccircle cx='19.8' cy='22.8' r='2.8' stroke-width='2'/%3E%3C/g%3E%3C/svg%3E");
|
||||
}
|
||||
|
||||
button#copy {
|
||||
background-image: url("data:image/svg+xml,%3Csvg height='48' viewBox='0 0 48 48' width='48' xmlns='http://www.w3.org/2000/svg'%3E%3Cpath d='M0 0h48v48h-48z' fill='none'/%3E%3Cpath d='M32 2h-24c-2.21 0-4 1.79-4 4v28h4v-28h24v-4zm6 8h-22c-2.21 0-4 1.79-4 4v28c0 2.21 1.79 4 4 4h22c2.21 0 4-1.79 4-4v-28c0-2.21-1.79-4-4-4zm0 32h-22v-28h22v28z'/%3E%3C/svg%3E");
|
||||
background-position: 6px 6px;
|
||||
background-repeat: no-repeat;
|
||||
background-size: 26px 26px;
|
||||
border-radius: 6px;
|
||||
height: 40px;
|
||||
width: 40px;
|
||||
margin: 10px 5px;
|
||||
}
|
||||
|
||||
button#copy:active {
|
||||
box-shadow: inset 0px 0px 5px #6c6c6c;
|
||||
}
|
||||
|
||||
textarea.clipboard-temp {
|
||||
position: fixed;
|
||||
top: 0;
|
||||
left: 0;
|
||||
width: 2em;
|
||||
height: 2em;
|
||||
padding: 0;
|
||||
border: None;
|
||||
outline: None;
|
||||
box-shadow: None;
|
||||
background: transparent;
|
||||
}
|
||||
|
||||
.left-most-button {
|
||||
border-right: 0;
|
||||
border-top-left-radius: 6px;
|
||||
border-bottom-left-radius: 6px;
|
||||
}
|
||||
|
||||
.middle-button {
|
||||
border-right: 0;
|
||||
}
|
||||
|
||||
.right-most-button {
|
||||
border-top-right-radius: 6px;
|
||||
border-bottom-right-radius: 6px;
|
||||
}
|
||||
|
||||
.button-container {
|
||||
font-size: 0;
|
||||
margin: 10px 10px 10px 0px;
|
||||
}
|
||||
|
||||
.dark .button-container {
|
||||
filter: invert(1);
|
||||
}
|
||||
|
||||
.button-container button {
|
||||
background-size: 32px 32px;
|
||||
background-position: 5px 5px;
|
||||
background-repeat: no-repeat;
|
||||
}
|
||||
|
||||
@media print {
|
||||
.hideonprint {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
|
||||
canvas {
|
||||
cursor: crosshair;
|
||||
}
|
||||
|
||||
canvas:active {
|
||||
cursor: grabbing;
|
||||
}
|
||||
|
||||
.fileinfo {
|
||||
width: 100%;
|
||||
max-width: 1000px;
|
||||
border: none;
|
||||
padding: 5px;
|
||||
}
|
||||
|
||||
.fileinfo .title {
|
||||
font-size: 20pt;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.fileinfo td {
|
||||
overflow: hidden;
|
||||
white-space: nowrap;
|
||||
max-width: 1px;
|
||||
width: 50%;
|
||||
text-overflow: ellipsis;
|
||||
}
|
||||
|
||||
.bom {
|
||||
border-collapse: collapse;
|
||||
font-family: Consolas, "DejaVu Sans Mono", Monaco, monospace;
|
||||
font-size: 10pt;
|
||||
table-layout: fixed;
|
||||
width: 100%;
|
||||
margin-top: 1px;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.bom th,
|
||||
.bom td {
|
||||
border: 1px solid black;
|
||||
padding: 5px;
|
||||
word-wrap: break-word;
|
||||
text-align: center;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.dark .bom th,
|
||||
.dark .bom td {
|
||||
border: 1px solid #777;
|
||||
}
|
||||
|
||||
.bom th {
|
||||
background-color: #CCCCCC;
|
||||
background-clip: padding-box;
|
||||
}
|
||||
|
||||
.dark .bom th {
|
||||
background-color: #3b4749;
|
||||
}
|
||||
|
||||
.bom tr.highlighted:nth-child(n) {
|
||||
background-color: #cfc;
|
||||
}
|
||||
|
||||
.dark .bom tr.highlighted:nth-child(n) {
|
||||
background-color: #226022;
|
||||
}
|
||||
|
||||
.bom tr:nth-child(even) {
|
||||
background-color: #f2f2f2;
|
||||
}
|
||||
|
||||
.dark .bom tr:nth-child(even) {
|
||||
background-color: #313b40;
|
||||
}
|
||||
|
||||
.bom tr.checked {
|
||||
color: #1cb53d;
|
||||
}
|
||||
|
||||
.dark .bom tr.checked {
|
||||
color: #2cce54;
|
||||
}
|
||||
|
||||
.bom tr {
|
||||
transition: background-color 0.2s;
|
||||
}
|
||||
|
||||
.bom .numCol {
|
||||
width: 30px;
|
||||
}
|
||||
|
||||
.bom .value {
|
||||
width: 15%;
|
||||
}
|
||||
|
||||
.bom .quantity {
|
||||
width: 65px;
|
||||
}
|
||||
|
||||
.bom th .sortmark {
|
||||
position: absolute;
|
||||
right: 1px;
|
||||
top: 1px;
|
||||
margin-top: -5px;
|
||||
border-width: 5px;
|
||||
border-style: solid;
|
||||
border-color: transparent transparent #221 transparent;
|
||||
transform-origin: 50% 85%;
|
||||
transition: opacity 0.2s, transform 0.4s;
|
||||
}
|
||||
|
||||
.dark .bom th .sortmark {
|
||||
filter: invert(1);
|
||||
}
|
||||
|
||||
.bom th .sortmark.none {
|
||||
opacity: 0;
|
||||
}
|
||||
|
||||
.bom th .sortmark.desc {
|
||||
transform: rotate(180deg);
|
||||
}
|
||||
|
||||
.bom th:hover .sortmark.none {
|
||||
opacity: 0.5;
|
||||
}
|
||||
|
||||
.bom .bom-checkbox {
|
||||
width: 30px;
|
||||
position: relative;
|
||||
user-select: none;
|
||||
-moz-user-select: none;
|
||||
}
|
||||
|
||||
.bom .bom-checkbox:before {
|
||||
content: "";
|
||||
position: absolute;
|
||||
border-width: 15px;
|
||||
border-style: solid;
|
||||
border-color: #51829f transparent transparent transparent;
|
||||
visibility: hidden;
|
||||
top: -15px;
|
||||
}
|
||||
|
||||
.bom .bom-checkbox:after {
|
||||
content: "Double click to set/unset all";
|
||||
position: absolute;
|
||||
color: white;
|
||||
top: -35px;
|
||||
left: -26px;
|
||||
background: #51829f;
|
||||
padding: 5px 15px;
|
||||
border-radius: 8px;
|
||||
white-space: nowrap;
|
||||
visibility: hidden;
|
||||
}
|
||||
|
||||
.bom .bom-checkbox:hover:before,
|
||||
.bom .bom-checkbox:hover:after {
|
||||
visibility: visible;
|
||||
transition: visibility 0.2s linear 1s;
|
||||
}
|
||||
|
||||
.split {
|
||||
-webkit-box-sizing: border-box;
|
||||
-moz-box-sizing: border-box;
|
||||
box-sizing: border-box;
|
||||
overflow-y: auto;
|
||||
overflow-x: hidden;
|
||||
background-color: inherit;
|
||||
}
|
||||
|
||||
.split.split-horizontal,
|
||||
.gutter.gutter-horizontal {
|
||||
height: 100%;
|
||||
float: left;
|
||||
}
|
||||
|
||||
.gutter {
|
||||
background-color: #ddd;
|
||||
background-repeat: no-repeat;
|
||||
background-position: 50%;
|
||||
transition: background-color 0.3s;
|
||||
}
|
||||
|
||||
.dark .gutter {
|
||||
background-color: #777;
|
||||
}
|
||||
|
||||
.gutter.gutter-horizontal {
|
||||
background-image: url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAUAAAAeCAYAAADkftS9AAAAIklEQVQoU2M4c+bMfxAGAgYYmwGrIIiDjrELjpo5aiZeMwF+yNnOs5KSvgAAAABJRU5ErkJggg==');
|
||||
cursor: ew-resize;
|
||||
width: 5px;
|
||||
}
|
||||
|
||||
.gutter.gutter-vertical {
|
||||
background-image: url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAB4AAAAFAQMAAABo7865AAAABlBMVEVHcEzMzMzyAv2sAAAAAXRSTlMAQObYZgAAABBJREFUeF5jOAMEEAIEEFwAn3kMwcB6I2AAAAAASUVORK5CYII=');
|
||||
cursor: ns-resize;
|
||||
height: 5px;
|
||||
}
|
||||
|
||||
.searchbox {
|
||||
float: left;
|
||||
height: 40px;
|
||||
margin: 10px 5px;
|
||||
padding: 12px 32px;
|
||||
font-family: Consolas, "DejaVu Sans Mono", Monaco, monospace;
|
||||
font-size: 18px;
|
||||
box-sizing: border-box;
|
||||
border: 1px solid #888;
|
||||
border-radius: 6px;
|
||||
outline: none;
|
||||
background-color: #eee;
|
||||
transition: background-color 0.2s, border 0.2s;
|
||||
background-image: url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAABNklEQVQ4T8XSMUvDQBQH8P/LElFa/AIZHcTBQSz0I/gFstTBRR2KUC4ldDxw7h0Bl3RRUATxi4iiODgoiLNrbQYp5J6cpJJqomkX33Z37/14d/dIa33MzDuYI4johOI4XhyNRteO46zNYjDzAxE1yBZprVeZ+QbAUhXEGJMA2Ox2u4+fQIa0mPmsCgCgJYQ4t7lfgF0opQYAdv9ABkKI/UnOFCClXKjX61cA1osQY8x9kiRNKeV7IWA3oyhaSdP0FkAtjxhj3hzH2RBCPOf3pzqYHCilfAAX+URm9oMguPzeWSGQvUcMYC8rOBJCHBRdqxTo9/vbRHRqi8bj8XKv1xvODbiuW2u32/bvf0SlDv4XYOY7z/Mavu+nM1+BmQ+NMc0wDF/LprP0DbTWW0T00ul0nn4b7Q87+X4Qmfiq2wAAAABJRU5ErkJggg==');
|
||||
background-position: 10px 10px;
|
||||
background-repeat: no-repeat;
|
||||
}
|
||||
|
||||
.dark .searchbox {
|
||||
background-color: #111;
|
||||
color: #eee;
|
||||
}
|
||||
|
||||
.searchbox::placeholder {
|
||||
color: #ccc;
|
||||
}
|
||||
|
||||
.dark .searchbox::placeholder {
|
||||
color: #666;
|
||||
}
|
||||
|
||||
.filter {
|
||||
width: calc(60% - 64px);
|
||||
}
|
||||
|
||||
.reflookup {
|
||||
width: calc(40% - 10px);
|
||||
}
|
||||
|
||||
input[type=text]:focus {
|
||||
background-color: white;
|
||||
border: 1px solid #333;
|
||||
}
|
||||
|
||||
.dark input[type=text]:focus {
|
||||
background-color: #333;
|
||||
border: 1px solid #ccc;
|
||||
}
|
||||
|
||||
mark.highlight {
|
||||
background-color: #5050ff;
|
||||
color: #fff;
|
||||
padding: 2px;
|
||||
border-radius: 6px;
|
||||
}
|
||||
|
||||
.dark mark.highlight {
|
||||
background-color: #76a6da;
|
||||
color: #111;
|
||||
}
|
||||
|
||||
.menubtn {
|
||||
background-color: white;
|
||||
border: none;
|
||||
background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='36' height='36' viewBox='0 0 20 20'%3E%3Cpath fill='none' d='M0 0h20v20H0V0z'/%3E%3Cpath d='M15.95 10.78c.03-.25.05-.51.05-.78s-.02-.53-.06-.78l1.69-1.32c.15-.12.19-.34.1-.51l-1.6-2.77c-.1-.18-.31-.24-.49-.18l-1.99.8c-.42-.32-.86-.58-1.35-.78L12 2.34c-.03-.2-.2-.34-.4-.34H8.4c-.2 0-.36.14-.39.34l-.3 2.12c-.49.2-.94.47-1.35.78l-1.99-.8c-.18-.07-.39 0-.49.18l-1.6 2.77c-.1.18-.06.39.1.51l1.69 1.32c-.04.25-.07.52-.07.78s.02.53.06.78L2.37 12.1c-.15.12-.19.34-.1.51l1.6 2.77c.1.18.31.24.49.18l1.99-.8c.42.32.86.58 1.35.78l.3 2.12c.04.2.2.34.4.34h3.2c.2 0 .37-.14.39-.34l.3-2.12c.49-.2.94-.47 1.35-.78l1.99.8c.18.07.39 0 .49-.18l1.6-2.77c.1-.18.06-.39-.1-.51l-1.67-1.32zM10 13c-1.65 0-3-1.35-3-3s1.35-3 3-3 3 1.35 3 3-1.35 3-3 3z'/%3E%3C/svg%3E%0A");
|
||||
background-position: center;
|
||||
background-repeat: no-repeat;
|
||||
}
|
||||
|
||||
.statsbtn {
|
||||
background-color: white;
|
||||
border: none;
|
||||
background-image: url("data:image/svg+xml,%3Csvg width='36' height='36' xmlns='http://www.w3.org/2000/svg'%3E%3Cpath d='M4 6h28v24H4V6zm0 8h28v8H4m9-16v24h10V5.8' fill='none' stroke='%23000' stroke-width='2'/%3E%3C/svg%3E");
|
||||
background-position: center;
|
||||
background-repeat: no-repeat;
|
||||
}
|
||||
|
||||
.iobtn {
|
||||
background-color: white;
|
||||
border: none;
|
||||
background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='36' height='36'%3E%3Cpath fill='none' stroke='%23000' stroke-width='2' d='M3 33v-7l6.8-7h16.5l6.7 7v7H3zM3.2 26H33M21 9l5-5.9 5 6h-2.5V15h-5V9H21zm-4.9 0l-5 6-5-6h2.5V3h5v6h2.5z'/%3E%3Cpath fill='none' stroke='%23000' d='M6.1 29.5H10'/%3E%3C/svg%3E");
|
||||
background-position: center;
|
||||
background-repeat: no-repeat;
|
||||
}
|
||||
|
||||
.visbtn {
|
||||
background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24'%3E%3Cpath fill='none' stroke='%23333' d='M2.5 4.5h5v15h-5zM9.5 4.5h5v15h-5zM16.5 4.5h5v15h-5z'/%3E%3C/svg%3E");
|
||||
background-position: center;
|
||||
background-repeat: no-repeat;
|
||||
padding: 15px;
|
||||
}
|
||||
|
||||
#vismenu-content {
|
||||
left: 0px;
|
||||
font-family: Verdana, sans-serif;
|
||||
}
|
||||
|
||||
.dark .statsbtn,
|
||||
.dark .savebtn,
|
||||
.dark .menubtn,
|
||||
.dark .iobtn,
|
||||
.dark .visbtn {
|
||||
filter: invert(1);
|
||||
}
|
||||
|
||||
.flexbox {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.savebtn {
|
||||
background-color: #d6d6d6;
|
||||
width: auto;
|
||||
height: 30px;
|
||||
flex-grow: 1;
|
||||
margin: 5px;
|
||||
border-radius: 4px;
|
||||
}
|
||||
|
||||
.savebtn:active {
|
||||
background-color: #0a0;
|
||||
color: white;
|
||||
}
|
||||
|
||||
.dark .savebtn:active {
|
||||
/* This will be inverted */
|
||||
background-color: #b3b;
|
||||
}
|
||||
|
||||
.stats {
|
||||
border-collapse: collapse;
|
||||
font-size: 12pt;
|
||||
table-layout: fixed;
|
||||
width: 100%;
|
||||
min-width: 450px;
|
||||
}
|
||||
|
||||
.dark .stats td {
|
||||
border: 1px solid #bbb;
|
||||
}
|
||||
|
||||
.stats td {
|
||||
border: 1px solid black;
|
||||
padding: 5px;
|
||||
word-wrap: break-word;
|
||||
text-align: center;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
#checkbox-stats div {
|
||||
position: absolute;
|
||||
left: 0;
|
||||
top: 0;
|
||||
height: 100%;
|
||||
width: 100%;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
#checkbox-stats .bar {
|
||||
background-color: rgba(28, 251, 0, 0.6);
|
||||
}
|
||||
|
||||
.menu {
|
||||
position: relative;
|
||||
display: inline-block;
|
||||
margin: 10px 10px 10px 0px;
|
||||
}
|
||||
|
||||
.menu-content {
|
||||
font-size: 12pt !important;
|
||||
text-align: left !important;
|
||||
font-weight: normal !important;
|
||||
display: none;
|
||||
position: absolute;
|
||||
background-color: white;
|
||||
right: 0;
|
||||
min-width: 300px;
|
||||
box-shadow: 0px 8px 16px 0px rgba(0, 0, 0, 0.2);
|
||||
z-index: 100;
|
||||
padding: 8px;
|
||||
}
|
||||
|
||||
.dark .menu-content {
|
||||
background-color: #111;
|
||||
}
|
||||
|
||||
.menu:hover .menu-content {
|
||||
display: block;
|
||||
}
|
||||
|
||||
.menu:hover .menubtn,
|
||||
.menu:hover .iobtn,
|
||||
.menu:hover .statsbtn {
|
||||
background-color: #eee;
|
||||
}
|
||||
|
||||
.menu-label {
|
||||
display: inline-block;
|
||||
padding: 8px;
|
||||
border: 1px solid #ccc;
|
||||
border-top: 0;
|
||||
width: calc(100% - 18px);
|
||||
}
|
||||
|
||||
.menu-label-top {
|
||||
border-top: 1px solid #ccc;
|
||||
}
|
||||
|
||||
.menu-textbox {
|
||||
float: left;
|
||||
height: 24px;
|
||||
margin: 10px 5px;
|
||||
padding: 5px 5px;
|
||||
font-family: Consolas, "DejaVu Sans Mono", Monaco, monospace;
|
||||
font-size: 14px;
|
||||
box-sizing: border-box;
|
||||
border: 1px solid #888;
|
||||
border-radius: 4px;
|
||||
outline: none;
|
||||
background-color: #eee;
|
||||
transition: background-color 0.2s, border 0.2s;
|
||||
width: calc(100% - 10px);
|
||||
}
|
||||
|
||||
.menu-textbox.invalid,
|
||||
.dark .menu-textbox.invalid {
|
||||
color: red;
|
||||
}
|
||||
|
||||
.dark .menu-textbox {
|
||||
background-color: #222;
|
||||
color: #eee;
|
||||
}
|
||||
|
||||
.radio-container {
|
||||
margin: 4px;
|
||||
}
|
||||
|
||||
.topmostdiv {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
background-color: white;
|
||||
transition: background-color 0.3s;
|
||||
}
|
||||
|
||||
#top {
|
||||
height: 78px;
|
||||
border-bottom: 2px solid black;
|
||||
}
|
||||
|
||||
.dark #top {
|
||||
border-bottom: 2px solid #ccc;
|
||||
}
|
||||
|
||||
#dbg {
|
||||
display: block;
|
||||
}
|
||||
|
||||
::-webkit-scrollbar {
|
||||
width: 8px;
|
||||
}
|
||||
|
||||
::-webkit-scrollbar-track {
|
||||
background: #aaa;
|
||||
}
|
||||
|
||||
::-webkit-scrollbar-thumb {
|
||||
background: #666;
|
||||
border-radius: 3px;
|
||||
}
|
||||
|
||||
::-webkit-scrollbar-thumb:hover {
|
||||
background: #555;
|
||||
}
|
||||
|
||||
.slider {
|
||||
-webkit-appearance: none;
|
||||
width: 100%;
|
||||
margin: 3px 0;
|
||||
padding: 0;
|
||||
outline: none;
|
||||
opacity: 0.7;
|
||||
-webkit-transition: .2s;
|
||||
transition: opacity .2s;
|
||||
border-radius: 3px;
|
||||
}
|
||||
|
||||
.slider:hover {
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
.slider:focus {
|
||||
outline: none;
|
||||
}
|
||||
|
||||
.slider::-webkit-slider-runnable-track {
|
||||
-webkit-appearance: none;
|
||||
width: 100%;
|
||||
height: 8px;
|
||||
background: #d3d3d3;
|
||||
border-radius: 3px;
|
||||
border: none;
|
||||
}
|
||||
|
||||
.slider::-webkit-slider-thumb {
|
||||
-webkit-appearance: none;
|
||||
width: 15px;
|
||||
height: 15px;
|
||||
border-radius: 50%;
|
||||
background: #0a0;
|
||||
cursor: pointer;
|
||||
margin-top: -4px;
|
||||
}
|
||||
|
||||
.dark .slider::-webkit-slider-thumb {
|
||||
background: #3d3;
|
||||
}
|
||||
|
||||
.slider::-moz-range-thumb {
|
||||
width: 15px;
|
||||
height: 15px;
|
||||
border-radius: 50%;
|
||||
background: #0a0;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.slider::-moz-range-track {
|
||||
height: 8px;
|
||||
background: #d3d3d3;
|
||||
border-radius: 3px;
|
||||
}
|
||||
|
||||
.dark .slider::-moz-range-thumb {
|
||||
background: #3d3;
|
||||
}
|
||||
|
||||
.slider::-ms-track {
|
||||
width: 100%;
|
||||
height: 8px;
|
||||
border-width: 3px 0;
|
||||
background: transparent;
|
||||
border-color: transparent;
|
||||
color: transparent;
|
||||
transition: opacity .2s;
|
||||
}
|
||||
|
||||
.slider::-ms-fill-lower {
|
||||
background: #d3d3d3;
|
||||
border: none;
|
||||
border-radius: 3px;
|
||||
}
|
||||
|
||||
.slider::-ms-fill-upper {
|
||||
background: #d3d3d3;
|
||||
border: none;
|
||||
border-radius: 3px;
|
||||
}
|
||||
|
||||
.slider::-ms-thumb {
|
||||
width: 15px;
|
||||
height: 15px;
|
||||
border-radius: 50%;
|
||||
background: #0a0;
|
||||
cursor: pointer;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.shameless-plug {
|
||||
font-size: 0.8em;
|
||||
text-align: center;
|
||||
display: block;
|
||||
}
|
||||
|
||||
a {
|
||||
color: #0278a4;
|
||||
}
|
||||
|
||||
.dark a {
|
||||
color: #00b9fd;
|
||||
}
|
||||
|
||||
#frontcanvas,
|
||||
#backcanvas {
|
||||
touch-action: none;
|
||||
}
|
||||
|
||||
.placeholder {
|
||||
border: 1px dashed #9f9fda !important;
|
||||
background-color: #edf2f7 !important;
|
||||
}
|
||||
|
||||
.dragging {
|
||||
z-index: 999;
|
||||
}
|
||||
|
||||
.dark .dragging>table>tbody>tr {
|
||||
background-color: #252c30;
|
||||
}
|
||||
|
||||
.dark .placeholder {
|
||||
filter: invert(1);
|
||||
}
|
||||
|
||||
.column-spacer {
|
||||
top: 0;
|
||||
left: 0;
|
||||
width: calc(100% - 4px);
|
||||
position: absolute;
|
||||
cursor: pointer;
|
||||
user-select: none;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
.column-width-handle {
|
||||
top: 0;
|
||||
right: 0;
|
||||
width: 4px;
|
||||
position: absolute;
|
||||
cursor: col-resize;
|
||||
user-select: none;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
.column-width-handle:hover {
|
||||
background-color: #4f99bd;
|
||||
}
|
||||
|
||||
.help-link {
|
||||
border: 1px solid #0278a4;
|
||||
padding-inline: 0.3rem;
|
||||
border-radius: 3px;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.dark .help-link {
|
||||
border: 1px solid #00b9fd;
|
||||
}
|
|
@ -0,0 +1,319 @@
|
|||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>Interactive BOM for KiCAD</title>
|
||||
<style type="text/css">
|
||||
///CSS///
|
||||
///USERCSS///
|
||||
</style>
|
||||
<script type="text/javascript" >
|
||||
///////////////////////////////////////////////
|
||||
///SPLITJS///
|
||||
///////////////////////////////////////////////
|
||||
|
||||
///////////////////////////////////////////////
|
||||
///LZ-STRING///
|
||||
///////////////////////////////////////////////
|
||||
|
||||
///////////////////////////////////////////////
|
||||
///POINTER_EVENTS_POLYFILL///
|
||||
///////////////////////////////////////////////
|
||||
|
||||
///////////////////////////////////////////////
|
||||
///CONFIG///
|
||||
///////////////////////////////////////////////
|
||||
|
||||
///////////////////////////////////////////////
|
||||
///PCBDATA///
|
||||
///////////////////////////////////////////////
|
||||
|
||||
///////////////////////////////////////////////
|
||||
///UTILJS///
|
||||
///////////////////////////////////////////////
|
||||
|
||||
///////////////////////////////////////////////
|
||||
///RENDERJS///
|
||||
///////////////////////////////////////////////
|
||||
|
||||
///////////////////////////////////////////////
|
||||
///TABLEUTILJS///
|
||||
///////////////////////////////////////////////
|
||||
|
||||
///////////////////////////////////////////////
|
||||
///IBOMJS///
|
||||
///////////////////////////////////////////////
|
||||
|
||||
///////////////////////////////////////////////
|
||||
///USERJS///
|
||||
///////////////////////////////////////////////
|
||||
</script>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
///USERHEADER///
|
||||
<div id="topmostdiv" class="topmostdiv">
|
||||
<div id="top">
|
||||
<div style="float: right; height: 100%;">
|
||||
<div class="hideonprint menu" style="float: right; top: 8px;">
|
||||
<button class="menubtn"></button>
|
||||
<div class="menu-content">
|
||||
<label class="menu-label menu-label-top" style="width: calc(50% - 18px)">
|
||||
<input id="darkmodeCheckbox" type="checkbox" onchange="setDarkMode(this.checked)">
|
||||
Dark mode
|
||||
</label><!-- This comment eats space! All of it!
|
||||
--><label class="menu-label menu-label-top" style="width: calc(50% - 17px); border-left: 0;">
|
||||
<input id="fullscreenCheckbox" type="checkbox" onchange="setFullscreen(this.checked)">
|
||||
Full Screen
|
||||
</label>
|
||||
<label class="menu-label" style="width: calc(50% - 18px)">
|
||||
<input id="fabricationCheckbox" type="checkbox" checked onchange="fabricationVisible(this.checked)">
|
||||
Fab layer
|
||||
</label><!-- This comment eats space! All of it!
|
||||
--><label class="menu-label" style="width: calc(50% - 17px); border-left: 0;">
|
||||
<input id="silkscreenCheckbox" type="checkbox" checked onchange="silkscreenVisible(this.checked)">
|
||||
Silkscreen
|
||||
</label>
|
||||
<label class="menu-label" style="width: calc(50% - 18px)">
|
||||
<input id="referencesCheckbox" type="checkbox" checked onchange="referencesVisible(this.checked)">
|
||||
References
|
||||
</label><!-- This comment eats space! All of it!
|
||||
--><label class="menu-label" style="width: calc(50% - 17px); border-left: 0;">
|
||||
<input id="valuesCheckbox" type="checkbox" checked onchange="valuesVisible(this.checked)">
|
||||
Values
|
||||
</label>
|
||||
<div id="tracksAndZonesCheckboxes">
|
||||
<label class="menu-label" style="width: calc(50% - 18px)">
|
||||
<input id="tracksCheckbox" type="checkbox" checked onchange="tracksVisible(this.checked)">
|
||||
Tracks
|
||||
</label><!-- This comment eats space! All of it!
|
||||
--><label class="menu-label" style="width: calc(50% - 17px); border-left: 0;">
|
||||
<input id="zonesCheckbox" type="checkbox" checked onchange="zonesVisible(this.checked)">
|
||||
Zones
|
||||
</label>
|
||||
</div>
|
||||
<label class="menu-label" style="width: calc(50% - 18px)">
|
||||
<input id="padsCheckbox" type="checkbox" checked onchange="padsVisible(this.checked)">
|
||||
Pads
|
||||
</label><!-- This comment eats space! All of it!
|
||||
--><label class="menu-label" style="width: calc(50% - 17px); border-left: 0;">
|
||||
<input id="dnpOutlineCheckbox" type="checkbox" checked onchange="dnpOutline(this.checked)">
|
||||
DNP outlined
|
||||
</label>
|
||||
<label class="menu-label">
|
||||
<input id="highlightpin1Checkbox" type="checkbox" onchange="setHighlightPin1(this.checked)">
|
||||
Highlight first pin
|
||||
</label>
|
||||
<label class="menu-label">
|
||||
<input id="dragCheckbox" type="checkbox" checked onchange="setRedrawOnDrag(this.checked)">
|
||||
Continuous redraw on drag
|
||||
</label>
|
||||
<label class="menu-label">
|
||||
<span>Board rotation</span>
|
||||
<span style="float: right"><span id="rotationDegree">0</span>°</span>
|
||||
<input id="boardRotation" type="range" min="-36" max="36" value="0" class="slider" oninput="setBoardRotation(this.value)">
|
||||
</label>
|
||||
<label class="menu-label">
|
||||
<input id="offsetBackRotationCheckbox" type="checkbox" onchange="setOffsetBackRotation(this.checked)">
|
||||
Offset back rotation
|
||||
</label>
|
||||
<label class="menu-label">
|
||||
<div style="margin-left: 5px">Bom checkboxes</div>
|
||||
<input id="bomCheckboxes" class="menu-textbox" type=text
|
||||
oninput="setBomCheckboxes(this.value)">
|
||||
</label>
|
||||
<label class="menu-label">
|
||||
<div style="margin-left: 5px">Mark when checked</div>
|
||||
<div id="markWhenCheckedContainer"></div>
|
||||
</label>
|
||||
<label class="menu-label">
|
||||
<span class="shameless-plug">
|
||||
<span>Created using</span>
|
||||
<a id="github-link" target="blank" href="https://github.com/openscopeproject/InteractiveHtmlBom">InteractiveHtmlBom</a>
|
||||
<a target="blank" title="Mouse and keyboard help" href="https://github.com/openscopeproject/InteractiveHtmlBom/wiki/Usage#bom-page-mouse-actions" style="text-decoration: none;"><label class="help-link">?</label></a>
|
||||
</span>
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
<div class="button-container hideonprint"
|
||||
style="float: right; position: relative; top: 8px">
|
||||
<button id="fl-btn" class="left-most-button" onclick="changeCanvasLayout('F')"
|
||||
title="Front only">F
|
||||
</button>
|
||||
<button id="fb-btn" class="middle-button" onclick="changeCanvasLayout('FB')"
|
||||
title="Front and Back">FB
|
||||
</button>
|
||||
<button id="bl-btn" class="right-most-button" onclick="changeCanvasLayout('B')"
|
||||
title="Back only">B
|
||||
</button>
|
||||
</div>
|
||||
<div class="button-container hideonprint"
|
||||
style="float: right; position: relative; top: 8px">
|
||||
<button id="bom-btn" class="left-most-button" onclick="changeBomLayout('bom-only')"
|
||||
title="BOM only"></button>
|
||||
<button id="lr-btn" class="middle-button" onclick="changeBomLayout('left-right')"
|
||||
title="BOM left, drawings right"></button>
|
||||
<button id="tb-btn" class="right-most-button" onclick="changeBomLayout('top-bottom')"
|
||||
title="BOM top, drawings bot"></button>
|
||||
</div>
|
||||
<div class="button-container hideonprint"
|
||||
style="float: right; position: relative; top: 8px">
|
||||
<button id="bom-grouped-btn" class="left-most-button" onclick="changeBomMode('grouped')"
|
||||
title="Grouped BOM"></button>
|
||||
<button id="bom-ungrouped-btn" class="middle-button" onclick="changeBomMode('ungrouped')"
|
||||
title="Ungrouped BOM"></button>
|
||||
<button id="bom-netlist-btn" class="right-most-button" onclick="changeBomMode('netlist')"
|
||||
title="Netlist"></button>
|
||||
</div>
|
||||
<div class="hideonprint menu" style="float: right; top: 8px;">
|
||||
<button class="statsbtn"></button>
|
||||
<div class="menu-content">
|
||||
<table class="stats">
|
||||
<tbody>
|
||||
<tr>
|
||||
<td width="40%">Board stats</td>
|
||||
<td>Front</td>
|
||||
<td>Back</td>
|
||||
<td>Total</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Components</td>
|
||||
<td id="stats-components-front">~</td>
|
||||
<td id="stats-components-back">~</td>
|
||||
<td id="stats-components-total">~</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Groups</td>
|
||||
<td id="stats-groups-front">~</td>
|
||||
<td id="stats-groups-back">~</td>
|
||||
<td id="stats-groups-total">~</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>SMD pads</td>
|
||||
<td id="stats-smd-pads-front">~</td>
|
||||
<td id="stats-smd-pads-back">~</td>
|
||||
<td id="stats-smd-pads-total">~</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>TH pads</td>
|
||||
<td colspan=3 id="stats-th-pads">~</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
<table class="stats">
|
||||
<col width="40%"/><col />
|
||||
<tbody id="checkbox-stats">
|
||||
<tr>
|
||||
<td colspan=2 style="border-top: 0">Checkboxes</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
<div class="hideonprint menu" style="float: right; top: 8px;">
|
||||
<button class="iobtn"></button>
|
||||
<div class="menu-content">
|
||||
<div class="menu-label menu-label-top">
|
||||
<div style="margin-left: 5px;">Save board image</div>
|
||||
<div class="flexbox">
|
||||
<input id="render-save-width" class="menu-textbox" type="text" value="1000" placeholder="Width"
|
||||
style="flex-grow: 1; width: 50px;" oninput="validateSaveImgDimension(this)">
|
||||
<span>X</span>
|
||||
<input id="render-save-height" class="menu-textbox" type="text" value="1000" placeholder="Height"
|
||||
style="flex-grow: 1; width: 50px;" oninput="validateSaveImgDimension(this)">
|
||||
</div>
|
||||
<label>
|
||||
<input id="render-save-transparent" type="checkbox">
|
||||
Transparent background
|
||||
</label>
|
||||
<div class="flexbox">
|
||||
<button class="savebtn" onclick="saveImage('F')">Front</button>
|
||||
<button class="savebtn" onclick="saveImage('B')">Back</button>
|
||||
</div>
|
||||
</div>
|
||||
<div class="menu-label">
|
||||
<span style="margin-left: 5px;">Config and checkbox state</span>
|
||||
<div class="flexbox">
|
||||
<button class="savebtn" onclick="saveSettings()">Export</button>
|
||||
<button class="savebtn" onclick="loadSettings()">Import</button>
|
||||
</div>
|
||||
</div>
|
||||
<div class="menu-label">
|
||||
<span style="margin-left: 5px;">Save bom table as</span>
|
||||
<div class="flexbox">
|
||||
<button class="savebtn" onclick="saveBomTable('csv')">csv</button>
|
||||
<button class="savebtn" onclick="saveBomTable('txt')">txt</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div id="fileinfodiv" style="overflow: auto;">
|
||||
<table class="fileinfo">
|
||||
<tbody>
|
||||
<tr>
|
||||
<td id="title" class="title" style="width: 70%">
|
||||
Title
|
||||
</td>
|
||||
<td id="revision" class="title" style="width: 30%">
|
||||
Revision
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td id="company">
|
||||
Company
|
||||
</td>
|
||||
<td id="filedate">
|
||||
Date
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
<div id="bot" class="split" style="height: calc(100% - 80px)">
|
||||
<div id="bomdiv" class="split split-horizontal">
|
||||
<div style="width: 100%">
|
||||
<input id="reflookup" class="textbox searchbox reflookup hideonprint" type="text" placeholder="Ref lookup"
|
||||
oninput="updateRefLookup(this.value)">
|
||||
<input id="filter" class="textbox searchbox filter hideonprint" type="text" placeholder="Filter"
|
||||
oninput="updateFilter(this.value)">
|
||||
<div class="button-container hideonprint" style="float: left; margin: 0;">
|
||||
<button id="copy" title="Copy bom table to clipboard"
|
||||
onclick="saveBomTable('clipboard')"></button>
|
||||
</div>
|
||||
</div>
|
||||
<div id="dbg"></div>
|
||||
<table class="bom" id="bomtable">
|
||||
<thead id="bomhead">
|
||||
</thead>
|
||||
<tbody id="bombody">
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
<div id="canvasdiv" class="split split-horizontal">
|
||||
<div id="frontcanvas" class="split" touch-action="none" style="overflow: hidden">
|
||||
<div style="position: relative; width: 100%; height: 100%;">
|
||||
<canvas id="F_bg" style="position: absolute; left: 0; top: 0; z-index: 0;"></canvas>
|
||||
<canvas id="F_fab" style="position: absolute; left: 0; top: 0; z-index: 1;"></canvas>
|
||||
<canvas id="F_slk" style="position: absolute; left: 0; top: 0; z-index: 2;"></canvas>
|
||||
<canvas id="F_hl" style="position: absolute; left: 0; top: 0; z-index: 3;"></canvas>
|
||||
</div>
|
||||
</div>
|
||||
<div id="backcanvas" class="split" touch-action="none" style="overflow: hidden">
|
||||
<div style="position: relative; width: 100%; height: 100%;">
|
||||
<canvas id="B_bg" style="position: absolute; left: 0; top: 0; z-index: 0;"></canvas>
|
||||
<canvas id="B_fab" style="position: absolute; left: 0; top: 0; z-index: 1;"></canvas>
|
||||
<canvas id="B_slk" style="position: absolute; left: 0; top: 0; z-index: 2;"></canvas>
|
||||
<canvas id="B_hl" style="position: absolute; left: 0; top: 0; z-index: 3;"></canvas>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
///USERFOOTER///
|
||||
</body>
|
||||
|
||||
</html>
|
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,10 @@
|
|||
// Copyright (c) 2013 Pieroxy <pieroxy@pieroxy.net>
|
||||
// This work is free. You can redistribute it and/or modify it
|
||||
// under the terms of the WTFPL, Version 2
|
||||
// For more information see LICENSE.txt or http://www.wtfpl.net/
|
||||
//
|
||||
// For more information, the home page:
|
||||
// http://pieroxy.net/blog/pages/lz-string/testing.html
|
||||
//
|
||||
// LZ-based compression algorithm, version 1.4.4
|
||||
var LZString=function(){var o=String.fromCharCode,i={};var n={decompressFromBase64:function(o){return null==o?"":""==o?null:n._decompress(o.length,32,function(n){return function(o,n){if(!i[o]){i[o]={};for(var t=0;t<o.length;t++)i[o][o.charAt(t)]=t}return i[o][n]}("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=",o.charAt(n))})},_decompress:function(i,n,t){var r,e,a,s,p,u,l,f=[],c=4,d=4,h=3,v="",g=[],m={val:t(0),position:n,index:1};for(r=0;r<3;r+=1)f[r]=r;for(a=0,p=Math.pow(2,2),u=1;u!=p;)s=m.val&m.position,m.position>>=1,0==m.position&&(m.position=n,m.val=t(m.index++)),a|=(s>0?1:0)*u,u<<=1;switch(a){case 0:for(a=0,p=Math.pow(2,8),u=1;u!=p;)s=m.val&m.position,m.position>>=1,0==m.position&&(m.position=n,m.val=t(m.index++)),a|=(s>0?1:0)*u,u<<=1;l=o(a);break;case 1:for(a=0,p=Math.pow(2,16),u=1;u!=p;)s=m.val&m.position,m.position>>=1,0==m.position&&(m.position=n,m.val=t(m.index++)),a|=(s>0?1:0)*u,u<<=1;l=o(a);break;case 2:return""}for(f[3]=l,e=l,g.push(l);;){if(m.index>i)return"";for(a=0,p=Math.pow(2,h),u=1;u!=p;)s=m.val&m.position,m.position>>=1,0==m.position&&(m.position=n,m.val=t(m.index++)),a|=(s>0?1:0)*u,u<<=1;switch(l=a){case 0:for(a=0,p=Math.pow(2,8),u=1;u!=p;)s=m.val&m.position,m.position>>=1,0==m.position&&(m.position=n,m.val=t(m.index++)),a|=(s>0?1:0)*u,u<<=1;f[d++]=o(a),l=d-1,c--;break;case 1:for(a=0,p=Math.pow(2,16),u=1;u!=p;)s=m.val&m.position,m.position>>=1,0==m.position&&(m.position=n,m.val=t(m.index++)),a|=(s>0?1:0)*u,u<<=1;f[d++]=o(a),l=d-1,c--;break;case 2:return g.join("")}if(0==c&&(c=Math.pow(2,h),h++),f[l])v=f[l];else{if(l!==d)return null;v=e+e.charAt(0)}g.push(v),f[d++]=e+v.charAt(0),e=v,0==--c&&(c=Math.pow(2,h),h++)}}};return n}();"function"==typeof define&&define.amd?define(function(){return LZString}):"undefined"!=typeof module&&null!=module?module.exports=LZString:"undefined"!=typeof angular&&null!=angular&&angular.module("LZString",[]).factory("LZString",function(){return LZString});
|
|
@ -0,0 +1,43 @@
|
|||
/*!
|
||||
* PEP v0.4.3 | https://github.com/jquery/PEP
|
||||
* Copyright jQuery Foundation and other contributors | http://jquery.org/license
|
||||
*/
|
||||
!function(a,b){"object"==typeof exports&&"undefined"!=typeof module?module.exports=b():"function"==typeof define&&define.amd?define(b):a.PointerEventsPolyfill=b()}(this,function(){"use strict";function a(a,b){b=b||Object.create(null);var c=document.createEvent("Event");c.initEvent(a,b.bubbles||!1,b.cancelable||!1);
|
||||
for(var d,e=2;e<m.length;e++)d=m[e],c[d]=b[d]||n[e];c.buttons=b.buttons||0;
|
||||
var f=0;return f=b.pressure&&c.buttons?b.pressure:c.buttons?.5:0,c.x=c.clientX,c.y=c.clientY,c.pointerId=b.pointerId||0,c.width=b.width||0,c.height=b.height||0,c.pressure=f,c.tiltX=b.tiltX||0,c.tiltY=b.tiltY||0,c.twist=b.twist||0,c.tangentialPressure=b.tangentialPressure||0,c.pointerType=b.pointerType||"",c.hwTimestamp=b.hwTimestamp||0,c.isPrimary=b.isPrimary||!1,c}function b(){this.array=[],this.size=0}function c(a,b,c,d){this.addCallback=a.bind(d),this.removeCallback=b.bind(d),this.changedCallback=c.bind(d),A&&(this.observer=new A(this.mutationWatcher.bind(this)))}function d(a){return"body /shadow-deep/ "+e(a)}function e(a){return'[touch-action="'+a+'"]'}function f(a){return"{ -ms-touch-action: "+a+"; touch-action: "+a+"; }"}function g(){if(F){D.forEach(function(a){String(a)===a?(E+=e(a)+f(a)+"\n",G&&(E+=d(a)+f(a)+"\n")):(E+=a.selectors.map(e)+f(a.rule)+"\n",G&&(E+=a.selectors.map(d)+f(a.rule)+"\n"))});var a=document.createElement("style");a.textContent=E,document.head.appendChild(a)}}function h(){if(!window.PointerEvent){if(window.PointerEvent=a,window.navigator.msPointerEnabled){var b=window.navigator.msMaxTouchPoints;Object.defineProperty(window.navigator,"maxTouchPoints",{value:b,enumerable:!0}),u.registerSource("ms",_)}else Object.defineProperty(window.navigator,"maxTouchPoints",{value:0,enumerable:!0}),u.registerSource("mouse",N),void 0!==window.ontouchstart&&u.registerSource("touch",V);u.register(document)}}function i(a){if(!u.pointermap.has(a)){var b=new Error("InvalidPointerId");throw b.name="InvalidPointerId",b}}function j(a){for(var b=a.parentNode;b&&b!==a.ownerDocument;)b=b.parentNode;if(!b){var c=new Error("InvalidStateError");throw c.name="InvalidStateError",c}}function k(a){var b=u.pointermap.get(a);return 0!==b.buttons}function l(){window.Element&&!Element.prototype.setPointerCapture&&Object.defineProperties(Element.prototype,{setPointerCapture:{value:W},releasePointerCapture:{value:X},hasPointerCapture:{value:Y}})}
|
||||
var m=["bubbles","cancelable","view","detail","screenX","screenY","clientX","clientY","ctrlKey","altKey","shiftKey","metaKey","button","relatedTarget","pageX","pageY"],n=[!1,!1,null,null,0,0,0,0,!1,!1,!1,!1,0,null,0,0],o=window.Map&&window.Map.prototype.forEach,p=o?Map:b;b.prototype={set:function(a,b){return void 0===b?this["delete"](a):(this.has(a)||this.size++,void(this.array[a]=b))},has:function(a){return void 0!==this.array[a]},"delete":function(a){this.has(a)&&(delete this.array[a],this.size--)},get:function(a){return this.array[a]},clear:function(){this.array.length=0,this.size=0},forEach:function(a,b){return this.array.forEach(function(c,d){a.call(b,c,d,this)},this)}};var q=["bubbles","cancelable","view","detail","screenX","screenY","clientX","clientY","ctrlKey","altKey","shiftKey","metaKey","button","relatedTarget","buttons","pointerId","width","height","pressure","tiltX","tiltY","pointerType","hwTimestamp","isPrimary","type","target","currentTarget","which","pageX","pageY","timeStamp"],r=[!1,!1,null,null,0,0,0,0,!1,!1,!1,!1,0,null,0,0,0,0,0,0,0,"",0,!1,"",null,null,0,0,0,0],s={pointerover:1,pointerout:1,pointerenter:1,pointerleave:1},t="undefined"!=typeof SVGElementInstance,u={pointermap:new p,eventMap:Object.create(null),captureInfo:Object.create(null),eventSources:Object.create(null),eventSourceList:[],registerSource:function(a,b){var c=b,d=c.events;d&&(d.forEach(function(a){c[a]&&(this.eventMap[a]=c[a].bind(c))},this),this.eventSources[a]=c,this.eventSourceList.push(c))},register:function(a){for(var b,c=this.eventSourceList.length,d=0;d<c&&(b=this.eventSourceList[d]);d++)
|
||||
b.register.call(b,a)},unregister:function(a){for(var b,c=this.eventSourceList.length,d=0;d<c&&(b=this.eventSourceList[d]);d++)
|
||||
b.unregister.call(b,a)},contains:function(a,b){try{return a.contains(b)}catch(c){return!1}},down:function(a){a.bubbles=!0,this.fireEvent("pointerdown",a)},move:function(a){a.bubbles=!0,this.fireEvent("pointermove",a)},up:function(a){a.bubbles=!0,this.fireEvent("pointerup",a)},enter:function(a){a.bubbles=!1,this.fireEvent("pointerenter",a)},leave:function(a){a.bubbles=!1,this.fireEvent("pointerleave",a)},over:function(a){a.bubbles=!0,this.fireEvent("pointerover",a)},out:function(a){a.bubbles=!0,this.fireEvent("pointerout",a)},cancel:function(a){a.bubbles=!0,this.fireEvent("pointercancel",a)},leaveOut:function(a){this.out(a),this.propagate(a,this.leave,!1)},enterOver:function(a){this.over(a),this.propagate(a,this.enter,!0)},eventHandler:function(a){if(!a._handledByPE){var b=a.type,c=this.eventMap&&this.eventMap[b];c&&c(a),a._handledByPE=!0}},listen:function(a,b){b.forEach(function(b){this.addEvent(a,b)},this)},unlisten:function(a,b){b.forEach(function(b){this.removeEvent(a,b)},this)},addEvent:function(a,b){a.addEventListener(b,this.boundHandler)},removeEvent:function(a,b){a.removeEventListener(b,this.boundHandler)},makeEvent:function(b,c){this.captureInfo[c.pointerId]&&(c.relatedTarget=null);var d=new a(b,c);return c.preventDefault&&(d.preventDefault=c.preventDefault),d._target=d._target||c.target,d},fireEvent:function(a,b){var c=this.makeEvent(a,b);return this.dispatchEvent(c)},cloneEvent:function(a){for(var b,c=Object.create(null),d=0;d<q.length;d++)b=q[d],c[b]=a[b]||r[d],!t||"target"!==b&&"relatedTarget"!==b||c[b]instanceof SVGElementInstance&&(c[b]=c[b].correspondingUseElement);return a.preventDefault&&(c.preventDefault=function(){a.preventDefault()}),c},getTarget:function(a){var b=this.captureInfo[a.pointerId];return b?a._target!==b&&a.type in s?void 0:b:a._target},propagate:function(a,b,c){for(var d=a.target,e=[];d!==document&&!d.contains(a.relatedTarget);) if(e.push(d),d=d.parentNode,!d)return;c&&e.reverse(),e.forEach(function(c){a.target=c,b.call(this,a)},this)},setCapture:function(b,c,d){this.captureInfo[b]&&this.releaseCapture(b,d),this.captureInfo[b]=c,this.implicitRelease=this.releaseCapture.bind(this,b,d),document.addEventListener("pointerup",this.implicitRelease),document.addEventListener("pointercancel",this.implicitRelease);var e=new a("gotpointercapture");e.pointerId=b,e._target=c,d||this.asyncDispatchEvent(e)},releaseCapture:function(b,c){var d=this.captureInfo[b];if(d){this.captureInfo[b]=void 0,document.removeEventListener("pointerup",this.implicitRelease),document.removeEventListener("pointercancel",this.implicitRelease);var e=new a("lostpointercapture");e.pointerId=b,e._target=d,c||this.asyncDispatchEvent(e)}},dispatchEvent:/*scope.external.dispatchEvent || */function(a){var b=this.getTarget(a);if(b)return b.dispatchEvent(a)},asyncDispatchEvent:function(a){requestAnimationFrame(this.dispatchEvent.bind(this,a))}};u.boundHandler=u.eventHandler.bind(u);var v={shadow:function(a){if(a)return a.shadowRoot||a.webkitShadowRoot},canTarget:function(a){return a&&Boolean(a.elementFromPoint)},targetingShadow:function(a){var b=this.shadow(a);if(this.canTarget(b))return b},olderShadow:function(a){var b=a.olderShadowRoot;if(!b){var c=a.querySelector("shadow");c&&(b=c.olderShadowRoot)}return b},allShadows:function(a){for(var b=[],c=this.shadow(a);c;)b.push(c),c=this.olderShadow(c);return b},searchRoot:function(a,b,c){if(a){var d,e,f=a.elementFromPoint(b,c);for(e=this.targetingShadow(f);e;){if(d=e.elementFromPoint(b,c)){var g=this.targetingShadow(d);return this.searchRoot(g,b,c)||d} e=this.olderShadow(e)} return f}},owner:function(a){
|
||||
for(var b=a;b.parentNode;)b=b.parentNode;
|
||||
return b.nodeType!==Node.DOCUMENT_NODE&&b.nodeType!==Node.DOCUMENT_FRAGMENT_NODE&&(b=document),b},findTarget:function(a){var b=a.clientX,c=a.clientY,d=this.owner(a.target);
|
||||
return d.elementFromPoint(b,c)||(d=document),this.searchRoot(d,b,c)}},w=Array.prototype.forEach.call.bind(Array.prototype.forEach),x=Array.prototype.map.call.bind(Array.prototype.map),y=Array.prototype.slice.call.bind(Array.prototype.slice),z=Array.prototype.filter.call.bind(Array.prototype.filter),A=window.MutationObserver||window.WebKitMutationObserver,B="[touch-action]",C={subtree:!0,childList:!0,attributes:!0,attributeOldValue:!0,attributeFilter:["touch-action"]};c.prototype={watchSubtree:function(a){
|
||||
//
|
||||
this.observer&&v.canTarget(a)&&this.observer.observe(a,C)},enableOnSubtree:function(a){this.watchSubtree(a),a===document&&"complete"!==document.readyState?this.installOnLoad():this.installNewSubtree(a)},installNewSubtree:function(a){w(this.findElements(a),this.addElement,this)},findElements:function(a){return a.querySelectorAll?a.querySelectorAll(B):[]},removeElement:function(a){this.removeCallback(a)},addElement:function(a){this.addCallback(a)},elementChanged:function(a,b){this.changedCallback(a,b)},concatLists:function(a,b){return a.concat(y(b))},
|
||||
installOnLoad:function(){document.addEventListener("readystatechange",function(){"complete"===document.readyState&&this.installNewSubtree(document)}.bind(this))},isElement:function(a){return a.nodeType===Node.ELEMENT_NODE},flattenMutationTree:function(a){
|
||||
var b=x(a,this.findElements,this);
|
||||
return b.push(z(a,this.isElement)),b.reduce(this.concatLists,[])},mutationWatcher:function(a){a.forEach(this.mutationHandler,this)},mutationHandler:function(a){if("childList"===a.type){var b=this.flattenMutationTree(a.addedNodes);b.forEach(this.addElement,this);var c=this.flattenMutationTree(a.removedNodes);c.forEach(this.removeElement,this)}else"attributes"===a.type&&this.elementChanged(a.target,a.oldValue)}};var D=["none","auto","pan-x","pan-y",{rule:"pan-x pan-y",selectors:["pan-x pan-y","pan-y pan-x"]}],E="",F=window.PointerEvent||window.MSPointerEvent,G=!window.ShadowDOMPolyfill&&document.head.createShadowRoot,H=u.pointermap,I=25,J=[1,4,2,8,16],K=!1;try{K=1===new MouseEvent("test",{buttons:1}).buttons}catch(L){}
|
||||
var M,N={POINTER_ID:1,POINTER_TYPE:"mouse",events:["mousedown","mousemove","mouseup","mouseover","mouseout"],register:function(a){u.listen(a,this.events)},unregister:function(a){u.unlisten(a,this.events)},lastTouches:[],
|
||||
isEventSimulatedFromTouch:function(a){for(var b,c=this.lastTouches,d=a.clientX,e=a.clientY,f=0,g=c.length;f<g&&(b=c[f]);f++){
|
||||
var h=Math.abs(d-b.x),i=Math.abs(e-b.y);if(h<=I&&i<=I)return!0}},prepareEvent:function(a){var b=u.cloneEvent(a),c=b.preventDefault;return b.preventDefault=function(){a.preventDefault(),c()},b.pointerId=this.POINTER_ID,b.isPrimary=!0,b.pointerType=this.POINTER_TYPE,b},prepareButtonsForMove:function(a,b){var c=H.get(this.POINTER_ID);
|
||||
0!==b.which&&c?a.buttons=c.buttons:a.buttons=0,b.buttons=a.buttons},mousedown:function(a){if(!this.isEventSimulatedFromTouch(a)){var b=H.get(this.POINTER_ID),c=this.prepareEvent(a);K||(c.buttons=J[c.button],b&&(c.buttons|=b.buttons),a.buttons=c.buttons),H.set(this.POINTER_ID,a),b&&0!==b.buttons?u.move(c):u.down(c)}},mousemove:function(a){if(!this.isEventSimulatedFromTouch(a)){var b=this.prepareEvent(a);K||this.prepareButtonsForMove(b,a),b.button=-1,H.set(this.POINTER_ID,a),u.move(b)}},mouseup:function(a){if(!this.isEventSimulatedFromTouch(a)){var b=H.get(this.POINTER_ID),c=this.prepareEvent(a);if(!K){var d=J[c.button];
|
||||
c.buttons=b?b.buttons&~d:0,a.buttons=c.buttons}H.set(this.POINTER_ID,a),
|
||||
c.buttons&=~J[c.button],0===c.buttons?u.up(c):u.move(c)}},mouseover:function(a){if(!this.isEventSimulatedFromTouch(a)){var b=this.prepareEvent(a);K||this.prepareButtonsForMove(b,a),b.button=-1,H.set(this.POINTER_ID,a),u.enterOver(b)}},mouseout:function(a){if(!this.isEventSimulatedFromTouch(a)){var b=this.prepareEvent(a);K||this.prepareButtonsForMove(b,a),b.button=-1,u.leaveOut(b)}},cancel:function(a){var b=this.prepareEvent(a);u.cancel(b),this.deactivateMouse()},deactivateMouse:function(){H["delete"](this.POINTER_ID)}},O=u.captureInfo,P=v.findTarget.bind(v),Q=v.allShadows.bind(v),R=u.pointermap,S=2500,T=200,U="touch-action",V={events:["touchstart","touchmove","touchend","touchcancel"],register:function(a){M.enableOnSubtree(a)},unregister:function(){},elementAdded:function(a){var b=a.getAttribute(U),c=this.touchActionToScrollType(b);c&&(a._scrollType=c,u.listen(a,this.events),
|
||||
Q(a).forEach(function(a){a._scrollType=c,u.listen(a,this.events)},this))},elementRemoved:function(a){a._scrollType=void 0,u.unlisten(a,this.events),
|
||||
Q(a).forEach(function(a){a._scrollType=void 0,u.unlisten(a,this.events)},this)},elementChanged:function(a,b){var c=a.getAttribute(U),d=this.touchActionToScrollType(c),e=this.touchActionToScrollType(b);
|
||||
d&&e?(a._scrollType=d,Q(a).forEach(function(a){a._scrollType=d},this)):e?this.elementRemoved(a):d&&this.elementAdded(a)},scrollTypes:{EMITTER:"none",XSCROLLER:"pan-x",YSCROLLER:"pan-y",SCROLLER:/^(?:pan-x pan-y)|(?:pan-y pan-x)|auto$/},touchActionToScrollType:function(a){var b=a,c=this.scrollTypes;return"none"===b?"none":b===c.XSCROLLER?"X":b===c.YSCROLLER?"Y":c.SCROLLER.exec(b)?"XY":void 0},POINTER_TYPE:"touch",firstTouch:null,isPrimaryTouch:function(a){return this.firstTouch===a.identifier},setPrimaryTouch:function(a){
|
||||
(0===R.size||1===R.size&&R.has(1))&&(this.firstTouch=a.identifier,this.firstXY={X:a.clientX,Y:a.clientY},this.scrolling=!1,this.cancelResetClickCount())},removePrimaryPointer:function(a){a.isPrimary&&(this.firstTouch=null,this.firstXY=null,this.resetClickCount())},clickCount:0,resetId:null,resetClickCount:function(){var a=function(){this.clickCount=0,this.resetId=null}.bind(this);this.resetId=setTimeout(a,T)},cancelResetClickCount:function(){this.resetId&&clearTimeout(this.resetId)},typeToButtons:function(a){var b=0;return"touchstart"!==a&&"touchmove"!==a||(b=1),b},touchToPointer:function(a){var b=this.currentTouchEvent,c=u.cloneEvent(a),d=c.pointerId=a.identifier+2;c.target=O[d]||P(c),c.bubbles=!0,c.cancelable=!0,c.detail=this.clickCount,c.button=0,c.buttons=this.typeToButtons(b.type),c.width=2*(a.radiusX||a.webkitRadiusX||0),c.height=2*(a.radiusY||a.webkitRadiusY||0),c.pressure=a.force||a.webkitForce||.5,c.isPrimary=this.isPrimaryTouch(a),c.pointerType=this.POINTER_TYPE,
|
||||
c.altKey=b.altKey,c.ctrlKey=b.ctrlKey,c.metaKey=b.metaKey,c.shiftKey=b.shiftKey;
|
||||
var e=this;return c.preventDefault=function(){e.scrolling=!1,e.firstXY=null,b.preventDefault()},c},processTouches:function(a,b){var c=a.changedTouches;this.currentTouchEvent=a;for(var d,e=0;e<c.length;e++)d=c[e],b.call(this,this.touchToPointer(d))},
|
||||
shouldScroll:function(a){if(this.firstXY){var b,c=a.currentTarget._scrollType;if("none"===c)
|
||||
b=!1;else if("XY"===c)
|
||||
b=!0;else{var d=a.changedTouches[0],e=c,f="Y"===c?"X":"Y",g=Math.abs(d["client"+e]-this.firstXY[e]),h=Math.abs(d["client"+f]-this.firstXY[f]);
|
||||
b=g>=h}return this.firstXY=null,b}},findTouch:function(a,b){for(var c,d=0,e=a.length;d<e&&(c=a[d]);d++)if(c.identifier===b)return!0},
|
||||
vacuumTouches:function(a){var b=a.touches;
|
||||
if(R.size>=b.length){var c=[];R.forEach(function(a,d){
|
||||
if(1!==d&&!this.findTouch(b,d-2)){var e=a.out;c.push(e)}},this),c.forEach(this.cancelOut,this)}},touchstart:function(a){this.vacuumTouches(a),this.setPrimaryTouch(a.changedTouches[0]),this.dedupSynthMouse(a),this.scrolling||(this.clickCount++,this.processTouches(a,this.overDown))},overDown:function(a){R.set(a.pointerId,{target:a.target,out:a,outTarget:a.target}),u.enterOver(a),u.down(a)},touchmove:function(a){this.scrolling||(this.shouldScroll(a)?(this.scrolling=!0,this.touchcancel(a)):(a.preventDefault(),this.processTouches(a,this.moveOverOut)))},moveOverOut:function(a){var b=a,c=R.get(b.pointerId);
|
||||
if(c){var d=c.out,e=c.outTarget;u.move(b),d&&e!==b.target&&(d.relatedTarget=b.target,b.relatedTarget=e,
|
||||
d.target=e,b.target?(u.leaveOut(d),u.enterOver(b)):(
|
||||
b.target=e,b.relatedTarget=null,this.cancelOut(b))),c.out=b,c.outTarget=b.target}},touchend:function(a){this.dedupSynthMouse(a),this.processTouches(a,this.upOut)},upOut:function(a){this.scrolling||(u.up(a),u.leaveOut(a)),this.cleanUpPointer(a)},touchcancel:function(a){this.processTouches(a,this.cancelOut)},cancelOut:function(a){u.cancel(a),u.leaveOut(a),this.cleanUpPointer(a)},cleanUpPointer:function(a){R["delete"](a.pointerId),this.removePrimaryPointer(a)},
|
||||
dedupSynthMouse:function(a){var b=N.lastTouches,c=a.changedTouches[0];
|
||||
if(this.isPrimaryTouch(c)){
|
||||
var d={x:c.clientX,y:c.clientY};b.push(d);var e=function(a,b){var c=a.indexOf(b);c>-1&&a.splice(c,1)}.bind(null,b,d);setTimeout(e,S)}}};M=new c(V.elementAdded,V.elementRemoved,V.elementChanged,V);var W,X,Y,Z=u.pointermap,$=window.MSPointerEvent&&"number"==typeof window.MSPointerEvent.MSPOINTER_TYPE_MOUSE,_={events:["MSPointerDown","MSPointerMove","MSPointerUp","MSPointerOut","MSPointerOver","MSPointerCancel","MSGotPointerCapture","MSLostPointerCapture"],register:function(a){u.listen(a,this.events)},unregister:function(a){u.unlisten(a,this.events)},POINTER_TYPES:["","unavailable","touch","pen","mouse"],prepareEvent:function(a){var b=a;return $&&(b=u.cloneEvent(a),b.pointerType=this.POINTER_TYPES[a.pointerType]),b},cleanup:function(a){Z["delete"](a)},MSPointerDown:function(a){Z.set(a.pointerId,a);var b=this.prepareEvent(a);u.down(b)},MSPointerMove:function(a){var b=this.prepareEvent(a);u.move(b)},MSPointerUp:function(a){var b=this.prepareEvent(a);u.up(b),this.cleanup(a.pointerId)},MSPointerOut:function(a){var b=this.prepareEvent(a);u.leaveOut(b)},MSPointerOver:function(a){var b=this.prepareEvent(a);u.enterOver(b)},MSPointerCancel:function(a){var b=this.prepareEvent(a);u.cancel(b),this.cleanup(a.pointerId)},MSLostPointerCapture:function(a){var b=u.makeEvent("lostpointercapture",a);u.dispatchEvent(b)},MSGotPointerCapture:function(a){var b=u.makeEvent("gotpointercapture",a);u.dispatchEvent(b)}},aa=window.navigator;aa.msPointerEnabled?(W=function(a){i(a),j(this),k(a)&&(u.setCapture(a,this,!0),this.msSetPointerCapture(a))},X=function(a){i(a),u.releaseCapture(a,!0),this.msReleasePointerCapture(a)}):(W=function(a){i(a),j(this),k(a)&&u.setCapture(a,this)},X=function(a){i(a),u.releaseCapture(a)}),Y=function(a){return!!u.captureInfo[a]},g(),h(),l();var ba={dispatcher:u,Installer:c,PointerEvent:a,PointerMap:p,targetFinding:v};return ba});
|
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,6 @@
|
|||
/*
|
||||
Split.js - v1.3.5
|
||||
MIT License
|
||||
https://github.com/nathancahill/Split.js
|
||||
*/
|
||||
!function(e,t){"object"==typeof exports&&"undefined"!=typeof module?module.exports=t():"function"==typeof define&&define.amd?define(t):e.Split=t()}(this,function(){"use strict";var e=window,t=e.document,n="addEventListener",i="removeEventListener",r="getBoundingClientRect",s=function(){return!1},o=e.attachEvent&&!e[n],a=["","-webkit-","-moz-","-o-"].filter(function(e){var n=t.createElement("div");return n.style.cssText="width:"+e+"calc(9px)",!!n.style.length}).shift()+"calc",l=function(e){return"string"==typeof e||e instanceof String?t.querySelector(e):e};return function(u,c){function z(e,t,n){var i=A(y,t,n);Object.keys(i).forEach(function(t){return e.style[t]=i[t]})}function h(e,t){var n=B(y,t);Object.keys(n).forEach(function(t){return e.style[t]=n[t]})}function f(e){var t=E[this.a],n=E[this.b],i=t.size+n.size;t.size=e/this.size*i,n.size=i-e/this.size*i,z(t.element,t.size,this.aGutterSize),z(n.element,n.size,this.bGutterSize)}function m(e){var t;this.dragging&&((t="touches"in e?e.touches[0][b]-this.start:e[b]-this.start)<=E[this.a].minSize+M+this.aGutterSize?t=E[this.a].minSize+this.aGutterSize:t>=this.size-(E[this.b].minSize+M+this.bGutterSize)&&(t=this.size-(E[this.b].minSize+this.bGutterSize)),f.call(this,t),c.onDrag&&c.onDrag())}function g(){var e=E[this.a].element,t=E[this.b].element;this.size=e[r]()[y]+t[r]()[y]+this.aGutterSize+this.bGutterSize,this.start=e[r]()[G]}function d(){var t=this,n=E[t.a].element,r=E[t.b].element;t.dragging&&c.onDragEnd&&c.onDragEnd(),t.dragging=!1,e[i]("mouseup",t.stop),e[i]("touchend",t.stop),e[i]("touchcancel",t.stop),t.parent[i]("mousemove",t.move),t.parent[i]("touchmove",t.move),delete t.stop,delete t.move,n[i]("selectstart",s),n[i]("dragstart",s),r[i]("selectstart",s),r[i]("dragstart",s),n.style.userSelect="",n.style.webkitUserSelect="",n.style.MozUserSelect="",n.style.pointerEvents="",r.style.userSelect="",r.style.webkitUserSelect="",r.style.MozUserSelect="",r.style.pointerEvents="",t.gutter.style.cursor="",t.parent.style.cursor=""}function S(t){var i=this,r=E[i.a].element,o=E[i.b].element;!i.dragging&&c.onDragStart&&c.onDragStart(),t.preventDefault(),i.dragging=!0,i.move=m.bind(i),i.stop=d.bind(i),e[n]("mouseup",i.stop),e[n]("touchend",i.stop),e[n]("touchcancel",i.stop),i.parent[n]("mousemove",i.move),i.parent[n]("touchmove",i.move),r[n]("selectstart",s),r[n]("dragstart",s),o[n]("selectstart",s),o[n]("dragstart",s),r.style.userSelect="none",r.style.webkitUserSelect="none",r.style.MozUserSelect="none",r.style.pointerEvents="none",o.style.userSelect="none",o.style.webkitUserSelect="none",o.style.MozUserSelect="none",o.style.pointerEvents="none",i.gutter.style.cursor=j,i.parent.style.cursor=j,g.call(i)}function v(e){e.forEach(function(t,n){if(n>0){var i=F[n-1],r=E[i.a],s=E[i.b];r.size=e[n-1],s.size=t,z(r.element,r.size,i.aGutterSize),z(s.element,s.size,i.bGutterSize)}})}function p(){F.forEach(function(e){e.parent.removeChild(e.gutter),E[e.a].element.style[y]="",E[e.b].element.style[y]=""})}void 0===c&&(c={});var y,b,G,E,w=l(u[0]).parentNode,D=e.getComputedStyle(w).flexDirection,U=c.sizes||u.map(function(){return 100/u.length}),k=void 0!==c.minSize?c.minSize:100,x=Array.isArray(k)?k:u.map(function(){return k}),L=void 0!==c.gutterSize?c.gutterSize:10,M=void 0!==c.snapOffset?c.snapOffset:30,O=c.direction||"horizontal",j=c.cursor||("horizontal"===O?"ew-resize":"ns-resize"),C=c.gutter||function(e,n){var i=t.createElement("div");return i.className="gutter gutter-"+n,i},A=c.elementStyle||function(e,t,n){var i={};return"string"==typeof t||t instanceof String?i[e]=t:i[e]=o?t+"%":a+"("+t+"% - "+n+"px)",i},B=c.gutterStyle||function(e,t){return n={},n[e]=t+"px",n;var n};"horizontal"===O?(y="width","clientWidth",b="clientX",G="left","paddingLeft"):"vertical"===O&&(y="height","clientHeight",b="clientY",G="top","paddingTop");var F=[];return E=u.map(function(e,t){var i,s={element:l(e),size:U[t],minSize:x[t]};if(t>0&&(i={a:t-1,b:t,dragging:!1,isFirst:1===t,isLast:t===u.length-1,direction:O,parent:w},i.aGutterSize=L,i.bGutterSize=L,i.isFirst&&(i.aGutterSize=L/2),i.isLast&&(i.bGutterSize=L/2),"row-reverse"===D||"column-reverse"===D)){var a=i.a;i.a=i.b,i.b=a}if(!o&&t>0){var c=C(t,O);h(c,L),c[n]("mousedown",S.bind(i)),c[n]("touchstart",S.bind(i)),w.insertBefore(c,s.element),i.gutter=c}0===t||t===u.length-1?z(s.element,s.size,L/2):z(s.element,s.size,L);var f=s.element[r]()[y];return f<s.minSize&&(s.minSize=f),t>0&&F.push(i),s}),o?{setSizes:v,destroy:p}:{setSizes:v,getSizes:function(){return E.map(function(e){return e.size})},collapse:function(e){if(e===F.length){var t=F[e-1];g.call(t),o||f.call(t,t.size-t.bGutterSize)}else{var n=F[e];g.call(n),o||f.call(n,n.aGutterSize)}},destroy:p}}});
|
|
@ -0,0 +1,371 @@
|
|||
/*
|
||||
* Table reordering via Drag'n'Drop
|
||||
* Inspired by: https://htmldom.dev/drag-and-drop-table-column
|
||||
*/
|
||||
|
||||
function setBomHandlers() {
|
||||
|
||||
const bom = document.getElementById('bomtable');
|
||||
|
||||
let dragName;
|
||||
let placeHolderElements;
|
||||
let draggingElement;
|
||||
let forcePopulation;
|
||||
let xOffset;
|
||||
let yOffset;
|
||||
let wasDragged;
|
||||
|
||||
const mouseUpHandler = function(e) {
|
||||
// Delete dragging element
|
||||
draggingElement.remove();
|
||||
|
||||
// Make BOM selectable again
|
||||
bom.style.removeProperty("userSelect");
|
||||
|
||||
// Remove listeners
|
||||
document.removeEventListener('mousemove', mouseMoveHandler);
|
||||
document.removeEventListener('mouseup', mouseUpHandler);
|
||||
|
||||
if (wasDragged) {
|
||||
// Redraw whole BOM
|
||||
populateBomTable();
|
||||
}
|
||||
}
|
||||
|
||||
const mouseMoveHandler = function(e) {
|
||||
// Notice the dragging
|
||||
wasDragged = true;
|
||||
|
||||
// Make the dragged element visible
|
||||
draggingElement.style.removeProperty("display");
|
||||
|
||||
// Set elements position to mouse position
|
||||
draggingElement.style.left = `${e.screenX - xOffset}px`;
|
||||
draggingElement.style.top = `${e.screenY - yOffset}px`;
|
||||
|
||||
// Forced redrawing of BOM table
|
||||
if (forcePopulation) {
|
||||
forcePopulation = false;
|
||||
// Copy array
|
||||
phe = Array.from(placeHolderElements);
|
||||
// populate BOM table again
|
||||
populateBomHeader(dragName, phe);
|
||||
populateBomBody(dragName, phe);
|
||||
}
|
||||
|
||||
// Set up array of hidden columns
|
||||
var hiddenColumns = Array.from(settings.hiddenColumns);
|
||||
// In the ungrouped mode, quantity don't exist
|
||||
if (settings.bommode === "ungrouped")
|
||||
hiddenColumns.push("Quantity");
|
||||
// If no checkbox fields can be found, we consider them hidden
|
||||
if (settings.checkboxes.length == 0)
|
||||
hiddenColumns.push("checkboxes");
|
||||
|
||||
// Get table headers and group them into checkboxes, extrafields and normal headers
|
||||
const bh = document.getElementById("bomhead");
|
||||
headers = Array.from(bh.querySelectorAll("th"))
|
||||
headers.shift() // numCol is not part of the columnOrder
|
||||
headerGroups = []
|
||||
lastCompoundClass = null;
|
||||
for (i = 0; i < settings.columnOrder.length; i++) {
|
||||
cElem = settings.columnOrder[i];
|
||||
if (hiddenColumns.includes(cElem)) {
|
||||
// Hidden columns appear as a dummy element
|
||||
headerGroups.push([]);
|
||||
continue;
|
||||
}
|
||||
elem = headers.filter(e => getColumnOrderName(e) === cElem)[0];
|
||||
if (elem.classList.contains("bom-checkbox")) {
|
||||
if (lastCompoundClass === "bom-checkbox") {
|
||||
cbGroup = headerGroups.pop();
|
||||
cbGroup.push(elem);
|
||||
headerGroups.push(cbGroup);
|
||||
} else {
|
||||
lastCompoundClass = "bom-checkbox";
|
||||
headerGroups.push([elem])
|
||||
}
|
||||
} else {
|
||||
headerGroups.push([elem])
|
||||
}
|
||||
}
|
||||
|
||||
// Copy settings.columnOrder
|
||||
var columns = Array.from(settings.columnOrder)
|
||||
|
||||
// Set up array with indices of hidden columns
|
||||
var hiddenIndices = hiddenColumns.map(e => settings.columnOrder.indexOf(e));
|
||||
var dragIndex = columns.indexOf(dragName);
|
||||
var swapIndex = dragIndex;
|
||||
var swapDone = false;
|
||||
|
||||
// Check if the current dragged element is swapable with the left or right element
|
||||
if (dragIndex > 0) {
|
||||
// Get left headers boundingbox
|
||||
swapIndex = dragIndex - 1;
|
||||
while (hiddenIndices.includes(swapIndex) && swapIndex > 0)
|
||||
swapIndex--;
|
||||
if (!hiddenIndices.includes(swapIndex)) {
|
||||
box = getBoundingClientRectFromMultiple(headerGroups[swapIndex]);
|
||||
if (e.clientX < box.left + window.scrollX + (box.width / 2)) {
|
||||
swapElement = columns[dragIndex];
|
||||
columns.splice(dragIndex, 1);
|
||||
columns.splice(swapIndex, 0, swapElement);
|
||||
forcePopulation = true;
|
||||
swapDone = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
if ((!swapDone) && dragIndex < headerGroups.length - 1) {
|
||||
// Get right headers boundingbox
|
||||
swapIndex = dragIndex + 1;
|
||||
while (hiddenIndices.includes(swapIndex))
|
||||
swapIndex++;
|
||||
if (swapIndex < headerGroups.length) {
|
||||
box = getBoundingClientRectFromMultiple(headerGroups[swapIndex]);
|
||||
if (e.clientX > box.left + window.scrollX + (box.width / 2)) {
|
||||
swapElement = columns[dragIndex];
|
||||
columns.splice(dragIndex, 1);
|
||||
columns.splice(swapIndex, 0, swapElement);
|
||||
forcePopulation = true;
|
||||
swapDone = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Write back change to storage
|
||||
if (swapDone) {
|
||||
settings.columnOrder = columns
|
||||
writeStorage("columnOrder", JSON.stringify(columns));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
const mouseDownHandler = function(e) {
|
||||
var target = e.target;
|
||||
if (target.tagName.toLowerCase() != "td")
|
||||
target = target.parentElement;
|
||||
|
||||
// Used to check if a dragging has ever happened
|
||||
wasDragged = false;
|
||||
|
||||
// Create new element which will be displayed as the dragged column
|
||||
draggingElement = document.createElement("div")
|
||||
draggingElement.classList.add("dragging");
|
||||
draggingElement.style.display = "none";
|
||||
draggingElement.style.position = "absolute";
|
||||
draggingElement.style.overflow = "hidden";
|
||||
|
||||
// Get bomhead and bombody elements
|
||||
const bh = document.getElementById("bomhead");
|
||||
const bb = document.getElementById("bombody");
|
||||
|
||||
// Get all compound headers for the current column
|
||||
var compoundHeaders;
|
||||
if (target.classList.contains("bom-checkbox")) {
|
||||
compoundHeaders = Array.from(bh.querySelectorAll("th.bom-checkbox"));
|
||||
} else {
|
||||
compoundHeaders = [target];
|
||||
}
|
||||
|
||||
// Create new table which will display the column
|
||||
var newTable = document.createElement("table");
|
||||
newTable.classList.add("bom");
|
||||
newTable.style.background = "white";
|
||||
draggingElement.append(newTable);
|
||||
|
||||
// Create new header element
|
||||
var newHeader = document.createElement("thead");
|
||||
newTable.append(newHeader);
|
||||
|
||||
// Set up array for storing all placeholder elements
|
||||
placeHolderElements = [];
|
||||
|
||||
// Add all compound headers to the new thead element and placeholders
|
||||
compoundHeaders.forEach(function(h) {
|
||||
clone = cloneElementWithDimensions(h);
|
||||
newHeader.append(clone);
|
||||
placeHolderElements.push(clone);
|
||||
});
|
||||
|
||||
// Create new body element
|
||||
var newBody = document.createElement("tbody");
|
||||
newTable.append(newBody);
|
||||
|
||||
// Get indices for compound headers
|
||||
var idxs = compoundHeaders.map(e => getBomTableHeaderIndex(e));
|
||||
|
||||
// For each row in the BOM body...
|
||||
var rows = bb.querySelectorAll("tr");
|
||||
rows.forEach(function(row) {
|
||||
// ..get the cells for the compound column
|
||||
const tds = row.querySelectorAll("td");
|
||||
var copytds = idxs.map(i => tds[i]);
|
||||
// Add them to the new element and the placeholders
|
||||
var newRow = document.createElement("tr");
|
||||
copytds.forEach(function(td) {
|
||||
clone = cloneElementWithDimensions(td);
|
||||
newRow.append(clone);
|
||||
placeHolderElements.push(clone);
|
||||
});
|
||||
newBody.append(newRow);
|
||||
});
|
||||
|
||||
// Compute width for compound header
|
||||
var width = compoundHeaders.reduce((acc, x) => acc + x.clientWidth, 0);
|
||||
draggingElement.style.width = `${width}px`;
|
||||
|
||||
// Insert the new dragging element and disable selection on BOM
|
||||
bom.insertBefore(draggingElement, null);
|
||||
bom.style.userSelect = "none";
|
||||
|
||||
// Determine the mouse position offset
|
||||
xOffset = e.screenX - compoundHeaders.reduce((acc, x) => Math.min(acc, x.offsetLeft), compoundHeaders[0].offsetLeft);
|
||||
yOffset = e.screenY - compoundHeaders[0].offsetTop;
|
||||
|
||||
// Get name for the column in settings.columnOrder
|
||||
dragName = getColumnOrderName(target);
|
||||
|
||||
// Change text and class for placeholder elements
|
||||
placeHolderElements = placeHolderElements.map(function(e) {
|
||||
newElem = cloneElementWithDimensions(e);
|
||||
newElem.textContent = "";
|
||||
newElem.classList.add("placeholder");
|
||||
return newElem;
|
||||
});
|
||||
|
||||
// On next mouse move, the whole BOM needs to be redrawn to show the placeholders
|
||||
forcePopulation = true;
|
||||
|
||||
// Add listeners for move and up on mouse
|
||||
document.addEventListener('mousemove', mouseMoveHandler);
|
||||
document.addEventListener('mouseup', mouseUpHandler);
|
||||
}
|
||||
|
||||
// In netlist mode, there is nothing to reorder
|
||||
if (settings.bommode === "netlist")
|
||||
return;
|
||||
|
||||
// Add mouseDownHandler to every column except the numCol
|
||||
bom.querySelectorAll("th")
|
||||
.forEach(function(head) {
|
||||
if (!head.classList.contains("numCol")) {
|
||||
head.onmousedown = mouseDownHandler;
|
||||
}
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
function getBoundingClientRectFromMultiple(elements) {
|
||||
var elems = Array.from(elements);
|
||||
|
||||
if (elems.length == 0)
|
||||
return null;
|
||||
|
||||
var box = elems.shift()
|
||||
.getBoundingClientRect();
|
||||
|
||||
elems.forEach(function(elem) {
|
||||
var elembox = elem.getBoundingClientRect();
|
||||
box.left = Math.min(elembox.left, box.left);
|
||||
box.top = Math.min(elembox.top, box.top);
|
||||
box.width += elembox.width;
|
||||
box.height = Math.max(elembox.height, box.height);
|
||||
});
|
||||
|
||||
return box;
|
||||
}
|
||||
|
||||
function cloneElementWithDimensions(elem) {
|
||||
var newElem = elem.cloneNode(true);
|
||||
newElem.style.height = window.getComputedStyle(elem).height;
|
||||
newElem.style.width = window.getComputedStyle(elem).width;
|
||||
return newElem;
|
||||
}
|
||||
|
||||
function getBomTableHeaderIndex(elem) {
|
||||
const bh = document.getElementById('bomhead');
|
||||
const ths = Array.from(bh.querySelectorAll("th"));
|
||||
return ths.indexOf(elem);
|
||||
}
|
||||
|
||||
function getColumnOrderName(elem) {
|
||||
var cname = elem.getAttribute("col_name");
|
||||
if (cname === "bom-checkbox")
|
||||
return "checkboxes";
|
||||
else
|
||||
return cname;
|
||||
}
|
||||
|
||||
function resizableGrid(tablehead) {
|
||||
var cols = tablehead.firstElementChild.children;
|
||||
var rowWidth = tablehead.offsetWidth;
|
||||
|
||||
for (var i = 1; i < cols.length; i++) {
|
||||
if (cols[i].classList.contains("bom-checkbox"))
|
||||
continue;
|
||||
cols[i].style.width = ((cols[i].clientWidth - paddingDiff(cols[i])) * 100 / rowWidth) + '%';
|
||||
}
|
||||
|
||||
for (var i = 1; i < cols.length - 1; i++) {
|
||||
var div = document.createElement('div');
|
||||
div.className = "column-width-handle";
|
||||
cols[i].appendChild(div);
|
||||
setListeners(div);
|
||||
}
|
||||
|
||||
function setListeners(div) {
|
||||
var startX, curCol, nxtCol, curColWidth, nxtColWidth, rowWidth;
|
||||
|
||||
div.addEventListener('mousedown', function(e) {
|
||||
e.preventDefault();
|
||||
e.stopPropagation();
|
||||
|
||||
curCol = e.target.parentElement;
|
||||
nxtCol = curCol.nextElementSibling;
|
||||
startX = e.pageX;
|
||||
|
||||
var padding = paddingDiff(curCol);
|
||||
|
||||
rowWidth = curCol.parentElement.offsetWidth;
|
||||
curColWidth = curCol.clientWidth - padding;
|
||||
nxtColWidth = nxtCol.clientWidth - padding;
|
||||
});
|
||||
|
||||
document.addEventListener('mousemove', function(e) {
|
||||
if (startX) {
|
||||
var diffX = e.pageX - startX;
|
||||
diffX = -Math.min(-diffX, curColWidth - 20);
|
||||
diffX = Math.min(diffX, nxtColWidth - 20);
|
||||
|
||||
curCol.style.width = ((curColWidth + diffX) * 100 / rowWidth) + '%';
|
||||
nxtCol.style.width = ((nxtColWidth - diffX) * 100 / rowWidth) + '%';
|
||||
console.log(`${curColWidth + nxtColWidth} ${(curColWidth + diffX) * 100 / rowWidth + (nxtColWidth - diffX) * 100 / rowWidth}`);
|
||||
}
|
||||
});
|
||||
|
||||
document.addEventListener('mouseup', function(e) {
|
||||
curCol = undefined;
|
||||
nxtCol = undefined;
|
||||
startX = undefined;
|
||||
nxtColWidth = undefined;
|
||||
curColWidth = undefined
|
||||
});
|
||||
}
|
||||
|
||||
function paddingDiff(col) {
|
||||
|
||||
if (getStyleVal(col, 'box-sizing') == 'border-box') {
|
||||
return 0;
|
||||
}
|
||||
|
||||
var padLeft = getStyleVal(col, 'padding-left');
|
||||
var padRight = getStyleVal(col, 'padding-right');
|
||||
return (parseInt(padLeft) + parseInt(padRight));
|
||||
|
||||
}
|
||||
|
||||
function getStyleVal(elm, css) {
|
||||
return (window.getComputedStyle(elm, null).getPropertyValue(css))
|
||||
}
|
||||
}
|
|
@ -0,0 +1 @@
|
|||
/* Add custom css styles and overrides here. */
|
|
@ -0,0 +1,4 @@
|
|||
// Example event listener.
|
||||
// Event argument will always be {eventType, args} dict
|
||||
|
||||
EventHandler.registerCallback(IBOM_EVENT_TYPES.ALL, (e) => console.log(e));
|
|
@ -0,0 +1,5 @@
|
|||
<!-- example footer -->
|
||||
</div>
|
||||
<div style="width:100%; height: 50px;">
|
||||
Hello footer
|
||||
</div>
|
|
@ -0,0 +1,5 @@
|
|||
<!-- example header -->
|
||||
<div style="width:100%; height: 50px;">
|
||||
Hello header
|
||||
</div>
|
||||
<div style="height: calc(100% - 100px)">
|
|
@ -0,0 +1,610 @@
|
|||
/* Utility functions */
|
||||
|
||||
var storagePrefix = 'KiCad_HTML_BOM__' + pcbdata.metadata.title + '__' +
|
||||
pcbdata.metadata.revision + '__#';
|
||||
var storage;
|
||||
|
||||
function initStorage(key) {
|
||||
try {
|
||||
window.localStorage.getItem("blank");
|
||||
storage = window.localStorage;
|
||||
} catch (e) {
|
||||
// localStorage not available
|
||||
}
|
||||
if (!storage) {
|
||||
try {
|
||||
window.sessionStorage.getItem("blank");
|
||||
storage = window.sessionStorage;
|
||||
} catch (e) {
|
||||
// sessionStorage also not available
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function readStorage(key) {
|
||||
if (storage) {
|
||||
return storage.getItem(storagePrefix + key);
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
function writeStorage(key, value) {
|
||||
if (storage) {
|
||||
storage.setItem(storagePrefix + key, value);
|
||||
}
|
||||
}
|
||||
|
||||
function fancyDblClickHandler(el, onsingle, ondouble) {
|
||||
return function() {
|
||||
if (el.getAttribute("data-dblclick") == null) {
|
||||
el.setAttribute("data-dblclick", 1);
|
||||
setTimeout(function() {
|
||||
if (el.getAttribute("data-dblclick") == 1) {
|
||||
onsingle();
|
||||
}
|
||||
el.removeAttribute("data-dblclick");
|
||||
}, 200);
|
||||
} else {
|
||||
el.removeAttribute("data-dblclick");
|
||||
ondouble();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function smoothScrollToRow(rowid) {
|
||||
document.getElementById(rowid).scrollIntoView({
|
||||
behavior: "smooth",
|
||||
block: "center",
|
||||
inline: "nearest"
|
||||
});
|
||||
}
|
||||
|
||||
function focusInputField(input) {
|
||||
input.scrollIntoView(false);
|
||||
input.focus();
|
||||
input.select();
|
||||
}
|
||||
|
||||
function saveBomTable(output) {
|
||||
var text = '';
|
||||
for (var node of bomhead.childNodes[0].childNodes) {
|
||||
if (node.firstChild) {
|
||||
text += (output == 'csv' ? `"${node.firstChild.nodeValue}"` : node.firstChild.nodeValue);
|
||||
}
|
||||
if (node != bomhead.childNodes[0].lastChild) {
|
||||
text += (output == 'csv' ? ',' : '\t');
|
||||
}
|
||||
}
|
||||
text += '\n';
|
||||
for (var row of bombody.childNodes) {
|
||||
for (var cell of row.childNodes) {
|
||||
let val = '';
|
||||
for (var node of cell.childNodes) {
|
||||
if (node.nodeName == "INPUT") {
|
||||
if (node.checked) {
|
||||
val += '✓';
|
||||
}
|
||||
} else if ((node.nodeName == "MARK") || (node.nodeName == "A")) {
|
||||
val += node.firstChild.nodeValue;
|
||||
} else {
|
||||
val += node.nodeValue;
|
||||
}
|
||||
}
|
||||
if (output == 'csv') {
|
||||
val = val.replace(/\"/g, '\"\"'); // pair of double-quote characters
|
||||
if (isNumeric(val)) {
|
||||
val = +val; // use number
|
||||
} else {
|
||||
val = `"${val}"`; // enclosed within double-quote
|
||||
}
|
||||
}
|
||||
text += val;
|
||||
if (cell != row.lastChild) {
|
||||
text += (output == 'csv' ? ',' : '\t');
|
||||
}
|
||||
}
|
||||
text += '\n';
|
||||
}
|
||||
|
||||
if (output != 'clipboard') {
|
||||
// To file: csv or txt
|
||||
var blob = new Blob([text], {
|
||||
type: `text/${output}`
|
||||
});
|
||||
saveFile(`${pcbdata.metadata.title}.${output}`, blob);
|
||||
} else {
|
||||
// To clipboard
|
||||
var textArea = document.createElement("textarea");
|
||||
textArea.classList.add('clipboard-temp');
|
||||
textArea.value = text;
|
||||
|
||||
document.body.appendChild(textArea);
|
||||
textArea.focus();
|
||||
textArea.select();
|
||||
|
||||
try {
|
||||
if (document.execCommand('copy')) {
|
||||
console.log('Bom copied to clipboard.');
|
||||
}
|
||||
} catch (err) {
|
||||
console.log('Can not copy to clipboard.');
|
||||
}
|
||||
|
||||
document.body.removeChild(textArea);
|
||||
}
|
||||
}
|
||||
|
||||
function isNumeric(str) {
|
||||
/* https://stackoverflow.com/a/175787 */
|
||||
return (typeof str != "string" ? false : !isNaN(str) && !isNaN(parseFloat(str)));
|
||||
}
|
||||
|
||||
function removeGutterNode(node) {
|
||||
for (var i = 0; i < node.childNodes.length; i++) {
|
||||
if (node.childNodes[i].classList &&
|
||||
node.childNodes[i].classList.contains("gutter")) {
|
||||
node.removeChild(node.childNodes[i]);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function cleanGutters() {
|
||||
removeGutterNode(document.getElementById("bot"));
|
||||
removeGutterNode(document.getElementById("canvasdiv"));
|
||||
}
|
||||
|
||||
var units = {
|
||||
prefixes: {
|
||||
giga: ["G", "g", "giga", "Giga", "GIGA"],
|
||||
mega: ["M", "mega", "Mega", "MEGA"],
|
||||
kilo: ["K", "k", "kilo", "Kilo", "KILO"],
|
||||
milli: ["m", "milli", "Milli", "MILLI"],
|
||||
micro: ["U", "u", "micro", "Micro", "MICRO", "μ", "µ"], // different utf8 μ
|
||||
nano: ["N", "n", "nano", "Nano", "NANO"],
|
||||
pico: ["P", "p", "pico", "Pico", "PICO"],
|
||||
},
|
||||
unitsShort: ["R", "r", "Ω", "F", "f", "H", "h"],
|
||||
unitsLong: [
|
||||
"OHM", "Ohm", "ohm", "ohms",
|
||||
"FARAD", "Farad", "farad",
|
||||
"HENRY", "Henry", "henry"
|
||||
],
|
||||
getMultiplier: function(s) {
|
||||
if (this.prefixes.giga.includes(s)) return 1e9;
|
||||
if (this.prefixes.mega.includes(s)) return 1e6;
|
||||
if (this.prefixes.kilo.includes(s)) return 1e3;
|
||||
if (this.prefixes.milli.includes(s)) return 1e-3;
|
||||
if (this.prefixes.micro.includes(s)) return 1e-6;
|
||||
if (this.prefixes.nano.includes(s)) return 1e-9;
|
||||
if (this.prefixes.pico.includes(s)) return 1e-12;
|
||||
return 1;
|
||||
},
|
||||
valueRegex: null,
|
||||
}
|
||||
|
||||
function initUtils() {
|
||||
var allPrefixes = units.prefixes.giga
|
||||
.concat(units.prefixes.mega)
|
||||
.concat(units.prefixes.kilo)
|
||||
.concat(units.prefixes.milli)
|
||||
.concat(units.prefixes.micro)
|
||||
.concat(units.prefixes.nano)
|
||||
.concat(units.prefixes.pico);
|
||||
var allUnits = units.unitsShort.concat(units.unitsLong);
|
||||
units.valueRegex = new RegExp("^([0-9\.]+)" +
|
||||
"\\s*(" + allPrefixes.join("|") + ")?" +
|
||||
"(" + allUnits.join("|") + ")?" +
|
||||
"(\\b.*)?$", "");
|
||||
units.valueAltRegex = new RegExp("^([0-9]*)" +
|
||||
"(" + units.unitsShort.join("|") + ")?" +
|
||||
"([GgMmKkUuNnPp])?" +
|
||||
"([0-9]*)" +
|
||||
"(\\b.*)?$", "");
|
||||
if (config.fields.includes("Value")) {
|
||||
var index = config.fields.indexOf("Value");
|
||||
pcbdata.bom["parsedValues"] = {};
|
||||
for (var id in pcbdata.bom.fields) {
|
||||
pcbdata.bom.parsedValues[id] = parseValue(pcbdata.bom.fields[id][index])
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function parseValue(val, ref) {
|
||||
var inferUnit = (unit, ref) => {
|
||||
if (unit) {
|
||||
unit = unit.toLowerCase();
|
||||
if (unit == 'Ω' || unit == "ohm" || unit == "ohms") {
|
||||
unit = 'r';
|
||||
}
|
||||
unit = unit[0];
|
||||
} else {
|
||||
ref = /^([a-z]+)\d+$/i.exec(ref);
|
||||
if (ref) {
|
||||
ref = ref[1].toLowerCase();
|
||||
if (ref == "c") unit = 'f';
|
||||
else if (ref == "l") unit = 'h';
|
||||
else if (ref == "r" || ref == "rv") unit = 'r';
|
||||
else unit = null;
|
||||
}
|
||||
}
|
||||
return unit;
|
||||
};
|
||||
val = val.replace(/,/g, "");
|
||||
var match = units.valueRegex.exec(val);
|
||||
var unit;
|
||||
if (match) {
|
||||
val = parseFloat(match[1]);
|
||||
if (match[2]) {
|
||||
val = val * units.getMultiplier(match[2]);
|
||||
}
|
||||
unit = inferUnit(match[3], ref);
|
||||
if (!unit) return null;
|
||||
else return {
|
||||
val: val,
|
||||
unit: unit,
|
||||
extra: match[4],
|
||||
}
|
||||
}
|
||||
match = units.valueAltRegex.exec(val);
|
||||
if (match && (match[1] || match[4])) {
|
||||
val = parseFloat(match[1] + "." + match[4]);
|
||||
if (match[3]) {
|
||||
val = val * units.getMultiplier(match[3]);
|
||||
}
|
||||
unit = inferUnit(match[2], ref);
|
||||
if (!unit) return null;
|
||||
else return {
|
||||
val: val,
|
||||
unit: unit,
|
||||
extra: match[5],
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
function valueCompare(a, b, stra, strb) {
|
||||
if (a === null && b === null) {
|
||||
// Failed to parse both values, compare them as strings.
|
||||
if (stra != strb) return stra > strb ? 1 : -1;
|
||||
else return 0;
|
||||
} else if (a === null) {
|
||||
return 1;
|
||||
} else if (b === null) {
|
||||
return -1;
|
||||
} else {
|
||||
if (a.unit != b.unit) return a.unit > b.unit ? 1 : -1;
|
||||
else if (a.val != b.val) return a.val > b.val ? 1 : -1;
|
||||
else if (a.extra != b.extra) return a.extra > b.extra ? 1 : -1;
|
||||
else return 0;
|
||||
}
|
||||
}
|
||||
|
||||
function validateSaveImgDimension(element) {
|
||||
var valid = false;
|
||||
var intValue = 0;
|
||||
if (/^[1-9]\d*$/.test(element.value)) {
|
||||
intValue = parseInt(element.value);
|
||||
if (intValue <= 16000) {
|
||||
valid = true;
|
||||
}
|
||||
}
|
||||
if (valid) {
|
||||
element.classList.remove("invalid");
|
||||
} else {
|
||||
element.classList.add("invalid");
|
||||
}
|
||||
return intValue;
|
||||
}
|
||||
|
||||
function saveImage(layer) {
|
||||
var width = validateSaveImgDimension(document.getElementById("render-save-width"));
|
||||
var height = validateSaveImgDimension(document.getElementById("render-save-height"));
|
||||
var bgcolor = null;
|
||||
if (!document.getElementById("render-save-transparent").checked) {
|
||||
var style = getComputedStyle(topmostdiv);
|
||||
bgcolor = style.getPropertyValue("background-color");
|
||||
}
|
||||
if (!width || !height) return;
|
||||
|
||||
// Prepare image
|
||||
var canvas = document.createElement("canvas");
|
||||
var layerdict = {
|
||||
transform: {
|
||||
x: 0,
|
||||
y: 0,
|
||||
s: 1,
|
||||
panx: 0,
|
||||
pany: 0,
|
||||
zoom: 1,
|
||||
},
|
||||
bg: canvas,
|
||||
fab: canvas,
|
||||
silk: canvas,
|
||||
highlight: canvas,
|
||||
layer: layer,
|
||||
}
|
||||
// Do the rendering
|
||||
recalcLayerScale(layerdict, width, height);
|
||||
prepareLayer(layerdict);
|
||||
clearCanvas(canvas, bgcolor);
|
||||
drawBackground(layerdict, false);
|
||||
drawHighlightsOnLayer(layerdict, false);
|
||||
|
||||
// Save image
|
||||
var imgdata = canvas.toDataURL("image/png");
|
||||
|
||||
var filename = pcbdata.metadata.title;
|
||||
if (pcbdata.metadata.revision) {
|
||||
filename += `.${pcbdata.metadata.revision}`;
|
||||
}
|
||||
filename += `.${layer}.png`;
|
||||
saveFile(filename, dataURLtoBlob(imgdata));
|
||||
}
|
||||
|
||||
function saveSettings() {
|
||||
var data = {
|
||||
type: "InteractiveHtmlBom settings",
|
||||
version: 1,
|
||||
pcbmetadata: pcbdata.metadata,
|
||||
settings: settings,
|
||||
}
|
||||
var blob = new Blob([JSON.stringify(data, null, 4)], {
|
||||
type: "application/json"
|
||||
});
|
||||
saveFile(`${pcbdata.metadata.title}.settings.json`, blob);
|
||||
}
|
||||
|
||||
function loadSettings() {
|
||||
var input = document.createElement("input");
|
||||
input.type = "file";
|
||||
input.accept = ".settings.json";
|
||||
input.onchange = function(e) {
|
||||
var file = e.target.files[0];
|
||||
var reader = new FileReader();
|
||||
reader.onload = readerEvent => {
|
||||
var content = readerEvent.target.result;
|
||||
var newSettings;
|
||||
try {
|
||||
newSettings = JSON.parse(content);
|
||||
} catch (e) {
|
||||
alert("Selected file is not InteractiveHtmlBom settings file.");
|
||||
return;
|
||||
}
|
||||
if (newSettings.type != "InteractiveHtmlBom settings") {
|
||||
alert("Selected file is not InteractiveHtmlBom settings file.");
|
||||
return;
|
||||
}
|
||||
var metadataMatches = newSettings.hasOwnProperty("pcbmetadata");
|
||||
if (metadataMatches) {
|
||||
for (var k in pcbdata.metadata) {
|
||||
if (!newSettings.pcbmetadata.hasOwnProperty(k) || newSettings.pcbmetadata[k] != pcbdata.metadata[k]) {
|
||||
metadataMatches = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!metadataMatches) {
|
||||
var currentMetadata = JSON.stringify(pcbdata.metadata, null, 4);
|
||||
var fileMetadata = JSON.stringify(newSettings.pcbmetadata, null, 4);
|
||||
if (!confirm(
|
||||
`Settins file metadata does not match current metadata.\n\n` +
|
||||
`Page metadata:\n${currentMetadata}\n\n` +
|
||||
`Settings file metadata:\n${fileMetadata}\n\n` +
|
||||
`Press OK if you would like to import settings anyway.`)) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
overwriteSettings(newSettings.settings);
|
||||
}
|
||||
reader.readAsText(file, 'UTF-8');
|
||||
}
|
||||
input.click();
|
||||
}
|
||||
|
||||
function overwriteSettings(newSettings) {
|
||||
initDone = false;
|
||||
Object.assign(settings, newSettings);
|
||||
writeStorage("bomlayout", settings.bomlayout);
|
||||
writeStorage("bommode", settings.bommode);
|
||||
writeStorage("canvaslayout", settings.canvaslayout);
|
||||
writeStorage("bomCheckboxes", settings.checkboxes.join(","));
|
||||
document.getElementById("bomCheckboxes").value = settings.checkboxes.join(",");
|
||||
for (var checkbox of settings.checkboxes) {
|
||||
writeStorage("checkbox_" + checkbox, settings.checkboxStoredRefs[checkbox]);
|
||||
}
|
||||
writeStorage("markWhenChecked", settings.markWhenChecked);
|
||||
padsVisible(settings.renderPads);
|
||||
document.getElementById("padsCheckbox").checked = settings.renderPads;
|
||||
fabricationVisible(settings.renderFabrication);
|
||||
document.getElementById("fabricationCheckbox").checked = settings.renderFabrication;
|
||||
silkscreenVisible(settings.renderSilkscreen);
|
||||
document.getElementById("silkscreenCheckbox").checked = settings.renderSilkscreen;
|
||||
referencesVisible(settings.renderReferences);
|
||||
document.getElementById("referencesCheckbox").checked = settings.renderReferences;
|
||||
valuesVisible(settings.renderValues);
|
||||
document.getElementById("valuesCheckbox").checked = settings.renderValues;
|
||||
tracksVisible(settings.renderTracks);
|
||||
document.getElementById("tracksCheckbox").checked = settings.renderTracks;
|
||||
zonesVisible(settings.renderZones);
|
||||
document.getElementById("zonesCheckbox").checked = settings.renderZones;
|
||||
dnpOutline(settings.renderDnpOutline);
|
||||
document.getElementById("dnpOutlineCheckbox").checked = settings.renderDnpOutline;
|
||||
setRedrawOnDrag(settings.redrawOnDrag);
|
||||
document.getElementById("dragCheckbox").checked = settings.redrawOnDrag;
|
||||
setDarkMode(settings.darkMode);
|
||||
document.getElementById("darkmodeCheckbox").checked = settings.darkMode;
|
||||
setHighlightPin1(settings.highlightpin1);
|
||||
document.getElementById("highlightpin1Checkbox").checked = settings.highlightpin1;
|
||||
writeStorage("boardRotation", settings.boardRotation);
|
||||
document.getElementById("boardRotation").value = settings.boardRotation / 5;
|
||||
document.getElementById("rotationDegree").textContent = settings.boardRotation;
|
||||
setOffsetBackRotation(settings.offsetBackRotation);
|
||||
document.getElementById("offsetBackRotationCheckbox").checked = settings.offsetBackRotation;
|
||||
initDone = true;
|
||||
prepCheckboxes();
|
||||
changeBomLayout(settings.bomlayout);
|
||||
}
|
||||
|
||||
function saveFile(filename, blob) {
|
||||
var link = document.createElement("a");
|
||||
var objurl = URL.createObjectURL(blob);
|
||||
link.download = filename;
|
||||
link.href = objurl;
|
||||
link.click();
|
||||
}
|
||||
|
||||
function dataURLtoBlob(dataurl) {
|
||||
var arr = dataurl.split(','),
|
||||
mime = arr[0].match(/:(.*?);/)[1],
|
||||
bstr = atob(arr[1]),
|
||||
n = bstr.length,
|
||||
u8arr = new Uint8Array(n);
|
||||
while (n--) {
|
||||
u8arr[n] = bstr.charCodeAt(n);
|
||||
}
|
||||
return new Blob([u8arr], {
|
||||
type: mime
|
||||
});
|
||||
}
|
||||
|
||||
var settings = {
|
||||
canvaslayout: "default",
|
||||
bomlayout: "default",
|
||||
bommode: "grouped",
|
||||
checkboxes: [],
|
||||
checkboxStoredRefs: {},
|
||||
darkMode: false,
|
||||
highlightpin1: false,
|
||||
redrawOnDrag: true,
|
||||
boardRotation: 0,
|
||||
offsetBackRotation: false,
|
||||
renderPads: true,
|
||||
renderReferences: true,
|
||||
renderValues: true,
|
||||
renderSilkscreen: true,
|
||||
renderFabrication: true,
|
||||
renderDnpOutline: false,
|
||||
renderTracks: true,
|
||||
renderZones: true,
|
||||
columnOrder: [],
|
||||
hiddenColumns: []
|
||||
}
|
||||
|
||||
function initDefaults() {
|
||||
settings.bomlayout = readStorage("bomlayout");
|
||||
if (settings.bomlayout === null) {
|
||||
settings.bomlayout = config.bom_view;
|
||||
}
|
||||
if (!['bom-only', 'left-right', 'top-bottom'].includes(settings.bomlayout)) {
|
||||
settings.bomlayout = config.bom_view;
|
||||
}
|
||||
settings.bommode = readStorage("bommode");
|
||||
if (settings.bommode === null) {
|
||||
settings.bommode = "grouped";
|
||||
}
|
||||
if (!["grouped", "ungrouped", "netlist"].includes(settings.bommode)) {
|
||||
settings.bommode = "grouped";
|
||||
}
|
||||
settings.canvaslayout = readStorage("canvaslayout");
|
||||
if (settings.canvaslayout === null) {
|
||||
settings.canvaslayout = config.layer_view;
|
||||
}
|
||||
var bomCheckboxes = readStorage("bomCheckboxes");
|
||||
if (bomCheckboxes === null) {
|
||||
bomCheckboxes = config.checkboxes;
|
||||
}
|
||||
settings.checkboxes = bomCheckboxes.split(",").filter((e) => e);
|
||||
document.getElementById("bomCheckboxes").value = bomCheckboxes;
|
||||
|
||||
settings.markWhenChecked = readStorage("markWhenChecked") || "";
|
||||
populateMarkWhenCheckedOptions();
|
||||
|
||||
function initBooleanSetting(storageString, def, elementId, func) {
|
||||
var b = readStorage(storageString);
|
||||
if (b === null) {
|
||||
b = def;
|
||||
} else {
|
||||
b = (b == "true");
|
||||
}
|
||||
document.getElementById(elementId).checked = b;
|
||||
func(b);
|
||||
}
|
||||
|
||||
initBooleanSetting("padsVisible", config.show_pads, "padsCheckbox", padsVisible);
|
||||
initBooleanSetting("fabricationVisible", config.show_fabrication, "fabricationCheckbox", fabricationVisible);
|
||||
initBooleanSetting("silkscreenVisible", config.show_silkscreen, "silkscreenCheckbox", silkscreenVisible);
|
||||
initBooleanSetting("referencesVisible", true, "referencesCheckbox", referencesVisible);
|
||||
initBooleanSetting("valuesVisible", true, "valuesCheckbox", valuesVisible);
|
||||
if ("tracks" in pcbdata) {
|
||||
initBooleanSetting("tracksVisible", true, "tracksCheckbox", tracksVisible);
|
||||
initBooleanSetting("zonesVisible", true, "zonesCheckbox", zonesVisible);
|
||||
} else {
|
||||
document.getElementById("tracksAndZonesCheckboxes").style.display = "none";
|
||||
tracksVisible(false);
|
||||
zonesVisible(false);
|
||||
}
|
||||
initBooleanSetting("dnpOutline", false, "dnpOutlineCheckbox", dnpOutline);
|
||||
initBooleanSetting("redrawOnDrag", config.redraw_on_drag, "dragCheckbox", setRedrawOnDrag);
|
||||
initBooleanSetting("darkmode", config.dark_mode, "darkmodeCheckbox", setDarkMode);
|
||||
initBooleanSetting("highlightpin1", config.highlight_pin1, "highlightpin1Checkbox", setHighlightPin1);
|
||||
|
||||
var fields = ["checkboxes", "References"].concat(config.fields).concat(["Quantity"]);
|
||||
var hcols = JSON.parse(readStorage("hiddenColumns"));
|
||||
if (hcols === null) {
|
||||
hcols = [];
|
||||
}
|
||||
settings.hiddenColumns = hcols.filter(e => fields.includes(e));
|
||||
|
||||
var cord = JSON.parse(readStorage("columnOrder"));
|
||||
if (cord === null) {
|
||||
cord = fields;
|
||||
} else {
|
||||
cord = cord.filter(e => fields.includes(e));
|
||||
if (cord.length != fields.length)
|
||||
cord = fields;
|
||||
}
|
||||
settings.columnOrder = cord;
|
||||
|
||||
settings.boardRotation = readStorage("boardRotation");
|
||||
if (settings.boardRotation === null) {
|
||||
settings.boardRotation = config.board_rotation * 5;
|
||||
} else {
|
||||
settings.boardRotation = parseInt(settings.boardRotation);
|
||||
}
|
||||
document.getElementById("boardRotation").value = settings.boardRotation / 5;
|
||||
document.getElementById("rotationDegree").textContent = settings.boardRotation;
|
||||
initBooleanSetting("offsetBackRotation", config.offset_back_rotation, "offsetBackRotationCheckbox", setOffsetBackRotation);
|
||||
}
|
||||
|
||||
// Helper classes for user js callbacks.
|
||||
|
||||
const IBOM_EVENT_TYPES = {
|
||||
ALL: "all",
|
||||
HIGHLIGHT_EVENT: "highlightEvent",
|
||||
CHECKBOX_CHANGE_EVENT: "checkboxChangeEvent",
|
||||
BOM_BODY_CHANGE_EVENT: "bomBodyChangeEvent",
|
||||
}
|
||||
|
||||
const EventHandler = {
|
||||
callbacks: {},
|
||||
init: function() {
|
||||
for (eventType of Object.values(IBOM_EVENT_TYPES))
|
||||
this.callbacks[eventType] = [];
|
||||
},
|
||||
registerCallback: function(eventType, callback) {
|
||||
this.callbacks[eventType].push(callback);
|
||||
},
|
||||
emitEvent: function(eventType, eventArgs) {
|
||||
event = {
|
||||
eventType: eventType,
|
||||
args: eventArgs,
|
||||
}
|
||||
var callback;
|
||||
for (callback of this.callbacks[eventType])
|
||||
callback(event);
|
||||
for (callback of this.callbacks[IBOM_EVENT_TYPES.ALL])
|
||||
callback(event);
|
||||
}
|
||||
}
|
||||
EventHandler.init();
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue