bbs_3mf.cpp 416 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499150015011502150315041505150615071508150915101511151215131514151515161517151815191520152115221523152415251526152715281529153015311532153315341535153615371538153915401541154215431544154515461547154815491550155115521553155415551556155715581559156015611562156315641565156615671568156915701571157215731574157515761577157815791580158115821583158415851586158715881589159015911592159315941595159615971598159916001601160216031604160516061607160816091610161116121613161416151616161716181619162016211622162316241625162616271628162916301631163216331634163516361637163816391640164116421643164416451646164716481649165016511652165316541655165616571658165916601661166216631664166516661667166816691670167116721673167416751676167716781679168016811682168316841685168616871688168916901691169216931694169516961697169816991700170117021703170417051706170717081709171017111712171317141715171617171718171917201721172217231724172517261727172817291730173117321733173417351736173717381739174017411742174317441745174617471748174917501751175217531754175517561757175817591760176117621763176417651766176717681769177017711772177317741775177617771778177917801781178217831784178517861787178817891790179117921793179417951796179717981799180018011802180318041805180618071808180918101811181218131814181518161817181818191820182118221823182418251826182718281829183018311832183318341835183618371838183918401841184218431844184518461847184818491850185118521853185418551856185718581859186018611862186318641865186618671868186918701871187218731874187518761877187818791880188118821883188418851886188718881889189018911892189318941895189618971898189919001901190219031904190519061907190819091910191119121913191419151916191719181919192019211922192319241925192619271928192919301931193219331934193519361937193819391940194119421943194419451946194719481949195019511952195319541955195619571958195919601961196219631964196519661967196819691970197119721973197419751976197719781979198019811982198319841985198619871988198919901991199219931994199519961997199819992000200120022003200420052006200720082009201020112012201320142015201620172018201920202021202220232024202520262027202820292030203120322033203420352036203720382039204020412042204320442045204620472048204920502051205220532054205520562057205820592060206120622063206420652066206720682069207020712072207320742075207620772078207920802081208220832084208520862087208820892090209120922093209420952096209720982099210021012102210321042105210621072108210921102111211221132114211521162117211821192120212121222123212421252126212721282129213021312132213321342135213621372138213921402141214221432144214521462147214821492150215121522153215421552156215721582159216021612162216321642165216621672168216921702171217221732174217521762177217821792180218121822183218421852186218721882189219021912192219321942195219621972198219922002201220222032204220522062207220822092210221122122213221422152216221722182219222022212222222322242225222622272228222922302231223222332234223522362237223822392240224122422243224422452246224722482249225022512252225322542255225622572258225922602261226222632264226522662267226822692270227122722273227422752276227722782279228022812282228322842285228622872288228922902291229222932294229522962297229822992300230123022303230423052306230723082309231023112312231323142315231623172318231923202321232223232324232523262327232823292330233123322333233423352336233723382339234023412342234323442345234623472348234923502351235223532354235523562357235823592360236123622363236423652366236723682369237023712372237323742375237623772378237923802381238223832384238523862387238823892390239123922393239423952396239723982399240024012402240324042405240624072408240924102411241224132414241524162417241824192420242124222423242424252426242724282429243024312432243324342435243624372438243924402441244224432444244524462447244824492450245124522453245424552456245724582459246024612462246324642465246624672468246924702471247224732474247524762477247824792480248124822483248424852486248724882489249024912492249324942495249624972498249925002501250225032504250525062507250825092510251125122513251425152516251725182519252025212522252325242525252625272528252925302531253225332534253525362537253825392540254125422543254425452546254725482549255025512552255325542555255625572558255925602561256225632564256525662567256825692570257125722573257425752576257725782579258025812582258325842585258625872588258925902591259225932594259525962597259825992600260126022603260426052606260726082609261026112612261326142615261626172618261926202621262226232624262526262627262826292630263126322633263426352636263726382639264026412642264326442645264626472648264926502651265226532654265526562657265826592660266126622663266426652666266726682669267026712672267326742675267626772678267926802681268226832684268526862687268826892690269126922693269426952696269726982699270027012702270327042705270627072708270927102711271227132714271527162717271827192720272127222723272427252726272727282729273027312732273327342735273627372738273927402741274227432744274527462747274827492750275127522753275427552756275727582759276027612762276327642765276627672768276927702771277227732774277527762777277827792780278127822783278427852786278727882789279027912792279327942795279627972798279928002801280228032804280528062807280828092810281128122813281428152816281728182819282028212822282328242825282628272828282928302831283228332834283528362837283828392840284128422843284428452846284728482849285028512852285328542855285628572858285928602861286228632864286528662867286828692870287128722873287428752876287728782879288028812882288328842885288628872888288928902891289228932894289528962897289828992900290129022903290429052906290729082909291029112912291329142915291629172918291929202921292229232924292529262927292829292930293129322933293429352936293729382939294029412942294329442945294629472948294929502951295229532954295529562957295829592960296129622963296429652966296729682969297029712972297329742975297629772978297929802981298229832984298529862987298829892990299129922993299429952996299729982999300030013002300330043005300630073008300930103011301230133014301530163017301830193020302130223023302430253026302730283029303030313032303330343035303630373038303930403041304230433044304530463047304830493050305130523053305430553056305730583059306030613062306330643065306630673068306930703071307230733074307530763077307830793080308130823083308430853086308730883089309030913092309330943095309630973098309931003101310231033104310531063107310831093110311131123113311431153116311731183119312031213122312331243125312631273128312931303131313231333134313531363137313831393140314131423143314431453146314731483149315031513152315331543155315631573158315931603161316231633164316531663167316831693170317131723173317431753176317731783179318031813182318331843185318631873188318931903191319231933194319531963197319831993200320132023203320432053206320732083209321032113212321332143215321632173218321932203221322232233224322532263227322832293230323132323233323432353236323732383239324032413242324332443245324632473248324932503251325232533254325532563257325832593260326132623263326432653266326732683269327032713272327332743275327632773278327932803281328232833284328532863287328832893290329132923293329432953296329732983299330033013302330333043305330633073308330933103311331233133314331533163317331833193320332133223323332433253326332733283329333033313332333333343335333633373338333933403341334233433344334533463347334833493350335133523353335433553356335733583359336033613362336333643365336633673368336933703371337233733374337533763377337833793380338133823383338433853386338733883389339033913392339333943395339633973398339934003401340234033404340534063407340834093410341134123413341434153416341734183419342034213422342334243425342634273428342934303431343234333434343534363437343834393440344134423443344434453446344734483449345034513452345334543455345634573458345934603461346234633464346534663467346834693470347134723473347434753476347734783479348034813482348334843485348634873488348934903491349234933494349534963497349834993500350135023503350435053506350735083509351035113512351335143515351635173518351935203521352235233524352535263527352835293530353135323533353435353536353735383539354035413542354335443545354635473548354935503551355235533554355535563557355835593560356135623563356435653566356735683569357035713572357335743575357635773578357935803581358235833584358535863587358835893590359135923593359435953596359735983599360036013602360336043605360636073608360936103611361236133614361536163617361836193620362136223623362436253626362736283629363036313632363336343635363636373638363936403641364236433644364536463647364836493650365136523653365436553656365736583659366036613662366336643665366636673668366936703671367236733674367536763677367836793680368136823683368436853686368736883689369036913692369336943695369636973698369937003701370237033704370537063707370837093710371137123713371437153716371737183719372037213722372337243725372637273728372937303731373237333734373537363737373837393740374137423743374437453746374737483749375037513752375337543755375637573758375937603761376237633764376537663767376837693770377137723773377437753776377737783779378037813782378337843785378637873788378937903791379237933794379537963797379837993800380138023803380438053806380738083809381038113812381338143815381638173818381938203821382238233824382538263827382838293830383138323833383438353836383738383839384038413842384338443845384638473848384938503851385238533854385538563857385838593860386138623863386438653866386738683869387038713872387338743875387638773878387938803881388238833884388538863887388838893890389138923893389438953896389738983899390039013902390339043905390639073908390939103911391239133914391539163917391839193920392139223923392439253926392739283929393039313932393339343935393639373938393939403941394239433944394539463947394839493950395139523953395439553956395739583959396039613962396339643965396639673968396939703971397239733974397539763977397839793980398139823983398439853986398739883989399039913992399339943995399639973998399940004001400240034004400540064007400840094010401140124013401440154016401740184019402040214022402340244025402640274028402940304031403240334034403540364037403840394040404140424043404440454046404740484049405040514052405340544055405640574058405940604061406240634064406540664067406840694070407140724073407440754076407740784079408040814082408340844085408640874088408940904091409240934094409540964097409840994100410141024103410441054106410741084109411041114112411341144115411641174118411941204121412241234124412541264127412841294130413141324133413441354136413741384139414041414142414341444145414641474148414941504151415241534154415541564157415841594160416141624163416441654166416741684169417041714172417341744175417641774178417941804181418241834184418541864187418841894190419141924193419441954196419741984199420042014202420342044205420642074208420942104211421242134214421542164217421842194220422142224223422442254226422742284229423042314232423342344235423642374238423942404241424242434244424542464247424842494250425142524253425442554256425742584259426042614262426342644265426642674268426942704271427242734274427542764277427842794280428142824283428442854286428742884289429042914292429342944295429642974298429943004301430243034304430543064307430843094310431143124313431443154316431743184319432043214322432343244325432643274328432943304331433243334334433543364337433843394340434143424343434443454346434743484349435043514352435343544355435643574358435943604361436243634364436543664367436843694370437143724373437443754376437743784379438043814382438343844385438643874388438943904391439243934394439543964397439843994400440144024403440444054406440744084409441044114412441344144415441644174418441944204421442244234424442544264427442844294430443144324433443444354436443744384439444044414442444344444445444644474448444944504451445244534454445544564457445844594460446144624463446444654466446744684469447044714472447344744475447644774478447944804481448244834484448544864487448844894490449144924493449444954496449744984499450045014502450345044505450645074508450945104511451245134514451545164517451845194520452145224523452445254526452745284529453045314532453345344535453645374538453945404541454245434544454545464547454845494550455145524553455445554556455745584559456045614562456345644565456645674568456945704571457245734574457545764577457845794580458145824583458445854586458745884589459045914592459345944595459645974598459946004601460246034604460546064607460846094610461146124613461446154616461746184619462046214622462346244625462646274628462946304631463246334634463546364637463846394640464146424643464446454646464746484649465046514652465346544655465646574658465946604661466246634664466546664667466846694670467146724673467446754676467746784679468046814682468346844685468646874688468946904691469246934694469546964697469846994700470147024703470447054706470747084709471047114712471347144715471647174718471947204721472247234724472547264727472847294730473147324733473447354736473747384739474047414742474347444745474647474748474947504751475247534754475547564757475847594760476147624763476447654766476747684769477047714772477347744775477647774778477947804781478247834784478547864787478847894790479147924793479447954796479747984799480048014802480348044805480648074808480948104811481248134814481548164817481848194820482148224823482448254826482748284829483048314832483348344835483648374838483948404841484248434844484548464847484848494850485148524853485448554856485748584859486048614862486348644865486648674868486948704871487248734874487548764877487848794880488148824883488448854886488748884889489048914892489348944895489648974898489949004901490249034904490549064907490849094910491149124913491449154916491749184919492049214922492349244925492649274928492949304931493249334934493549364937493849394940494149424943494449454946494749484949495049514952495349544955495649574958495949604961496249634964496549664967496849694970497149724973497449754976497749784979498049814982498349844985498649874988498949904991499249934994499549964997499849995000500150025003500450055006500750085009501050115012501350145015501650175018501950205021502250235024502550265027502850295030503150325033503450355036503750385039504050415042504350445045504650475048504950505051505250535054505550565057505850595060506150625063506450655066506750685069507050715072507350745075507650775078507950805081508250835084508550865087508850895090509150925093509450955096509750985099510051015102510351045105510651075108510951105111511251135114511551165117511851195120512151225123512451255126512751285129513051315132513351345135513651375138513951405141514251435144514551465147514851495150515151525153515451555156515751585159516051615162516351645165516651675168516951705171517251735174517551765177517851795180518151825183518451855186518751885189519051915192519351945195519651975198519952005201520252035204520552065207520852095210521152125213521452155216521752185219522052215222522352245225522652275228522952305231523252335234523552365237523852395240524152425243524452455246524752485249525052515252525352545255525652575258525952605261526252635264526552665267526852695270527152725273527452755276527752785279528052815282528352845285528652875288528952905291529252935294529552965297529852995300530153025303530453055306530753085309531053115312531353145315531653175318531953205321532253235324532553265327532853295330533153325333533453355336533753385339534053415342534353445345534653475348534953505351535253535354535553565357535853595360536153625363536453655366536753685369537053715372537353745375537653775378537953805381538253835384538553865387538853895390539153925393539453955396539753985399540054015402540354045405540654075408540954105411541254135414541554165417541854195420542154225423542454255426542754285429543054315432543354345435543654375438543954405441544254435444544554465447544854495450545154525453545454555456545754585459546054615462546354645465546654675468546954705471547254735474547554765477547854795480548154825483548454855486548754885489549054915492549354945495549654975498549955005501550255035504550555065507550855095510551155125513551455155516551755185519552055215522552355245525552655275528552955305531553255335534553555365537553855395540554155425543554455455546554755485549555055515552555355545555555655575558555955605561556255635564556555665567556855695570557155725573557455755576557755785579558055815582558355845585558655875588558955905591559255935594559555965597559855995600560156025603560456055606560756085609561056115612561356145615561656175618561956205621562256235624562556265627562856295630563156325633563456355636563756385639564056415642564356445645564656475648564956505651565256535654565556565657565856595660566156625663566456655666566756685669567056715672567356745675567656775678567956805681568256835684568556865687568856895690569156925693569456955696569756985699570057015702570357045705570657075708570957105711571257135714571557165717571857195720572157225723572457255726572757285729573057315732573357345735573657375738573957405741574257435744574557465747574857495750575157525753575457555756575757585759576057615762576357645765576657675768576957705771577257735774577557765777577857795780578157825783578457855786578757885789579057915792579357945795579657975798579958005801580258035804580558065807580858095810581158125813581458155816581758185819582058215822582358245825582658275828582958305831583258335834583558365837583858395840584158425843584458455846584758485849585058515852585358545855585658575858585958605861586258635864586558665867586858695870587158725873587458755876587758785879588058815882588358845885588658875888588958905891589258935894589558965897589858995900590159025903590459055906590759085909591059115912591359145915591659175918591959205921592259235924592559265927592859295930593159325933593459355936593759385939594059415942594359445945594659475948594959505951595259535954595559565957595859595960596159625963596459655966596759685969597059715972597359745975597659775978597959805981598259835984598559865987598859895990599159925993599459955996599759985999600060016002600360046005600660076008600960106011601260136014601560166017601860196020602160226023602460256026602760286029603060316032603360346035603660376038603960406041604260436044604560466047604860496050605160526053605460556056605760586059606060616062606360646065606660676068606960706071607260736074607560766077607860796080608160826083608460856086608760886089609060916092609360946095609660976098609961006101610261036104610561066107610861096110611161126113611461156116611761186119612061216122612361246125612661276128612961306131613261336134613561366137613861396140614161426143614461456146614761486149615061516152615361546155615661576158615961606161616261636164616561666167616861696170617161726173617461756176617761786179618061816182618361846185618661876188618961906191619261936194619561966197619861996200620162026203620462056206620762086209621062116212621362146215621662176218621962206221622262236224622562266227622862296230623162326233623462356236623762386239624062416242624362446245624662476248624962506251625262536254625562566257625862596260626162626263626462656266626762686269627062716272627362746275627662776278627962806281628262836284628562866287628862896290629162926293629462956296629762986299630063016302630363046305630663076308630963106311631263136314631563166317631863196320632163226323632463256326632763286329633063316332633363346335633663376338633963406341634263436344634563466347634863496350635163526353635463556356635763586359636063616362636363646365636663676368636963706371637263736374637563766377637863796380638163826383638463856386638763886389639063916392639363946395639663976398639964006401640264036404640564066407640864096410641164126413641464156416641764186419642064216422642364246425642664276428642964306431643264336434643564366437643864396440644164426443644464456446644764486449645064516452645364546455645664576458645964606461646264636464646564666467646864696470647164726473647464756476647764786479648064816482648364846485648664876488648964906491649264936494649564966497649864996500650165026503650465056506650765086509651065116512651365146515651665176518651965206521652265236524652565266527652865296530653165326533653465356536653765386539654065416542654365446545654665476548654965506551655265536554655565566557655865596560656165626563656465656566656765686569657065716572657365746575657665776578657965806581658265836584658565866587658865896590659165926593659465956596659765986599660066016602660366046605660666076608660966106611661266136614661566166617661866196620662166226623662466256626662766286629663066316632663366346635663666376638663966406641664266436644664566466647664866496650665166526653665466556656665766586659666066616662666366646665666666676668666966706671667266736674667566766677667866796680668166826683668466856686668766886689669066916692669366946695669666976698669967006701670267036704670567066707670867096710671167126713671467156716671767186719672067216722672367246725672667276728672967306731673267336734673567366737673867396740674167426743674467456746674767486749675067516752675367546755675667576758675967606761676267636764676567666767676867696770677167726773677467756776677767786779678067816782678367846785678667876788678967906791679267936794679567966797679867996800680168026803680468056806680768086809681068116812681368146815681668176818681968206821682268236824682568266827682868296830683168326833683468356836683768386839684068416842684368446845684668476848684968506851685268536854685568566857685868596860686168626863686468656866686768686869687068716872687368746875687668776878687968806881688268836884688568866887688868896890689168926893689468956896689768986899690069016902690369046905690669076908690969106911691269136914691569166917691869196920692169226923692469256926692769286929693069316932693369346935693669376938693969406941694269436944694569466947694869496950695169526953695469556956695769586959696069616962696369646965696669676968696969706971697269736974697569766977697869796980698169826983698469856986698769886989699069916992699369946995699669976998699970007001700270037004700570067007700870097010701170127013701470157016701770187019702070217022702370247025702670277028702970307031703270337034703570367037703870397040704170427043704470457046704770487049705070517052705370547055705670577058705970607061706270637064706570667067706870697070707170727073707470757076707770787079708070817082708370847085708670877088708970907091709270937094709570967097709870997100710171027103710471057106710771087109711071117112711371147115711671177118711971207121712271237124712571267127712871297130713171327133713471357136713771387139714071417142714371447145714671477148714971507151715271537154715571567157715871597160716171627163716471657166716771687169717071717172717371747175717671777178717971807181718271837184718571867187718871897190719171927193719471957196719771987199720072017202720372047205720672077208720972107211721272137214721572167217721872197220722172227223722472257226722772287229723072317232723372347235723672377238723972407241724272437244724572467247724872497250725172527253725472557256725772587259726072617262726372647265726672677268726972707271727272737274727572767277727872797280728172827283728472857286728772887289729072917292729372947295729672977298729973007301730273037304730573067307730873097310731173127313731473157316731773187319732073217322732373247325732673277328732973307331733273337334733573367337733873397340734173427343734473457346734773487349735073517352735373547355735673577358735973607361736273637364736573667367736873697370737173727373737473757376737773787379738073817382738373847385738673877388738973907391739273937394739573967397739873997400740174027403740474057406740774087409741074117412741374147415741674177418741974207421742274237424742574267427742874297430743174327433743474357436743774387439744074417442744374447445744674477448744974507451745274537454745574567457745874597460746174627463746474657466746774687469747074717472747374747475747674777478747974807481748274837484748574867487748874897490749174927493749474957496749774987499750075017502750375047505750675077508750975107511751275137514751575167517751875197520752175227523752475257526752775287529753075317532753375347535753675377538753975407541754275437544754575467547754875497550755175527553755475557556755775587559756075617562756375647565756675677568756975707571757275737574757575767577757875797580758175827583758475857586758775887589759075917592759375947595759675977598759976007601760276037604760576067607760876097610761176127613761476157616761776187619762076217622762376247625762676277628762976307631763276337634763576367637763876397640764176427643764476457646764776487649765076517652765376547655765676577658765976607661766276637664766576667667766876697670767176727673767476757676767776787679768076817682768376847685768676877688768976907691769276937694769576967697769876997700770177027703770477057706770777087709771077117712771377147715771677177718771977207721772277237724772577267727772877297730773177327733773477357736773777387739774077417742774377447745774677477748774977507751775277537754775577567757775877597760776177627763776477657766776777687769777077717772777377747775777677777778777977807781778277837784778577867787778877897790779177927793779477957796779777987799780078017802780378047805780678077808780978107811781278137814781578167817781878197820782178227823782478257826782778287829783078317832783378347835783678377838783978407841784278437844784578467847784878497850785178527853785478557856785778587859786078617862786378647865786678677868786978707871787278737874787578767877787878797880788178827883788478857886788778887889789078917892789378947895789678977898789979007901790279037904790579067907790879097910791179127913791479157916791779187919792079217922792379247925792679277928792979307931793279337934793579367937793879397940794179427943794479457946794779487949795079517952795379547955795679577958795979607961796279637964796579667967796879697970797179727973797479757976797779787979798079817982798379847985798679877988798979907991799279937994799579967997799879998000800180028003800480058006800780088009801080118012801380148015801680178018801980208021802280238024802580268027802880298030803180328033803480358036803780388039804080418042804380448045804680478048804980508051805280538054805580568057805880598060806180628063806480658066806780688069807080718072807380748075807680778078807980808081808280838084808580868087808880898090809180928093809480958096809780988099810081018102810381048105810681078108810981108111811281138114811581168117811881198120812181228123812481258126812781288129813081318132813381348135813681378138813981408141814281438144814581468147814881498150815181528153815481558156815781588159816081618162816381648165816681678168816981708171817281738174817581768177817881798180818181828183818481858186818781888189819081918192819381948195819681978198819982008201820282038204820582068207820882098210821182128213821482158216821782188219822082218222822382248225822682278228822982308231823282338234823582368237823882398240824182428243824482458246824782488249825082518252825382548255825682578258825982608261826282638264826582668267826882698270827182728273827482758276827782788279828082818282828382848285828682878288828982908291829282938294829582968297829882998300830183028303830483058306830783088309831083118312831383148315831683178318831983208321832283238324832583268327832883298330833183328333833483358336833783388339834083418342834383448345834683478348834983508351835283538354835583568357835883598360836183628363836483658366836783688369837083718372837383748375837683778378837983808381838283838384838583868387838883898390839183928393839483958396839783988399840084018402840384048405840684078408840984108411841284138414841584168417841884198420842184228423842484258426842784288429843084318432843384348435843684378438843984408441844284438444844584468447844884498450845184528453845484558456845784588459846084618462846384648465846684678468846984708471847284738474847584768477847884798480848184828483848484858486848784888489849084918492849384948495849684978498849985008501850285038504850585068507850885098510851185128513851485158516851785188519852085218522852385248525852685278528852985308531853285338534853585368537853885398540854185428543854485458546854785488549855085518552855385548555855685578558855985608561856285638564856585668567856885698570857185728573857485758576857785788579858085818582858385848585858685878588858985908591859285938594859585968597859885998600860186028603860486058606860786088609861086118612861386148615861686178618861986208621862286238624862586268627862886298630863186328633863486358636863786388639864086418642864386448645864686478648864986508651865286538654865586568657865886598660866186628663866486658666866786688669867086718672867386748675867686778678867986808681868286838684868586868687868886898690869186928693869486958696869786988699870087018702870387048705870687078708870987108711871287138714871587168717871887198720872187228723872487258726872787288729873087318732873387348735873687378738873987408741874287438744874587468747874887498750875187528753875487558756875787588759876087618762876387648765876687678768876987708771877287738774877587768777877887798780878187828783878487858786878787888789879087918792879387948795879687978798879988008801880288038804880588068807880888098810881188128813881488158816881788188819882088218822882388248825882688278828882988308831883288338834883588368837883888398840884188428843884488458846884788488849
  1. ///|/ Copyright (c) Prusa Research 2018 - 2023 Oleksandra Iushchenko @YuSanka, David Kocík @kocikdav, Enrico Turri @enricoturri1966, Lukáš Matěna @lukasmatena, Lukáš Hejl @hejllukas, Filip Sykala @Jony01, Vojtěch Bubník @bubnikv, Tomáš Mészáros @tamasmeszaros
  2. ///|/ Copyright (c) 2020 Henner Zeller
  3. ///|/
  4. ///|/ PrusaSlicer is released under the terms of the AGPLv3 or higher
  5. ///|/
  6. #include "bbs_3mf.hpp"
  7. #include "../libslic3r.h"
  8. #include "../Exception.hpp"
  9. #include "../Model.hpp"
  10. #include "../Preset.hpp"
  11. #include "../Utils.hpp"
  12. #include "../LocalesUtils.hpp"
  13. #include "../GCode.hpp"
  14. #include "../GCode/GCodeProcessor.hpp"
  15. #include "../GCode/ThumbnailData.hpp"
  16. #include "../Geometry.hpp"
  17. #include "../Semver.hpp"
  18. #include "../Time.hpp"
  19. #include "BBConfig.hpp"
  20. #include "../I18N.hpp"
  21. #include <limits>
  22. #include <mutex>
  23. #include <stdexcept>
  24. #include <iomanip>
  25. #include <boost/assign.hpp>
  26. #include <boost/bimap.hpp>
  27. #include <boost/algorithm/string/classification.hpp>
  28. #include <boost/algorithm/string/split.hpp>
  29. #include <boost/algorithm/string/predicate.hpp>
  30. #include <boost/algorithm/string/replace.hpp>
  31. #include <boost/filesystem/operations.hpp>
  32. #include <boost/nowide/fstream.hpp>
  33. #include <boost/nowide/cstdio.hpp>
  34. #include <boost/spirit/include/karma.hpp>
  35. #include <boost/spirit/include/qi_int.hpp>
  36. #include <boost/log/trivial.hpp>
  37. #include <boost/beast/core/detail/base64.hpp>
  38. #include <boost/property_tree/ptree.hpp>
  39. #include <boost/property_tree/xml_parser.hpp>
  40. #include <boost/foreach.hpp>
  41. //#include <openssl/md5.h>
  42. namespace pt = boost::property_tree;
  43. #include <tbb/parallel_for.h>
  44. #include <tbb/parallel_reduce.h>
  45. #include <expat.h>
  46. #include <Eigen/Dense>
  47. #include "miniz_extension.hpp"
  48. #include "nlohmann/json.hpp"
  49. //#include "TextConfiguration.hpp" //Susi_not_impl
  50. //#include "EmbossShape.hpp" //Susi_not_impl
  51. //#include "ExPolygonSerialize.hpp" //Susi_not_impl
  52. //#include "NSVGUtils.hpp"
  53. #include <fast_float/fast_float.h>
  54. // Slightly faster than sprintf("%.9g"), but there is an issue with the karma floating point formatter,
  55. // https://github.com/boostorg/spirit/pull/586
  56. // where the exported string is one digit shorter than it should be to guarantee lossless round trip.
  57. // The code is left here for the ocasion boost guys improve.
  58. #define EXPORT_3MF_USE_SPIRIT_KARMA_FP 0
  59. #define WRITE_ZIP_LANGUAGE_ENCODING 1
  60. // @see https://commons.apache.org/proper/commons-compress/apidocs/src-html/org/apache/commons/compress/archivers/zip/AbstractUnicodeExtraField.html
  61. struct ZipUnicodePathExtraField
  62. {
  63. static std::string encode(std::string const& u8path, std::string const& path) {
  64. std::string extra;
  65. if (u8path != path) {
  66. // 0x7075 - for Unicode filenames
  67. extra.push_back('\x75');
  68. extra.push_back('\x70');
  69. boost::uint16_t len = 5 + u8path.length();
  70. extra.push_back((char)(len & 0xff));
  71. extra.push_back((char)(len >> 8));
  72. auto crc = mz_crc32(0, (unsigned char *) path.c_str(), path.length());
  73. extra.push_back('\x01'); // version 1
  74. extra.append((char *)&crc, (char *)&crc + 4); // Little Endian
  75. extra.append(u8path);
  76. }
  77. return extra;
  78. }
  79. static std::string decode(std::string const& extra, std::string const& path = {}) {
  80. char const * p = extra.data();
  81. char const * e = p + extra.length();
  82. while (p + 4 < e) {
  83. boost::uint16_t len = ((boost::uint16_t)p[2]) | ((boost::uint16_t)p[3] << 8);
  84. if (p[0] == '\x75' && p[1] == '\x70' && len >= 5 && p + 4 + len < e && p[4] == '\x01') {
  85. return std::string(p + 9, p + 4 + len);
  86. }
  87. else {
  88. p += 4 + len;
  89. }
  90. }
  91. return Slic3r::decode_path(path.c_str());
  92. }
  93. };
  94. //FIXME this has potentially O(n^2) time complexity!
  95. std::string xml_escape(std::string text, bool is_marked/* = false*/)
  96. {
  97. std::string::size_type pos = 0;
  98. for (;;)
  99. {
  100. pos = text.find_first_of("\"\'&<>", pos);
  101. if (pos == std::string::npos)
  102. break;
  103. std::string replacement;
  104. switch (text[pos])
  105. {
  106. case '\"': replacement = "&quot;"; break;
  107. case '\'': replacement = "&apos;"; break;
  108. case '&': replacement = "&amp;"; break;
  109. case '<': replacement = is_marked ? "<" :"&lt;"; break;
  110. case '>': replacement = is_marked ? ">" :"&gt;"; break;
  111. default: break;
  112. }
  113. text.replace(pos, 1, replacement);
  114. pos += replacement.size();
  115. }
  116. return text;
  117. }
  118. // Definition of escape symbols https://www.w3.org/TR/REC-xml/#AVNormalize
  119. // During the read of xml attribute normalization of white spaces is applied
  120. // Soo for not lose white space character it is escaped before store
  121. std::string xml_escape_double_quotes_attribute_value(std::string text)
  122. {
  123. std::string::size_type pos = 0;
  124. for (;;) {
  125. pos = text.find_first_of("\"&<\r\n\t", pos);
  126. if (pos == std::string::npos) break;
  127. std::string replacement;
  128. switch (text[pos]) {
  129. case '\"': replacement = "&quot;"; break;
  130. case '&': replacement = "&amp;"; break;
  131. case '<': replacement = "&lt;"; break;
  132. case '\r': replacement = "&#xD;"; break;
  133. case '\n': replacement = "&#xA;"; break;
  134. case '\t': replacement = "&#x9;"; break;
  135. default: break;
  136. }
  137. text.replace(pos, 1, replacement);
  138. pos += replacement.size();
  139. }
  140. return text;
  141. }
  142. std::string xml_unescape(std::string s)
  143. {
  144. std::string ret;
  145. std::string::size_type i = 0;
  146. std::string::size_type pos = 0;
  147. while (i < s.size()) {
  148. std::string rep;
  149. if (s[i] == '&') {
  150. if (s.substr(i, 4) == "&lt;") {
  151. ret += s.substr(pos, i - pos) + "<";
  152. i += 4;
  153. pos = i;
  154. }
  155. else if (s.substr(i, 4) == "&gt;") {
  156. ret += s.substr(pos, i - pos) + ">";
  157. i += 4;
  158. pos = i;
  159. }
  160. else if (s.substr(i, 5) == "&amp;") {
  161. ret += s.substr(pos, i - pos) + "&";
  162. i += 5;
  163. pos = i;
  164. }
  165. else {
  166. ++i;
  167. }
  168. }
  169. else {
  170. ++i;
  171. }
  172. }
  173. ret += s.substr(pos);
  174. return ret;
  175. }
  176. void save_string_file(const std_path& p, const std::string& str)
  177. {
  178. boost::nowide::ofstream file;
  179. file.exceptions(std::ios_base::failbit | std::ios_base::badbit);
  180. file.open(p.generic_string(), std::ios_base::binary);
  181. file.write(str.c_str(), str.size());
  182. }
  183. #define BBL_JSON_KEY_VERSION "version"
  184. // VERSION NUMBERS
  185. // 0 : .3mf, files saved by older slic3r or other applications. No version definition in them.
  186. // 1 : Introduction of 3mf versioning. No other change in data saved into 3mf files.
  187. // 2 : Volumes' matrices and source data added to Metadata/Slic3r_PE_model.config file, meshes transformed back to their coordinate system on loading.
  188. // WARNING !! -> the version number has been rolled back to 1
  189. // the next change should use 3
  190. const unsigned int VERSION_BBS_3MF = 1;
  191. // Allow loading version 2 file as well.
  192. const unsigned int VERSION_BBS_3MF_COMPATIBLE = 2;
  193. const char* BBS_3MF_VERSION1 = "bamboo_slicer:Version3mf"; // definition of the metadata name saved into .model file
  194. const char* BBS_3MF_VERSION = "BambuStudio:3mfVersion"; //compatible with prusa currently
  195. // Painting gizmos data version numbers
  196. // 0 : initial version of fdm, seam, mm
  197. const unsigned int FDM_SUPPORTS_PAINTING_VERSION = 0;
  198. const unsigned int SEAM_PAINTING_VERSION = 0;
  199. const unsigned int MM_PAINTING_VERSION = 0;
  200. const std::string BBS_FDM_SUPPORTS_PAINTING_VERSION = "BambuStudio:FdmSupportsPaintingVersion";
  201. const std::string BBS_SEAM_PAINTING_VERSION = "BambuStudio:SeamPaintingVersion";
  202. const std::string BBS_MM_PAINTING_VERSION = "BambuStudio:MmPaintingVersion";
  203. const std::string BBL_MODEL_ID_TAG = "model_id";
  204. const std::string BBL_MODEL_NAME_TAG = "Title";
  205. const std::string BBL_ORIGIN_TAG = "Origin";
  206. const std::string BBL_DESIGNER_TAG = "Designer";
  207. const std::string BBL_DESIGNER_USER_ID_TAG = "DesignerUserId";
  208. const std::string BBL_DESIGNER_COVER_FILE_TAG = "DesignerCover";
  209. const std::string BBL_DESCRIPTION_TAG = "Description";
  210. const std::string BBL_COPYRIGHT_TAG = "CopyRight";
  211. const std::string BBL_COPYRIGHT_NORMATIVE_TAG = "Copyright";
  212. const std::string BBL_LICENSE_TAG = "License";
  213. const std::string BBL_REGION_TAG = "Region";
  214. const std::string BBL_MODIFICATION_TAG = "ModificationDate";
  215. const std::string BBL_CREATION_DATE_TAG = "CreationDate";
  216. const std::string BBL_APPLICATION_TAG = "Application";
  217. const std::string BBL_PROFILE_TITLE_TAG = "ProfileTitle";
  218. const std::string BBL_PROFILE_COVER_TAG = "ProfileCover";
  219. const std::string BBL_PROFILE_DESCRIPTION_TAG = "ProfileDescription";
  220. const std::string BBL_PROFILE_USER_ID_TAG = "ProfileUserId";
  221. const std::string BBL_PROFILE_USER_NAME_TAG = "ProfileUserName";
  222. const std::string MODEL_FOLDER = "3D/";
  223. const std::string MODEL_EXTENSION = ".model";
  224. const std::string MODEL_FILE = "3D/3dmodel.model"; // << this is the only format of the string which works with CURA
  225. const std::string MODEL_RELS_FILE = "3D/_rels/3dmodel.model.rels";
  226. //BBS: add metadata_folder
  227. const std::string METADATA_DIR = "Metadata/";
  228. const std::string ACCESOR_DIR = "accesories/";
  229. const std::string GCODE_EXTENSION = ".gcode";
  230. const std::string THUMBNAIL_EXTENSION = ".png";
  231. const std::string CALIBRATION_INFO_EXTENSION = ".json";
  232. const std::string CONTENT_TYPES_FILE = "[Content_Types].xml";
  233. const std::string RELATIONSHIPS_FILE = "_rels/.rels";
  234. const std::string THUMBNAIL_FILE = "Metadata/plate_1.png";
  235. const std::string THUMBNAIL_FOR_PRINTER_FILE = "Metadata/bbl_thumbnail.png";
  236. const std::string PRINTER_THUMBNAIL_SMALL_FILE = "/Auxiliaries/.thumbnails/thumbnail_small.png";
  237. const std::string PRINTER_THUMBNAIL_MIDDLE_FILE = "/Auxiliaries/.thumbnails/thumbnail_middle.png";
  238. const std::string _3MF_COVER_FILE = "/Auxiliaries/.thumbnails/thumbnail_3mf.png";
  239. //const std::string PRINT_CONFIG_FILE = "Metadata/Slic3r_PE.config";
  240. //const std::string MODEL_CONFIG_FILE = "Metadata/Slic3r_PE_model.config";
  241. const std::string BBS_PRINT_CONFIG_FILE = "Metadata/print_profile.config";
  242. const std::string BBS_PROJECT_CONFIG_FILE = "Metadata/project_settings.config";
  243. const std::string BBS_MODEL_CONFIG_FILE = "Metadata/model_settings.config";
  244. const std::string BBS_MODEL_CONFIG_RELS_FILE = "Metadata/_rels/model_settings.config.rels";
  245. const std::string SLICE_INFO_CONFIG_FILE = "Metadata/slice_info.config";
  246. const std::string BBS_LAYER_HEIGHTS_PROFILE_FILE = "Metadata/layer_heights_profile.txt";
  247. const std::string LAYER_CONFIG_RANGES_FILE = "Metadata/layer_config_ranges.xml";
  248. /*const std::string SLA_SUPPORT_POINTS_FILE = "Metadata/Slic3r_PE_sla_support_points.txt";
  249. const std::string SLA_DRAIN_HOLES_FILE = "Metadata/Slic3r_PE_sla_drain_holes.txt";*/
  250. const std::string CUSTOM_GCODE_PER_PRINT_Z_FILE = "Metadata/custom_gcode_per_layer.xml";
  251. const std::string AUXILIARY_DIR = "Auxiliaries/";
  252. const std::string PROJECT_EMBEDDED_PRINT_PRESETS_FILE = "Metadata/print_setting_";
  253. const std::string PROJECT_EMBEDDED_SLICE_PRESETS_FILE = "Metadata/process_settings_";
  254. const std::string PROJECT_EMBEDDED_FILAMENT_PRESETS_FILE = "Metadata/filament_settings_";
  255. const std::string PROJECT_EMBEDDED_PRINTER_PRESETS_FILE = "Metadata/machine_settings_";
  256. const std::string CUT_INFORMATION_FILE = "Metadata/cut_information.xml";
  257. const unsigned int AUXILIARY_STR_LEN = 12;
  258. const unsigned int METADATA_STR_LEN = 9;
  259. static constexpr const char* MODEL_TAG = "model";
  260. static constexpr const char* RESOURCES_TAG = "resources";
  261. static constexpr const char* COLOR_GROUP_TAG = "m:colorgroup";
  262. static constexpr const char* COLOR_TAG = "m:color";
  263. static constexpr const char* OBJECT_TAG = "object";
  264. static constexpr const char* MESH_TAG = "mesh";
  265. static constexpr const char* MESH_STAT_TAG = "mesh_stat";
  266. static constexpr const char* VERTICES_TAG = "vertices";
  267. static constexpr const char* VERTEX_TAG = "vertex";
  268. static constexpr const char* TRIANGLES_TAG = "triangles";
  269. static constexpr const char* TRIANGLE_TAG = "triangle";
  270. static constexpr const char* COMPONENTS_TAG = "components";
  271. static constexpr const char* COMPONENT_TAG = "component";
  272. static constexpr const char* BUILD_TAG = "build";
  273. static constexpr const char* ITEM_TAG = "item";
  274. static constexpr const char* METADATA_TAG = "metadata";
  275. static constexpr const char* FILAMENT_TAG = "filament";
  276. static constexpr const char* SLICE_WARNING_TAG = "warning";
  277. static constexpr const char* WARNING_MSG_TAG = "msg";
  278. static constexpr const char *FILAMENT_ID_TAG = "id";
  279. static constexpr const char* FILAMENT_TYPE_TAG = "type";
  280. static constexpr const char *FILAMENT_COLOR_TAG = "color";
  281. static constexpr const char *FILAMENT_USED_M_TAG = "used_m";
  282. static constexpr const char *FILAMENT_USED_G_TAG = "used_g";
  283. static constexpr const char* CONFIG_TAG = "config";
  284. static constexpr const char* VOLUME_TAG = "volume";
  285. static constexpr const char* PART_TAG = "part";
  286. static constexpr const char* PLATE_TAG = "plate";
  287. static constexpr const char* INSTANCE_TAG = "model_instance";
  288. //BBS
  289. static constexpr const char* ASSEMBLE_TAG = "assemble";
  290. static constexpr const char* ASSEMBLE_ITEM_TAG = "assemble_item";
  291. static constexpr const char* SLICE_HEADER_TAG = "header";
  292. static constexpr const char* SLICE_HEADER_ITEM_TAG = "header_item";
  293. // Deprecated: text_info
  294. static constexpr const char* TEXT_INFO_TAG = "text_info";
  295. static constexpr const char* TEXT_ATTR = "text";
  296. static constexpr const char* FONT_NAME_ATTR = "font_name";
  297. static constexpr const char* FONT_INDEX_ATTR = "font_index";
  298. static constexpr const char* FONT_SIZE_ATTR = "font_size";
  299. static constexpr const char* THICKNESS_ATTR = "thickness";
  300. static constexpr const char* EMBEDED_DEPTH_ATTR = "embeded_depth";
  301. static constexpr const char* ROTATE_ANGLE_ATTR = "rotate_angle";
  302. static constexpr const char* TEXT_GAP_ATTR = "text_gap";
  303. static constexpr const char* BOLD_ATTR = "bold";
  304. static constexpr const char* ITALIC_ATTR = "italic";
  305. static constexpr const char* SURFACE_TEXT_ATTR = "surface_text";
  306. static constexpr const char* KEEP_HORIZONTAL_ATTR = "keep_horizontal";
  307. static constexpr const char* HIT_MESH_ATTR = "hit_mesh";
  308. static constexpr const char* HIT_POSITION_ATTR = "hit_position";
  309. static constexpr const char* HIT_NORMAL_ATTR = "hit_normal";
  310. // BBS: encrypt
  311. static constexpr const char* RELATIONSHIP_TAG = "Relationship";
  312. static constexpr const char* PID_ATTR = "pid";
  313. static constexpr const char* PUUID_ATTR = "p:UUID";
  314. static constexpr const char* PUUID_LOWER_ATTR = "p:uuid";
  315. static constexpr const char* PPATH_ATTR = "p:path";
  316. static constexpr const char *OBJECT_UUID_SUFFIX = "-61cb-4c03-9d28-80fed5dfa1dc";
  317. static constexpr const char *OBJECT_UUID_SUFFIX2 = "-71cb-4c03-9d28-80fed5dfa1dc";
  318. static constexpr const char *SUB_OBJECT_UUID_SUFFIX = "-81cb-4c03-9d28-80fed5dfa1dc";
  319. static constexpr const char *COMPONENT_UUID_SUFFIX = "-b206-40ff-9872-83e8017abed1";
  320. static constexpr const char* BUILD_UUID = "2c7c17d8-22b5-4d84-8835-1976022ea369";
  321. static constexpr const char* BUILD_UUID_SUFFIX = "-b1ec-4553-aec9-835e5b724bb4";
  322. static constexpr const char* TARGET_ATTR = "Target";
  323. static constexpr const char* RELS_TYPE_ATTR = "Type";
  324. static constexpr const char* UNIT_ATTR = "unit";
  325. static constexpr const char* NAME_ATTR = "name";
  326. static constexpr const char* COLOR_ATTR = "color";
  327. static constexpr const char* TYPE_ATTR = "type";
  328. static constexpr const char* ID_ATTR = "id";
  329. static constexpr const char* X_ATTR = "x";
  330. static constexpr const char* Y_ATTR = "y";
  331. static constexpr const char* Z_ATTR = "z";
  332. static constexpr const char* V1_ATTR = "v1";
  333. static constexpr const char* V2_ATTR = "v2";
  334. static constexpr const char* V3_ATTR = "v3";
  335. static constexpr const char* OBJECTID_ATTR = "objectid";
  336. static constexpr const char* TRANSFORM_ATTR = "transform";
  337. // BBS
  338. static constexpr const char* OFFSET_ATTR = "offset";
  339. static constexpr const char* PRINTABLE_ATTR = "printable";
  340. static constexpr const char* INSTANCESCOUNT_ATTR = "instances_count";
  341. static constexpr const char* CUSTOM_SUPPORTS_ATTR = "paint_supports";
  342. static constexpr const char* CUSTOM_SEAM_ATTR = "paint_seam";
  343. static constexpr const char* MMU_SEGMENTATION_ATTR = "paint_color";
  344. // BBS
  345. static constexpr const char* FACE_PROPERTY_ATTR = "face_property";
  346. static constexpr const char* KEY_ATTR = "key";
  347. static constexpr const char* VALUE_ATTR = "value";
  348. static constexpr const char* FIRST_TRIANGLE_ID_ATTR = "firstid";
  349. static constexpr const char* LAST_TRIANGLE_ID_ATTR = "lastid";
  350. static constexpr const char* SUBTYPE_ATTR = "subtype";
  351. static constexpr const char* LOCK_ATTR = "locked";
  352. static constexpr const char* BED_TYPE_ATTR = "bed_type";
  353. static constexpr const char* PRINT_SEQUENCE_ATTR = "print_sequence";
  354. static constexpr const char* FIRST_LAYER_PRINT_SEQUENCE_ATTR = "first_layer_print_sequence";
  355. static constexpr const char* SPIRAL_VASE_MODE = "spiral_mode";
  356. static constexpr const char* GCODE_FILE_ATTR = "gcode_file";
  357. static constexpr const char* THUMBNAIL_FILE_ATTR = "thumbnail_file";
  358. static constexpr const char* TOP_FILE_ATTR = "top_file";
  359. static constexpr const char* PICK_FILE_ATTR = "pick_file";
  360. static constexpr const char* PATTERN_FILE_ATTR = "pattern_file";
  361. static constexpr const char* PATTERN_BBOX_FILE_ATTR = "pattern_bbox_file";
  362. static constexpr const char* OBJECT_ID_ATTR = "object_id";
  363. static constexpr const char* INSTANCEID_ATTR = "instance_id";
  364. static constexpr const char* IDENTIFYID_ATTR = "identify_id";
  365. static constexpr const char* PLATERID_ATTR = "plater_id";
  366. static constexpr const char* PLATER_NAME_ATTR = "plater_name";
  367. static constexpr const char* PLATE_IDX_ATTR = "index";
  368. static constexpr const char* PRINTER_MODEL_ID_ATTR = "printer_model_id";
  369. static constexpr const char* NOZZLE_DIAMETERS_ATTR = "nozzle_diameters";
  370. static constexpr const char* SLICE_PREDICTION_ATTR = "prediction";
  371. static constexpr const char* SLICE_WEIGHT_ATTR = "weight";
  372. static constexpr const char* TIMELAPSE_TYPE_ATTR = "timelapse_type";
  373. static constexpr const char* TIMELAPSE_ERROR_CODE_ATTR = "timelapse_error_code";
  374. static constexpr const char* OUTSIDE_ATTR = "outside";
  375. static constexpr const char* SUPPORT_USED_ATTR = "support_used";
  376. static constexpr const char* LABEL_OBJECT_ENABLED_ATTR = "label_object_enabled";
  377. static constexpr const char* SKIPPED_ATTR = "skipped";
  378. static constexpr const char* OBJECT_TYPE = "object";
  379. static constexpr const char* VOLUME_TYPE = "volume";
  380. static constexpr const char* PART_TYPE = "part";
  381. static constexpr const char* NAME_KEY = "name";
  382. static constexpr const char* VOLUME_TYPE_KEY = "volume_type";
  383. static constexpr const char* PART_TYPE_KEY = "part_type";
  384. static constexpr const char* MATRIX_KEY = "matrix";
  385. static constexpr const char* SOURCE_FILE_KEY = "source_file";
  386. static constexpr const char* SOURCE_OBJECT_ID_KEY = "source_object_id";
  387. static constexpr const char* SOURCE_VOLUME_ID_KEY = "source_volume_id";
  388. static constexpr const char* SOURCE_OFFSET_X_KEY = "source_offset_x";
  389. static constexpr const char* SOURCE_OFFSET_Y_KEY = "source_offset_y";
  390. static constexpr const char* SOURCE_OFFSET_Z_KEY = "source_offset_z";
  391. static constexpr const char* SOURCE_IN_INCHES = "source_in_inches";
  392. static constexpr const char* SOURCE_IN_METERS = "source_in_meters";
  393. static constexpr const char* MESH_SHARED_KEY = "mesh_shared";
  394. static constexpr const char* MESH_STAT_EDGES_FIXED = "edges_fixed";
  395. static constexpr const char* MESH_STAT_DEGENERATED_FACETS = "degenerate_facets";
  396. static constexpr const char* MESH_STAT_FACETS_REMOVED = "facets_removed";
  397. static constexpr const char* MESH_STAT_FACETS_RESERVED = "facets_reversed";
  398. static constexpr const char* MESH_STAT_BACKWARDS_EDGES = "backwards_edges";
  399. // Store / load of TextConfiguration
  400. static constexpr const char *TEXT_TAG = "slic3rpe:text";
  401. static constexpr const char *TEXT_DATA_ATTR = "text";
  402. // TextConfiguration::EmbossStyle
  403. static constexpr const char *STYLE_NAME_ATTR = "style_name";
  404. static constexpr const char *FONT_DESCRIPTOR_ATTR = "font_descriptor";
  405. static constexpr const char *FONT_DESCRIPTOR_TYPE_ATTR = "font_descriptor_type";
  406. // TextConfiguration::FontProperty
  407. static constexpr const char *CHAR_GAP_ATTR = "char_gap";
  408. static constexpr const char *LINE_GAP_ATTR = "line_gap";
  409. static constexpr const char *LINE_HEIGHT_ATTR = "line_height";
  410. static constexpr const char *BOLDNESS_ATTR = "boldness";
  411. static constexpr const char *SKEW_ATTR = "skew";
  412. static constexpr const char *PER_GLYPH_ATTR = "per_glyph";
  413. static constexpr const char *HORIZONTAL_ALIGN_ATTR = "horizontal";
  414. static constexpr const char *VERTICAL_ALIGN_ATTR = "vertical";
  415. static constexpr const char *COLLECTION_NUMBER_ATTR = "collection";
  416. static constexpr const char *FONT_FAMILY_ATTR = "family";
  417. static constexpr const char *FONT_FACE_NAME_ATTR = "face_name";
  418. static constexpr const char *FONT_STYLE_ATTR = "style";
  419. static constexpr const char *FONT_WEIGHT_ATTR = "weight";
  420. // Store / load of EmbossShape
  421. static constexpr const char *SHAPE_TAG = "slic3rpe:shape";
  422. static constexpr const char *SHAPE_SCALE_ATTR = "scale";
  423. static constexpr const char *UNHEALED_ATTR = "unhealed";
  424. static constexpr const char *SVG_FILE_PATH_ATTR = "filepath";
  425. static constexpr const char *SVG_FILE_PATH_IN_3MF_ATTR = "filepath3mf";
  426. // EmbossProjection
  427. static constexpr const char *DEPTH_ATTR = "depth";
  428. static constexpr const char *USE_SURFACE_ATTR = "use_surface";
  429. // static constexpr const char *FIX_TRANSFORMATION_ATTR = "transform";
  430. const unsigned int BBS_VALID_OBJECT_TYPES_COUNT = 2;
  431. const char* BBS_VALID_OBJECT_TYPES[] =
  432. {
  433. "model",
  434. "other"
  435. };
  436. const char* BBS_INVALID_OBJECT_TYPES[] =
  437. {
  438. "solidsupport",
  439. "support",
  440. "surface"
  441. };
  442. template <typename T>
  443. struct hex_wrap
  444. {
  445. T t;
  446. };
  447. namespace std {
  448. template <class _Elem, class _Traits, class _Arg>
  449. basic_ostream<_Elem, _Traits>& operator<<(basic_ostream<_Elem, _Traits>& ostr,
  450. const hex_wrap<_Arg>& wrap) { // insert by calling function with output stream and argument
  451. auto of = ostr.fill('0');
  452. ostr << setw(sizeof(_Arg) * 2) << std::hex << wrap.t;
  453. ostr << std::dec << setw(0);
  454. ostr.fill(of);
  455. return ostr;
  456. }
  457. }
  458. class version_error : public Slic3r::FileIOError
  459. {
  460. public:
  461. version_error(const std::string& what_arg) : Slic3r::FileIOError(what_arg) {}
  462. version_error(const char* what_arg) : Slic3r::FileIOError(what_arg) {}
  463. };
  464. const char* bbs_get_attribute_value_charptr(const char** attributes, unsigned int attributes_size, const char* attribute_key)
  465. {
  466. if ((attributes == nullptr) || (attributes_size == 0) || (attributes_size % 2 != 0) || (attribute_key == nullptr))
  467. return nullptr;
  468. for (unsigned int a = 0; a < attributes_size; a += 2) {
  469. if (::strcmp(attributes[a], attribute_key) == 0)
  470. return attributes[a + 1];
  471. }
  472. return nullptr;
  473. }
  474. std::string bbs_get_attribute_value_string(const char** attributes, unsigned int attributes_size, const char* attribute_key)
  475. {
  476. const char* text = bbs_get_attribute_value_charptr(attributes, attributes_size, attribute_key);
  477. return (text != nullptr) ? text : "";
  478. }
  479. float bbs_get_attribute_value_float(const char** attributes, unsigned int attributes_size, const char* attribute_key)
  480. {
  481. float value = 0.0f;
  482. if (const char *text = bbs_get_attribute_value_charptr(attributes, attributes_size, attribute_key); text != nullptr)
  483. fast_float::from_chars(text, text + strlen(text), value);
  484. return value;
  485. }
  486. int bbs_get_attribute_value_int(const char** attributes, unsigned int attributes_size, const char* attribute_key)
  487. {
  488. int value = 0;
  489. if (const char *text = bbs_get_attribute_value_charptr(attributes, attributes_size, attribute_key); text != nullptr)
  490. boost::spirit::qi::parse(text, text + strlen(text), boost::spirit::qi::int_, value);
  491. return value;
  492. }
  493. bool bbs_get_attribute_value_bool(const char** attributes, unsigned int attributes_size, const char* attribute_key)
  494. {
  495. const char* text = bbs_get_attribute_value_charptr(attributes, attributes_size, attribute_key);
  496. return (text != nullptr) ? (bool)::atoi(text) : true;
  497. }
  498. void add_vec3(std::stringstream &stream, const Slic3r::Vec3f &tr)
  499. {
  500. for (unsigned r = 0; r < 3; ++r) {
  501. stream << tr(r);
  502. if (r != 2)
  503. stream << " ";
  504. }
  505. }
  506. Slic3r::Vec3f get_vec3_from_string(const std::string &pos_str)
  507. {
  508. Slic3r::Vec3f pos(0, 0, 0);
  509. if (pos_str.empty())
  510. return pos;
  511. std::vector<std::string> values;
  512. boost::split(values, pos_str, boost::is_any_of(" "), boost::token_compress_on);
  513. if (values.size() != 3)
  514. return pos;
  515. for (int i = 0; i < 3; ++i)
  516. pos(i) = ::atof(values[i].c_str());
  517. return pos;
  518. }
  519. Slic3r::Transform3d bbs_get_transform_from_3mf_specs_string(const std::string& mat_str)
  520. {
  521. // check: https://3mf.io/3d-manufacturing-format/ or https://github.com/3MFConsortium/spec_core/blob/master/3MF%20Core%20Specification.md
  522. // to see how matrices are stored inside 3mf according to specifications
  523. Slic3r::Transform3d ret = Slic3r::Transform3d::Identity();
  524. if (mat_str.empty())
  525. // empty string means default identity matrix
  526. return ret;
  527. std::vector<std::string> mat_elements_str;
  528. boost::split(mat_elements_str, mat_str, boost::is_any_of(" "), boost::token_compress_on);
  529. unsigned int size = (unsigned int)mat_elements_str.size();
  530. if (size != 12)
  531. // invalid data, return identity matrix
  532. return ret;
  533. unsigned int i = 0;
  534. // matrices are stored into 3mf files as 4x3
  535. // we need to transpose them
  536. for (unsigned int c = 0; c < 4; ++c) {
  537. for (unsigned int r = 0; r < 3; ++r) {
  538. ret(r, c) = ::atof(mat_elements_str[i++].c_str());
  539. }
  540. }
  541. return ret;
  542. }
  543. Slic3r::Vec3d bbs_get_offset_from_3mf_specs_string(const std::string& vec_str)
  544. {
  545. Slic3r::Vec3d ofs2ass(0, 0, 0);
  546. if (vec_str.empty())
  547. // empty string means default zero offset
  548. return ofs2ass;
  549. std::vector<std::string> vec_elements_str;
  550. boost::split(vec_elements_str, vec_str, boost::is_any_of(" "), boost::token_compress_on);
  551. unsigned int size = (unsigned int)vec_elements_str.size();
  552. if (size != 3)
  553. // invalid data, return zero offset
  554. return ofs2ass;
  555. for (unsigned int i = 0; i < 3; i++) {
  556. ofs2ass(i) = ::atof(vec_elements_str[i].c_str());
  557. }
  558. return ofs2ass;
  559. }
  560. float bbs_get_unit_factor(const std::string& unit)
  561. {
  562. const char* text = unit.c_str();
  563. if (::strcmp(text, "micron") == 0)
  564. return 0.001f;
  565. else if (::strcmp(text, "centimeter") == 0)
  566. return 10.0f;
  567. else if (::strcmp(text, "inch") == 0)
  568. return 25.4f;
  569. else if (::strcmp(text, "foot") == 0)
  570. return 304.8f;
  571. else if (::strcmp(text, "meter") == 0)
  572. return 1000.0f;
  573. else
  574. // default "millimeters" (see specification)
  575. return 1.0f;
  576. }
  577. bool bbs_is_valid_object_type(const std::string& type)
  578. {
  579. // if the type is empty defaults to "model" (see specification)
  580. if (type.empty())
  581. return true;
  582. for (unsigned int i = 0; i < BBS_VALID_OBJECT_TYPES_COUNT; ++i) {
  583. if (::strcmp(type.c_str(), BBS_VALID_OBJECT_TYPES[i]) == 0)
  584. return true;
  585. }
  586. return false;
  587. }
  588. namespace Slic3r {
  589. void PlateData::parse_filament_info(GCodeProcessorResult *result)
  590. {
  591. if (!result) return;
  592. PrintEstimatedStatistics &ps = result->print_statistics;
  593. std::vector<float> m_filament_diameters = result->filament_diameters;
  594. std::vector<float> m_filament_densities = result->filament_densities;
  595. auto get_used_filament_from_volume = [m_filament_diameters, m_filament_densities](double volume, int extruder_id) {
  596. double koef = 0.001;
  597. std::pair<double, double> ret = {koef * volume / (PI * sqr(0.5 * m_filament_diameters[extruder_id])), volume * m_filament_densities[extruder_id] * 0.001};
  598. return ret;
  599. };
  600. for (auto it = ps.volumes_per_extruder.begin(); it != ps.volumes_per_extruder.end(); it++) {
  601. double volume = it->second;
  602. auto [used_filament_m, used_filament_g] = get_used_filament_from_volume(volume, it->first);
  603. FilamentInfo info;
  604. info.id = it->first;
  605. //if (ps.flush_per_filament.find(it->first) != ps.flush_per_filament.end()) {
  606. // volume = ps.flush_per_filament.at(it->first);
  607. // auto [flushed_filament_m, flushed_filament_g] = get_used_filament_from_volume(volume, it->first);
  608. // info.used_m = used_filament_m + flushed_filament_m;
  609. // info.used_g = used_filament_g + flushed_filament_g;
  610. //} else { //Susi_not_impl
  611. info.used_m = used_filament_m;
  612. info.used_g = used_filament_g;
  613. //}
  614. slice_filaments_info.push_back(info);
  615. }
  616. /* only for test
  617. GCodeProcessorResult::SliceWarning sw;
  618. sw.msg = BED_TEMP_TOO_HIGH_THAN_FILAMENT;
  619. sw.level = 1;
  620. result->warnings.push_back(sw);
  621. */
  622. //warnings = result->warnings; //Susi_not_impl
  623. }
  624. //! macro used to mark string used at localization,
  625. //! return same string
  626. #define L(s) (s)
  627. #define _(s) Slic3r::I18N::translate(s)
  628. // Base class with error messages management
  629. class _BBS_3MF_Base
  630. {
  631. mutable std::mutex mutex_error;
  632. mutable std::vector<std::string> m_errors;
  633. protected:
  634. void add_error(const std::string& error) const { std::lock_guard l(mutex_error); m_errors.push_back(error); }
  635. void clear_errors() { m_errors.clear(); }
  636. public:
  637. void log_errors()
  638. {
  639. for (const std::string& error : m_errors)
  640. BOOST_LOG_TRIVIAL(error) << error;
  641. }
  642. };
  643. class _BBS_3MF_Importer : public _BBS_3MF_Base
  644. {
  645. typedef std::pair<std::string, int> Id; // BBS: encrypt
  646. struct Component
  647. {
  648. Id object_id;
  649. Transform3d transform;
  650. explicit Component(Id object_id)
  651. : object_id(object_id)
  652. , transform(Transform3d::Identity())
  653. {
  654. }
  655. Component(Id object_id, const Transform3d& transform)
  656. : object_id(object_id)
  657. , transform(transform)
  658. {
  659. }
  660. };
  661. typedef std::vector<Component> ComponentsList;
  662. struct Geometry
  663. {
  664. std::vector<Vec3f> vertices;
  665. std::vector<Vec3i32> triangles;
  666. std::vector<std::string> custom_supports;
  667. std::vector<std::string> custom_seam;
  668. std::vector<std::string> mmu_segmentation;
  669. // BBS
  670. std::vector<std::string> face_properties;
  671. bool empty() { return vertices.empty() || triangles.empty(); }
  672. // backup & restore
  673. void swap(Geometry& o) {
  674. std::swap(vertices, o.vertices);
  675. std::swap(triangles, o.triangles);
  676. std::swap(custom_supports, o.custom_supports);
  677. std::swap(custom_seam, o.custom_seam);
  678. }
  679. void reset() {
  680. vertices.clear();
  681. triangles.clear();
  682. custom_supports.clear();
  683. custom_seam.clear();
  684. mmu_segmentation.clear();
  685. }
  686. };
  687. struct CurrentObject
  688. {
  689. // ID of the object inside the 3MF file, 1 based.
  690. int id;
  691. // Index of the ModelObject in its respective Model, zero based.
  692. int model_object_idx;
  693. Geometry geometry;
  694. ModelObject* object;
  695. ComponentsList components;
  696. //BBS: sub object id
  697. //int subobject_id;
  698. std::string name;
  699. std::string uuid;
  700. int pid{-1};
  701. //bool is_model_object;
  702. CurrentObject() { reset(); }
  703. void reset() {
  704. id = -1;
  705. model_object_idx = -1;
  706. geometry.reset();
  707. object = nullptr;
  708. components.clear();
  709. //BBS: sub object id
  710. uuid.clear();
  711. name.clear();
  712. }
  713. };
  714. struct CurrentConfig
  715. {
  716. int object_id {-1};
  717. int volume_id {-1};
  718. };
  719. struct CurrentInstance
  720. {
  721. int object_id;
  722. int instance_id;
  723. int identify_id;
  724. };
  725. struct Instance
  726. {
  727. ModelInstance* instance;
  728. Transform3d transform;
  729. Instance(ModelInstance* instance, const Transform3d& transform)
  730. : instance(instance)
  731. , transform(transform)
  732. {
  733. }
  734. };
  735. struct Metadata
  736. {
  737. std::string key;
  738. std::string value;
  739. Metadata(const std::string& key, const std::string& value)
  740. : key(key)
  741. , value(value)
  742. {
  743. }
  744. };
  745. typedef std::vector<Metadata> MetadataList;
  746. struct ObjectMetadata
  747. {
  748. struct VolumeMetadata
  749. {
  750. //BBS: refine the part logic
  751. unsigned int first_triangle_id;
  752. unsigned int last_triangle_id;
  753. int subobject_id;
  754. MetadataList metadata;
  755. RepairedMeshErrors mesh_stats;
  756. ModelVolumeType part_type;
  757. //std::optional<TextConfiguration> text_configuration; //Susi_not_impl
  758. //std::optional<EmbossShape> shape_configuration; //Susi_not_impl
  759. VolumeMetadata(unsigned int first_triangle_id, unsigned int last_triangle_id, ModelVolumeType type = ModelVolumeType::MODEL_PART)
  760. : first_triangle_id(first_triangle_id)
  761. , last_triangle_id(last_triangle_id)
  762. , part_type(type)
  763. , subobject_id(-1)
  764. {
  765. }
  766. VolumeMetadata(int sub_id, ModelVolumeType type = ModelVolumeType::MODEL_PART)
  767. : subobject_id(sub_id)
  768. , part_type(type)
  769. , first_triangle_id(0)
  770. , last_triangle_id(0)
  771. {
  772. }
  773. };
  774. typedef std::vector<VolumeMetadata> VolumeMetadataList;
  775. MetadataList metadata;
  776. VolumeMetadataList volumes;
  777. };
  778. struct CutObjectInfo
  779. {
  780. struct Connector
  781. {
  782. int volume_id;
  783. int type;
  784. float r_tolerance;
  785. float h_tolerance;
  786. };
  787. CutObjectBase id;
  788. std::vector<Connector> connectors;
  789. };
  790. // Map from a 1 based 3MF object ID to a 0 based ModelObject index inside m_model->objects.
  791. //typedef std::pair<std::string, int> Id; // BBS: encrypt
  792. typedef std::map<Id, CurrentObject> IdToCurrentObjectMap;
  793. typedef std::map<int, std::string> IndexToPathMap;
  794. typedef std::map<Id, int> IdToModelObjectMap;
  795. //typedef std::map<Id, ComponentsList> IdToAliasesMap;
  796. typedef std::vector<Instance> InstancesList;
  797. typedef std::map<int, ObjectMetadata> IdToMetadataMap;
  798. //typedef std::map<Id, Geometry> IdToGeometryMap;
  799. typedef std::map<int, std::vector<coordf_t>> IdToLayerHeightsProfileMap;
  800. typedef std::map<int, t_layer_config_ranges> IdToLayerConfigRangesMap;
  801. typedef std::map<int, CutObjectInfo> IdToCutObjectInfoMap;
  802. /*typedef std::map<int, std::vector<sla::SupportPoint>> IdToSlaSupportPointsMap;
  803. typedef std::map<int, std::vector<sla::DrainHole>> IdToSlaDrainHolesMap;*/
  804. using PathToEmbossShapeFileMap = std::map<std::string, std::shared_ptr<std::string>>;
  805. struct ObjectImporter
  806. {
  807. IdToCurrentObjectMap object_list;
  808. CurrentObject *current_object{nullptr};
  809. std::string object_path;
  810. std::string zip_path;
  811. _BBS_3MF_Importer *top_importer{nullptr};
  812. XML_Parser object_xml_parser;
  813. bool obj_parse_error { false };
  814. std::string obj_parse_error_message;
  815. //local parsed datas
  816. std::string obj_curr_metadata_name;
  817. std::string obj_curr_characters;
  818. float object_unit_factor;
  819. int object_current_color_group{-1};
  820. std::map<int, std::string> object_group_id_to_color;
  821. bool is_bbl_3mf { false };
  822. ObjectImporter(_BBS_3MF_Importer *importer, std::string file_path, std::string obj_path)
  823. {
  824. top_importer = importer;
  825. object_path = obj_path;
  826. zip_path = file_path;
  827. }
  828. ~ObjectImporter()
  829. {
  830. _destroy_object_xml_parser();
  831. }
  832. void _destroy_object_xml_parser()
  833. {
  834. if (object_xml_parser != nullptr) {
  835. XML_ParserFree(object_xml_parser);
  836. object_xml_parser = nullptr;
  837. }
  838. }
  839. void _stop_object_xml_parser(const std::string& msg = std::string())
  840. {
  841. assert(! obj_parse_error);
  842. assert(obj_parse_error_message.empty());
  843. assert(object_xml_parser != nullptr);
  844. obj_parse_error = true;
  845. obj_parse_error_message = msg;
  846. XML_StopParser(object_xml_parser, false);
  847. }
  848. bool object_parse_error() const { return obj_parse_error; }
  849. const char* object_parse_error_message() const {
  850. return obj_parse_error ?
  851. // The error was signalled by the user code, not the expat parser.
  852. (obj_parse_error_message.empty() ? "Invalid 3MF format" : obj_parse_error_message.c_str()) :
  853. // The error was signalled by the expat parser.
  854. XML_ErrorString(XML_GetErrorCode(object_xml_parser));
  855. }
  856. bool _extract_object_from_archive(mz_zip_archive& archive, const mz_zip_archive_file_stat& stat);
  857. bool extract_object_model()
  858. {
  859. mz_zip_archive archive;
  860. //mz_zip_archive_file_stat stat;
  861. mz_zip_zero_struct(&archive);
  862. if (!open_zip_reader(&archive, zip_path)) {
  863. top_importer->add_error("Unable to open the zipfile "+ zip_path);
  864. return false;
  865. }
  866. if (!top_importer->_extract_from_archive(archive, object_path, [this] (mz_zip_archive& archive, const mz_zip_archive_file_stat& stat) {
  867. return _extract_object_from_archive(archive, stat);
  868. }, top_importer->m_load_restore)) {
  869. std::string error_msg = std::string("Archive does not contain a valid model for ") + object_path;
  870. top_importer->add_error(error_msg);
  871. close_zip_reader(&archive);
  872. return false;
  873. }
  874. close_zip_reader(&archive);
  875. if (obj_parse_error) {
  876. //already add_error inside
  877. //top_importer->add_error(object_parse_error_message());
  878. BOOST_LOG_TRIVIAL(error) << __FUNCTION__ << ":" << __LINE__ << boost::format(", Found error while extrace object %1%\n")%object_path;
  879. return false;
  880. }
  881. return true;
  882. }
  883. bool _handle_object_start_model(const char** attributes, unsigned int num_attributes);
  884. bool _handle_object_end_model();
  885. bool _handle_object_start_resources(const char** attributes, unsigned int num_attributes);
  886. bool _handle_object_end_resources();
  887. bool _handle_object_start_object(const char** attributes, unsigned int num_attributes);
  888. bool _handle_object_end_object();
  889. bool _handle_object_start_color_group(const char **attributes, unsigned int num_attributes);
  890. bool _handle_object_end_color_group();
  891. bool _handle_object_start_color(const char **attributes, unsigned int num_attributes);
  892. bool _handle_object_end_color();
  893. bool _handle_object_start_mesh(const char** attributes, unsigned int num_attributes);
  894. bool _handle_object_end_mesh();
  895. bool _handle_object_start_vertices(const char** attributes, unsigned int num_attributes);
  896. bool _handle_object_end_vertices();
  897. bool _handle_object_start_vertex(const char** attributes, unsigned int num_attributes);
  898. bool _handle_object_end_vertex();
  899. bool _handle_object_start_triangles(const char** attributes, unsigned int num_attributes);
  900. bool _handle_object_end_triangles();
  901. bool _handle_object_start_triangle(const char** attributes, unsigned int num_attributes);
  902. bool _handle_object_end_triangle();
  903. bool _handle_object_start_components(const char** attributes, unsigned int num_attributes);
  904. bool _handle_object_end_components();
  905. bool _handle_object_start_component(const char** attributes, unsigned int num_attributes);
  906. bool _handle_object_end_component();
  907. bool _handle_object_start_metadata(const char** attributes, unsigned int num_attributes);
  908. bool _handle_object_end_metadata();
  909. void _handle_object_start_model_xml_element(const char* name, const char** attributes);
  910. void _handle_object_end_model_xml_element(const char* name);
  911. void _handle_object_xml_characters(const XML_Char* s, int len);
  912. // callbacks to parse the .model file of an object
  913. static void XMLCALL _handle_object_start_model_xml_element(void* userData, const char* name, const char** attributes);
  914. static void XMLCALL _handle_object_end_model_xml_element(void* userData, const char* name);
  915. static void XMLCALL _handle_object_xml_characters(void* userData, const XML_Char* s, int len);
  916. };
  917. // Version of the 3mf file
  918. unsigned int m_version;
  919. bool m_check_version = false;
  920. bool m_load_model = false;
  921. bool m_load_aux = false;
  922. bool m_load_config = false;
  923. // backup & restore
  924. bool m_load_restore = false;
  925. std::string m_backup_path;
  926. std::string m_origin_file;
  927. // Semantic version of Orca Slicer, that generated this 3MF.
  928. boost::optional<Semver> m_bambuslicer_generator_version;
  929. unsigned int m_fdm_supports_painting_version = 0;
  930. unsigned int m_seam_painting_version = 0;
  931. unsigned int m_mm_painting_version = 0;
  932. std::string m_model_id;
  933. std::string m_contry_code;
  934. std::string m_designer;
  935. std::string m_designer_user_id;
  936. std::string m_designer_cover;
  937. //ModelInfo model_info; //Susi_not_impl
  938. //BBLProject project_info; //Susi_not_impl
  939. std::string m_profile_title;
  940. std::string m_profile_cover;
  941. std::string m_Profile_description;
  942. std::string m_profile_user_id;
  943. std::string m_profile_user_name;
  944. XML_Parser m_xml_parser;
  945. // Error code returned by the application side of the parser. In that case the expat may not reliably deliver the error state
  946. // after returning from XML_Parse() function, thus we keep the error state here.
  947. bool m_parse_error { false };
  948. std::string m_parse_error_message;
  949. Model* m_model;
  950. float m_unit_factor;
  951. CurrentObject* m_curr_object{nullptr};
  952. IdToCurrentObjectMap m_current_objects;
  953. IndexToPathMap m_index_paths;
  954. IdToModelObjectMap m_objects;
  955. //IdToAliasesMap m_objects_aliases;
  956. InstancesList m_instances;
  957. //IdToGeometryMap m_geometries;
  958. //IdToGeometryMap m_orig_geometries; // backup & restore
  959. CurrentConfig m_curr_config;
  960. IdToMetadataMap m_objects_metadata;
  961. IdToCutObjectInfoMap m_cut_object_infos;
  962. IdToLayerHeightsProfileMap m_layer_heights_profiles;
  963. IdToLayerConfigRangesMap m_layer_config_ranges;
  964. /*IdToSlaSupportPointsMap m_sla_support_points;
  965. IdToSlaDrainHolesMap m_sla_drain_holes;*/
  966. PathToEmbossShapeFileMap m_path_to_emboss_shape_files;
  967. std::string m_curr_metadata_name;
  968. std::string m_curr_characters;
  969. std::string m_name;
  970. std::string m_sub_model_path;
  971. std::string m_start_part_path;
  972. std::string m_thumbnail_path;
  973. std::string m_thumbnail_middle;
  974. std::string m_thumbnail_small;
  975. std::vector<std::string> m_sub_model_paths;
  976. std::vector<ObjectImporter*> m_object_importers;
  977. std::map<int, ModelVolume*> m_shared_meshes;
  978. //BBS: plater related structures
  979. bool m_is_bbl_3mf { false };
  980. bool m_parsing_slice_info { false };
  981. PlateDataMaps m_plater_data;
  982. PlateData* m_curr_plater;
  983. CurrentInstance m_curr_instance;
  984. int m_current_color_group{-1};
  985. std::map<int, std::string> m_group_id_to_color;
  986. public:
  987. _BBS_3MF_Importer();
  988. ~_BBS_3MF_Importer();
  989. //BBS: add plate data related logic
  990. // add backup & restore logic
  991. bool load_model_from_file(const std::string& filename, Model& model, PlateDataPtrs& plate_data_list, std::vector<Preset*>& project_presets, DynamicPrintConfig& config,
  992. ConfigSubstitutionContext& config_substitutions, LoadStrategy strategy, bool& is_bbl_3mf, Semver& file_version, Import3mfProgressFn proFn = nullptr, /*BBLProject *project = nullptr,*/ int plate_id = 0);
  993. bool get_thumbnail(const std::string &filename, std::string &data);
  994. bool load_gcode_3mf_from_stream(std::istream & data, Model& model, PlateDataPtrs& plate_data_list, DynamicPrintConfig& config, Semver& file_version);
  995. unsigned int version() const { return m_version; }
  996. private:
  997. void _destroy_xml_parser();
  998. void _stop_xml_parser(const std::string& msg = std::string());
  999. bool parse_error() const { return m_parse_error; }
  1000. const char* parse_error_message() const {
  1001. return m_parse_error ?
  1002. // The error was signalled by the user code, not the expat parser.
  1003. (m_parse_error_message.empty() ? "Invalid 3MF format" : m_parse_error_message.c_str()) :
  1004. // The error was signalled by the expat parser.
  1005. XML_ErrorString(XML_GetErrorCode(m_xml_parser));
  1006. }
  1007. //BBS: add plate data related logic
  1008. // add backup & restore logic
  1009. bool _load_model_from_file(std::string filename, Model& model, PlateDataPtrs& plate_data_list, std::vector<Preset*>& project_presets, DynamicPrintConfig& config, ConfigSubstitutionContext& config_substitutions, Import3mfProgressFn proFn = nullptr,
  1010. /*BBLProject* project = nullptr, */int plate_id = 0);
  1011. bool _is_svg_shape_file(const std::string &filename) const;
  1012. bool _extract_from_archive(mz_zip_archive& archive, std::string const & path, std::function<bool (mz_zip_archive& archive, const mz_zip_archive_file_stat& stat)>, bool restore = false);
  1013. bool _extract_xml_from_archive(mz_zip_archive& archive, std::string const & path, XML_StartElementHandler start_handler, XML_EndElementHandler end_handler);
  1014. bool _extract_xml_from_archive(mz_zip_archive& archive, const mz_zip_archive_file_stat& stat, XML_StartElementHandler start_handler, XML_EndElementHandler end_handler);
  1015. bool _extract_model_from_archive(mz_zip_archive& archive, const mz_zip_archive_file_stat& stat);
  1016. void _extract_cut_information_from_archive(mz_zip_archive& archive, const mz_zip_archive_file_stat& stat, ConfigSubstitutionContext& config_substitutions);
  1017. void _extract_layer_heights_profile_config_from_archive(mz_zip_archive& archive, const mz_zip_archive_file_stat& stat);
  1018. void _extract_layer_config_ranges_from_archive(mz_zip_archive& archive, const mz_zip_archive_file_stat& stat, ConfigSubstitutionContext& config_substitutions);
  1019. void _extract_sla_support_points_from_archive(mz_zip_archive& archive, const mz_zip_archive_file_stat& stat);
  1020. void _extract_sla_drain_holes_from_archive(mz_zip_archive& archive, const mz_zip_archive_file_stat& stat);
  1021. void _extract_custom_gcode_per_print_z_from_archive(mz_zip_archive& archive, const mz_zip_archive_file_stat& stat);
  1022. void _extract_print_config_from_archive(mz_zip_archive& archive, const mz_zip_archive_file_stat& stat, DynamicPrintConfig& config, ConfigSubstitutionContext& subs_context, const std::string& archive_filename);
  1023. //BBS: add project config file logic
  1024. void _extract_project_config_from_archive(mz_zip_archive& archive, const mz_zip_archive_file_stat& stat, DynamicPrintConfig& config, ConfigSubstitutionContext& subs_context, Model& model);
  1025. //BBS: extract project embedded presets
  1026. void _extract_project_embedded_presets_from_archive(mz_zip_archive& archive, const mz_zip_archive_file_stat& stat, std::vector<Preset*>&project_presets, Model& model, Preset::Type type, bool use_json = true);
  1027. void _extract_auxiliary_file_from_archive(mz_zip_archive& archive, const mz_zip_archive_file_stat& stat, Model& model);
  1028. void _extract_file_from_archive(mz_zip_archive& archive, const mz_zip_archive_file_stat& stat);
  1029. void _extract_embossed_svg_shape_file(const std::string &filename, mz_zip_archive &archive, const mz_zip_archive_file_stat &stat);
  1030. // handlers to parse the .model file
  1031. void _handle_start_model_xml_element(const char* name, const char** attributes);
  1032. void _handle_end_model_xml_element(const char* name);
  1033. void _handle_xml_characters(const XML_Char* s, int len);
  1034. // handlers to parse the MODEL_CONFIG_FILE file
  1035. void _handle_start_config_xml_element(const char* name, const char** attributes);
  1036. void _handle_end_config_xml_element(const char* name);
  1037. bool _handle_start_model(const char** attributes, unsigned int num_attributes);
  1038. bool _handle_end_model();
  1039. bool _handle_start_resources(const char** attributes, unsigned int num_attributes);
  1040. bool _handle_end_resources();
  1041. bool _handle_start_object(const char** attributes, unsigned int num_attributes);
  1042. bool _handle_end_object();
  1043. bool _handle_start_color_group(const char **attributes, unsigned int num_attributes);
  1044. bool _handle_end_color_group();
  1045. bool _handle_start_color(const char **attributes, unsigned int num_attributes);
  1046. bool _handle_end_color();
  1047. bool _handle_start_mesh(const char** attributes, unsigned int num_attributes);
  1048. bool _handle_end_mesh();
  1049. bool _handle_start_vertices(const char** attributes, unsigned int num_attributes);
  1050. bool _handle_end_vertices();
  1051. bool _handle_start_vertex(const char** attributes, unsigned int num_attributes);
  1052. bool _handle_end_vertex();
  1053. bool _handle_start_triangles(const char** attributes, unsigned int num_attributes);
  1054. bool _handle_end_triangles();
  1055. bool _handle_start_triangle(const char** attributes, unsigned int num_attributes);
  1056. bool _handle_end_triangle();
  1057. bool _handle_start_components(const char** attributes, unsigned int num_attributes);
  1058. bool _handle_end_components();
  1059. bool _handle_start_component(const char** attributes, unsigned int num_attributes);
  1060. bool _handle_end_component();
  1061. bool _handle_start_build(const char** attributes, unsigned int num_attributes);
  1062. bool _handle_end_build();
  1063. bool _handle_start_item(const char** attributes, unsigned int num_attributes);
  1064. bool _handle_end_item();
  1065. bool _handle_start_metadata(const char** attributes, unsigned int num_attributes);
  1066. bool _handle_end_metadata();
  1067. bool _handle_start_text_configuration(const char** attributes, unsigned int num_attributes);
  1068. bool _handle_start_shape_configuration(const char **attributes, unsigned int num_attributes);
  1069. bool _create_object_instance(std::string const & path, int object_id, const Transform3d& transform, const bool printable, unsigned int recur_counter);
  1070. void _apply_transform(ModelInstance& instance, const Transform3d& transform);
  1071. bool _handle_start_config(const char** attributes, unsigned int num_attributes);
  1072. bool _handle_end_config();
  1073. bool _handle_start_config_object(const char** attributes, unsigned int num_attributes);
  1074. bool _handle_end_config_object();
  1075. bool _handle_start_config_volume(const char** attributes, unsigned int num_attributes);
  1076. bool _handle_start_config_volume_mesh(const char** attributes, unsigned int num_attributes);
  1077. bool _handle_end_config_volume();
  1078. bool _handle_end_config_volume_mesh();
  1079. bool _handle_start_config_metadata(const char** attributes, unsigned int num_attributes);
  1080. bool _handle_end_config_metadata();
  1081. bool _handle_start_config_filament(const char** attributes, unsigned int num_attributes);
  1082. bool _handle_end_config_filament();
  1083. bool _handle_start_config_warning(const char** attributes, unsigned int num_attributes);
  1084. bool _handle_end_config_warning();
  1085. //BBS: add plater config parse functions
  1086. bool _handle_start_config_plater(const char** attributes, unsigned int num_attributes);
  1087. bool _handle_end_config_plater();
  1088. bool _handle_start_config_plater_instance(const char** attributes, unsigned int num_attributes);
  1089. bool _handle_end_config_plater_instance();
  1090. bool _handle_start_assemble(const char** attributes, unsigned int num_attributes);
  1091. bool _handle_end_assemble();
  1092. bool _handle_start_assemble_item(const char** attributes, unsigned int num_attributes);
  1093. bool _handle_end_assemble_item();
  1094. bool _handle_start_text_info_item(const char **attributes, unsigned int num_attributes);
  1095. bool _handle_end_text_info_item();
  1096. // BBS: callbacks to parse the .rels file
  1097. static void XMLCALL _handle_start_relationships_element(void* userData, const char* name, const char** attributes);
  1098. static void XMLCALL _handle_end_relationships_element(void* userData, const char* name);
  1099. void _handle_start_relationships_element(const char* name, const char** attributes);
  1100. void _handle_end_relationships_element(const char* name);
  1101. bool _handle_start_relationship(const char** attributes, unsigned int num_attributes);
  1102. void _generate_current_object_list(std::vector<Component> &sub_objects, Id object_id, IdToCurrentObjectMap& current_objects);
  1103. bool _generate_volumes_new(ModelObject& object, const std::vector<Component> &sub_objects, const ObjectMetadata::VolumeMetadataList& volumes, const DynamicPrintConfig &config, ConfigSubstitutionContext& config_substitutions);
  1104. //bool _generate_volumes(ModelObject& object, const Geometry& geometry, const ObjectMetadata::VolumeMetadataList& volumes, ConfigSubstitutionContext& config_substitutions);
  1105. // callbacks to parse the .model file
  1106. static void XMLCALL _handle_start_model_xml_element(void* userData, const char* name, const char** attributes);
  1107. static void XMLCALL _handle_end_model_xml_element(void* userData, const char* name);
  1108. static void XMLCALL _handle_xml_characters(void* userData, const XML_Char* s, int len);
  1109. // callbacks to parse the MODEL_CONFIG_FILE file
  1110. static void XMLCALL _handle_start_config_xml_element(void* userData, const char* name, const char** attributes);
  1111. static void XMLCALL _handle_end_config_xml_element(void* userData, const char* name);
  1112. };
  1113. _BBS_3MF_Importer::_BBS_3MF_Importer()
  1114. : m_version(0)
  1115. , m_check_version(false)
  1116. , m_xml_parser(nullptr)
  1117. , m_model(nullptr)
  1118. , m_unit_factor(1.0f)
  1119. , m_curr_metadata_name("")
  1120. , m_curr_characters("")
  1121. , m_name("")
  1122. , m_curr_plater(nullptr)
  1123. {
  1124. }
  1125. _BBS_3MF_Importer::~_BBS_3MF_Importer()
  1126. {
  1127. _destroy_xml_parser();
  1128. clear_errors();
  1129. if (m_curr_object) {
  1130. delete m_curr_object;
  1131. m_curr_object = nullptr;
  1132. }
  1133. m_current_objects.clear();
  1134. m_index_paths.clear();
  1135. m_objects.clear();
  1136. m_instances.clear();
  1137. m_objects_metadata.clear();
  1138. m_curr_metadata_name.clear();
  1139. m_curr_characters.clear();
  1140. std::map<int, PlateData*>::iterator it = m_plater_data.begin();
  1141. while (it != m_plater_data.end())
  1142. {
  1143. delete it->second;
  1144. it++;
  1145. }
  1146. m_plater_data.clear();
  1147. }
  1148. //BBS: add plate data related logic
  1149. // add backup & restore logic
  1150. bool _BBS_3MF_Importer::load_model_from_file(const std::string& filename, Model& model, PlateDataPtrs& plate_data_list, std::vector<Preset*>& project_presets, DynamicPrintConfig& config,
  1151. ConfigSubstitutionContext& config_substitutions, LoadStrategy strategy, bool& is_bbl_3mf, Semver& file_version, Import3mfProgressFn proFn, /*BBLProject *project,*/ int plate_id)
  1152. {
  1153. m_version = 0;
  1154. m_fdm_supports_painting_version = 0;
  1155. m_seam_painting_version = 0;
  1156. m_mm_painting_version = 0;
  1157. m_check_version = strategy & LoadStrategy::CheckVersion;
  1158. //BBS: auxiliary data
  1159. m_load_model = strategy & LoadStrategy::LoadModel;
  1160. m_load_aux = strategy & LoadStrategy::LoadAuxiliary;
  1161. m_load_restore = strategy & LoadStrategy::Restore;
  1162. m_load_config = strategy & LoadStrategy::LoadConfig;
  1163. m_model = &model;
  1164. m_unit_factor = 1.0f;
  1165. m_curr_object = nullptr;
  1166. m_current_objects.clear();
  1167. m_index_paths.clear();
  1168. m_objects.clear();
  1169. //m_objects_aliases.clear();
  1170. m_instances.clear();
  1171. //m_geometries.clear();
  1172. m_curr_config.object_id = -1;
  1173. m_curr_config.volume_id = -1;
  1174. m_objects_metadata.clear();
  1175. m_layer_heights_profiles.clear();
  1176. m_layer_config_ranges.clear();
  1177. //m_sla_support_points.clear();
  1178. m_curr_metadata_name.clear();
  1179. m_curr_characters.clear();
  1180. //BBS: plater data init
  1181. m_plater_data.clear();
  1182. m_curr_instance.object_id = -1;
  1183. m_curr_instance.instance_id = -1;
  1184. m_curr_instance.identify_id = 0;
  1185. clear_errors();
  1186. // restore
  1187. //if (m_load_restore) {
  1188. // m_backup_path = filename.substr(0, filename.size() - 5);
  1189. // model.set_backup_path(m_backup_path);
  1190. // try {
  1191. // if (boost::filesystem::exists(model.get_backup_path() + "/origin.txt"))
  1192. // load_string_file(model.get_backup_path() + "/origin.txt", m_origin_file);
  1193. // } catch (...) {}
  1194. // save_string_file(
  1195. // model.get_backup_path() + "/lock.txt",
  1196. // boost::lexical_cast<std::string>(get_current_pid()));
  1197. //}
  1198. //else {
  1199. // m_backup_path = model.get_backup_path();
  1200. //}
  1201. bool result = _load_model_from_file(filename, model, plate_data_list, project_presets, config, config_substitutions, proFn, /*project,*/ plate_id);
  1202. is_bbl_3mf = m_is_bbl_3mf;
  1203. if (m_bambuslicer_generator_version)
  1204. file_version = *m_bambuslicer_generator_version;
  1205. if (result && plate_data_list.size() > 0) {
  1206. if(config.opt<ConfigOptionBool>("gcode_label_objects") == nullptr)
  1207. config.set_key_value("gcode_label_objects", config.get_option_def("gcode_label_objects")->default_value->clone());
  1208. bool has_label_objests = config.opt<ConfigOptionBool>("gcode_label_objects")->value;
  1209. for (PlateData *plate : plate_data_list)
  1210. has_label_objests = has_label_objests || plate->is_label_object_enabled;
  1211. config.opt<ConfigOptionBool>("gcode_label_objects")->value = has_label_objests;
  1212. std::string print_vars = config.opt<ConfigOptionString>("print_custom_variables") == nullptr ? "" : config.opt<ConfigOptionString>("print_custom_variables")->value;
  1213. if (print_vars.find("plate_name") == std::string::npos && plate_data_list.front()) {
  1214. std::string plate_name = "";
  1215. for (PlateData *plate : plate_data_list)
  1216. plate_name += plate_name.empty() ? plate->plate_name : std::string(" ; ") + plate->plate_name;
  1217. boost::replace_all(plate_name, "\"", "'");
  1218. if (!print_vars.empty() && print_vars.back() != '\n')
  1219. print_vars += std::string("\n");
  1220. print_vars += std::string("plate_name=\"") + plate_name + std::string("\"\n");
  1221. config.set_key_value("print_custom_variables", new ConfigOptionString(print_vars));
  1222. }
  1223. }
  1224. // save for restore
  1225. //if (result && m_load_aux && !m_load_restore) {
  1226. // save_string_file(model.get_backup_path() + "/origin.txt", filename);
  1227. //}
  1228. //if (m_load_restore && !result) // not clear failed backup data for later analyze
  1229. // model.set_backup_path("detach");
  1230. return result;
  1231. }
  1232. bool _BBS_3MF_Importer::get_thumbnail(const std::string &filename, std::string &data)
  1233. {
  1234. mz_zip_archive archive;
  1235. mz_zip_zero_struct(&archive);
  1236. struct close_lock
  1237. {
  1238. mz_zip_archive *archive;
  1239. void close()
  1240. {
  1241. if (archive) {
  1242. close_zip_reader(archive);
  1243. archive = nullptr;
  1244. }
  1245. }
  1246. ~close_lock() { close(); }
  1247. } lock{&archive};
  1248. if (!open_zip_reader(&archive, filename)) {
  1249. add_error("Unable to open the file "+filename);
  1250. return false;
  1251. }
  1252. // BBS: load relationships
  1253. if (!_extract_xml_from_archive(archive, RELATIONSHIPS_FILE, _handle_start_relationships_element, _handle_end_relationships_element))
  1254. return false;
  1255. if (m_thumbnail_middle.empty()) m_thumbnail_middle = m_thumbnail_path;
  1256. if (!m_thumbnail_middle.empty()) {
  1257. return _extract_from_archive(archive, m_thumbnail_middle, [&data](auto &archive, auto const &stat) -> bool {
  1258. data.resize(stat.m_uncomp_size);
  1259. return mz_zip_reader_extract_to_mem(&archive, stat.m_file_index, data.data(), data.size(), 0);
  1260. });
  1261. }
  1262. return _extract_from_archive(archive, THUMBNAIL_FILE, [&data](auto &archive, auto const &stat) -> bool {
  1263. data.resize(stat.m_uncomp_size);
  1264. return mz_zip_reader_extract_to_mem(&archive, stat.m_file_index, data.data(), data.size(), 0);
  1265. });
  1266. }
  1267. static size_t mz_zip_read_istream(void *pOpaque, mz_uint64 file_ofs, void *pBuf, size_t n)
  1268. {
  1269. auto & is = *reinterpret_cast<std::istream*>(pOpaque);
  1270. is.seekg(file_ofs, std::istream::beg);
  1271. if (!is)
  1272. return 0;
  1273. is.read((char *)pBuf, n);
  1274. return is.gcount();
  1275. }
  1276. bool _BBS_3MF_Importer::load_gcode_3mf_from_stream(std::istream &data, Model &model, PlateDataPtrs &plate_data_list, DynamicPrintConfig &config, Semver &file_version)
  1277. {
  1278. mz_zip_archive archive;
  1279. mz_zip_zero_struct(&archive);
  1280. archive.m_pRead = mz_zip_read_istream;
  1281. archive.m_pIO_opaque = &data;
  1282. data.seekg(0, std::istream::end);
  1283. mz_uint64 size = data.tellg();
  1284. data.seekg(0, std::istream::beg);
  1285. if (!mz_zip_reader_init(&archive, size, 0))
  1286. return false;
  1287. struct close_lock
  1288. {
  1289. mz_zip_archive *archive;
  1290. void close()
  1291. {
  1292. if (archive) {
  1293. mz_zip_reader_end(archive);
  1294. archive = nullptr;
  1295. }
  1296. }
  1297. ~close_lock() { close(); }
  1298. } lock{&archive};
  1299. // BBS: load relationships
  1300. if (!_extract_xml_from_archive(archive, RELATIONSHIPS_FILE, _handle_start_relationships_element, _handle_end_relationships_element))
  1301. return false;
  1302. if (m_start_part_path.empty())
  1303. return false;
  1304. //extract model files
  1305. m_model = &model;
  1306. if (!_extract_from_archive(archive, m_start_part_path, [this] (mz_zip_archive& archive, const mz_zip_archive_file_stat& stat) {
  1307. return _extract_model_from_archive(archive, stat);
  1308. })) {
  1309. add_error("Archive does not contain a valid model");
  1310. return false;
  1311. }
  1312. //if (!m_designer.empty()) {
  1313. // m_model->design_info = std::make_shared<ModelDesignInfo>();
  1314. // m_model->design_info->DesignerUserId = m_designer_user_id;
  1315. // m_model->design_info->Designer = m_designer;
  1316. //}
  1317. //m_model->profile_info = std::make_shared<ModelProfileInfo>();
  1318. //m_model->profile_info->ProfileTile = m_profile_title;
  1319. //m_model->profile_info->ProfileCover = m_profile_cover;
  1320. //m_model->profile_info->ProfileDescription = m_Profile_description;
  1321. //m_model->profile_info->ProfileUserId = m_profile_user_id;
  1322. //m_model->profile_info->ProfileUserName = m_profile_user_name;
  1323. //m_model->model_info = std::make_shared<ModelInfo>();
  1324. //m_model->model_info->load(model_info); //Susi_not_impl
  1325. if (m_thumbnail_middle.empty()) m_thumbnail_middle = m_thumbnail_path;
  1326. if (m_thumbnail_small.empty()) m_thumbnail_small = m_thumbnail_path;
  1327. if (!m_thumbnail_small.empty() && m_thumbnail_small.front() == '/')
  1328. m_thumbnail_small.erase(m_thumbnail_small.begin());
  1329. if (!m_thumbnail_middle.empty() && m_thumbnail_middle.front() == '/')
  1330. m_thumbnail_middle.erase(m_thumbnail_middle.begin());
  1331. //m_model->model_info->metadata_items.emplace("Thumbnail", m_thumbnail_middle);
  1332. //m_model->model_info->metadata_items.emplace("Poster", m_thumbnail_middle); //Susi_not_impl
  1333. // we then loop again the entries to read other files stored in the archive
  1334. mz_uint num_entries = mz_zip_reader_get_num_files(&archive);
  1335. mz_zip_archive_file_stat stat;
  1336. for (mz_uint i = 0; i < num_entries; ++i) {
  1337. if (mz_zip_reader_file_stat(&archive, i, &stat)) {
  1338. std::string name(stat.m_filename);
  1339. std::replace(name.begin(), name.end(), '\\', '/');
  1340. BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << ":" << __LINE__ << boost::format("extract %1%th file %2%, total=%3%\n")%(i+1)%name%num_entries;
  1341. if (boost::algorithm::iequals(name, BBS_PROJECT_CONFIG_FILE)) {
  1342. // extract slic3r print config file
  1343. ConfigSubstitutionContext config_substitutions(ForwardCompatibilitySubstitutionRule::Disable);
  1344. _extract_project_config_from_archive(archive, stat, config, config_substitutions, model);
  1345. }
  1346. else if (boost::algorithm::iequals(name, BBS_MODEL_CONFIG_FILE)) {
  1347. // extract slic3r model config file
  1348. if (!_extract_xml_from_archive(archive, stat, _handle_start_config_xml_element, _handle_end_config_xml_element)) {
  1349. add_error("Archive does not contain a valid model config");
  1350. return false;
  1351. }
  1352. }
  1353. else if (boost::algorithm::iequals(name, SLICE_INFO_CONFIG_FILE)) {
  1354. m_parsing_slice_info = true;
  1355. //extract slice info from archive
  1356. _extract_xml_from_archive(archive, stat, _handle_start_config_xml_element, _handle_end_config_xml_element);
  1357. m_parsing_slice_info = false;
  1358. }
  1359. }
  1360. }
  1361. //BBS: load the plate info into plate_data_list
  1362. std::map<int, PlateData*>::iterator it = m_plater_data.begin();
  1363. plate_data_list.clear();
  1364. plate_data_list.reserve(m_plater_data.size());
  1365. for (unsigned int i = 0; i < m_plater_data.size(); i++)
  1366. {
  1367. PlateData* plate = new PlateData();
  1368. plate_data_list.push_back(plate);
  1369. }
  1370. while (it != m_plater_data.end())
  1371. {
  1372. if (it->first > m_plater_data.size())
  1373. {
  1374. add_error("invalid plate index");
  1375. return false;
  1376. }
  1377. PlateData * plate = plate_data_list[it->first-1];
  1378. plate->locked = it->second->locked;
  1379. plate->plate_index = it->second->plate_index-1;
  1380. plate->obj_inst_map = it->second->obj_inst_map;
  1381. plate->gcode_file = it->second->gcode_file;
  1382. plate->gcode_prediction = it->second->gcode_prediction;
  1383. plate->gcode_weight = it->second->gcode_weight;
  1384. plate->toolpath_outside = it->second->toolpath_outside;
  1385. plate->is_support_used = it->second->is_support_used;
  1386. plate->is_label_object_enabled = it->second->is_label_object_enabled;
  1387. plate->skipped_objects = it->second->skipped_objects;
  1388. plate->slice_filaments_info = it->second->slice_filaments_info;
  1389. plate->warnings = it->second->warnings;
  1390. plate->thumbnail_file = it->second->thumbnail_file;
  1391. if (plate->thumbnail_file.empty()) {
  1392. plate->thumbnail_file = plate->gcode_file;
  1393. boost::algorithm::replace_all(plate->thumbnail_file, ".gcode", ".png");
  1394. }
  1395. //plate->pattern_file = it->second->pattern_file;
  1396. plate->top_file = it->second->top_file;
  1397. plate->pick_file = it->second->pick_file.empty();
  1398. plate->pattern_bbox_file = it->second->pattern_bbox_file.empty();
  1399. plate->config = it->second->config;
  1400. if (!plate->thumbnail_file.empty())
  1401. _extract_from_archive(archive, plate->thumbnail_file, [&pixels = plate_data_list[it->first - 1]->plate_thumbnail.pixels](auto &archive, auto const &stat) -> bool {
  1402. pixels.resize(stat.m_uncomp_size);
  1403. return mz_zip_reader_extract_to_mem(&archive, stat.m_file_index, pixels.data(), pixels.size(), 0);
  1404. });
  1405. BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << ":" << __LINE__ << boost::format(", plate %1%, thumbnail_file=%2%")%it->first %plate->thumbnail_file;
  1406. BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << ":" << __LINE__ << boost::format(", top_thumbnail_file=%1%, pick_thumbnail_file=%2%")%plate->top_file %plate->pick_file;
  1407. it++;
  1408. }
  1409. lock.close();
  1410. return true;
  1411. }
  1412. void _BBS_3MF_Importer::_destroy_xml_parser()
  1413. {
  1414. if (m_xml_parser != nullptr) {
  1415. XML_ParserFree(m_xml_parser);
  1416. m_xml_parser = nullptr;
  1417. }
  1418. }
  1419. void _BBS_3MF_Importer::_stop_xml_parser(const std::string &msg)
  1420. {
  1421. assert(! m_parse_error);
  1422. assert(m_parse_error_message.empty());
  1423. assert(m_xml_parser != nullptr);
  1424. m_parse_error = true;
  1425. m_parse_error_message = msg;
  1426. XML_StopParser(m_xml_parser, false);
  1427. }
  1428. //BBS: add plate data related logic
  1429. bool _BBS_3MF_Importer::_load_model_from_file(
  1430. std::string filename,
  1431. Model& model,
  1432. PlateDataPtrs& plate_data_list,
  1433. std::vector<Preset*>& project_presets,
  1434. DynamicPrintConfig& config,
  1435. ConfigSubstitutionContext& config_substitutions,
  1436. Import3mfProgressFn proFn,
  1437. /*BBLProject *project,*/ //Susi_not_impl
  1438. int plate_id)
  1439. {
  1440. bool cb_cancel = false;
  1441. //BBS progress point
  1442. // prepare restore
  1443. if (m_load_restore) {
  1444. BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << ":" << __LINE__ << boost::format("import 3mf IMPORT_STAGE_RESTORE\n");
  1445. if (proFn) {
  1446. proFn(IMPORT_STAGE_RESTORE, 0, 1, cb_cancel);
  1447. if (cb_cancel)
  1448. return false;
  1449. }
  1450. }
  1451. //BBS progress point
  1452. BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << ":" << __LINE__ << boost::format("import 3mf IMPORT_STAGE_OPEN, m_load_restore=%1%\n")%m_load_restore;
  1453. if (proFn) {
  1454. proFn(IMPORT_STAGE_OPEN, 0, 1, cb_cancel);
  1455. if (cb_cancel)
  1456. return false;
  1457. }
  1458. mz_zip_archive archive;
  1459. mz_zip_zero_struct(&archive);
  1460. struct close_lock
  1461. {
  1462. mz_zip_archive * archive;
  1463. void close() {
  1464. if (archive) {
  1465. close_zip_reader(archive);
  1466. archive = nullptr;
  1467. }
  1468. }
  1469. ~close_lock() {
  1470. close();
  1471. }
  1472. } lock{ &archive };
  1473. if (!open_zip_reader(&archive, filename)) {
  1474. add_error("Unable to open the file"+filename);
  1475. return false;
  1476. }
  1477. mz_uint num_entries = mz_zip_reader_get_num_files(&archive);
  1478. mz_zip_archive_file_stat stat;
  1479. m_name = boost::filesystem::path(filename).stem().string();
  1480. //BBS progress point
  1481. BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << ":" << __LINE__ << boost::format("import 3mf IMPORT_STAGE_READ_FILES\n");
  1482. if (proFn) {
  1483. proFn(IMPORT_STAGE_READ_FILES, 0, 3, cb_cancel);
  1484. if (cb_cancel)
  1485. return false;
  1486. }
  1487. // BBS: load relationships
  1488. if (!_extract_xml_from_archive(archive, RELATIONSHIPS_FILE, _handle_start_relationships_element, _handle_end_relationships_element))
  1489. return false;
  1490. if (m_start_part_path.empty())
  1491. return false;
  1492. // BBS: load sub models (Production Extension)
  1493. std::string sub_rels = m_start_part_path;
  1494. sub_rels.insert(boost::find_last(sub_rels, "/").end() - sub_rels.begin(), "_rels/");
  1495. sub_rels.append(".rels");
  1496. if (sub_rels.front() == '/') sub_rels = sub_rels.substr(1);
  1497. //check whether sub relation file is exist or not
  1498. int sub_index = mz_zip_reader_locate_file(&archive, sub_rels.c_str(), nullptr, 0);
  1499. if (sub_index == -1) {
  1500. //no submodule files found, use only one 3dmodel.model
  1501. }
  1502. else {
  1503. _extract_xml_from_archive(archive, sub_rels, _handle_start_relationships_element, _handle_end_relationships_element);
  1504. int index = 0;
  1505. #if 0
  1506. for (auto path : m_sub_model_paths) {
  1507. if (proFn) {
  1508. proFn(IMPORT_STAGE_READ_FILES, ++index, 3 + m_sub_model_paths.size(), cb_cancel);
  1509. if (cb_cancel)
  1510. return false;
  1511. }
  1512. else
  1513. ++index;
  1514. BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << ":" << __LINE__ << boost::format(", read %1%th sub model file %2%\n")%index %path;
  1515. m_sub_model_path = path;
  1516. if (!_extract_from_archive(archive, path, [this] (mz_zip_archive& archive, const mz_zip_archive_file_stat& stat) {
  1517. return _extract_model_from_archive(archive, stat);
  1518. }, m_load_restore)) {
  1519. add_error("Archive does not contain a valid model");
  1520. return false;
  1521. }
  1522. m_sub_model_path.clear();
  1523. }
  1524. #else
  1525. for (auto path : m_sub_model_paths) {
  1526. ObjectImporter *object_importer = new ObjectImporter(this, filename, path);
  1527. m_object_importers.push_back(object_importer);
  1528. }
  1529. bool object_load_result = true;
  1530. std::mutex mutex_load;
  1531. tbb::parallel_for(
  1532. tbb::blocked_range<size_t>(0, m_object_importers.size()),
  1533. [this, &mutex_load, &object_load_result](const tbb::blocked_range<size_t>& importer_range) {
  1534. CNumericLocalesSetter locales_setter;
  1535. for (size_t object_index = importer_range.begin(); object_index < importer_range.end(); ++ object_index) {
  1536. bool result = m_object_importers[object_index]->extract_object_model();
  1537. {
  1538. std::lock_guard l(mutex_load);
  1539. object_load_result &= result;
  1540. }
  1541. }
  1542. }
  1543. );
  1544. if (!object_load_result) {
  1545. BOOST_LOG_TRIVIAL(error) << __FUNCTION__ << ":" << __LINE__ << boost::format(", loading sub-objects error\n");
  1546. return false;
  1547. }
  1548. //merge these objects into one
  1549. for (auto obj_importer : m_object_importers) {
  1550. for (const IdToCurrentObjectMap::value_type& obj : obj_importer->object_list)
  1551. m_current_objects.insert({ std::move(obj.first), std::move(obj.second)});
  1552. for (auto group_color : obj_importer->object_group_id_to_color)
  1553. m_group_id_to_color.insert(std::move(group_color));
  1554. delete obj_importer;
  1555. }
  1556. m_object_importers.clear();
  1557. #endif
  1558. // BBS: load root model
  1559. if (proFn) {
  1560. proFn(IMPORT_STAGE_READ_FILES, 2, 3, cb_cancel);
  1561. if (cb_cancel)
  1562. return false;
  1563. }
  1564. }
  1565. //extract model files
  1566. if (!_extract_from_archive(archive, m_start_part_path, [this] (mz_zip_archive& archive, const mz_zip_archive_file_stat& stat) {
  1567. return _extract_model_from_archive(archive, stat);
  1568. })) {
  1569. add_error("Archive does not contain a valid model");
  1570. return false;
  1571. }
  1572. //if (!m_designer.empty()) {
  1573. // m_model->design_info = std::make_shared<ModelDesignInfo>();
  1574. // m_model->design_info->DesignerUserId = m_designer_user_id;
  1575. // m_model->design_info->Designer = m_designer;
  1576. //} //Susi_not_impl
  1577. //m_model->profile_info = std::make_shared<ModelProfileInfo>();
  1578. //m_model->profile_info->ProfileTile = m_profile_title;
  1579. //m_model->profile_info->ProfileCover = m_profile_cover;
  1580. //m_model->profile_info->ProfileDescription = m_Profile_description;
  1581. //m_model->profile_info->ProfileUserId = m_profile_user_id;
  1582. //m_model->profile_info->ProfileUserName = m_profile_user_name; //Susi_not_impl
  1583. //m_model->model_info = std::make_shared<ModelInfo>();
  1584. //m_model->model_info->load(model_info);
  1585. //if (!m_thumbnail_small.empty()) m_model->model_info->metadata_items.emplace("Thumbnail_Small", m_thumbnail_small);
  1586. //if (!m_thumbnail_middle.empty()) m_model->model_info->metadata_items.emplace("Thumbnail_Middle", m_thumbnail_middle); //Susi_not_impl
  1587. //got project id
  1588. //if (project) {
  1589. // project->project_model_id = m_model_id;
  1590. // project->project_country_code = m_contry_code;
  1591. //}
  1592. // Orca: skip version check
  1593. bool dont_load_config = !m_load_config;
  1594. // if (m_bambuslicer_generator_version) {
  1595. // Semver app_version = *(Semver::parse(SoftFever_VERSION));
  1596. // Semver file_version = *m_bambuslicer_generator_version;
  1597. // if (file_version.maj() != app_version.maj())
  1598. // dont_load_config = true;
  1599. // }
  1600. // else {
  1601. // m_bambuslicer_generator_version = Semver::parse("0.0.0.0");
  1602. // dont_load_config = true;
  1603. // }
  1604. // we then loop again the entries to read other files stored in the archive
  1605. for (mz_uint i = 0; i < num_entries; ++i) {
  1606. if (mz_zip_reader_file_stat(&archive, i, &stat)) {
  1607. //BBS progress point
  1608. if (proFn) {
  1609. proFn(IMPORT_STAGE_EXTRACT, i, num_entries, cb_cancel);
  1610. if (cb_cancel)
  1611. return false;
  1612. }
  1613. std::string name(stat.m_filename);
  1614. std::replace(name.begin(), name.end(), '\\', '/');
  1615. BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << ":" << __LINE__ << boost::format("extract %1%th file %2%, total=%3%")%(i+1)%name%num_entries;
  1616. if (name.find("/../") != std::string::npos) {
  1617. BOOST_LOG_TRIVIAL(warning) << __FUNCTION__ << boost::format(", find file path including /../, not valid, skip it\n");
  1618. continue;
  1619. }
  1620. if (boost::algorithm::iequals(name, BBS_LAYER_HEIGHTS_PROFILE_FILE)) {
  1621. // extract slic3r layer heights profile file
  1622. _extract_layer_heights_profile_config_from_archive(archive, stat);
  1623. }
  1624. else
  1625. if (boost::algorithm::iequals(name, LAYER_CONFIG_RANGES_FILE)) {
  1626. // extract slic3r layer config ranges file
  1627. _extract_layer_config_ranges_from_archive(archive, stat, config_substitutions);
  1628. }
  1629. //BBS: disable SLA related files currently
  1630. /*else if (boost::algorithm::iequals(name, SLA_SUPPORT_POINTS_FILE)) {
  1631. // extract sla support points file
  1632. _extract_sla_support_points_from_archive(archive, stat);
  1633. }
  1634. else if (boost::algorithm::iequals(name, SLA_DRAIN_HOLES_FILE)) {
  1635. // extract sla support points file
  1636. _extract_sla_drain_holes_from_archive(archive, stat);
  1637. }*/
  1638. //BBS: project setting file
  1639. //if (!dont_load_config && boost::algorithm::iequals(name, BBS_PRINT_CONFIG_FILE)) {
  1640. // extract slic3r print config file
  1641. // _extract_print_config_from_archive(archive, stat, config, config_substitutions, filename);
  1642. //} else
  1643. if (!dont_load_config && boost::algorithm::iequals(name, BBS_PROJECT_CONFIG_FILE)) {
  1644. // extract slic3r print config file
  1645. _extract_project_config_from_archive(archive, stat, config, config_substitutions, model);
  1646. }
  1647. else if (boost::algorithm::iequals(name, CUT_INFORMATION_FILE)) {
  1648. // extract object cut info
  1649. _extract_cut_information_from_archive(archive, stat, config_substitutions);
  1650. }
  1651. //BBS: project embedded presets
  1652. else if (!dont_load_config && boost::algorithm::istarts_with(name, PROJECT_EMBEDDED_PRINT_PRESETS_FILE)) {
  1653. // extract slic3r layer config ranges file
  1654. _extract_project_embedded_presets_from_archive(archive, stat, project_presets, model, Preset::TYPE_FFF_PRINT, false);
  1655. }
  1656. else if (!dont_load_config && boost::algorithm::istarts_with(name, PROJECT_EMBEDDED_SLICE_PRESETS_FILE)) {
  1657. // extract slic3r layer config ranges file
  1658. _extract_project_embedded_presets_from_archive(archive, stat, project_presets, model, Preset::TYPE_FFF_PRINT);
  1659. }
  1660. else if (!dont_load_config && boost::algorithm::istarts_with(name, PROJECT_EMBEDDED_FILAMENT_PRESETS_FILE)) {
  1661. // extract slic3r layer config ranges file
  1662. _extract_project_embedded_presets_from_archive(archive, stat, project_presets, model, Preset::TYPE_FFF_FILAMENT);
  1663. }
  1664. else if (!dont_load_config && boost::algorithm::istarts_with(name, PROJECT_EMBEDDED_PRINTER_PRESETS_FILE)) {
  1665. // extract slic3r layer config ranges file
  1666. _extract_project_embedded_presets_from_archive(archive, stat, project_presets, model, Preset::TYPE_PRINTER);
  1667. }
  1668. else if (!dont_load_config && boost::algorithm::iequals(name, CUSTOM_GCODE_PER_PRINT_Z_FILE)) {
  1669. // extract slic3r layer config ranges file
  1670. _extract_custom_gcode_per_print_z_from_archive(archive, stat);
  1671. }
  1672. else if (boost::algorithm::iequals(name, BBS_MODEL_CONFIG_FILE)) {
  1673. // extract slic3r model config file
  1674. if (!_extract_xml_from_archive(archive, stat, _handle_start_config_xml_element, _handle_end_config_xml_element)) {
  1675. add_error("Archive does not contain a valid model config");
  1676. return false;
  1677. }
  1678. }
  1679. else if (_is_svg_shape_file(name)) {
  1680. _extract_embossed_svg_shape_file(name, archive, stat);
  1681. }
  1682. else if (!dont_load_config && boost::algorithm::iequals(name, SLICE_INFO_CONFIG_FILE)) {
  1683. m_parsing_slice_info = true;
  1684. //extract slice info from archive
  1685. _extract_xml_from_archive(archive, stat, _handle_start_config_xml_element, _handle_end_config_xml_element);
  1686. m_parsing_slice_info = false;
  1687. }
  1688. else if (boost::algorithm::istarts_with(name, AUXILIARY_DIR)) {
  1689. // extract auxiliary directory to temp directory, do nothing for restore
  1690. if (m_load_aux && !m_load_restore)
  1691. _extract_auxiliary_file_from_archive(archive, stat, model);
  1692. }
  1693. else if (!dont_load_config && boost::algorithm::istarts_with(name, METADATA_DIR) && boost::algorithm::iends_with(name, GCODE_EXTENSION)) {
  1694. //load gcode files
  1695. _extract_file_from_archive(archive, stat);
  1696. }
  1697. else if (!dont_load_config && boost::algorithm::istarts_with(name, METADATA_DIR) && boost::algorithm::iends_with(name, THUMBNAIL_EXTENSION)) {
  1698. //BBS parsing pattern thumbnail and plate thumbnails
  1699. _extract_file_from_archive(archive, stat);
  1700. }
  1701. else if (!dont_load_config && boost::algorithm::istarts_with(name, METADATA_DIR) && boost::algorithm::iends_with(name, CALIBRATION_INFO_EXTENSION)) {
  1702. //BBS parsing pattern config files
  1703. _extract_file_from_archive(archive, stat);
  1704. }
  1705. else {
  1706. BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << ":" << __LINE__ << boost::format(", %1% skipped, already parsed or a directory or not supported\n")%name;
  1707. }
  1708. }
  1709. }
  1710. lock.close();
  1711. if (!m_is_bbl_3mf) {
  1712. // if the 3mf was not produced by OrcaSlicer and there is more than one instance,
  1713. // split the object in as many objects as instances
  1714. BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << ":" << __LINE__ << boost::format(", found 3mf from other vendor, split as instance");
  1715. for (const IdToModelObjectMap::value_type& object : m_objects) {
  1716. if (object.second >= int(m_model->objects.size())) {
  1717. add_error("3rd 3mf, invalid object, id: "+std::to_string(object.first.second));
  1718. return false;
  1719. }
  1720. ModelObject* model_object = m_model->objects[object.second];
  1721. if (model_object->instances.size() > 1) {
  1722. IdToCurrentObjectMap::const_iterator current_object = m_current_objects.find(object.first);
  1723. if (current_object == m_current_objects.end()) {
  1724. add_error("3rd 3mf, can not find object, id " + std::to_string(object.first.second));
  1725. return false;
  1726. }
  1727. std::vector<Component> object_id_list;
  1728. _generate_current_object_list(object_id_list, object.first, m_current_objects);
  1729. ObjectMetadata::VolumeMetadataList volumes;
  1730. ObjectMetadata::VolumeMetadataList* volumes_ptr = nullptr;
  1731. for (int k = 0; k < object_id_list.size(); k++)
  1732. {
  1733. Id object_id = object_id_list[k].object_id;
  1734. volumes.emplace_back(object_id.second);
  1735. }
  1736. // select as volumes
  1737. volumes_ptr = &volumes;
  1738. // for each instance after the 1st, create a new model object containing only that instance
  1739. // and copy into it the geometry
  1740. while (model_object->instances.size() > 1) {
  1741. ModelObject* new_model_object = m_model->add_object(*model_object);
  1742. new_model_object->clear_instances();
  1743. new_model_object->add_instance(*model_object->instances.back());
  1744. model_object->delete_last_instance();
  1745. if (!_generate_volumes_new(*new_model_object, object_id_list, *volumes_ptr, config, config_substitutions))
  1746. return false;
  1747. }
  1748. }
  1749. }
  1750. }
  1751. BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << ":" << __LINE__ << boost::format(", process group colors, size %1%\n")%m_group_id_to_color.size();
  1752. std::map<int, int> color_group_id_to_extruder_id_map;
  1753. std::map<std::string, int> color_to_extruder_id_map;
  1754. int extruder_id = 0;
  1755. for (auto group_iter = m_group_id_to_color.begin(); group_iter != m_group_id_to_color.end(); ++group_iter) {
  1756. auto color_iter = color_to_extruder_id_map.find(group_iter->second);
  1757. if (color_iter == color_to_extruder_id_map.end()) {
  1758. ++extruder_id;
  1759. color_to_extruder_id_map[group_iter->second] = extruder_id;
  1760. color_group_id_to_extruder_id_map[group_iter->first] = extruder_id;
  1761. } else {
  1762. color_group_id_to_extruder_id_map[group_iter->first] = color_iter->second;
  1763. }
  1764. }
  1765. BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << ":" << __LINE__ << boost::format(", begin to assemble objects, size %1%\n")%m_objects.size();
  1766. //only load objects in plate_id
  1767. PlateData* current_plate_data = nullptr;
  1768. if ((plate_id > 0) && (plate_id <= m_plater_data.size())) {
  1769. std::map<int, PlateData*>::iterator it =m_plater_data.find(plate_id);
  1770. if (it != m_plater_data.end()) {
  1771. current_plate_data = it->second;
  1772. }
  1773. }
  1774. for (const IdToModelObjectMap::value_type& object : m_objects) {
  1775. if (object.second >= int(m_model->objects.size())) {
  1776. add_error("invalid object, id: "+std::to_string(object.first.second));
  1777. return false;
  1778. }
  1779. if (current_plate_data) {
  1780. std::map<int, std::pair<int, int>>::iterator it = current_plate_data->obj_inst_map.find(object.first.second);
  1781. if (it == current_plate_data->obj_inst_map.end()) {
  1782. //not in current plate, skip
  1783. BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << ":" << __LINE__ << boost::format(", could not find object %1% in plate %2%, skip it\n")%object.first.second %plate_id;
  1784. continue;
  1785. }
  1786. }
  1787. ModelObject* model_object = m_model->objects[object.second];
  1788. /*IdToGeometryMap::const_iterator obj_geometry = m_geometries.find(object.first);
  1789. if (obj_geometry == m_geometries.end()) {
  1790. add_error("Unable to find object geometry");
  1791. return false;
  1792. }*/
  1793. // m_layer_heights_profiles are indexed by a 1 based model object index.
  1794. IdToLayerHeightsProfileMap::iterator obj_layer_heights_profile = m_layer_heights_profiles.find(object.second + 1);
  1795. if (obj_layer_heights_profile != m_layer_heights_profiles.end())
  1796. model_object->layer_height_profile.set(std::move(obj_layer_heights_profile->second));
  1797. // m_layer_config_ranges are indexed by a 1 based model object index.
  1798. IdToLayerConfigRangesMap::iterator obj_layer_config_ranges = m_layer_config_ranges.find(object.second + 1);
  1799. if (obj_layer_config_ranges != m_layer_config_ranges.end())
  1800. model_object->layer_config_ranges = std::move(obj_layer_config_ranges->second);
  1801. // m_sla_support_points are indexed by a 1 based model object index.
  1802. /*IdToSlaSupportPointsMap::iterator obj_sla_support_points = m_sla_support_points.find(object.second + 1);
  1803. if (obj_sla_support_points != m_sla_support_points.end() && !obj_sla_support_points->second.empty()) {
  1804. model_object->sla_support_points = std::move(obj_sla_support_points->second);
  1805. model_object->sla_points_status = sla::PointsStatus::UserModified;
  1806. }
  1807. IdToSlaDrainHolesMap::iterator obj_drain_holes = m_sla_drain_holes.find(object.second + 1);
  1808. if (obj_drain_holes != m_sla_drain_holes.end() && !obj_drain_holes->second.empty()) {
  1809. model_object->sla_drain_holes = std::move(obj_drain_holes->second);
  1810. }*/
  1811. std::vector<Component> object_id_list;
  1812. _generate_current_object_list(object_id_list, object.first, m_current_objects);
  1813. ObjectMetadata::VolumeMetadataList volumes;
  1814. ObjectMetadata::VolumeMetadataList* volumes_ptr = nullptr;
  1815. IdToMetadataMap::iterator obj_metadata = m_objects_metadata.find(object.first.second);
  1816. if (obj_metadata != m_objects_metadata.end()) {
  1817. // config data has been found, this model was saved using slic3r pe
  1818. std::map<std::string, std::string> key_values_to_deserialize;
  1819. // apply object's name and config data
  1820. for (const Metadata& metadata : obj_metadata->second.metadata) {
  1821. if (metadata.key == "name")
  1822. model_object->name = metadata.value;
  1823. //BBS: add module name
  1824. else if (metadata.key == "module")
  1825. ; //model_object->module_name = metadata.value; //Susi_not_impl
  1826. else
  1827. key_values_to_deserialize[metadata.key] = metadata.value;
  1828. //model_object->config.set_deserialize(metadata.key, metadata.value, config_substitutions);
  1829. }
  1830. convert_settings_from_bambu(key_values_to_deserialize, config, model_object->config, config_substitutions, false);
  1831. // select object's detected volumes
  1832. volumes_ptr = &obj_metadata->second.volumes;
  1833. }
  1834. else {
  1835. // config data not found, this model was not saved using slic3r pe
  1836. // add the entire geometry as the single volume to generate
  1837. //volumes.emplace_back(0, (int)obj_geometry->second.triangles.size() - 1);
  1838. for (int k = 0; k < object_id_list.size(); k++)
  1839. {
  1840. Id object_id = object_id_list[k].object_id;
  1841. volumes.emplace_back(object_id.second);
  1842. }
  1843. IdToCurrentObjectMap::const_iterator current_object = m_current_objects.find(object.first);
  1844. if (current_object != m_current_objects.end()) {
  1845. // get name
  1846. if (!current_object->second.name.empty())
  1847. model_object->name = current_object->second.name;
  1848. else
  1849. model_object->name = "Object_"+std::to_string(object.second+1);
  1850. // get color
  1851. auto extruder_itor = color_group_id_to_extruder_id_map.find(current_object->second.pid);
  1852. if (extruder_itor != color_group_id_to_extruder_id_map.end()) {
  1853. model_object->config.set_key_value("extruder", new ConfigOptionInt(extruder_itor->second));
  1854. }
  1855. }
  1856. // select as volumes
  1857. volumes_ptr = &volumes;
  1858. }
  1859. if (!_generate_volumes_new(*model_object, object_id_list, *volumes_ptr, config, config_substitutions))
  1860. return false;
  1861. // Apply cut information for object if any was loaded
  1862. // m_cut_object_ids are indexed by a 1 based model object index.
  1863. IdToCutObjectInfoMap::iterator cut_object_info = m_cut_object_infos.find(object.second + 1);
  1864. if (cut_object_info != m_cut_object_infos.end()) {
  1865. //model_object->cut_id = cut_object_info->second.id; //Susi_not_impl
  1866. int vol_cnt = int(model_object->volumes.size());
  1867. for (auto connector : cut_object_info->second.connectors) {
  1868. if (connector.volume_id < 0 || connector.volume_id >= vol_cnt) {
  1869. add_error("Invalid connector is found");
  1870. continue;
  1871. }
  1872. //model_object->volumes[connector.volume_id]->cut_info =
  1873. // ModelVolume::CutInfo(CutConnectorType(connector.type), connector.r_tolerance, connector.h_tolerance, true); //Susi_not_impl
  1874. }
  1875. }
  1876. }
  1877. // If instances contain a single volume, the volume offset should be 0,0,0
  1878. // This equals to say that instance world position and volume world position should match
  1879. // Correct all instances/volumes for which this does not hold
  1880. for (int obj_id = 0; obj_id < int(model.objects.size()); ++obj_id) {
  1881. ModelObject *o = model.objects[obj_id];
  1882. if (o->volumes.size() == 1) {
  1883. ModelVolume * v = o->volumes.front();
  1884. const Slic3r::Geometry::Transformation &first_inst_trafo = o->instances.front()->get_transformation();
  1885. const Vec3d world_vol_offset = (first_inst_trafo * v->get_transformation()).get_offset();
  1886. const Vec3d world_inst_offset = first_inst_trafo.get_offset();
  1887. if (!world_vol_offset.isApprox(world_inst_offset)) {
  1888. const Slic3r::Geometry::Transformation &vol_trafo = v->get_transformation();
  1889. for (int inst_id = 0; inst_id < int(o->instances.size()); ++inst_id) {
  1890. ModelInstance * i = o->instances[inst_id];
  1891. const Slic3r::Geometry::Transformation &inst_trafo = i->get_transformation();
  1892. i->set_offset((inst_trafo * vol_trafo).get_offset());
  1893. }
  1894. v->set_offset(Vec3d::Zero());
  1895. }
  1896. }
  1897. }
  1898. int object_idx = 0;
  1899. for (ModelObject* o : model.objects) {
  1900. int volume_idx = 0;
  1901. for (ModelVolume* v : o->volumes) {
  1902. if (v->source.input_file.empty() && v->type() == ModelVolumeType::MODEL_PART) {
  1903. v->source.input_file = filename;
  1904. if (v->source.volume_idx == -1)
  1905. v->source.volume_idx = volume_idx;
  1906. if (v->source.object_idx == -1)
  1907. v->source.object_idx = object_idx;
  1908. }
  1909. ++volume_idx;
  1910. }
  1911. ++object_idx;
  1912. }
  1913. const ConfigOptionStrings* filament_ids_opt = config.option<ConfigOptionStrings>("filament_settings_id");
  1914. int max_filament_id = filament_ids_opt ? filament_ids_opt->size() : std::numeric_limits<int>::max();
  1915. for (ModelObject* mo : m_model->objects) {
  1916. const ConfigOptionInt* extruder_opt = dynamic_cast<const ConfigOptionInt*>(mo->config.option("extruder"));
  1917. int extruder_id = 0;
  1918. if (extruder_opt != nullptr)
  1919. extruder_id = extruder_opt->get_int();
  1920. if (extruder_id == 0 || extruder_id > max_filament_id)
  1921. mo->config.set_key_value("extruder", new ConfigOptionInt(1));
  1922. if (mo->volumes.size() == 1) {
  1923. mo->volumes[0]->config.erase("extruder");
  1924. }
  1925. else {
  1926. for (ModelVolume* mv : mo->volumes) {
  1927. const ConfigOptionInt* vol_extruder_opt = dynamic_cast<const ConfigOptionInt*>(mv->config.option("extruder"));
  1928. if (vol_extruder_opt == nullptr)
  1929. continue;
  1930. if (vol_extruder_opt->get_int() == 0)
  1931. mv->config.erase("extruder");
  1932. else if (vol_extruder_opt->get_int() > max_filament_id)
  1933. mv->config.set_key_value("extruder", new ConfigOptionInt(1));
  1934. }
  1935. }
  1936. }
  1937. // // fixes the min z of the model if negative
  1938. // model.adjust_min_z();
  1939. //BBS progress point
  1940. BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << ":" << __LINE__ << boost::format("import 3mf IMPORT_STAGE_LOADING_PLATES, m_plater_data size %1%, m_backup_path %2%\n")%m_plater_data.size() %m_backup_path;
  1941. if (proFn) {
  1942. proFn(IMPORT_STAGE_LOADING_PLATES, 0, 1, cb_cancel);
  1943. if (cb_cancel)
  1944. return false;
  1945. }
  1946. //BBS: load the plate info into plate_data_list
  1947. std::map<int, PlateData*>::iterator it = m_plater_data.begin();
  1948. plate_data_list.clear();
  1949. plate_data_list.reserve(m_plater_data.size());
  1950. for (unsigned int i = 0; i < m_plater_data.size(); i++)
  1951. {
  1952. PlateData* plate = new PlateData();
  1953. plate_data_list.push_back(plate);
  1954. }
  1955. while (it != m_plater_data.end())
  1956. {
  1957. if (it->first > m_plater_data.size())
  1958. {
  1959. add_error("invalid plate index");
  1960. return false;
  1961. }
  1962. plate_data_list[it->first-1]->locked = it->second->locked;
  1963. plate_data_list[it->first-1]->plate_index = it->second->plate_index-1;
  1964. plate_data_list[it->first-1]->plate_name = it->second->plate_name;
  1965. plate_data_list[it->first-1]->obj_inst_map = it->second->obj_inst_map;
  1966. plate_data_list[it->first-1]->gcode_file = (m_load_restore || it->second->gcode_file.empty()) ? it->second->gcode_file : m_backup_path + "/" + it->second->gcode_file;
  1967. plate_data_list[it->first-1]->gcode_prediction = it->second->gcode_prediction;
  1968. plate_data_list[it->first-1]->gcode_weight = it->second->gcode_weight;
  1969. plate_data_list[it->first-1]->toolpath_outside = it->second->toolpath_outside;
  1970. plate_data_list[it->first-1]->is_support_used = it->second->is_support_used;
  1971. plate_data_list[it->first-1]->is_label_object_enabled = it->second->is_label_object_enabled;
  1972. plate_data_list[it->first-1]->slice_filaments_info = it->second->slice_filaments_info;
  1973. plate_data_list[it->first-1]->skipped_objects = it->second->skipped_objects;
  1974. plate_data_list[it->first-1]->warnings = it->second->warnings;
  1975. plate_data_list[it->first-1]->thumbnail_file = (m_load_restore || it->second->thumbnail_file.empty()) ? it->second->thumbnail_file : m_backup_path + "/" + it->second->thumbnail_file;
  1976. //plate_data_list[it->first-1]->pattern_file = (m_load_restore || it->second->pattern_file.empty()) ? it->second->pattern_file : m_backup_path + "/" + it->second->pattern_file;
  1977. plate_data_list[it->first-1]->top_file = (m_load_restore || it->second->top_file.empty()) ? it->second->top_file : m_backup_path + "/" + it->second->top_file;
  1978. plate_data_list[it->first-1]->pick_file = (m_load_restore || it->second->pick_file.empty()) ? it->second->pick_file : m_backup_path + "/" + it->second->pick_file;
  1979. plate_data_list[it->first-1]->pattern_bbox_file = (m_load_restore || it->second->pattern_bbox_file.empty()) ? it->second->pattern_bbox_file : m_backup_path + "/" + it->second->pattern_bbox_file;
  1980. plate_data_list[it->first-1]->config = it->second->config;
  1981. current_plate_data = plate_data_list[it->first - 1];
  1982. BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << ":" << __LINE__ << boost::format(", plate %1%, thumbnail_file=%2%")%it->first %plate_data_list[it->first-1]->thumbnail_file;
  1983. BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << ":" << __LINE__ << boost::format(", top_thumbnail_file=%1%, pick_thumbnail_file=%2%")%plate_data_list[it->first-1]->top_file %plate_data_list[it->first-1]->pick_file;
  1984. it++;
  1985. //update the arrange order
  1986. std::map<int, std::pair<int, int>>::iterator map_it = current_plate_data->obj_inst_map.begin();
  1987. while (map_it != current_plate_data->obj_inst_map.end()) {
  1988. int obj_index, obj_id = map_it->first, inst_index = map_it->second.first;
  1989. IndexToPathMap::iterator index_iter = m_index_paths.find(obj_id);
  1990. if (index_iter == m_index_paths.end()) {
  1991. BOOST_LOG_TRIVIAL(warning) << __FUNCTION__ << ":" << __LINE__
  1992. << boost::format(", can not find object from plate's obj_map, id=%1%, skip this object")%obj_id;
  1993. map_it++;
  1994. continue;
  1995. }
  1996. Id temp_id = std::make_pair(index_iter->second, index_iter->first);
  1997. IdToModelObjectMap::iterator object_item = m_objects.find(temp_id);
  1998. if (object_item == m_objects.end()) {
  1999. BOOST_LOG_TRIVIAL(warning) << __FUNCTION__ << ":" << __LINE__
  2000. << boost::format(", can not find object from plate's obj_map, ID <%1%, %2%>, skip this object")%index_iter->second %index_iter->first;
  2001. map_it++;
  2002. continue;
  2003. }
  2004. obj_index = object_item->second;
  2005. if (obj_index >= m_model->objects.size()) {
  2006. BOOST_LOG_TRIVIAL(warning) << __FUNCTION__ << ":" << __LINE__ << boost::format("invalid object id %1%\n")%obj_index;
  2007. map_it++;
  2008. continue;
  2009. }
  2010. ModelObject* obj = m_model->objects[obj_index];
  2011. if (inst_index >= obj->instances.size()) {
  2012. BOOST_LOG_TRIVIAL(warning) << __FUNCTION__ << ":" << __LINE__ << boost::format("invalid instance id %1%\n")%inst_index;
  2013. map_it++;
  2014. continue;
  2015. }
  2016. ModelInstance* inst = obj->instances[inst_index];
  2017. //inst->loaded_id = map_it->second.second; //Susi_not_impl
  2018. map_it++;
  2019. }
  2020. }
  2021. if ((plate_id > 0) && (plate_id <= m_plater_data.size())) {
  2022. //remove the no need objects
  2023. std::vector<size_t> delete_ids;
  2024. for (int index = 0; index < m_model->objects.size(); index++) {
  2025. ModelObject* obj = m_model->objects[index];
  2026. if (obj->volumes.size() == 0) {
  2027. //remove this model objects
  2028. delete_ids.push_back(index);
  2029. }
  2030. }
  2031. for (int index = delete_ids.size() - 1; index >= 0; index--)
  2032. m_model->delete_object(delete_ids[index]);
  2033. }
  2034. //BBS progress point
  2035. BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << ":" << __LINE__ << boost::format("import 3mf IMPORT_STAGE_FINISH\n");
  2036. if (proFn) {
  2037. proFn(IMPORT_STAGE_FINISH, 0, 1, cb_cancel);
  2038. if (cb_cancel)
  2039. return false;
  2040. }
  2041. return true;
  2042. }
  2043. bool _BBS_3MF_Importer::_is_svg_shape_file(const std::string &name) const {
  2044. return boost::starts_with(name, MODEL_FOLDER) && boost::ends_with(name, ".svg");
  2045. }
  2046. bool _BBS_3MF_Importer::_extract_from_archive(mz_zip_archive& archive, std::string const & path, std::function<bool (mz_zip_archive& archive, const mz_zip_archive_file_stat& stat)> extract, bool restore)
  2047. {
  2048. mz_uint num_entries = mz_zip_reader_get_num_files(&archive);
  2049. mz_zip_archive_file_stat stat;
  2050. std::string path2 = path;
  2051. if (path2.front() == '/') path2 = path2.substr(1);
  2052. // try utf8 encoding
  2053. int index = mz_zip_reader_locate_file(&archive, path2.c_str(), nullptr, 0);
  2054. if (index < 0) {
  2055. // try native encoding
  2056. std::string native_path = encode_path(path2.c_str());
  2057. index = mz_zip_reader_locate_file(&archive, native_path.c_str(), nullptr, 0);
  2058. }
  2059. if (index < 0) {
  2060. // try unicode path extra
  2061. std::string extra(1024, 0);
  2062. for (mz_uint i = 0; i < archive.m_total_files; ++i) {
  2063. size_t n = mz_zip_reader_get_extra(&archive, i, extra.data(), extra.size());
  2064. if (n > 0 && path2 == ZipUnicodePathExtraField::decode(extra.substr(0, n))) {
  2065. index = i;
  2066. break;
  2067. }
  2068. }
  2069. }
  2070. if (index < 0 || !mz_zip_reader_file_stat(&archive, index, &stat)) {
  2071. if (restore) {
  2072. std::vector<std::string> paths = {m_backup_path + path};
  2073. if (!m_origin_file.empty()) paths.push_back(m_origin_file);
  2074. for (auto & path2 : paths) {
  2075. bool result = false;
  2076. if (boost::filesystem::exists(path2)) {
  2077. mz_zip_archive archive;
  2078. mz_zip_zero_struct(&archive);
  2079. if (open_zip_reader(&archive, path2)) {
  2080. result = _extract_from_archive(archive, path, extract);
  2081. close_zip_reader(&archive);
  2082. }
  2083. }
  2084. if (result) return result;
  2085. }
  2086. }
  2087. char error_buf[1024];
  2088. ::snprintf(error_buf, 1024, "File %s not found from archive", path.c_str());
  2089. add_error(error_buf);
  2090. return false;
  2091. }
  2092. try
  2093. {
  2094. if (!extract(archive, stat)) {
  2095. return false;
  2096. }
  2097. }
  2098. catch (const std::exception& e)
  2099. {
  2100. // ensure the zip archive is closed and rethrow the exception
  2101. add_error(e.what());
  2102. return false;
  2103. }
  2104. return true;
  2105. }
  2106. bool _BBS_3MF_Importer::_extract_xml_from_archive(mz_zip_archive& archive, const std::string & path, XML_StartElementHandler start_handler, XML_EndElementHandler end_handler)
  2107. {
  2108. return _extract_from_archive(archive, path, [this, start_handler, end_handler](mz_zip_archive& archive, const mz_zip_archive_file_stat& stat) {
  2109. return _extract_xml_from_archive(archive, stat, start_handler, end_handler);
  2110. });
  2111. }
  2112. bool _BBS_3MF_Importer::_extract_xml_from_archive(mz_zip_archive& archive, const mz_zip_archive_file_stat& stat, XML_StartElementHandler start_handler, XML_EndElementHandler end_handler)
  2113. {
  2114. if (stat.m_uncomp_size == 0) {
  2115. add_error("Found invalid size");
  2116. return false;
  2117. }
  2118. _destroy_xml_parser();
  2119. m_xml_parser = XML_ParserCreate(nullptr);
  2120. if (m_xml_parser == nullptr) {
  2121. add_error("Unable to create parser");
  2122. return false;
  2123. }
  2124. XML_SetUserData(m_xml_parser, (void*)this);
  2125. XML_SetElementHandler(m_xml_parser, start_handler, end_handler);
  2126. XML_SetCharacterDataHandler(m_xml_parser, _BBS_3MF_Importer::_handle_xml_characters);
  2127. void* parser_buffer = XML_GetBuffer(m_xml_parser, (int)stat.m_uncomp_size);
  2128. if (parser_buffer == nullptr) {
  2129. add_error("Unable to create buffer");
  2130. return false;
  2131. }
  2132. mz_bool res = mz_zip_reader_extract_file_to_mem(&archive, stat.m_filename, parser_buffer, (size_t)stat.m_uncomp_size, 0);
  2133. if (res == 0) {
  2134. add_error("Error while reading config data to buffer");
  2135. return false;
  2136. }
  2137. if (!XML_ParseBuffer(m_xml_parser, (int)stat.m_uncomp_size, 1)) {
  2138. char error_buf[1024];
  2139. ::snprintf(error_buf, 1024, "Error (%s) while parsing xml file at line %d", XML_ErrorString(XML_GetErrorCode(m_xml_parser)), (int)XML_GetCurrentLineNumber(m_xml_parser));
  2140. add_error(error_buf);
  2141. return false;
  2142. }
  2143. return true;
  2144. }
  2145. bool _BBS_3MF_Importer::_extract_model_from_archive(mz_zip_archive& archive, const mz_zip_archive_file_stat& stat)
  2146. {
  2147. if (stat.m_uncomp_size == 0) {
  2148. add_error("Found invalid size");
  2149. return false;
  2150. }
  2151. _destroy_xml_parser();
  2152. m_xml_parser = XML_ParserCreate(nullptr);
  2153. if (m_xml_parser == nullptr) {
  2154. add_error("Unable to create parser");
  2155. return false;
  2156. }
  2157. XML_SetUserData(m_xml_parser, (void*)this);
  2158. XML_SetElementHandler(m_xml_parser, _BBS_3MF_Importer::_handle_start_model_xml_element, _BBS_3MF_Importer::_handle_end_model_xml_element);
  2159. XML_SetCharacterDataHandler(m_xml_parser, _BBS_3MF_Importer::_handle_xml_characters);
  2160. struct CallbackData
  2161. {
  2162. XML_Parser& parser;
  2163. _BBS_3MF_Importer& importer;
  2164. const mz_zip_archive_file_stat& stat;
  2165. CallbackData(XML_Parser& parser, _BBS_3MF_Importer& importer, const mz_zip_archive_file_stat& stat) : parser(parser), importer(importer), stat(stat) {}
  2166. };
  2167. CallbackData data(m_xml_parser, *this, stat);
  2168. mz_bool res = 0;
  2169. try
  2170. {
  2171. mz_file_write_func callback = [](void* pOpaque, mz_uint64 file_ofs, const void* pBuf, size_t n)->size_t {
  2172. CallbackData* data = (CallbackData*)pOpaque;
  2173. if (!XML_Parse(data->parser, (const char*)pBuf, (int)n, (file_ofs + n == data->stat.m_uncomp_size) ? 1 : 0) || data->importer.parse_error()) {
  2174. char error_buf[1024];
  2175. ::snprintf(error_buf, 1024, "Error (%s) while parsing '%s' at line %d", data->importer.parse_error_message(), data->stat.m_filename, (int)XML_GetCurrentLineNumber(data->parser));
  2176. throw Slic3r::FileIOError(error_buf);
  2177. }
  2178. return n;
  2179. };
  2180. void* opaque = &data;
  2181. res = mz_zip_reader_extract_to_callback(&archive, stat.m_file_index, callback, opaque, 0);
  2182. }
  2183. catch (const version_error& e)
  2184. {
  2185. // rethrow the exception
  2186. throw Slic3r::FileIOError(e.what());
  2187. }
  2188. catch (std::exception& e)
  2189. {
  2190. add_error(e.what());
  2191. return false;
  2192. }
  2193. if (res == 0) {
  2194. add_error("Error while extracting model data from zip archive");
  2195. return false;
  2196. }
  2197. return true;
  2198. }
  2199. void _BBS_3MF_Importer::_extract_cut_information_from_archive(mz_zip_archive &archive, const mz_zip_archive_file_stat &stat, ConfigSubstitutionContext &config_substitutions)
  2200. {
  2201. if (stat.m_uncomp_size > 0) {
  2202. std::string buffer((size_t)stat.m_uncomp_size, 0);
  2203. mz_bool res = mz_zip_reader_extract_file_to_mem(&archive, stat.m_filename, (void *) buffer.data(), (size_t) stat.m_uncomp_size, 0);
  2204. if (res == 0) {
  2205. add_error("Error while reading cut information data to buffer");
  2206. return;
  2207. }
  2208. std::istringstream iss(buffer); // wrap returned xml to istringstream
  2209. pt::ptree objects_tree;
  2210. pt::read_xml(iss, objects_tree);
  2211. for (const auto& object : objects_tree.get_child("objects")) {
  2212. pt::ptree object_tree = object.second;
  2213. int obj_idx = object_tree.get<int>("<xmlattr>.id", -1);
  2214. if (obj_idx <= 0) {
  2215. add_error("Found invalid object id");
  2216. continue;
  2217. }
  2218. IdToCutObjectInfoMap::iterator object_item = m_cut_object_infos.find(obj_idx);
  2219. if (object_item != m_cut_object_infos.end()) {
  2220. add_error("Found duplicated cut_object_id");
  2221. continue;
  2222. }
  2223. CutObjectBase cut_id;
  2224. std::vector<CutObjectInfo::Connector> connectors;
  2225. for (const auto& obj_cut_info : object_tree) {
  2226. if (obj_cut_info.first == "cut_id") {
  2227. pt::ptree cut_id_tree = obj_cut_info.second;
  2228. cut_id = CutObjectBase(ObjectID( cut_id_tree.get<size_t>("<xmlattr>.id")),
  2229. cut_id_tree.get<size_t>("<xmlattr>.check_sum"),
  2230. cut_id_tree.get<size_t>("<xmlattr>.connectors_cnt"));
  2231. }
  2232. if (obj_cut_info.first == "connectors") {
  2233. pt::ptree cut_connectors_tree = obj_cut_info.second;
  2234. for (const auto& cut_connector : cut_connectors_tree) {
  2235. if (cut_connector.first != "connector")
  2236. continue;
  2237. pt::ptree connector_tree = cut_connector.second;
  2238. CutObjectInfo::Connector connector = {connector_tree.get<int>("<xmlattr>.volume_id"),
  2239. connector_tree.get<int>("<xmlattr>.type"),
  2240. connector_tree.get<float>("<xmlattr>.r_tolerance"),
  2241. connector_tree.get<float>("<xmlattr>.h_tolerance")};
  2242. connectors.emplace_back(connector);
  2243. }
  2244. }
  2245. }
  2246. CutObjectInfo cut_info {cut_id, connectors};
  2247. m_cut_object_infos.insert({ obj_idx, cut_info });
  2248. }
  2249. }
  2250. }
  2251. void _BBS_3MF_Importer::_extract_print_config_from_archive(mz_zip_archive& archive, const mz_zip_archive_file_stat& stat, DynamicPrintConfig& config, ConfigSubstitutionContext& config_substitutions, const std::string& archive_filename)
  2252. {
  2253. if (stat.m_uncomp_size > 0) {
  2254. std::string buffer((size_t)stat.m_uncomp_size, 0);
  2255. mz_bool res = mz_zip_reader_extract_file_to_mem(&archive, stat.m_filename, (void*)buffer.data(), (size_t)stat.m_uncomp_size, 0);
  2256. if (res == 0) {
  2257. add_error("Error while reading config data to buffer");
  2258. return;
  2259. }
  2260. ConfigBase::load_from_gcode_string_legacy(config, buffer.data(), config_substitutions);
  2261. }
  2262. }
  2263. //BBS: extract project config from json files
  2264. void _BBS_3MF_Importer::_extract_project_config_from_archive(mz_zip_archive& archive, const mz_zip_archive_file_stat& stat, DynamicPrintConfig& config, ConfigSubstitutionContext& config_substitutions, Model& model)
  2265. {
  2266. if (stat.m_uncomp_size > 0) {
  2267. std_path temp_file = extract_file(model, archive, stat);
  2268. bool ok = read_json_file_bambu(temp_file, config, config_substitutions, true);
  2269. if (!ok) {
  2270. add_error("Error load config from bambu/orca json");
  2271. return;
  2272. }
  2273. BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << boost::format(", load project config file successfully from %1%\n") % temp_file.string();
  2274. }
  2275. }
  2276. //BBS: extract project embedded presets
  2277. void _BBS_3MF_Importer::_extract_project_embedded_presets_from_archive(mz_zip_archive& archive, const mz_zip_archive_file_stat& stat, std::vector<Preset*>&project_presets, Model& model, Preset::Type type, bool use_json)
  2278. {
  2279. if (stat.m_uncomp_size > 0) {
  2280. std_path dest_file_path = extract_file(model, archive, stat);
  2281. std::string dest_file = dest_file_path.string();
  2282. //load presets
  2283. DynamicPrintConfig config;
  2284. ConfigSubstitutionContext config_substitution_context(ForwardCompatibilitySubstitutionRule::Enable);
  2285. std::map<std::string, std::string> key_values;
  2286. bool ok = false;
  2287. if (use_json) {
  2288. ok = read_json_file_bambu(dest_file_path, config, config_substitution_context, true);
  2289. } else {
  2290. key_values = read_ini_file_bambu(dest_file_path);
  2291. ok = convert_settings_from_bambu(key_values, config, config_substitution_context, true);
  2292. }
  2293. if (!ok) {
  2294. add_error("Error load config from bambu/orca json");
  2295. BOOST_LOG_TRIVIAL(error) << __FUNCTION__ << boost::format(", load project embedded config from %1% failed\n") % dest_file;
  2296. //skip this file
  2297. return;
  2298. }
  2299. /*std::string src_file = decode_path(stat.m_filename);
  2300. std::size_t found = src_file.find(METADATA_DIR);
  2301. if (found != std::string::npos)
  2302. src_file = src_file.substr(found + METADATA_STR_LEN);
  2303. else
  2304. return;*/
  2305. //std::string dest_file = m_backup_path + std::string("/") + "_temp_2.config";;
  2306. //std::string dest_zip_file = encode_path(dest_file.c_str());
  2307. //mz_bool res = mz_zip_reader_extract_to_file(&archive, stat.m_file_index, dest_zip_file.c_str(), 0);
  2308. //BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << boost::format(", extract %1% from 3mf %2%, ret %3%\n") % dest_file % stat.m_filename % res;
  2309. //if (res == 0) {
  2310. // add_error("Error while extract auxiliary file to file");
  2311. // return;
  2312. //}
  2313. ////load presets
  2314. //DynamicPrintConfig config;
  2315. ////ConfigSubstitutions config_substitutions = config.load_from_ini(dest_file, Enable);
  2316. //std::map<std::string, std::string> key_values;
  2317. //std::string reason;
  2318. //ConfigSubstitutions config_substitutions = use_json? config.load_from_json(dest_file, Enable, key_values, reason) : config.load_from_ini(dest_file, Enable);
  2319. //if (!reason.empty()) {
  2320. // BOOST_LOG_TRIVIAL(error) << __FUNCTION__ << boost::format(", load project embedded config from %1% failed\n") % dest_file;
  2321. // //skip this file
  2322. // return;
  2323. //}
  2324. ConfigOptionString* print_name;
  2325. ConfigOptionStrings* filament_names;
  2326. std::string preset_name;
  2327. if (type == Preset::TYPE_FFF_PRINT) {
  2328. print_name = dynamic_cast < ConfigOptionString* > (config.option("print_settings_id"));
  2329. if (!print_name) {
  2330. BOOST_LOG_TRIVIAL(error) << __FUNCTION__ << boost::format(", can not found print_settings_id from %1%\n") % dest_file;
  2331. //skip this file
  2332. return;
  2333. }
  2334. preset_name = print_name->value;
  2335. }
  2336. else if (type == Preset::TYPE_FFF_FILAMENT) {
  2337. filament_names = dynamic_cast < ConfigOptionStrings* > (config.option("filament_settings_id"));
  2338. if (!filament_names) {
  2339. BOOST_LOG_TRIVIAL(error) << __FUNCTION__ << boost::format(", can not found filament_settings_id from %1%\n") % dest_file;
  2340. //skip this file
  2341. return;
  2342. }
  2343. preset_name = filament_names->values[0];
  2344. }
  2345. else if (type == Preset::TYPE_PRINTER) {
  2346. print_name = dynamic_cast < ConfigOptionString* > (config.option("printer_settings_id"));
  2347. if (!print_name) {
  2348. BOOST_LOG_TRIVIAL(error) << __FUNCTION__ << boost::format(", can not found printer_settings_id from %1%\n") % dest_file;
  2349. //skip this file
  2350. return;
  2351. }
  2352. preset_name = print_name->value;
  2353. }
  2354. else {
  2355. BOOST_LOG_TRIVIAL(error) << __FUNCTION__ << boost::format(", invalid type %1% from file %2%\n")% Preset::type_name(type) % dest_file;
  2356. //skip this file
  2357. return;
  2358. }
  2359. //Preset(Type type, const std::string &name, bool is_default = false) : type(type), is_default(is_default), name(name) {}
  2360. Preset *preset = new Preset(type, preset_name, false);
  2361. preset->file = dest_file;
  2362. preset->config = std::move(config);
  2363. preset->loaded = true;
  2364. //preset->is_project_embedded = true; //Susi_not_impl
  2365. preset->is_external = true;
  2366. preset->is_dirty = false;
  2367. std::string version_str = key_values[BBL_JSON_KEY_VERSION];
  2368. boost::optional<Semver> version = Semver::parse(version_str);
  2369. if (version) {
  2370. //preset->version = *version; //Susi_not_impl
  2371. } else {
  2372. //preset->version = this->m_bambuslicer_generator_version?*this->m_bambuslicer_generator_version: Semver(); //Susi_not_impl
  2373. }
  2374. /*for (int i = 0; i < config_substitutions.size(); i++)
  2375. {
  2376. //ConfigSubstitution config_substitution;
  2377. //config_substitution.opt_def = optdef;
  2378. //config_substitution.old_value = value;
  2379. //config_substitution.new_value = ConfigOptionUniquePtr(opt->clone());
  2380. preset->loading_substitutions.emplace_back(std::move(config_substitutions[i]));
  2381. }*/
  2382. //if (!config_substitutions.empty()) {
  2383. // preset->loading_substitutions = new ConfigSubstitutions();
  2384. // *(preset->loading_substitutions) = std::move(config_substitutions);
  2385. //} //Susi_not_impl
  2386. project_presets.push_back(preset);
  2387. BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << boost::format(", create one project embedded preset: %1% from %2%, type %3%\n") % preset_name % dest_file %Preset::type_name(type);
  2388. }
  2389. }
  2390. void _BBS_3MF_Importer::_extract_auxiliary_file_from_archive(mz_zip_archive& archive, const mz_zip_archive_file_stat& stat, Model& model)
  2391. {
  2392. BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << boost::format(", stat.m_uncomp_size is %1%")%stat.m_uncomp_size;
  2393. if (stat.m_uncomp_size > 0) {
  2394. std::string dest_file;
  2395. if (stat.m_is_utf8) {
  2396. dest_file = stat.m_filename;
  2397. } else {
  2398. std::string extra(1024, 0);
  2399. size_t n = mz_zip_reader_get_extra(&archive, stat.m_file_index, extra.data(), extra.size());
  2400. dest_file = ZipUnicodePathExtraField::decode(extra.substr(0, n), stat.m_filename);
  2401. }
  2402. //aux directory from model
  2403. std_path dir = get_temp_file(model);
  2404. std::size_t found = dest_file.find(AUXILIARY_DIR);
  2405. if (found != std::string::npos)
  2406. dest_file = dest_file.substr(found + AUXILIARY_STR_LEN);
  2407. else
  2408. return;
  2409. if (dest_file.find('/') != std::string::npos) {
  2410. boost::filesystem::path src_path = boost::filesystem::path(dest_file);
  2411. boost::filesystem::path parent_path = src_path.parent_path();
  2412. std::string temp_path = dir.string() + std::string("/") + parent_path.string();
  2413. boost::filesystem::path parent_full_path = boost::filesystem::path(temp_path);
  2414. if (!boost::filesystem::exists(parent_full_path))
  2415. boost::filesystem::create_directories(parent_full_path);
  2416. }
  2417. dest_file = dir.string() + std::string("/") + dest_file;
  2418. std::string dest_zip_file = encode_path(dest_file.c_str());
  2419. mz_bool res = mz_zip_reader_extract_to_file(&archive, stat.m_file_index, dest_zip_file.c_str(), 0);
  2420. BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << boost::format(", extract %1% from 3mf %2%, ret %3%\n") % dest_file % stat.m_filename % res;
  2421. if (res == 0) {
  2422. add_error("Error while extract auxiliary file to file");
  2423. return;
  2424. }
  2425. }
  2426. }
  2427. void _BBS_3MF_Importer::_extract_file_from_archive(mz_zip_archive& archive, const mz_zip_archive_file_stat& stat)
  2428. {
  2429. if (stat.m_uncomp_size > 0) {
  2430. std::string src_file = decode_path(stat.m_filename);
  2431. // BBS: use backup path
  2432. //aux directory from model
  2433. boost::filesystem::path dest_path = boost::filesystem::path(m_backup_path + "/" + src_file);
  2434. std::string dest_zip_file = encode_path(dest_path.string().c_str());
  2435. mz_bool res = mz_zip_reader_extract_to_file(&archive, stat.m_file_index, dest_zip_file.c_str(), 0);
  2436. BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << boost::format(", extract %1% from 3mf %2%, ret %3%\n") % dest_path % stat.m_filename % res;
  2437. if (res == 0) {
  2438. add_error("Error while extract file to temp directory");
  2439. return;
  2440. }
  2441. }
  2442. return;
  2443. }
  2444. void _BBS_3MF_Importer::_extract_layer_heights_profile_config_from_archive(mz_zip_archive& archive, const mz_zip_archive_file_stat& stat)
  2445. {
  2446. if (stat.m_uncomp_size > 0) {
  2447. std::string buffer((size_t)stat.m_uncomp_size, 0);
  2448. mz_bool res = mz_zip_reader_extract_to_mem(&archive, stat.m_file_index, (void*)buffer.data(), (size_t)stat.m_uncomp_size, 0);
  2449. if (res == 0) {
  2450. add_error("Error while reading layer heights profile data to buffer");
  2451. return;
  2452. }
  2453. if (buffer.back() == '\n')
  2454. buffer.pop_back();
  2455. std::vector<std::string> objects;
  2456. boost::split(objects, buffer, boost::is_any_of("\n"), boost::token_compress_off);
  2457. for (const std::string& object : objects) {
  2458. std::vector<std::string> object_data;
  2459. boost::split(object_data, object, boost::is_any_of("|"), boost::token_compress_off);
  2460. if (object_data.size() != 2) {
  2461. add_error("Error while reading object data");
  2462. continue;
  2463. }
  2464. std::vector<std::string> object_data_id;
  2465. boost::split(object_data_id, object_data[0], boost::is_any_of("="), boost::token_compress_off);
  2466. if (object_data_id.size() != 2) {
  2467. add_error("Error while reading object id");
  2468. continue;
  2469. }
  2470. int object_id = std::atoi(object_data_id[1].c_str());
  2471. if (object_id == 0) {
  2472. add_error("Found invalid object id");
  2473. continue;
  2474. }
  2475. IdToLayerHeightsProfileMap::iterator object_item = m_layer_heights_profiles.find(object_id);
  2476. if (object_item != m_layer_heights_profiles.end()) {
  2477. add_error("Found duplicated layer heights profile");
  2478. continue;
  2479. }
  2480. std::vector<std::string> object_data_profile;
  2481. boost::split(object_data_profile, object_data[1], boost::is_any_of(";"), boost::token_compress_off);
  2482. if (object_data_profile.size() <= 4 || object_data_profile.size() % 2 != 0) {
  2483. add_error("Found invalid layer heights profile");
  2484. continue;
  2485. }
  2486. std::vector<coordf_t> profile;
  2487. profile.reserve(object_data_profile.size());
  2488. for (const std::string& value : object_data_profile) {
  2489. profile.push_back((coordf_t)std::atof(value.c_str()));
  2490. }
  2491. m_layer_heights_profiles.insert({ object_id, profile });
  2492. }
  2493. }
  2494. }
  2495. void _BBS_3MF_Importer::_extract_layer_config_ranges_from_archive(mz_zip_archive& archive, const mz_zip_archive_file_stat& stat, ConfigSubstitutionContext& config_substitutions)
  2496. {
  2497. if (stat.m_uncomp_size > 0) {
  2498. std::string buffer((size_t)stat.m_uncomp_size, 0);
  2499. mz_bool res = mz_zip_reader_extract_file_to_mem(&archive, stat.m_filename, (void*)buffer.data(), (size_t)stat.m_uncomp_size, 0);
  2500. if (res == 0) {
  2501. add_error("Error while reading layer config ranges data to buffer");
  2502. return;
  2503. }
  2504. std::istringstream iss(buffer); // wrap returned xml to istringstream
  2505. pt::ptree objects_tree;
  2506. pt::read_xml(iss, objects_tree);
  2507. for (const auto& object : objects_tree.get_child("objects")) {
  2508. pt::ptree object_tree = object.second;
  2509. int obj_idx = object_tree.get<int>("<xmlattr>.id", -1);
  2510. if (obj_idx <= 0) {
  2511. add_error("Found invalid object id");
  2512. continue;
  2513. }
  2514. IdToLayerConfigRangesMap::iterator object_item = m_layer_config_ranges.find(obj_idx);
  2515. if (object_item != m_layer_config_ranges.end()) {
  2516. add_error("Found duplicated layer config range");
  2517. continue;
  2518. }
  2519. t_layer_config_ranges config_ranges;
  2520. for (const auto& range : object_tree) {
  2521. if (range.first != "range")
  2522. continue;
  2523. pt::ptree range_tree = range.second;
  2524. double min_z = range_tree.get<double>("<xmlattr>.min_z");
  2525. double max_z = range_tree.get<double>("<xmlattr>.max_z");
  2526. // get Z range information
  2527. DynamicPrintConfig config;
  2528. std::map<std::string, std::string> key_values_to_deserialize;
  2529. for (const auto& option : range_tree) {
  2530. if (option.first != "option")
  2531. continue;
  2532. std::string opt_key = option.second.get<std::string>("<xmlattr>.opt_key");
  2533. std::string value = option.second.data();
  2534. //config.set_deserialize(opt_key, value, config_substitutions);
  2535. key_values_to_deserialize[opt_key] = value;
  2536. }
  2537. assert(key_values_to_deserialize.find("extruder") != key_values_to_deserialize.end());
  2538. assert(key_values_to_deserialize.find("layer_height") != key_values_to_deserialize.end());
  2539. convert_settings_from_bambu(key_values_to_deserialize, config, config_substitutions, false);
  2540. config_ranges[{ min_z, max_z }].assign_config(std::move(config));
  2541. }
  2542. if (!config_ranges.empty())
  2543. m_layer_config_ranges.insert({ obj_idx, std::move(config_ranges) });
  2544. }
  2545. }
  2546. }
  2547. /*
  2548. void _BBS_3MF_Importer::_extract_sla_support_points_from_archive(mz_zip_archive& archive, const mz_zip_archive_file_stat& stat)
  2549. {
  2550. if (stat.m_uncomp_size > 0) {
  2551. std::string buffer((size_t)stat.m_uncomp_size, 0);
  2552. mz_bool res = mz_zip_reader_extract_file_to_mem(&archive, stat.m_filename, (void*)buffer.data(), (size_t)stat.m_uncomp_size, 0);
  2553. if (res == 0) {
  2554. add_error("Error while reading sla support points data to buffer");
  2555. return;
  2556. }
  2557. if (buffer.back() == '\n')
  2558. buffer.pop_back();
  2559. std::vector<std::string> objects;
  2560. boost::split(objects, buffer, boost::is_any_of("\n"), boost::token_compress_off);
  2561. // Info on format versioning - see 3mf.hpp
  2562. int version = 0;
  2563. std::string key("support_points_format_version=");
  2564. if (!objects.empty() && objects[0].find(key) != std::string::npos) {
  2565. objects[0].erase(objects[0].begin(), objects[0].begin() + long(key.size())); // removes the string
  2566. version = std::stoi(objects[0]);
  2567. objects.erase(objects.begin()); // pop the header
  2568. }
  2569. for (const std::string& object : objects) {
  2570. std::vector<std::string> object_data;
  2571. boost::split(object_data, object, boost::is_any_of("|"), boost::token_compress_off);
  2572. if (object_data.size() != 2) {
  2573. add_error("Error while reading object data");
  2574. continue;
  2575. }
  2576. std::vector<std::string> object_data_id;
  2577. boost::split(object_data_id, object_data[0], boost::is_any_of("="), boost::token_compress_off);
  2578. if (object_data_id.size() != 2) {
  2579. add_error("Error while reading object id");
  2580. continue;
  2581. }
  2582. int object_id = std::atoi(object_data_id[1].c_str());
  2583. if (object_id == 0) {
  2584. add_error("Found invalid object id");
  2585. continue;
  2586. }
  2587. IdToSlaSupportPointsMap::iterator object_item = m_sla_support_points.find(object_id);
  2588. if (object_item != m_sla_support_points.end()) {
  2589. add_error("Found duplicated SLA support points");
  2590. continue;
  2591. }
  2592. std::vector<std::string> object_data_points;
  2593. boost::split(object_data_points, object_data[1], boost::is_any_of(" "), boost::token_compress_off);
  2594. std::vector<sla::SupportPoint> sla_support_points;
  2595. if (version == 0) {
  2596. for (unsigned int i=0; i<object_data_points.size(); i+=3)
  2597. sla_support_points.emplace_back(float(std::atof(object_data_points[i+0].c_str())),
  2598. float(std::atof(object_data_points[i+1].c_str())),
  2599. float(std::atof(object_data_points[i+2].c_str())),
  2600. 0.4f,
  2601. false);
  2602. }
  2603. if (version == 1) {
  2604. for (unsigned int i=0; i<object_data_points.size(); i+=5)
  2605. sla_support_points.emplace_back(float(std::atof(object_data_points[i+0].c_str())),
  2606. float(std::atof(object_data_points[i+1].c_str())),
  2607. float(std::atof(object_data_points[i+2].c_str())),
  2608. float(std::atof(object_data_points[i+3].c_str())),
  2609. //FIXME storing boolean as 0 / 1 and importing it as float.
  2610. std::abs(std::atof(object_data_points[i+4].c_str()) - 1.) < EPSILON);
  2611. }
  2612. if (!sla_support_points.empty())
  2613. m_sla_support_points.insert({ object_id, sla_support_points });
  2614. }
  2615. }
  2616. }
  2617. void _BBS_3MF_Importer::_extract_sla_drain_holes_from_archive(mz_zip_archive& archive, const mz_zip_archive_file_stat& stat)
  2618. {
  2619. if (stat.m_uncomp_size > 0) {
  2620. std::string buffer(size_t(stat.m_uncomp_size), 0);
  2621. mz_bool res = mz_zip_reader_extract_file_to_mem(&archive, stat.m_filename, (void*)buffer.data(), (size_t)stat.m_uncomp_size, 0);
  2622. if (res == 0) {
  2623. add_error("Error while reading sla support points data to buffer");
  2624. return;
  2625. }
  2626. if (buffer.back() == '\n')
  2627. buffer.pop_back();
  2628. std::vector<std::string> objects;
  2629. boost::split(objects, buffer, boost::is_any_of("\n"), boost::token_compress_off);
  2630. // Info on format versioning - see 3mf.hpp
  2631. int version = 0;
  2632. std::string key("drain_holes_format_version=");
  2633. if (!objects.empty() && objects[0].find(key) != std::string::npos) {
  2634. objects[0].erase(objects[0].begin(), objects[0].begin() + long(key.size())); // removes the string
  2635. version = std::stoi(objects[0]);
  2636. objects.erase(objects.begin()); // pop the header
  2637. }
  2638. for (const std::string& object : objects) {
  2639. std::vector<std::string> object_data;
  2640. boost::split(object_data, object, boost::is_any_of("|"), boost::token_compress_off);
  2641. if (object_data.size() != 2) {
  2642. add_error("Error while reading object data");
  2643. continue;
  2644. }
  2645. std::vector<std::string> object_data_id;
  2646. boost::split(object_data_id, object_data[0], boost::is_any_of("="), boost::token_compress_off);
  2647. if (object_data_id.size() != 2) {
  2648. add_error("Error while reading object id");
  2649. continue;
  2650. }
  2651. int object_id = std::atoi(object_data_id[1].c_str());
  2652. if (object_id == 0) {
  2653. add_error("Found invalid object id");
  2654. continue;
  2655. }
  2656. IdToSlaDrainHolesMap::iterator object_item = m_sla_drain_holes.find(object_id);
  2657. if (object_item != m_sla_drain_holes.end()) {
  2658. add_error("Found duplicated SLA drain holes");
  2659. continue;
  2660. }
  2661. std::vector<std::string> object_data_points;
  2662. boost::split(object_data_points, object_data[1], boost::is_any_of(" "), boost::token_compress_off);
  2663. sla::DrainHoles sla_drain_holes;
  2664. if (version == 1) {
  2665. for (unsigned int i=0; i<object_data_points.size(); i+=8)
  2666. sla_drain_holes.emplace_back(Vec3f{float(std::atof(object_data_points[i+0].c_str())),
  2667. float(std::atof(object_data_points[i+1].c_str())),
  2668. float(std::atof(object_data_points[i+2].c_str()))},
  2669. Vec3f{float(std::atof(object_data_points[i+3].c_str())),
  2670. float(std::atof(object_data_points[i+4].c_str())),
  2671. float(std::atof(object_data_points[i+5].c_str()))},
  2672. float(std::atof(object_data_points[i+6].c_str())),
  2673. float(std::atof(object_data_points[i+7].c_str())));
  2674. }
  2675. // The holes are saved elevated above the mesh and deeper (bad idea indeed).
  2676. // This is retained for compatibility.
  2677. // Place the hole to the mesh and make it shallower to compensate.
  2678. // The offset is 1 mm above the mesh.
  2679. for (sla::DrainHole& hole : sla_drain_holes) {
  2680. hole.pos += hole.normal.normalized();
  2681. hole.height -= 1.f;
  2682. }
  2683. if (!sla_drain_holes.empty())
  2684. m_sla_drain_holes.insert({ object_id, sla_drain_holes });
  2685. }
  2686. }
  2687. }*/
  2688. void _BBS_3MF_Importer::_extract_embossed_svg_shape_file(const std::string &filename, mz_zip_archive &archive, const mz_zip_archive_file_stat &stat){
  2689. assert(m_path_to_emboss_shape_files.find(filename) == m_path_to_emboss_shape_files.end());
  2690. auto file = std::make_unique<std::string>(stat.m_uncomp_size, '\0');
  2691. mz_bool res = mz_zip_reader_extract_to_mem(&archive, stat.m_file_index, (void *) file->data(), stat.m_uncomp_size, 0);
  2692. if (res == 0) {
  2693. add_error("Error while reading svg shape for emboss");
  2694. return;
  2695. }
  2696. // store for case svg is loaded before volume
  2697. m_path_to_emboss_shape_files[filename] = std::move(file);
  2698. // find embossed volume, for case svg is loaded after volume
  2699. //for (const ModelObject* object : m_model->objects)
  2700. //for (ModelVolume *volume : object->volumes) {
  2701. // std::optional<EmbossShape> &es = volume->emboss_shape;
  2702. // if (!es.has_value())
  2703. // continue;
  2704. // std::optional<EmbossShape::SvgFile> &svg = es->svg_file;
  2705. // if (!svg.has_value())
  2706. // continue;
  2707. // if (filename.compare(svg->path_in_3mf) == 0)
  2708. // svg->file_data = m_path_to_emboss_shape_files[filename];
  2709. //} //Susi_not_impl
  2710. }
  2711. void _BBS_3MF_Importer::_extract_custom_gcode_per_print_z_from_archive(::mz_zip_archive &archive, const mz_zip_archive_file_stat &stat)
  2712. {
  2713. //BBS: add plate tree related logic
  2714. if (stat.m_uncomp_size > 0) {
  2715. std::string buffer((size_t)stat.m_uncomp_size, 0);
  2716. mz_bool res = mz_zip_reader_extract_file_to_mem(&archive, stat.m_filename, (void*)buffer.data(), (size_t)stat.m_uncomp_size, 0);
  2717. if (res == 0) {
  2718. add_error("Error while reading custom Gcodes per height data to buffer");
  2719. return;
  2720. }
  2721. std::istringstream iss(buffer); // wrap returned xml to istringstream
  2722. pt::ptree main_tree;
  2723. pt::read_xml(iss, main_tree);
  2724. if (main_tree.front().first != "custom_gcodes_per_layer")
  2725. return;
  2726. auto extract_code = [this](int plate_id, pt::ptree code_tree) {
  2727. for (const auto& code : code_tree) {
  2728. if (code.first == "mode") {
  2729. pt::ptree tree = code.second;
  2730. std::string mode = tree.get<std::string>("<xmlattr>.value");
  2731. //m_model->plates_custom_gcodes[plate_id - 1].mode = mode == CustomGCode::SingleExtruderMode ? CustomGCode::Mode::SingleExtruder :
  2732. // mode == CustomGCode::MultiAsSingleMode ? CustomGCode::Mode::MultiAsSingle :
  2733. // CustomGCode::Mode::MultiExtruder; //Susi_not_impl
  2734. }
  2735. if (code.first == "layer") {
  2736. pt::ptree tree = code.second;
  2737. double print_z = tree.get<double>("<xmlattr>.top_z");
  2738. int extruder = tree.get<int>("<xmlattr>.extruder");
  2739. std::string color = tree.get<std::string>("<xmlattr>.color");
  2740. CustomGCode::Type type;
  2741. std::string extra;
  2742. pt::ptree attr_tree = tree.find("<xmlattr>")->second;
  2743. if (attr_tree.find("type") == attr_tree.not_found()) {
  2744. // It means that data was saved in old version (2.2.0 and older) of PrusaSlicer
  2745. // read old data ...
  2746. std::string gcode = tree.get<std::string>("<xmlattr>.gcode");
  2747. // ... and interpret them to the new data
  2748. type = gcode == "M600" ? CustomGCode::ColorChange :
  2749. gcode == "M601" ? CustomGCode::PausePrint :
  2750. gcode == "tool_change" ? CustomGCode::ToolChange : CustomGCode::Custom;
  2751. extra = type == CustomGCode::PausePrint ? color :
  2752. type == CustomGCode::Custom ? gcode : "";
  2753. }
  2754. else {
  2755. type = static_cast<CustomGCode::Type>(tree.get<int>("<xmlattr>.type"));
  2756. extra = tree.get<std::string>("<xmlattr>.extra");
  2757. }
  2758. //m_model->plates_custom_gcodes[plate_id - 1].gcodes.push_back(CustomGCode::Item{ print_z, type, extruder, color, extra }); //Susi_not_impl
  2759. }
  2760. }
  2761. };
  2762. //m_model->plates_custom_gcodes.clear(); //Susi_not_impl
  2763. bool has_plate_info = false;
  2764. for (const auto& element : main_tree.front().second) {
  2765. if (element.first == "plate") {
  2766. has_plate_info = true;
  2767. int plate_id = -1;
  2768. pt::ptree code_tree = element.second;
  2769. for (const auto& code : code_tree) {
  2770. if (code.first == "plate_info") {
  2771. plate_id = code.second.get<int>("<xmlattr>.id");
  2772. }
  2773. }
  2774. if (plate_id == -1)
  2775. continue;
  2776. extract_code(plate_id, code_tree);
  2777. }
  2778. }
  2779. if (!has_plate_info) {
  2780. int plate_id = 1;
  2781. pt::ptree code_tree = main_tree.front().second;
  2782. extract_code(plate_id, code_tree);
  2783. }
  2784. }
  2785. }
  2786. void _BBS_3MF_Importer::_handle_start_model_xml_element(const char* name, const char** attributes)
  2787. {
  2788. if (m_xml_parser == nullptr)
  2789. return;
  2790. bool res = true;
  2791. unsigned int num_attributes = (unsigned int)XML_GetSpecifiedAttributeCount(m_xml_parser);
  2792. if (::strcmp(MODEL_TAG, name) == 0)
  2793. res = _handle_start_model(attributes, num_attributes);
  2794. else if (::strcmp(RESOURCES_TAG, name) == 0)
  2795. res = _handle_start_resources(attributes, num_attributes);
  2796. else if (::strcmp(OBJECT_TAG, name) == 0)
  2797. res = _handle_start_object(attributes, num_attributes);
  2798. else if (::strcmp(COLOR_GROUP_TAG, name) == 0)
  2799. res = _handle_start_color_group(attributes, num_attributes);
  2800. else if (::strcmp(COLOR_TAG, name) == 0)
  2801. res = _handle_start_color(attributes, num_attributes);
  2802. else if (::strcmp(MESH_TAG, name) == 0)
  2803. res = _handle_start_mesh(attributes, num_attributes);
  2804. else if (::strcmp(VERTICES_TAG, name) == 0)
  2805. res = _handle_start_vertices(attributes, num_attributes);
  2806. else if (::strcmp(VERTEX_TAG, name) == 0)
  2807. res = _handle_start_vertex(attributes, num_attributes);
  2808. else if (::strcmp(TRIANGLES_TAG, name) == 0)
  2809. res = _handle_start_triangles(attributes, num_attributes);
  2810. else if (::strcmp(TRIANGLE_TAG, name) == 0)
  2811. res = _handle_start_triangle(attributes, num_attributes);
  2812. else if (::strcmp(COMPONENTS_TAG, name) == 0)
  2813. res = _handle_start_components(attributes, num_attributes);
  2814. else if (::strcmp(COMPONENT_TAG, name) == 0)
  2815. res = _handle_start_component(attributes, num_attributes);
  2816. else if (::strcmp(BUILD_TAG, name) == 0)
  2817. res = _handle_start_build(attributes, num_attributes);
  2818. else if (::strcmp(ITEM_TAG, name) == 0)
  2819. res = _handle_start_item(attributes, num_attributes);
  2820. else if (::strcmp(METADATA_TAG, name) == 0)
  2821. res = _handle_start_metadata(attributes, num_attributes);
  2822. if (!res)
  2823. _stop_xml_parser();
  2824. }
  2825. void _BBS_3MF_Importer::_handle_end_model_xml_element(const char* name)
  2826. {
  2827. if (m_xml_parser == nullptr)
  2828. return;
  2829. bool res = true;
  2830. if (::strcmp(MODEL_TAG, name) == 0)
  2831. res = _handle_end_model();
  2832. else if (::strcmp(RESOURCES_TAG, name) == 0)
  2833. res = _handle_end_resources();
  2834. else if (::strcmp(OBJECT_TAG, name) == 0)
  2835. res = _handle_end_object();
  2836. else if (::strcmp(COLOR_GROUP_TAG, name) == 0)
  2837. res = _handle_end_color_group();
  2838. else if (::strcmp(COLOR_TAG, name) == 0)
  2839. res = _handle_end_color();
  2840. else if (::strcmp(MESH_TAG, name) == 0)
  2841. res = _handle_end_mesh();
  2842. else if (::strcmp(VERTICES_TAG, name) == 0)
  2843. res = _handle_end_vertices();
  2844. else if (::strcmp(VERTEX_TAG, name) == 0)
  2845. res = _handle_end_vertex();
  2846. else if (::strcmp(TRIANGLES_TAG, name) == 0)
  2847. res = _handle_end_triangles();
  2848. else if (::strcmp(TRIANGLE_TAG, name) == 0)
  2849. res = _handle_end_triangle();
  2850. else if (::strcmp(COMPONENTS_TAG, name) == 0)
  2851. res = _handle_end_components();
  2852. else if (::strcmp(COMPONENT_TAG, name) == 0)
  2853. res = _handle_end_component();
  2854. else if (::strcmp(BUILD_TAG, name) == 0)
  2855. res = _handle_end_build();
  2856. else if (::strcmp(ITEM_TAG, name) == 0)
  2857. res = _handle_end_item();
  2858. else if (::strcmp(METADATA_TAG, name) == 0)
  2859. res = _handle_end_metadata();
  2860. if (!res)
  2861. _stop_xml_parser();
  2862. }
  2863. void _BBS_3MF_Importer::_handle_xml_characters(const XML_Char* s, int len)
  2864. {
  2865. m_curr_characters.append(s, len);
  2866. }
  2867. void _BBS_3MF_Importer::_handle_start_config_xml_element(const char* name, const char** attributes)
  2868. {
  2869. if (m_xml_parser == nullptr)
  2870. return;
  2871. bool res = true;
  2872. unsigned int num_attributes = (unsigned int)XML_GetSpecifiedAttributeCount(m_xml_parser);
  2873. if (::strcmp(CONFIG_TAG, name) == 0)
  2874. res = _handle_start_config(attributes, num_attributes);
  2875. else if (::strcmp(OBJECT_TAG, name) == 0)
  2876. res = _handle_start_config_object(attributes, num_attributes);
  2877. else if (::strcmp(VOLUME_TAG, name) == 0)
  2878. res = _handle_start_config_volume(attributes, num_attributes);
  2879. else if (::strcmp(PART_TAG, name) == 0)
  2880. res = _handle_start_config_volume(attributes, num_attributes);
  2881. else if (::strcmp(MESH_STAT_TAG, name) == 0)
  2882. res = _handle_start_config_volume_mesh(attributes, num_attributes);
  2883. else if (::strcmp(METADATA_TAG, name) == 0)
  2884. res = _handle_start_config_metadata(attributes, num_attributes);
  2885. else if (::strcmp(SHAPE_TAG, name) == 0)
  2886. res = _handle_start_shape_configuration(attributes, num_attributes);
  2887. else if (::strcmp(TEXT_TAG, name) == 0)
  2888. res = _handle_start_text_configuration(attributes, num_attributes);
  2889. else if (::strcmp(PLATE_TAG, name) == 0)
  2890. res = _handle_start_config_plater(attributes, num_attributes);
  2891. else if (::strcmp(INSTANCE_TAG, name) == 0)
  2892. res = _handle_start_config_plater_instance(attributes, num_attributes);
  2893. else if (::strcmp(FILAMENT_TAG, name) == 0)
  2894. res = _handle_start_config_filament(attributes, num_attributes);
  2895. else if (::strcmp(SLICE_WARNING_TAG, name) == 0)
  2896. res = _handle_start_config_warning(attributes, num_attributes);
  2897. else if (::strcmp(ASSEMBLE_TAG, name) == 0)
  2898. res = _handle_start_assemble(attributes, num_attributes);
  2899. else if (::strcmp(ASSEMBLE_ITEM_TAG, name) == 0)
  2900. res = _handle_start_assemble_item(attributes, num_attributes);
  2901. else if (::strcmp(TEXT_INFO_TAG, name) == 0)
  2902. res = _handle_start_text_info_item(attributes, num_attributes);
  2903. if (!res)
  2904. _stop_xml_parser();
  2905. }
  2906. void _BBS_3MF_Importer::_handle_end_config_xml_element(const char* name)
  2907. {
  2908. if (m_xml_parser == nullptr)
  2909. return;
  2910. bool res = true;
  2911. if (::strcmp(CONFIG_TAG, name) == 0)
  2912. res = _handle_end_config();
  2913. else if (::strcmp(OBJECT_TAG, name) == 0)
  2914. res = _handle_end_config_object();
  2915. else if (::strcmp(VOLUME_TAG, name) == 0)
  2916. res = _handle_end_config_volume();
  2917. else if (::strcmp(PART_TAG, name) == 0)
  2918. res = _handle_end_config_volume();
  2919. else if (::strcmp(MESH_STAT_TAG, name) == 0)
  2920. res = _handle_end_config_volume_mesh();
  2921. else if (::strcmp(METADATA_TAG, name) == 0)
  2922. res = _handle_end_config_metadata();
  2923. else if (::strcmp(PLATE_TAG, name) == 0)
  2924. res = _handle_end_config_plater();
  2925. else if (::strcmp(FILAMENT_TAG, name) == 0)
  2926. res = _handle_end_config_filament();
  2927. else if (::strcmp(INSTANCE_TAG, name) == 0)
  2928. res = _handle_end_config_plater_instance();
  2929. else if (::strcmp(ASSEMBLE_TAG, name) == 0)
  2930. res = _handle_end_assemble();
  2931. else if (::strcmp(ASSEMBLE_ITEM_TAG, name) == 0)
  2932. res = _handle_end_assemble_item();
  2933. if (!res)
  2934. _stop_xml_parser();
  2935. }
  2936. bool _BBS_3MF_Importer::_handle_start_model(const char** attributes, unsigned int num_attributes)
  2937. {
  2938. m_unit_factor = bbs_get_unit_factor(bbs_get_attribute_value_string(attributes, num_attributes, UNIT_ATTR));
  2939. return true;
  2940. }
  2941. bool _BBS_3MF_Importer::_handle_end_model()
  2942. {
  2943. // BBS: Production Extension
  2944. if (!m_sub_model_path.empty())
  2945. return true;
  2946. // deletes all non-built or non-instanced objects
  2947. for (const IdToModelObjectMap::value_type& object : m_objects) {
  2948. if (object.second >= int(m_model->objects.size())) {
  2949. add_error("Unable to find object");
  2950. return false;
  2951. }
  2952. ModelObject *model_object = m_model->objects[object.second];
  2953. if (model_object != nullptr && model_object->instances.size() == 0)
  2954. m_model->delete_object(model_object);
  2955. }
  2956. //construct the index maps
  2957. for (const IdToCurrentObjectMap::value_type& object : m_current_objects) {
  2958. m_index_paths.insert({ object.first.second, object.first.first});
  2959. }
  2960. if (!m_is_bbl_3mf) {
  2961. // if the 3mf was not produced by OrcaSlicer and there is only one object,
  2962. // set the object name to match the filename
  2963. if (m_model->objects.size() == 1)
  2964. m_model->objects.front()->name = m_name;
  2965. }
  2966. // applies instances' matrices
  2967. for (Instance& instance : m_instances) {
  2968. if (instance.instance != nullptr && instance.instance->get_object() != nullptr)
  2969. // apply the transform to the instance
  2970. _apply_transform(*instance.instance, instance.transform);
  2971. }
  2972. return true;
  2973. }
  2974. bool _BBS_3MF_Importer::_handle_start_resources(const char** attributes, unsigned int num_attributes)
  2975. {
  2976. // do nothing
  2977. return true;
  2978. }
  2979. bool _BBS_3MF_Importer::_handle_end_resources()
  2980. {
  2981. // do nothing
  2982. return true;
  2983. }
  2984. bool _BBS_3MF_Importer::_handle_start_object(const char** attributes, unsigned int num_attributes)
  2985. {
  2986. // reset current object data
  2987. if (m_curr_object) {
  2988. delete m_curr_object;
  2989. m_curr_object = nullptr;
  2990. }
  2991. std::string object_type = bbs_get_attribute_value_string(attributes, num_attributes, TYPE_ATTR);
  2992. if (bbs_is_valid_object_type(object_type)) {
  2993. if (!m_curr_object) {
  2994. m_curr_object = new CurrentObject();
  2995. // create new object (it may be removed later if no instances are generated from it)
  2996. /*m_curr_object->model_object_idx = (int)m_model->objects.size();
  2997. m_curr_object.object = m_model->add_object();
  2998. if (m_curr_object.object == nullptr) {
  2999. add_error("Unable to create object");
  3000. return false;
  3001. }*/
  3002. }
  3003. m_curr_object->id = bbs_get_attribute_value_int(attributes, num_attributes, ID_ATTR);
  3004. m_curr_object->name = bbs_get_attribute_value_string(attributes, num_attributes, NAME_ATTR);
  3005. m_curr_object->uuid = bbs_get_attribute_value_string(attributes, num_attributes, PUUID_ATTR);
  3006. if (m_curr_object->uuid.empty()) {
  3007. m_curr_object->uuid = bbs_get_attribute_value_string(attributes, num_attributes, PUUID_LOWER_ATTR);
  3008. }
  3009. m_curr_object->pid = bbs_get_attribute_value_int(attributes, num_attributes, PID_ATTR);
  3010. }
  3011. return true;
  3012. }
  3013. bool _BBS_3MF_Importer::_handle_end_object()
  3014. {
  3015. if (!m_load_model) {
  3016. delete m_curr_object;
  3017. m_curr_object = nullptr;
  3018. return true;
  3019. }
  3020. if (!m_curr_object || (m_curr_object->id == -1)) {
  3021. add_error("Found invalid object");
  3022. return false;
  3023. }
  3024. else {
  3025. if (m_is_bbl_3mf && boost::ends_with(m_curr_object->uuid, OBJECT_UUID_SUFFIX) && m_load_restore) {
  3026. // Adjust backup object/volume id
  3027. std::istringstream iss(m_curr_object->uuid);
  3028. int backup_id;
  3029. bool need_replace = false;
  3030. if (iss >> std::hex >> backup_id) {
  3031. need_replace = (m_curr_object->id != backup_id);
  3032. m_curr_object->id = backup_id;
  3033. }
  3034. if (!m_curr_object->components.empty())
  3035. {
  3036. Id first_id = m_curr_object->components.front().object_id;
  3037. first_id.second = 0;
  3038. IdToCurrentObjectMap::iterator current_object = m_current_objects.lower_bound(first_id);
  3039. IdToCurrentObjectMap new_map;
  3040. for (int index = 0; index < m_curr_object->components.size(); index++)
  3041. {
  3042. Component& component = m_curr_object->components[index];
  3043. Id new_id = component.object_id;
  3044. new_id.second = (index + 1) << 16 | backup_id;
  3045. if (current_object != m_current_objects.end()
  3046. && (new_id.first.empty() || new_id.first == current_object->first.first)) {
  3047. current_object->second.id = new_id.second;
  3048. new_map.insert({new_id, std::move(current_object->second)});
  3049. current_object = m_current_objects.erase(current_object);
  3050. }
  3051. else {
  3052. add_error("can not find object for component, id=" + std::to_string(component.object_id.second));
  3053. delete m_curr_object;
  3054. m_curr_object = nullptr;
  3055. return false;
  3056. }
  3057. component.object_id.second = new_id.second;
  3058. }
  3059. for (auto & obj : new_map)
  3060. m_current_objects.insert({obj.first, std::move(obj.second)});
  3061. }
  3062. }
  3063. Id id = std::make_pair(m_sub_model_path, m_curr_object->id);
  3064. if (m_current_objects.find(id) == m_current_objects.end()) {
  3065. m_current_objects.insert({ id, std::move(*m_curr_object) });
  3066. delete m_curr_object;
  3067. m_curr_object = nullptr;
  3068. }
  3069. else {
  3070. add_error("Found object with duplicate id");
  3071. delete m_curr_object;
  3072. m_curr_object = nullptr;
  3073. return false;
  3074. }
  3075. }
  3076. /*if (m_curr_object.object != nullptr) {
  3077. if (m_curr_object.id != -1) {
  3078. if (m_curr_object.geometry.empty()) {
  3079. // no geometry defined
  3080. // remove the object from the model
  3081. m_model->delete_object(m_curr_object.object);
  3082. if (m_curr_object.components.empty()) {
  3083. // no components defined -> invalid object, delete it
  3084. IdToModelObjectMap::iterator object_item = m_objects.find(id);
  3085. if (object_item != m_objects.end())
  3086. m_objects.erase(object_item);
  3087. IdToAliasesMap::iterator alias_item = m_objects_aliases.find(id);
  3088. if (alias_item != m_objects_aliases.end())
  3089. m_objects_aliases.erase(alias_item);
  3090. }
  3091. else
  3092. // adds components to aliases
  3093. m_objects_aliases.insert({ id, m_curr_object.components });
  3094. }
  3095. else {
  3096. // geometry defined, store it for later use
  3097. m_geometries.insert({ id, std::move(m_curr_object.geometry) });
  3098. // stores the object for later use
  3099. if (m_objects.find(id) == m_objects.end()) {
  3100. m_objects.insert({ id, m_curr_object.model_object_idx });
  3101. m_objects_aliases.insert({ id, { 1, Component(m_curr_object.id) } }); // aliases itself
  3102. }
  3103. else {
  3104. add_error("Found object with duplicate id");
  3105. return false;
  3106. }
  3107. }
  3108. }
  3109. else {
  3110. //sub objects
  3111. }
  3112. }*/
  3113. return true;
  3114. }
  3115. bool _BBS_3MF_Importer::_handle_start_color_group(const char **attributes, unsigned int num_attributes)
  3116. {
  3117. m_current_color_group = bbs_get_attribute_value_int(attributes, num_attributes, ID_ATTR);
  3118. return true;
  3119. }
  3120. bool _BBS_3MF_Importer::_handle_end_color_group()
  3121. {
  3122. // do nothing
  3123. return true;
  3124. }
  3125. bool _BBS_3MF_Importer::_handle_start_color(const char **attributes, unsigned int num_attributes)
  3126. {
  3127. std::string color = bbs_get_attribute_value_string(attributes, num_attributes, COLOR_ATTR);
  3128. m_group_id_to_color[m_current_color_group] = color;
  3129. return true;
  3130. }
  3131. bool _BBS_3MF_Importer::_handle_end_color()
  3132. {
  3133. // do nothing
  3134. return true;
  3135. }
  3136. bool _BBS_3MF_Importer::_handle_start_mesh(const char** attributes, unsigned int num_attributes)
  3137. {
  3138. // reset current geometry
  3139. if (m_curr_object)
  3140. m_curr_object->geometry.reset();
  3141. return true;
  3142. }
  3143. bool _BBS_3MF_Importer::_handle_end_mesh()
  3144. {
  3145. // do nothing
  3146. return true;
  3147. }
  3148. bool _BBS_3MF_Importer::_handle_start_vertices(const char** attributes, unsigned int num_attributes)
  3149. {
  3150. // reset current vertices
  3151. if (m_curr_object)
  3152. m_curr_object->geometry.vertices.clear();
  3153. return true;
  3154. }
  3155. bool _BBS_3MF_Importer::_handle_end_vertices()
  3156. {
  3157. // do nothing
  3158. return true;
  3159. }
  3160. bool _BBS_3MF_Importer::_handle_start_vertex(const char** attributes, unsigned int num_attributes)
  3161. {
  3162. // appends the vertex coordinates
  3163. // missing values are set equal to ZERO
  3164. if (m_curr_object)
  3165. m_curr_object->geometry.vertices.emplace_back(
  3166. m_unit_factor * bbs_get_attribute_value_float(attributes, num_attributes, X_ATTR),
  3167. m_unit_factor * bbs_get_attribute_value_float(attributes, num_attributes, Y_ATTR),
  3168. m_unit_factor * bbs_get_attribute_value_float(attributes, num_attributes, Z_ATTR));
  3169. return true;
  3170. }
  3171. bool _BBS_3MF_Importer::_handle_end_vertex()
  3172. {
  3173. // do nothing
  3174. return true;
  3175. }
  3176. bool _BBS_3MF_Importer::_handle_start_triangles(const char** attributes, unsigned int num_attributes)
  3177. {
  3178. // reset current triangles
  3179. if (m_curr_object)
  3180. m_curr_object->geometry.triangles.clear();
  3181. return true;
  3182. }
  3183. bool _BBS_3MF_Importer::_handle_end_triangles()
  3184. {
  3185. // do nothing
  3186. return true;
  3187. }
  3188. bool _BBS_3MF_Importer::_handle_start_triangle(const char** attributes, unsigned int num_attributes)
  3189. {
  3190. // we are ignoring the following attributes:
  3191. // p1
  3192. // p2
  3193. // p3
  3194. // pid
  3195. // see specifications
  3196. // appends the triangle's vertices indices
  3197. // missing values are set equal to ZERO
  3198. if (m_curr_object) {
  3199. m_curr_object->geometry.triangles.emplace_back(
  3200. bbs_get_attribute_value_int(attributes, num_attributes, V1_ATTR),
  3201. bbs_get_attribute_value_int(attributes, num_attributes, V2_ATTR),
  3202. bbs_get_attribute_value_int(attributes, num_attributes, V3_ATTR));
  3203. m_curr_object->geometry.custom_supports.push_back(bbs_get_attribute_value_string(attributes, num_attributes, CUSTOM_SUPPORTS_ATTR));
  3204. m_curr_object->geometry.custom_seam.push_back(bbs_get_attribute_value_string(attributes, num_attributes, CUSTOM_SEAM_ATTR));
  3205. m_curr_object->geometry.mmu_segmentation.push_back(bbs_get_attribute_value_string(attributes, num_attributes, MMU_SEGMENTATION_ATTR));
  3206. // BBS
  3207. m_curr_object->geometry.face_properties.push_back(bbs_get_attribute_value_string(attributes, num_attributes, FACE_PROPERTY_ATTR));
  3208. }
  3209. return true;
  3210. }
  3211. bool _BBS_3MF_Importer::_handle_end_triangle()
  3212. {
  3213. // do nothing
  3214. return true;
  3215. }
  3216. bool _BBS_3MF_Importer::_handle_start_components(const char** attributes, unsigned int num_attributes)
  3217. {
  3218. // reset current components
  3219. if (m_curr_object)
  3220. m_curr_object->components.clear();
  3221. return true;
  3222. }
  3223. bool _BBS_3MF_Importer::_handle_end_components()
  3224. {
  3225. // do nothing
  3226. return true;
  3227. }
  3228. bool _BBS_3MF_Importer::_handle_start_component(const char** attributes, unsigned int num_attributes)
  3229. {
  3230. std::string path = xml_unescape(bbs_get_attribute_value_string(attributes, num_attributes, PPATH_ATTR));
  3231. int object_id = bbs_get_attribute_value_int(attributes, num_attributes, OBJECTID_ATTR);
  3232. Transform3d transform = bbs_get_transform_from_3mf_specs_string(bbs_get_attribute_value_string(attributes, num_attributes, TRANSFORM_ATTR));
  3233. /*Id id = std::make_pair(m_sub_model_path, object_id);
  3234. IdToModelObjectMap::iterator object_item = m_objects.find(id);
  3235. if (object_item == m_objects.end()) {
  3236. IdToAliasesMap::iterator alias_item = m_objects_aliases.find(id);
  3237. if (alias_item == m_objects_aliases.end()) {
  3238. add_error("Found component with invalid object id");
  3239. return false;
  3240. }
  3241. }*/
  3242. if (m_curr_object) {
  3243. Id id = std::make_pair(m_sub_model_path.empty() ? path : m_sub_model_path, object_id);
  3244. m_curr_object->components.emplace_back(id, transform);
  3245. }
  3246. return true;
  3247. }
  3248. bool _BBS_3MF_Importer::_handle_end_component()
  3249. {
  3250. // do nothing
  3251. return true;
  3252. }
  3253. bool _BBS_3MF_Importer::_handle_start_build(const char** attributes, unsigned int num_attributes)
  3254. {
  3255. // do nothing
  3256. return true;
  3257. }
  3258. bool _BBS_3MF_Importer::_handle_end_build()
  3259. {
  3260. // do nothing
  3261. return true;
  3262. }
  3263. bool _BBS_3MF_Importer::_handle_start_item(const char** attributes, unsigned int num_attributes)
  3264. {
  3265. // we are ignoring the following attributes
  3266. // thumbnail
  3267. // partnumber
  3268. // pid
  3269. // pindex
  3270. // see specifications
  3271. int object_id = bbs_get_attribute_value_int(attributes, num_attributes, OBJECTID_ATTR);
  3272. std::string path = bbs_get_attribute_value_string(attributes, num_attributes, PPATH_ATTR);
  3273. Transform3d transform = bbs_get_transform_from_3mf_specs_string(bbs_get_attribute_value_string(attributes, num_attributes, TRANSFORM_ATTR));
  3274. int printable = bbs_get_attribute_value_bool(attributes, num_attributes, PRINTABLE_ATTR);
  3275. return !m_load_model || _create_object_instance(path, object_id, transform, printable, 1);
  3276. }
  3277. bool _BBS_3MF_Importer::_handle_end_item()
  3278. {
  3279. // do nothing
  3280. return true;
  3281. }
  3282. bool _BBS_3MF_Importer::_handle_start_metadata(const char** attributes, unsigned int num_attributes)
  3283. {
  3284. m_curr_characters.clear();
  3285. std::string name = bbs_get_attribute_value_string(attributes, num_attributes, NAME_ATTR);
  3286. if (!name.empty()) {
  3287. m_curr_metadata_name = name;
  3288. }
  3289. return true;
  3290. }
  3291. inline static void check_painting_version(unsigned int loaded_version, unsigned int highest_supported_version, const std::string &error_msg)
  3292. {
  3293. if (loaded_version > highest_supported_version)
  3294. throw version_error(error_msg);
  3295. }
  3296. bool _BBS_3MF_Importer::_handle_end_metadata()
  3297. {
  3298. if ((m_curr_metadata_name == BBS_3MF_VERSION)||(m_curr_metadata_name == BBS_3MF_VERSION1)) {
  3299. //m_is_bbl_3mf = true;
  3300. m_version = (unsigned int)atoi(m_curr_characters.c_str());
  3301. /*if (m_check_version && (m_version > VERSION_BBS_3MF_COMPATIBLE)) {
  3302. // std::string msg = _(L("The selected 3mf file has been saved with a newer version of " + std::string(SLIC3R_APP_NAME) + " and is not compatible."));
  3303. // throw version_error(msg.c_str());
  3304. const std::string msg = (boost::format(_(L("The selected 3mf file has been saved with a newer version of %1% and is not compatible."))) % std::string(SLIC3R_APP_NAME)).str();
  3305. throw version_error(msg);
  3306. }*/
  3307. } else if (m_curr_metadata_name == BBL_APPLICATION_TAG) {
  3308. // Generator application of the 3MF.
  3309. // SLIC3R_APP_KEY - SoftFever_VERSION
  3310. if (boost::starts_with(m_curr_characters, "BambuStudio-")) {
  3311. m_is_bbl_3mf = true;
  3312. m_bambuslicer_generator_version = Semver::parse(m_curr_characters.substr(12));
  3313. }
  3314. else if (boost::starts_with(m_curr_characters, "OrcaSlicer-")) {
  3315. m_is_bbl_3mf = true;
  3316. m_bambuslicer_generator_version = Semver::parse(m_curr_characters.substr(11));
  3317. }
  3318. //TODO: currently use version 0, no need to load&&save this string
  3319. /*} else if (m_curr_metadata_name == BBS_FDM_SUPPORTS_PAINTING_VERSION) {
  3320. m_fdm_supports_painting_version = (unsigned int) atoi(m_curr_characters.c_str());
  3321. check_painting_version(m_fdm_supports_painting_version, FDM_SUPPORTS_PAINTING_VERSION,
  3322. _(L("The selected 3MF contains FDM supports painted object using a newer version of OrcaSlicer and is not compatible.")));
  3323. } else if (m_curr_metadata_name == BBS_SEAM_PAINTING_VERSION) {
  3324. m_seam_painting_version = (unsigned int) atoi(m_curr_characters.c_str());
  3325. check_painting_version(m_seam_painting_version, SEAM_PAINTING_VERSION,
  3326. _(L("The selected 3MF contains seam painted object using a newer version of OrcaSlicer and is not compatible.")));
  3327. } else if (m_curr_metadata_name == BBS_MM_PAINTING_VERSION) {
  3328. m_mm_painting_version = (unsigned int) atoi(m_curr_characters.c_str());
  3329. check_painting_version(m_mm_painting_version, MM_PAINTING_VERSION,
  3330. _(L("The selected 3MF contains multi-material painted object using a newer version of OrcaSlicer and is not compatible.")));*/
  3331. } else if (m_curr_metadata_name == BBL_MODEL_ID_TAG) {
  3332. m_model_id = xml_unescape(m_curr_characters);
  3333. } else if (m_curr_metadata_name == BBL_MODEL_NAME_TAG) {
  3334. BOOST_LOG_TRIVIAL(trace) << "design_info, load_3mf found model name = " << m_curr_characters;
  3335. //model_info.model_name = xml_unescape(m_curr_characters); //Susi_not_impl
  3336. } else if (m_curr_metadata_name == BBL_ORIGIN_TAG) {
  3337. BOOST_LOG_TRIVIAL(trace) << "design_info, load_3mf found model name = " << m_curr_characters;
  3338. //model_info.origin = xml_unescape(m_curr_characters); //Susi_not_impl
  3339. } else if (m_curr_metadata_name == BBL_DESIGNER_TAG) {
  3340. BOOST_LOG_TRIVIAL(trace) << "design_info, load_3mf found designer = " << m_curr_characters;
  3341. m_designer = xml_unescape(m_curr_characters);
  3342. } else if (m_curr_metadata_name == BBL_DESIGNER_USER_ID_TAG) {
  3343. BOOST_LOG_TRIVIAL(trace) << "design_info, load_3mf found designer_user_id = " << m_curr_characters;
  3344. m_designer_user_id = xml_unescape(m_curr_characters);
  3345. } else if (m_curr_metadata_name == BBL_DESIGNER_COVER_FILE_TAG) {
  3346. BOOST_LOG_TRIVIAL(trace) << "design_info, load_3mf found designer_cover = " << m_curr_characters;
  3347. //model_info.cover_file = xml_unescape(m_curr_characters); //Susi_not_impl
  3348. } else if (m_curr_metadata_name == BBL_DESCRIPTION_TAG) {
  3349. BOOST_LOG_TRIVIAL(trace) << "design_info, load_3mf found description = " << m_curr_characters;
  3350. //model_info.description = xml_unescape(m_curr_characters); //Susi_not_impl
  3351. } else if (m_curr_metadata_name == BBL_LICENSE_TAG) {
  3352. BOOST_LOG_TRIVIAL(trace) << "design_info, load_3mf found license = " << m_curr_characters;
  3353. //model_info.license = xml_unescape(m_curr_characters); //Susi_not_impl
  3354. } else if (m_curr_metadata_name == BBL_COPYRIGHT_TAG) {
  3355. BOOST_LOG_TRIVIAL(trace) << "design_info, load_3mf found CopyRight = " << m_curr_characters;
  3356. //model_info.copyright = xml_unescape(m_curr_characters); //Susi_not_impl
  3357. } else if (m_curr_metadata_name == BBL_COPYRIGHT_NORMATIVE_TAG) {
  3358. BOOST_LOG_TRIVIAL(trace) << "design_info, load_3mf found Copyright = " << m_curr_characters;
  3359. //model_info.copyright = xml_unescape(m_curr_characters); //Susi_not_impl
  3360. } else if (m_curr_metadata_name == BBL_REGION_TAG) {
  3361. BOOST_LOG_TRIVIAL(trace) << "design_info, load_3mf found region = " << m_curr_characters;
  3362. m_contry_code = xml_unescape(m_curr_characters);
  3363. } else if (m_curr_metadata_name == BBL_PROFILE_TITLE_TAG) {
  3364. BOOST_LOG_TRIVIAL(trace) << "design_info, load_3mf found profile_title = " << m_curr_characters;
  3365. m_profile_title = xml_unescape(m_curr_characters);
  3366. } else if (m_curr_metadata_name == BBL_PROFILE_COVER_TAG) {
  3367. BOOST_LOG_TRIVIAL(trace) << "design_info, load_3mf found profile_cover = " << m_curr_characters;
  3368. m_profile_cover = xml_unescape(m_curr_characters);
  3369. } else if (m_curr_metadata_name == BBL_PROFILE_DESCRIPTION_TAG) {
  3370. BOOST_LOG_TRIVIAL(trace) << "design_info, load_3mf found profile_description = " << m_curr_characters;
  3371. m_Profile_description = xml_unescape(m_curr_characters);
  3372. } else if (m_curr_metadata_name == BBL_PROFILE_USER_ID_TAG) {
  3373. BOOST_LOG_TRIVIAL(trace) << "design_info, load_3mf found profile_user_id = " << m_curr_characters;
  3374. m_profile_user_id = xml_unescape(m_curr_characters);
  3375. }else if (m_curr_metadata_name == BBL_PROFILE_USER_NAME_TAG) {
  3376. BOOST_LOG_TRIVIAL(trace) << "design_info, load_3mf found profile_user_name = " << m_curr_characters;
  3377. m_profile_user_name = xml_unescape(m_curr_characters);
  3378. } else if (m_curr_metadata_name == BBL_CREATION_DATE_TAG) {
  3379. ;
  3380. } else if (m_curr_metadata_name == BBL_MODIFICATION_TAG) {
  3381. ;
  3382. } else {
  3383. ;
  3384. }
  3385. if (!m_curr_metadata_name.empty()) {
  3386. BOOST_LOG_TRIVIAL(info) << "load_3mf found metadata key = " << m_curr_metadata_name << ", value = " << xml_unescape(m_curr_characters);
  3387. //model_info.metadata_items[m_curr_metadata_name] = xml_unescape(m_curr_characters); //Susi_not_impl
  3388. }
  3389. return true;
  3390. }
  3391. //struct TextConfigurationSerialization
  3392. //{
  3393. //public:
  3394. // TextConfigurationSerialization() = delete;
  3395. //
  3396. // using TypeToName = boost::bimap<EmbossStyle::Type, std::string_view>;
  3397. // static const TypeToName type_to_name;
  3398. // using HorizontalAlignToName = boost::bimap<FontProp::HorizontalAlign, std::string_view>;
  3399. // static const HorizontalAlignToName horizontal_align_to_name;
  3400. // using VerticalAlignToName = boost::bimap<FontProp::VerticalAlign, std::string_view>;
  3401. // static const VerticalAlignToName vertical_align_to_name;
  3402. //
  3403. // static EmbossStyle::Type get_type(std::string_view type) {
  3404. // const auto& to_type = TextConfigurationSerialization::type_to_name.right;
  3405. // auto type_item = to_type.find(type);
  3406. // assert(type_item != to_type.end());
  3407. // if (type_item == to_type.end()) return EmbossStyle::Type::undefined;
  3408. // return type_item->second;
  3409. // }
  3410. // static std::string_view get_name(EmbossStyle::Type type) {
  3411. // const auto& to_name = TextConfigurationSerialization::type_to_name.left;
  3412. // auto type_name = to_name.find(type);
  3413. // assert(type_name != to_name.end());
  3414. // if (type_name == to_name.end()) return "unknown type";
  3415. // return type_name->second;
  3416. // }
  3417. // static void to_xml(std::stringstream &stream, const TextConfiguration &tc);
  3418. // static std::optional<TextConfiguration> read(const char **attributes, unsigned int num_attributes);
  3419. // static EmbossShape read_old(const char **attributes, unsigned int num_attributes);
  3420. //}; //Susi_not_impl
  3421. bool _BBS_3MF_Importer::_handle_start_text_configuration(const char **attributes, unsigned int num_attributes)
  3422. {
  3423. IdToMetadataMap::iterator object = m_objects_metadata.find(m_curr_config.object_id);
  3424. if (object == m_objects_metadata.end()) {
  3425. add_error("Can not assign volume mesh to a valid object");
  3426. return false;
  3427. }
  3428. if (object->second.volumes.empty()) {
  3429. add_error("Can not assign mesh to a valid volume");
  3430. return false;
  3431. }
  3432. ObjectMetadata::VolumeMetadata& volume = object->second.volumes.back();
  3433. //volume.text_configuration = TextConfigurationSerialization::read(attributes, num_attributes); //Susi_not_impl
  3434. //if (!volume.text_configuration.has_value()) //Susi_not_impl
  3435. // return false;
  3436. // Is 3mf version with shapes?
  3437. //if (volume.shape_configuration.has_value()) //Susi_not_impl
  3438. // return true;
  3439. // Back compatibility for 3mf version without shapes
  3440. //volume.shape_configuration = TextConfigurationSerialization::read_old(attributes, num_attributes); //Susi_not_impl
  3441. return true;
  3442. }
  3443. // Definition of read/write method for EmbossShape
  3444. static void to_xml(std::stringstream &stream, /*const EmbossShape &es, */const ModelVolume &volume, mz_zip_archive &archive);
  3445. //static std::optional<EmbossShape> read_emboss_shape(const char **attributes, unsigned int num_attributes); //Susi_not_impl
  3446. bool _BBS_3MF_Importer::_handle_start_shape_configuration(const char **attributes, unsigned int num_attributes)
  3447. {
  3448. IdToMetadataMap::iterator object = m_objects_metadata.find(m_curr_config.object_id);
  3449. if (object == m_objects_metadata.end()) {
  3450. add_error("Can not assign volume mesh to a valid object");
  3451. return false;
  3452. }
  3453. auto &volumes = object->second.volumes;
  3454. if (volumes.empty()) {
  3455. add_error("Can not assign mesh to a valid volume");
  3456. return false;
  3457. }
  3458. ObjectMetadata::VolumeMetadata &volume = volumes.back();
  3459. //volume.shape_configuration = read_emboss_shape(attributes, num_attributes);
  3460. //if (!volume.shape_configuration.has_value())
  3461. // return false; //Susi_not_impl
  3462. // Fill svg file content into shape_configuration
  3463. //std::optional<EmbossShape::SvgFile> &svg = volume.shape_configuration->svg_file;
  3464. //if (!svg.has_value())
  3465. // return true; // do not contain svg file //Susi_not_impl
  3466. //const std::string &path = svg->path_in_3mf;
  3467. //if (path.empty())
  3468. // return true; // do not contain svg file //Susi_not_impl
  3469. //auto it = m_path_to_emboss_shape_files.find(path);
  3470. //if (it == m_path_to_emboss_shape_files.end())
  3471. // return true; // svg file is not loaded yet //Susi_not_impl
  3472. //svg->file_data = it->second; //Susi_not_impl
  3473. return true;
  3474. }
  3475. bool _BBS_3MF_Importer::_create_object_instance(std::string const & path, int object_id, const Transform3d& transform, const bool printable, unsigned int recur_counter)
  3476. {
  3477. static const unsigned int MAX_RECURSIONS = 10;
  3478. // escape from circular aliasing
  3479. if (recur_counter > MAX_RECURSIONS) {
  3480. add_error("Too many recursions");
  3481. return false;
  3482. }
  3483. Id id{path, object_id};
  3484. IdToCurrentObjectMap::iterator it = m_current_objects.find(id);
  3485. if (it == m_current_objects.end()) {
  3486. add_error("can not find object id " + std::to_string(object_id) + " to builditem");
  3487. return false;
  3488. }
  3489. IdToModelObjectMap::iterator object_item = m_objects.find(id);
  3490. if (object_item == m_objects.end()) {
  3491. //add object
  3492. CurrentObject& current_object = it->second;
  3493. int object_index = (int)m_model->objects.size();
  3494. ModelObject* model_object = m_model->add_object();
  3495. if (model_object == nullptr) {
  3496. add_error("Unable to create object for builditem, id " + std::to_string(object_id));
  3497. return false;
  3498. }
  3499. m_objects.insert({ id, object_index });
  3500. current_object.model_object_idx = object_index;
  3501. current_object.object = model_object;
  3502. ModelInstance* instance = m_model->objects[object_index]->add_instance();
  3503. if (instance == nullptr) {
  3504. add_error("error when add object instance for id " + std::to_string(object_id));
  3505. return false;
  3506. }
  3507. instance->printable = printable;
  3508. m_instances.emplace_back(instance, transform);
  3509. if (m_is_bbl_3mf && boost::ends_with(current_object.uuid, OBJECT_UUID_SUFFIX)) {
  3510. std::istringstream iss(current_object.uuid);
  3511. int backup_id;
  3512. if (iss >> std::hex >> backup_id) {
  3513. //m_model->set_object_backup_id(*model_object, backup_id); //Susi_not_impl
  3514. }
  3515. }
  3516. /*if (!current_object.geometry.empty()) {
  3517. }
  3518. else if (!current_object.components.empty()) {
  3519. // recursively process nested components
  3520. for (const Component& component : it->second) {
  3521. if (!_create_object_instance(path, component.object_id, transform * component.transform, printable, recur_counter + 1))
  3522. return false;
  3523. }
  3524. }
  3525. else {
  3526. add_error("can not construct build items with invalid object, id " + std::to_string(object_id));
  3527. return false;
  3528. }*/
  3529. }
  3530. else {
  3531. //add instance
  3532. ModelInstance* instance = m_model->objects[object_item->second]->add_instance();
  3533. if (instance == nullptr) {
  3534. add_error("error when add object instance for id " + std::to_string(object_id));
  3535. return false;
  3536. }
  3537. instance->printable = printable;
  3538. m_instances.emplace_back(instance, transform);
  3539. }
  3540. /*if (it->second.size() == 1 && it->second[0].object_id == object_id) {
  3541. // aliasing to itself
  3542. IdToModelObjectMap::iterator object_item = m_objects.find(id);
  3543. if (object_item == m_objects.end() || object_item->second == -1) {
  3544. add_error("Found invalid object");
  3545. return false;
  3546. }
  3547. else {
  3548. ModelInstance* instance = m_model->objects[object_item->second]->add_instance();
  3549. if (instance == nullptr) {
  3550. add_error("Unable to add object instance");
  3551. return false;
  3552. }
  3553. instance->printable = printable;
  3554. m_instances.emplace_back(instance, transform);
  3555. }
  3556. }
  3557. else {
  3558. // recursively process nested components
  3559. for (const Component& component : it->second) {
  3560. if (!_create_object_instance(path, component.object_id, transform * component.transform, printable, recur_counter + 1))
  3561. return false;
  3562. }
  3563. }*/
  3564. return true;
  3565. }
  3566. void _BBS_3MF_Importer::_apply_transform(ModelInstance& instance, const Transform3d& transform)
  3567. {
  3568. Slic3r::Geometry::Transformation t(transform);
  3569. // invalid scale value, return
  3570. if (!t.get_scaling_factor().all())
  3571. return;
  3572. instance.set_transformation(t);
  3573. }
  3574. bool _BBS_3MF_Importer::_handle_start_config(const char** attributes, unsigned int num_attributes)
  3575. {
  3576. // do nothing
  3577. return true;
  3578. }
  3579. bool _BBS_3MF_Importer::_handle_end_config()
  3580. {
  3581. // do nothing
  3582. return true;
  3583. }
  3584. bool _BBS_3MF_Importer::_handle_start_config_object(const char** attributes, unsigned int num_attributes)
  3585. {
  3586. if (m_parsing_slice_info)
  3587. return true;
  3588. int object_id = bbs_get_attribute_value_int(attributes, num_attributes, ID_ATTR);
  3589. IdToMetadataMap::iterator object_item = m_objects_metadata.find(object_id);
  3590. if (object_item != m_objects_metadata.end()) {
  3591. add_error("Duplicated object id: " + std::to_string(object_id) + " in model_settings.config");
  3592. return false;
  3593. }
  3594. // Added because of github #3435, currently not used by PrusaSlicer
  3595. // int instances_count_id = bbs_get_attribute_value_int(attributes, num_attributes, INSTANCESCOUNT_ATTR);
  3596. m_objects_metadata.insert({ object_id, ObjectMetadata() });
  3597. m_curr_config.object_id = object_id;
  3598. return true;
  3599. }
  3600. bool _BBS_3MF_Importer::_handle_end_config_object()
  3601. {
  3602. m_curr_config.object_id = -1;
  3603. return true;
  3604. }
  3605. bool _BBS_3MF_Importer::_handle_start_config_volume(const char** attributes, unsigned int num_attributes)
  3606. {
  3607. IdToMetadataMap::iterator object = m_objects_metadata.find(m_curr_config.object_id);
  3608. if (object == m_objects_metadata.end()) {
  3609. add_error("can not find object for part, id " + std::to_string(m_curr_config.object_id) );
  3610. return false;
  3611. }
  3612. m_curr_config.volume_id = (int)object->second.volumes.size();
  3613. unsigned int first_triangle_id = (unsigned int)bbs_get_attribute_value_int(attributes, num_attributes, FIRST_TRIANGLE_ID_ATTR);
  3614. unsigned int last_triangle_id = (unsigned int)bbs_get_attribute_value_int(attributes, num_attributes, LAST_TRIANGLE_ID_ATTR);
  3615. //BBS: refine the part type logic
  3616. std::string subtype_str = bbs_get_attribute_value_string(attributes, num_attributes, SUBTYPE_ATTR);
  3617. if( "normal_part" == subtype_str)
  3618. subtype_str = "ModelPart";
  3619. else if("negative_part" == subtype_str)
  3620. subtype_str = "NegativeVolume";
  3621. else if("modifier_part" == subtype_str)
  3622. subtype_str = "ParameterModifier";
  3623. else if("support_enforcer" == subtype_str)
  3624. subtype_str = "SupportEnforcer";
  3625. else if("support_blocker" == subtype_str)
  3626. subtype_str = "SupportBlocker";
  3627. ModelVolumeType type = ModelVolume::type_from_string(subtype_str);
  3628. int subbject_id = bbs_get_attribute_value_int(attributes, num_attributes, ID_ATTR);
  3629. if (last_triangle_id > 0)
  3630. object->second.volumes.emplace_back(first_triangle_id, last_triangle_id, type);
  3631. else
  3632. object->second.volumes.emplace_back(subbject_id, type);
  3633. return true;
  3634. }
  3635. bool _BBS_3MF_Importer::_handle_start_config_volume_mesh(const char** attributes, unsigned int num_attributes)
  3636. {
  3637. IdToMetadataMap::iterator object = m_objects_metadata.find(m_curr_config.object_id);
  3638. if (object == m_objects_metadata.end()) {
  3639. add_error("can not find object for mesh_stats, id " + std::to_string(m_curr_config.object_id) );
  3640. return false;
  3641. }
  3642. if ((m_curr_config.volume_id == -1) || ((object->second.volumes.size() - 1) < m_curr_config.volume_id)) {
  3643. add_error("can not find part for mesh_stats");
  3644. return false;
  3645. }
  3646. ObjectMetadata::VolumeMetadata& volume = object->second.volumes[m_curr_config.volume_id];
  3647. int edges_fixed = bbs_get_attribute_value_int(attributes, num_attributes, MESH_STAT_EDGES_FIXED );
  3648. int degenerate_facets = bbs_get_attribute_value_int(attributes, num_attributes, MESH_STAT_DEGENERATED_FACETS);
  3649. int facets_removed = bbs_get_attribute_value_int(attributes, num_attributes, MESH_STAT_FACETS_REMOVED );
  3650. int facets_reversed = bbs_get_attribute_value_int(attributes, num_attributes, MESH_STAT_FACETS_RESERVED );
  3651. int backwards_edges = bbs_get_attribute_value_int(attributes, num_attributes, MESH_STAT_BACKWARDS_EDGES );
  3652. volume.mesh_stats = { edges_fixed, degenerate_facets, facets_removed, facets_reversed, backwards_edges };
  3653. return true;
  3654. }
  3655. bool _BBS_3MF_Importer::_handle_end_config_volume()
  3656. {
  3657. m_curr_config.volume_id = -1;
  3658. return true;
  3659. }
  3660. bool _BBS_3MF_Importer::_handle_end_config_volume_mesh()
  3661. {
  3662. // do nothing
  3663. return true;
  3664. }
  3665. bool _BBS_3MF_Importer::_handle_start_config_metadata(const char** attributes, unsigned int num_attributes)
  3666. {
  3667. //std::string type = bbs_get_attribute_value_string(attributes, num_attributes, TYPE_ATTR);
  3668. std::string key = bbs_get_attribute_value_string(attributes, num_attributes, KEY_ATTR);
  3669. std::string value = bbs_get_attribute_value_string(attributes, num_attributes, VALUE_ATTR);
  3670. if ((m_curr_plater == nullptr)&&!m_parsing_slice_info)
  3671. {
  3672. IdToMetadataMap::iterator object = m_objects_metadata.find(m_curr_config.object_id);
  3673. if (object == m_objects_metadata.end()) {
  3674. add_error("Cannot find object for metadata, id " + std::to_string(m_curr_config.object_id));
  3675. return false;
  3676. }
  3677. if (m_curr_config.volume_id == -1)
  3678. object->second.metadata.emplace_back(key, value);
  3679. else {
  3680. if (size_t(m_curr_config.volume_id) < object->second.volumes.size())
  3681. object->second.volumes[m_curr_config.volume_id].metadata.emplace_back(key, value);
  3682. }
  3683. }
  3684. else
  3685. {
  3686. //plater
  3687. if (key == PLATERID_ATTR)
  3688. {
  3689. m_curr_plater->plate_index = atoi(value.c_str());
  3690. }
  3691. else if (key == PLATER_NAME_ATTR) {
  3692. m_curr_plater->plate_name = xml_unescape(value.c_str());
  3693. }
  3694. else if (key == LOCK_ATTR)
  3695. {
  3696. std::istringstream(value) >> std::boolalpha >> m_curr_plater->locked;
  3697. }
  3698. else if (key == BED_TYPE_ATTR)
  3699. {
  3700. //BedType bed_type = BedType::btPC;
  3701. //ConfigOptionEnum<BedType>::from_string(value, bed_type);
  3702. //m_curr_plater->config.set_key_value("curr_bed_type", new ConfigOptionEnum<BedType>(bed_type)); //Susi_not_impl
  3703. }
  3704. else if (key == PRINT_SEQUENCE_ATTR)
  3705. {
  3706. //PrintSequence print_sequence = PrintSequence::ByLayer;
  3707. //ConfigOptionEnum<PrintSequence>::from_string(value, print_sequence);
  3708. //m_curr_plater->config.set_key_value("print_sequence", new ConfigOptionEnum<PrintSequence>(print_sequence));
  3709. if (value == "by layer")
  3710. m_curr_plater->config.set_key_value("complete_objects", new ConfigOptionBool(false));
  3711. else if (value == "by object")
  3712. m_curr_plater->config.set_key_value("complete_objects", new ConfigOptionBool(true));
  3713. else
  3714. assert(false);
  3715. }
  3716. else if (key == FIRST_LAYER_PRINT_SEQUENCE_ATTR) {
  3717. auto get_vector_from_string = [](const std::string &str) -> std::vector<int> {
  3718. std::stringstream stream(str);
  3719. int value;
  3720. std::vector<int> results;
  3721. while (stream >> value) {
  3722. results.push_back(value);
  3723. }
  3724. return results;
  3725. };
  3726. //m_curr_plater->config.set_key_value("first_layer_print_sequence", new ConfigOptionInts(get_vector_from_string(value))); //Susi_not_impl
  3727. }
  3728. else if (key == SPIRAL_VASE_MODE) {
  3729. bool spiral_mode = false;
  3730. std::istringstream(value) >> std::boolalpha >> spiral_mode;
  3731. m_curr_plater->config.set_key_value("spiral_vase"/*"spiral_mode"*/, new ConfigOptionBool(spiral_mode));
  3732. }
  3733. else if (key == GCODE_FILE_ATTR)
  3734. {
  3735. m_curr_plater->gcode_file = value;
  3736. }
  3737. else if (key == THUMBNAIL_FILE_ATTR)
  3738. {
  3739. m_curr_plater->thumbnail_file = value;
  3740. }
  3741. else if (key == TOP_FILE_ATTR)
  3742. {
  3743. m_curr_plater->top_file = value;
  3744. }
  3745. else if (key == PICK_FILE_ATTR)
  3746. {
  3747. m_curr_plater->pick_file = value;
  3748. }
  3749. //else if (key == PATTERN_FILE_ATTR)
  3750. //{
  3751. // m_curr_plater->pattern_file = value;
  3752. //}
  3753. else if (key == PATTERN_BBOX_FILE_ATTR)
  3754. {
  3755. m_curr_plater->pattern_bbox_file = value;
  3756. }
  3757. else if (key == INSTANCEID_ATTR)
  3758. {
  3759. m_curr_instance.instance_id = atoi(value.c_str());
  3760. }
  3761. else if (key == IDENTIFYID_ATTR)
  3762. {
  3763. m_curr_instance.identify_id = atoi(value.c_str());
  3764. }
  3765. else if (key == OBJECT_ID_ATTR)
  3766. {
  3767. m_curr_instance.object_id = atoi(value.c_str());
  3768. /*int obj_id = atoi(value.c_str());
  3769. m_curr_instance.object_id = -1;
  3770. IndexToPathMap::iterator index_iter = m_index_paths.find(obj_id);
  3771. if (index_iter == m_index_paths.end()) {
  3772. BOOST_LOG_TRIVIAL(warning) << __FUNCTION__ << ":" << __LINE__
  3773. << boost::format(", can not find object for plate's item, id=%1%, skip this object")%obj_id;
  3774. return true;
  3775. }
  3776. Id temp_id = std::make_pair(index_iter->second, index_iter->first);
  3777. IdToModelObjectMap::iterator object_item = m_objects.find(temp_id);
  3778. if (object_item == m_objects.end()) {
  3779. BOOST_LOG_TRIVIAL(warning) << __FUNCTION__ << ":" << __LINE__
  3780. << boost::format(", can not find object for plate's item, ID <%1%, %2%>, skip this object")%index_iter->second %index_iter->first;
  3781. return true;
  3782. }
  3783. m_curr_instance.object_id = object_item->second;*/
  3784. }
  3785. else if (key == PLATE_IDX_ATTR)
  3786. {
  3787. int plate_index = atoi(value.c_str());
  3788. std::map<int, PlateData*>::iterator it = m_plater_data.find(plate_index);
  3789. if (it != m_plater_data.end())
  3790. m_curr_plater = it->second;
  3791. }
  3792. else if (key == SLICE_PREDICTION_ATTR)
  3793. {
  3794. if (m_curr_plater)
  3795. m_curr_plater->gcode_prediction = value;
  3796. }
  3797. else if (key == SLICE_WEIGHT_ATTR)
  3798. {
  3799. if (m_curr_plater)
  3800. m_curr_plater->gcode_weight = value;
  3801. }
  3802. else if (key == OUTSIDE_ATTR)
  3803. {
  3804. if (m_curr_plater)
  3805. std::istringstream(value) >> std::boolalpha >> m_curr_plater->toolpath_outside;
  3806. }
  3807. else if (key == SUPPORT_USED_ATTR)
  3808. {
  3809. if (m_curr_plater)
  3810. std::istringstream(value) >> std::boolalpha >> m_curr_plater->is_support_used;
  3811. }
  3812. else if (key == LABEL_OBJECT_ENABLED_ATTR)
  3813. {
  3814. if (m_curr_plater)
  3815. std::istringstream(value) >> std::boolalpha >> m_curr_plater->is_label_object_enabled;
  3816. }
  3817. }
  3818. return true;
  3819. }
  3820. bool _BBS_3MF_Importer::_handle_end_config_metadata()
  3821. {
  3822. // do nothing
  3823. return true;
  3824. }
  3825. bool _BBS_3MF_Importer::_handle_start_config_filament(const char** attributes, unsigned int num_attributes)
  3826. {
  3827. if (m_curr_plater) {
  3828. std::string id = bbs_get_attribute_value_string(attributes, num_attributes, FILAMENT_ID_TAG);
  3829. std::string type = bbs_get_attribute_value_string(attributes, num_attributes, FILAMENT_TYPE_TAG);
  3830. std::string color = bbs_get_attribute_value_string(attributes, num_attributes, FILAMENT_COLOR_TAG);
  3831. std::string used_m = bbs_get_attribute_value_string(attributes, num_attributes, FILAMENT_USED_M_TAG);
  3832. std::string used_g = bbs_get_attribute_value_string(attributes, num_attributes, FILAMENT_USED_G_TAG);
  3833. FilamentInfo filament_info;
  3834. filament_info.id = atoi(id.c_str()) - 1;
  3835. filament_info.type = type;
  3836. filament_info.color = color;
  3837. filament_info.used_m = atof(used_m.c_str());
  3838. filament_info.used_g = atof(used_g.c_str());
  3839. m_curr_plater->slice_filaments_info.push_back(filament_info);
  3840. }
  3841. return true;
  3842. }
  3843. bool _BBS_3MF_Importer::_handle_end_config_filament()
  3844. {
  3845. // do nothing
  3846. return true;
  3847. }
  3848. bool _BBS_3MF_Importer::_handle_start_config_warning(const char** attributes, unsigned int num_attributes)
  3849. {
  3850. if (m_curr_plater) {
  3851. std::string msg = bbs_get_attribute_value_string(attributes, num_attributes, WARNING_MSG_TAG);
  3852. std::string lvl_str = bbs_get_attribute_value_string(attributes, num_attributes, "level");
  3853. //GCodeProcessorResult::SliceWarning sw;
  3854. //sw.msg = msg;
  3855. //try {
  3856. // sw.level = atoi(lvl_str.c_str());
  3857. //}
  3858. //catch(...) {
  3859. //}; //Susi_not_impl
  3860. m_curr_plater->warnings.push_back(msg);
  3861. }
  3862. return true;
  3863. }
  3864. bool _BBS_3MF_Importer::_handle_end_config_warning()
  3865. {
  3866. // do nothing
  3867. return true;
  3868. }
  3869. bool _BBS_3MF_Importer::_handle_start_config_plater(const char** attributes, unsigned int num_attributes)
  3870. {
  3871. if (!m_parsing_slice_info) {
  3872. m_curr_plater = new PlateData();
  3873. }
  3874. return true;
  3875. }
  3876. bool _BBS_3MF_Importer::_handle_end_config_plater()
  3877. {
  3878. if (!m_curr_plater)
  3879. {
  3880. add_error("_handle_end_config_plater: don't find plate created before");
  3881. return false;
  3882. }
  3883. m_plater_data.emplace(m_curr_plater->plate_index, m_curr_plater);
  3884. m_curr_plater = nullptr;
  3885. return true;
  3886. }
  3887. bool _BBS_3MF_Importer::_handle_start_config_plater_instance(const char** attributes, unsigned int num_attributes)
  3888. {
  3889. if (!m_curr_plater)
  3890. {
  3891. add_error("_handle_start_config_plater_instance: don't find plate created before");
  3892. return false;
  3893. }
  3894. //do nothing
  3895. return true;
  3896. }
  3897. bool _BBS_3MF_Importer::_handle_end_config_plater_instance()
  3898. {
  3899. if (!m_curr_plater)
  3900. {
  3901. add_error("_handle_end_config_plater_instance: don't find plate created before");
  3902. return false;
  3903. }
  3904. if ((m_curr_instance.object_id == -1) || (m_curr_instance.instance_id == -1))
  3905. {
  3906. //add_error("invalid object id/instance id");
  3907. //skip this instance
  3908. m_curr_instance.object_id = m_curr_instance.instance_id = -1;
  3909. m_curr_instance.identify_id = 0;
  3910. return true;
  3911. }
  3912. m_curr_plater->obj_inst_map.emplace(m_curr_instance.object_id, std::make_pair(m_curr_instance.instance_id, m_curr_instance.identify_id));
  3913. m_curr_instance.object_id = m_curr_instance.instance_id = -1;
  3914. m_curr_instance.identify_id = 0;
  3915. return true;
  3916. }
  3917. bool _BBS_3MF_Importer::_handle_start_assemble(const char** attributes, unsigned int num_attributes)
  3918. {
  3919. return true;
  3920. }
  3921. bool _BBS_3MF_Importer::_handle_end_assemble()
  3922. {
  3923. //do nothing
  3924. return true;
  3925. }
  3926. bool _BBS_3MF_Importer::_handle_start_assemble_item(const char** attributes, unsigned int num_attributes)
  3927. {
  3928. if (!m_load_model) return true;
  3929. int object_id = bbs_get_attribute_value_int(attributes, num_attributes, OBJECT_ID_ATTR);
  3930. int instance_id = bbs_get_attribute_value_int(attributes, num_attributes, INSTANCEID_ATTR);
  3931. IndexToPathMap::iterator index_iter = m_index_paths.find(object_id);
  3932. if (index_iter == m_index_paths.end()) {
  3933. add_error("can not find object for assemble item, id= " + std::to_string(object_id));
  3934. return false;
  3935. }
  3936. Id temp_id = std::make_pair(index_iter->second, index_iter->first);
  3937. IdToModelObjectMap::iterator object_item = m_objects.find(temp_id);
  3938. if (object_item == m_objects.end()) {
  3939. add_error("can not find object for assemble item, id= " + std::to_string(object_id));
  3940. return false;
  3941. }
  3942. object_id = object_item->second;
  3943. Transform3d transform = bbs_get_transform_from_3mf_specs_string(bbs_get_attribute_value_string(attributes, num_attributes, TRANSFORM_ATTR));
  3944. Vec3d ofs2ass = bbs_get_offset_from_3mf_specs_string(bbs_get_attribute_value_string(attributes, num_attributes, OFFSET_ATTR));
  3945. if (object_id < m_model->objects.size()) {
  3946. if (instance_id < m_model->objects[object_id]->instances.size()) {
  3947. //m_model->objects[object_id]->instances[instance_id]->set_assemble_from_transform(transform); //Susi_not_impl
  3948. //m_model->objects[object_id]->instances[instance_id]->set_offset_to_assembly(ofs2ass); //Susi_not_impl
  3949. }
  3950. }
  3951. return true;
  3952. }
  3953. bool _BBS_3MF_Importer::_handle_end_assemble_item()
  3954. {
  3955. return true;
  3956. }
  3957. bool _BBS_3MF_Importer::_handle_start_text_info_item(const char **attributes, unsigned int num_attributes)
  3958. {
  3959. IdToMetadataMap::iterator object = m_objects_metadata.find(m_curr_config.object_id);
  3960. if (object == m_objects_metadata.end()) {
  3961. add_error("can not find object for text_info, id " + std::to_string(m_curr_config.object_id));
  3962. return false;
  3963. }
  3964. if ((m_curr_config.volume_id == -1) || ((object->second.volumes.size() - 1) < m_curr_config.volume_id)) {
  3965. add_error("can not find part for text_info");
  3966. return false;
  3967. }
  3968. ObjectMetadata::VolumeMetadata &volume = object->second.volumes[m_curr_config.volume_id];
  3969. //if (volume.text_configuration.has_value()) {
  3970. // add_error("Both text_info and text_configuration found, ignore legacy text_info");
  3971. // return true;
  3972. //} //Susi_not_impl
  3973. // TODO: Orca: support legacy text info
  3974. /*
  3975. TextInfo text_info;
  3976. text_info.m_text = xml_unescape(bbs_get_attribute_value_string(attributes, num_attributes, TEXT_ATTR));
  3977. text_info.m_font_name = bbs_get_attribute_value_string(attributes, num_attributes, FONT_NAME_ATTR);
  3978. text_info.m_curr_font_idx = bbs_get_attribute_value_int(attributes, num_attributes, FONT_INDEX_ATTR);
  3979. text_info.m_font_size = bbs_get_attribute_value_float(attributes, num_attributes, FONT_SIZE_ATTR);
  3980. text_info.m_thickness = bbs_get_attribute_value_float(attributes, num_attributes, THICKNESS_ATTR);
  3981. text_info.m_embeded_depth = bbs_get_attribute_value_float(attributes, num_attributes, EMBEDED_DEPTH_ATTR);
  3982. text_info.m_rotate_angle = bbs_get_attribute_value_float(attributes, num_attributes, ROTATE_ANGLE_ATTR);
  3983. text_info.m_text_gap = bbs_get_attribute_value_float(attributes, num_attributes, TEXT_GAP_ATTR);
  3984. text_info.m_bold = bbs_get_attribute_value_int(attributes, num_attributes, BOLD_ATTR);
  3985. text_info.m_italic = bbs_get_attribute_value_int(attributes, num_attributes, ITALIC_ATTR);
  3986. text_info.m_is_surface_text = bbs_get_attribute_value_int(attributes, num_attributes, SURFACE_TEXT_ATTR);
  3987. text_info.m_keep_horizontal = bbs_get_attribute_value_int(attributes, num_attributes, KEEP_HORIZONTAL_ATTR);
  3988. text_info.m_rr.mesh_id = bbs_get_attribute_value_int(attributes, num_attributes, HIT_MESH_ATTR);
  3989. std::string hit_pos = bbs_get_attribute_value_string(attributes, num_attributes, HIT_POSITION_ATTR);
  3990. if (!hit_pos.empty())
  3991. text_info.m_rr.hit = get_vec3_from_string(hit_pos);
  3992. std::string hit_normal = bbs_get_attribute_value_string(attributes, num_attributes, HIT_NORMAL_ATTR);
  3993. if (!hit_normal.empty())
  3994. text_info.m_rr.normal = get_vec3_from_string(hit_normal);
  3995. volume.text_info = text_info;*/
  3996. return true;
  3997. }
  3998. bool _BBS_3MF_Importer::_handle_end_text_info_item()
  3999. {
  4000. return true;
  4001. }
  4002. void XMLCALL _BBS_3MF_Importer::_handle_start_relationships_element(void* userData, const char* name, const char** attributes)
  4003. {
  4004. _BBS_3MF_Importer* importer = (_BBS_3MF_Importer*)userData;
  4005. if (importer != nullptr)
  4006. importer->_handle_start_relationships_element(name, attributes);
  4007. }
  4008. void XMLCALL _BBS_3MF_Importer::_handle_end_relationships_element(void* userData, const char* name)
  4009. {
  4010. _BBS_3MF_Importer* importer = (_BBS_3MF_Importer*)userData;
  4011. if (importer != nullptr)
  4012. importer->_handle_end_relationships_element(name);
  4013. }
  4014. void _BBS_3MF_Importer::_handle_start_relationships_element(const char* name, const char** attributes)
  4015. {
  4016. if (m_xml_parser == nullptr)
  4017. return;
  4018. bool res = true;
  4019. unsigned int num_attributes = (unsigned int)XML_GetSpecifiedAttributeCount(m_xml_parser);
  4020. if (::strcmp(RELATIONSHIP_TAG, name) == 0)
  4021. res = _handle_start_relationship(attributes, num_attributes);
  4022. m_curr_characters.clear();
  4023. if (!res)
  4024. _stop_xml_parser();
  4025. }
  4026. void _BBS_3MF_Importer::_handle_end_relationships_element(const char* name)
  4027. {
  4028. if (m_xml_parser == nullptr)
  4029. return;
  4030. bool res = true;
  4031. if (!res)
  4032. _stop_xml_parser();
  4033. }
  4034. bool _BBS_3MF_Importer::_handle_start_relationship(const char** attributes, unsigned int num_attributes)
  4035. {
  4036. std::string path = bbs_get_attribute_value_string(attributes, num_attributes, TARGET_ATTR);
  4037. std::string type = bbs_get_attribute_value_string(attributes, num_attributes, RELS_TYPE_ATTR);
  4038. if (boost::starts_with(type, "http://schemas.microsoft.com/3dmanufacturing/") && boost::ends_with(type, "3dmodel")) {
  4039. if (m_start_part_path.empty()) m_start_part_path = path;
  4040. else m_sub_model_paths.push_back(path);
  4041. } else if (boost::starts_with(type, "http://schemas.openxmlformats.org/") && boost::ends_with(type, "thumbnail")) {
  4042. if (boost::algorithm::ends_with(path, ".png"))
  4043. m_thumbnail_path = path;
  4044. } else if (boost::starts_with(type, "http://schemas.bambulab.com/") && boost::ends_with(type, "cover-thumbnail-middle")) {
  4045. m_thumbnail_middle = path;
  4046. } else if (boost::starts_with(type, "http://schemas.bambulab.com/") && boost::ends_with(type, "cover-thumbnail-small")) {
  4047. m_thumbnail_small = path;
  4048. }
  4049. return true;
  4050. }
  4051. void _BBS_3MF_Importer::_generate_current_object_list(std::vector<Component> &sub_objects, Id object_id, IdToCurrentObjectMap &current_objects)
  4052. {
  4053. std::list<std::pair<Component, Transform3d>> id_list; // ???? it's filled then deleted? [merill]
  4054. id_list.push_back(std::make_pair(Component(object_id, Transform3d::Identity()), Transform3d::Identity()));
  4055. while (!id_list.empty())
  4056. {
  4057. auto current_item = id_list.front();
  4058. Component current_id = current_item.first;
  4059. id_list.pop_front();
  4060. IdToCurrentObjectMap::iterator current_object = current_objects.find(current_id.object_id);
  4061. if (current_object != current_objects.end()) {
  4062. //found one
  4063. if (!current_object->second.components.empty()) {
  4064. for (const Component &comp : current_object->second.components) {
  4065. id_list.push_back(std::pair(comp, current_item.second * comp.transform));
  4066. }
  4067. }
  4068. else if (!(current_object->second.geometry.empty())) {
  4069. //CurrentObject* ptr = &(current_objects[current_id]);
  4070. //CurrentObject* ptr2 = &(current_object->second);
  4071. sub_objects.push_back({ current_object->first, current_item.second});
  4072. }
  4073. }
  4074. }
  4075. }
  4076. bool _BBS_3MF_Importer::_generate_volumes_new(ModelObject& object, const std::vector<Component> &sub_objects, const ObjectMetadata::VolumeMetadataList& volumes, const DynamicPrintConfig &config, ConfigSubstitutionContext& config_substitutions)
  4077. {
  4078. if (!object.volumes.empty()) {
  4079. add_error("object already built with parts");
  4080. return false;
  4081. }
  4082. //unsigned int geo_tri_count = (unsigned int)geometry.triangles.size();
  4083. unsigned int renamed_volumes_count = 0;
  4084. for (unsigned int index = 0; index < sub_objects.size(); index++)
  4085. {
  4086. //find the volume metadata firstly
  4087. Component sub_comp = sub_objects[index];
  4088. Id object_id = sub_comp.object_id;
  4089. IdToCurrentObjectMap::iterator current_object = m_current_objects.find(object_id);
  4090. if (current_object == m_current_objects.end()) {
  4091. add_error("sub_objects can not be found, id=" + std::to_string(object_id.second));
  4092. return false;
  4093. }
  4094. CurrentObject* sub_object = &(current_object->second);
  4095. const ObjectMetadata::VolumeMetadata* volume_data = nullptr;
  4096. ObjectMetadata::VolumeMetadata default_volume_data(sub_object->id);
  4097. if (index < volumes.size() && volumes[index].subobject_id == sub_object->id)
  4098. volume_data = &volumes[index];
  4099. else for (const ObjectMetadata::VolumeMetadata& volume_iter : volumes) {
  4100. if (volume_iter.subobject_id == sub_object->id) {
  4101. volume_data = &volume_iter;
  4102. break;
  4103. }
  4104. }
  4105. Transform3d volume_matrix_to_object = Transform3d::Identity();
  4106. bool has_transform = false;
  4107. int shared_mesh_id = object_id.second;
  4108. if (volume_data)
  4109. {
  4110. int found_count = 0;
  4111. // extract the volume transformation from the volume's metadata, if present
  4112. for (const Metadata& metadata : volume_data->metadata) {
  4113. if (metadata.key == MATRIX_KEY) {
  4114. volume_matrix_to_object = Slic3r::Geometry::transform3d_from_string(metadata.value);
  4115. has_transform = ! volume_matrix_to_object.isApprox(Transform3d::Identity(), 1e-10);
  4116. found_count++;
  4117. }
  4118. else if (metadata.key == MESH_SHARED_KEY){
  4119. //add the shared mesh logic
  4120. shared_mesh_id = ::atoi(metadata.value.c_str());
  4121. found_count++;
  4122. BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << boost::format(": line %1%, shared_mesh_id %2%")%__LINE__%shared_mesh_id;
  4123. }
  4124. if (found_count >= 2)
  4125. break;
  4126. }
  4127. }
  4128. else {
  4129. //create a volume_data
  4130. volume_data = &default_volume_data;
  4131. }
  4132. ModelVolume* volume = nullptr;
  4133. ModelVolume *shared_volume = nullptr;
  4134. BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << boost::format(": line %1%, subobject_id %2%, shared_mesh_id %3%")%__LINE__ %sub_object->id %shared_mesh_id;
  4135. if (shared_mesh_id != -1) {
  4136. std::map<int, ModelVolume*>::iterator iter = m_shared_meshes.find(shared_mesh_id);
  4137. if (iter != m_shared_meshes.end()) {
  4138. shared_volume = iter->second;
  4139. BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << boost::format(": line %1%, found shared mesh, id %2%, mesh %3%")%__LINE__%shared_mesh_id%shared_volume;
  4140. }
  4141. }
  4142. else {
  4143. //for some cases, object point to this shared mesh already loaded, treat that one as the root
  4144. std::map<int, ModelVolume*>::iterator iter = m_shared_meshes.find(sub_object->id);
  4145. if (iter != m_shared_meshes.end()) {
  4146. shared_volume = iter->second;
  4147. BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << boost::format(": line %1%, already loaded copy-share mesh before, id %2%, mesh %3%")%__LINE__%sub_object->id%shared_volume;
  4148. }
  4149. }
  4150. const size_t triangles_count = sub_object->geometry.triangles.size();
  4151. if (triangles_count == 0) {
  4152. add_error("found no trianges in the object " + std::to_string(sub_object->id));
  4153. return false;
  4154. }
  4155. if (!shared_volume){
  4156. // splits volume out of imported geometry
  4157. indexed_triangle_set its;
  4158. its.indices.assign(sub_object->geometry.triangles.begin(), sub_object->geometry.triangles.end());
  4159. //const size_t triangles_count = its.indices.size();
  4160. //if (triangles_count == 0) {
  4161. // add_error("found no trianges in the object " + std::to_string(sub_object->id));
  4162. // return false;
  4163. //}
  4164. for (const Vec3i32& face : its.indices) {
  4165. for (const int tri_id : face) {
  4166. if (tri_id < 0 || tri_id >= int(sub_object->geometry.vertices.size())) {
  4167. add_error("invalid vertex id in object " + std::to_string(sub_object->id));
  4168. return false;
  4169. }
  4170. }
  4171. }
  4172. its.vertices.assign(sub_object->geometry.vertices.begin(), sub_object->geometry.vertices.end());
  4173. // BBS
  4174. //for (const std::string prop_str : sub_object->geometry.face_properties) {
  4175. // FaceProperty face_prop;
  4176. // face_prop.from_string(prop_str);
  4177. // its.properties.push_back(face_prop);
  4178. //} //Susi_not_impl
  4179. TriangleMesh triangle_mesh(std::move(its), volume_data->mesh_stats);
  4180. // BBS: no need to multiply the instance matrix into the volume
  4181. //if (!m_is_bbl_3mf) {
  4182. // // if the 3mf was not produced by BambuStudio and there is only one instance,
  4183. // // bake the transformation into the geometry to allow the reload from disk command
  4184. // // to work properly
  4185. // if (object.instances.size() == 1) {
  4186. // triangle_mesh.transform(object.instances.front()->get_transformation().get_matrix(), false);
  4187. // object.instances.front()->set_transformation(Slic3r::Geometry::Transformation());
  4188. // //FIXME do the mesh fixing?
  4189. // }
  4190. //}
  4191. if (triangle_mesh.volume() < 0)
  4192. triangle_mesh.flip_triangles();
  4193. volume = object.add_volume(std::move(triangle_mesh));
  4194. if (shared_mesh_id != -1)
  4195. //for some cases the shared mesh is in other plate and not loaded in cli slicing
  4196. //we need to use the first one in the same plate instead
  4197. m_shared_meshes[shared_mesh_id] = volume;
  4198. else
  4199. m_shared_meshes[sub_object->id] = volume;
  4200. }
  4201. else {
  4202. // create volume to use shared mesh
  4203. //volume = object.add_volume_with_shared_mesh(*shared_volume); //orca -> use susi equivalent
  4204. volume = object.add_volume(shared_volume->mesh(), ModelVolumeType::MODEL_PART, false);
  4205. BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << boost::format(": line %1%, create volume using shared_mesh %2%")%__LINE__%shared_volume;
  4206. }
  4207. // stores the volume matrix taken from the metadata, if present
  4208. if (has_transform)
  4209. volume->source.transform = Slic3r::Geometry::Transformation(volume_matrix_to_object);
  4210. volume->calculate_convex_hull();
  4211. //set transform from 3mf
  4212. Slic3r::Geometry::Transformation comp_transformatino(sub_comp.transform);
  4213. volume->set_transformation(comp_transformatino * volume->get_transformation());
  4214. if (shared_volume) {
  4215. const TriangleMesh& trangle_mesh = volume->mesh();
  4216. Vec3d shift = trangle_mesh.get_init_shift();
  4217. if (!shift.isApprox(Vec3d::Zero()))
  4218. volume->translate(shift);
  4219. }
  4220. // recreate custom supports, seam and mmu segmentation from previously loaded attribute
  4221. {
  4222. volume->supported_facets.reserve(triangles_count);
  4223. volume->seam_facets.reserve(triangles_count);
  4224. volume->mmu_segmentation_facets.reserve(triangles_count);
  4225. for (size_t i=0; i<triangles_count; ++i) {
  4226. assert(i < sub_object->geometry.custom_supports.size());
  4227. assert(i < sub_object->geometry.custom_seam.size());
  4228. assert(i < sub_object->geometry.mmu_segmentation.size());
  4229. if (! sub_object->geometry.custom_supports[i].empty())
  4230. volume->supported_facets.set_triangle_from_string(i, sub_object->geometry.custom_supports[i]);
  4231. if (! sub_object->geometry.custom_seam[i].empty())
  4232. volume->seam_facets.set_triangle_from_string(i, sub_object->geometry.custom_seam[i]);
  4233. if (! sub_object->geometry.mmu_segmentation[i].empty())
  4234. volume->mmu_segmentation_facets.set_triangle_from_string(i, sub_object->geometry.mmu_segmentation[i]);
  4235. }
  4236. volume->supported_facets.shrink_to_fit();
  4237. volume->seam_facets.shrink_to_fit();
  4238. volume->mmu_segmentation_facets.shrink_to_fit();
  4239. volume->mmu_segmentation_facets.touch();
  4240. }
  4241. volume->set_type(volume_data->part_type);
  4242. //if (auto &es = volume_data->shape_configuration; es.has_value())
  4243. // volume->emboss_shape = std::move(es);
  4244. //if (auto &tc = volume_data->text_configuration; tc.has_value())
  4245. // volume->text_configuration = std::move(tc); //Susi_not_impl
  4246. std::map<std::string, std::string> key_values_to_deserialize;
  4247. // apply the remaining volume's metadata
  4248. for (const Metadata& metadata : volume_data->metadata) {
  4249. if (metadata.key == NAME_KEY)
  4250. volume->name = metadata.value;
  4251. //else if ((metadata.key == MODIFIER_KEY) && (metadata.value == "1"))
  4252. // volume->set_type(ModelVolumeType::PARAMETER_MODIFIER);
  4253. //for old format
  4254. else if ((metadata.key == VOLUME_TYPE_KEY) || (metadata.key == PART_TYPE_KEY)) {
  4255. std::string subtype_str = metadata.value;
  4256. if( "normal_part" == subtype_str)
  4257. subtype_str = "ModelPart";
  4258. else if("negative_part" == subtype_str)
  4259. subtype_str = "NegativeVolume";
  4260. else if("modifier_part" == subtype_str)
  4261. subtype_str = "ParameterModifier";
  4262. else if("support_enforcer" == subtype_str)
  4263. subtype_str = "SupportEnforcer";
  4264. else if("support_blocker" == subtype_str)
  4265. subtype_str = "SupportBlocker";
  4266. volume->set_type(ModelVolume::type_from_string(subtype_str));
  4267. } else if (metadata.key == SOURCE_FILE_KEY)
  4268. volume->source.input_file = metadata.value;
  4269. else if (metadata.key == SOURCE_OBJECT_ID_KEY)
  4270. volume->source.object_idx = ::atoi(metadata.value.c_str());
  4271. else if (metadata.key == SOURCE_VOLUME_ID_KEY)
  4272. volume->source.volume_idx = ::atoi(metadata.value.c_str());
  4273. else if (metadata.key == SOURCE_OFFSET_X_KEY)
  4274. volume->source.mesh_offset(0) = ::atof(metadata.value.c_str());
  4275. else if (metadata.key == SOURCE_OFFSET_Y_KEY)
  4276. volume->source.mesh_offset(1) = ::atof(metadata.value.c_str());
  4277. else if (metadata.key == SOURCE_OFFSET_Z_KEY)
  4278. volume->source.mesh_offset(2) = ::atof(metadata.value.c_str());
  4279. else if (metadata.key == SOURCE_IN_INCHES)
  4280. volume->source.is_converted_from_inches = metadata.value == "1";
  4281. else if (metadata.key == SOURCE_IN_METERS)
  4282. volume->source.is_converted_from_meters = metadata.value == "1";
  4283. else if ((metadata.key == MATRIX_KEY) || (metadata.key == MESH_SHARED_KEY))
  4284. continue;
  4285. else
  4286. //volume->config.set_deserialize(metadata.key, metadata.value, config_substitutions);
  4287. key_values_to_deserialize[metadata.key] = metadata.value;
  4288. }
  4289. convert_settings_from_bambu(key_values_to_deserialize, config, volume->config, config_substitutions, false);
  4290. // this may happen for 3mf saved by 3rd part softwares
  4291. if (volume->name.empty()) {
  4292. volume->name = object.name;
  4293. if (renamed_volumes_count > 0)
  4294. volume->name += "_" + std::to_string(renamed_volumes_count + 1);
  4295. ++renamed_volumes_count;
  4296. }
  4297. }
  4298. return true;
  4299. }
  4300. /*
  4301. bool _BBS_3MF_Importer::_generate_volumes(ModelObject& object, const Geometry& geometry, const ObjectMetadata::VolumeMetadataList& volumes, ConfigSubstitutionContext& config_substitutions)
  4302. {
  4303. if (!object.volumes.empty()) {
  4304. add_error("Found invalid volumes count");
  4305. return false;
  4306. }
  4307. unsigned int geo_tri_count = (unsigned int)geometry.triangles.size();
  4308. unsigned int renamed_volumes_count = 0;
  4309. for (const ObjectMetadata::VolumeMetadata& volume_data : volumes) {
  4310. if (geo_tri_count <= volume_data.first_triangle_id || geo_tri_count <= volume_data.last_triangle_id || volume_data.last_triangle_id < volume_data.first_triangle_id) {
  4311. add_error("Found invalid triangle id");
  4312. return false;
  4313. }
  4314. Transform3d volume_matrix_to_object = Transform3d::Identity();
  4315. bool has_transform = false;
  4316. // extract the volume transformation from the volume's metadata, if present
  4317. for (const Metadata& metadata : volume_data.metadata) {
  4318. if (metadata.key == MATRIX_KEY) {
  4319. volume_matrix_to_object = Slic3r::Geometry::transform3d_from_string(metadata.value);
  4320. has_transform = ! volume_matrix_to_object.isApprox(Transform3d::Identity(), 1e-10);
  4321. break;
  4322. }
  4323. }
  4324. // splits volume out of imported geometry
  4325. indexed_triangle_set its;
  4326. its.indices.assign(geometry.triangles.begin() + volume_data.first_triangle_id, geometry.triangles.begin() + volume_data.last_triangle_id + 1);
  4327. const size_t triangles_count = its.indices.size();
  4328. if (triangles_count == 0) {
  4329. add_error("An empty triangle mesh found");
  4330. return false;
  4331. }
  4332. {
  4333. int min_id = its.indices.front()[0];
  4334. int max_id = min_id;
  4335. for (const Vec3i& face : its.indices) {
  4336. for (const int tri_id : face) {
  4337. if (tri_id < 0 || tri_id >= int(geometry.vertices.size())) {
  4338. add_error("Found invalid vertex id");
  4339. return false;
  4340. }
  4341. min_id = std::min(min_id, tri_id);
  4342. max_id = std::max(max_id, tri_id);
  4343. }
  4344. }
  4345. its.vertices.assign(geometry.vertices.begin() + min_id, geometry.vertices.begin() + max_id + 1);
  4346. // BBS
  4347. for (const std::string prop_str : geometry.face_properties) {
  4348. FaceProperty face_prop;
  4349. face_prop.from_string(prop_str);
  4350. its.properties.push_back(face_prop);
  4351. }
  4352. // rebase indices to the current vertices list
  4353. for (Vec3i& face : its.indices)
  4354. for (int& tri_id : face)
  4355. tri_id -= min_id;
  4356. }
  4357. TriangleMesh triangle_mesh(std::move(its), volume_data.mesh_stats);
  4358. if (!m_is_bbl_3mf) {
  4359. // if the 3mf was not produced by OrcaSlicer and there is only one instance,
  4360. // bake the transformation into the geometry to allow the reload from disk command
  4361. // to work properly
  4362. if (object.instances.size() == 1) {
  4363. triangle_mesh.transform(object.instances.front()->get_transformation().get_matrix(), false);
  4364. object.instances.front()->set_transformation(Slic3r::Geometry::Transformation());
  4365. //FIXME do the mesh fixing?
  4366. }
  4367. }
  4368. if (triangle_mesh.volume() < 0)
  4369. triangle_mesh.flip_triangles();
  4370. ModelVolume* volume = object.add_volume(std::move(triangle_mesh));
  4371. // stores the volume matrix taken from the metadata, if present
  4372. if (has_transform)
  4373. volume->source.transform = Slic3r::Geometry::Transformation(volume_matrix_to_object);
  4374. volume->calculate_convex_hull();
  4375. // recreate custom supports, seam and mmu segmentation from previously loaded attribute
  4376. volume->supported_facets.reserve(triangles_count);
  4377. volume->seam_facets.reserve(triangles_count);
  4378. volume->mmu_segmentation_facets.reserve(triangles_count);
  4379. for (size_t i=0; i<triangles_count; ++i) {
  4380. size_t index = volume_data.first_triangle_id + i;
  4381. assert(index < geometry.custom_supports.size());
  4382. assert(index < geometry.custom_seam.size());
  4383. assert(index < geometry.mmu_segmentation.size());
  4384. if (! geometry.custom_supports[index].empty())
  4385. volume->supported_facets.set_triangle_from_string(i, geometry.custom_supports[index]);
  4386. if (! geometry.custom_seam[index].empty())
  4387. volume->seam_facets.set_triangle_from_string(i, geometry.custom_seam[index]);
  4388. if (! geometry.mmu_segmentation[index].empty())
  4389. volume->mmu_segmentation_facets.set_triangle_from_string(i, geometry.mmu_segmentation[index]);
  4390. }
  4391. volume->supported_facets.shrink_to_fit();
  4392. volume->seam_facets.shrink_to_fit();
  4393. volume->mmu_segmentation_facets.shrink_to_fit();
  4394. volume->set_type(volume_data.part_type);
  4395. // apply the remaining volume's metadata
  4396. for (const Metadata& metadata : volume_data.metadata) {
  4397. if (metadata.key == NAME_KEY)
  4398. volume->name = metadata.value;
  4399. //else if ((metadata.key == MODIFIER_KEY) && (metadata.value == "1"))
  4400. // volume->set_type(ModelVolumeType::PARAMETER_MODIFIER);
  4401. //for old format
  4402. else if ((metadata.key == VOLUME_TYPE_KEY) || (metadata.key == PART_TYPE_KEY))
  4403. volume->set_type(ModelVolume::type_from_string(metadata.value));
  4404. else if (metadata.key == SOURCE_FILE_KEY)
  4405. volume->source.input_file = metadata.value;
  4406. else if (metadata.key == SOURCE_OBJECT_ID_KEY)
  4407. volume->source.object_idx = ::atoi(metadata.value.c_str());
  4408. else if (metadata.key == SOURCE_VOLUME_ID_KEY)
  4409. volume->source.volume_idx = ::atoi(metadata.value.c_str());
  4410. else if (metadata.key == SOURCE_OFFSET_X_KEY)
  4411. volume->source.mesh_offset(0) = ::atof(metadata.value.c_str());
  4412. else if (metadata.key == SOURCE_OFFSET_Y_KEY)
  4413. volume->source.mesh_offset(1) = ::atof(metadata.value.c_str());
  4414. else if (metadata.key == SOURCE_OFFSET_Z_KEY)
  4415. volume->source.mesh_offset(2) = ::atof(metadata.value.c_str());
  4416. else if (metadata.key == SOURCE_IN_INCHES)
  4417. volume->source.is_converted_from_inches = metadata.value == "1";
  4418. else if (metadata.key == SOURCE_IN_METERS)
  4419. volume->source.is_converted_from_meters = metadata.value == "1";
  4420. else
  4421. volume->config.set_deserialize(metadata.key, metadata.value, config_substitutions);
  4422. }
  4423. // this may happen for 3mf saved by 3rd part softwares
  4424. if (volume->name.empty()) {
  4425. volume->name = object.name;
  4426. if (renamed_volumes_count > 0)
  4427. volume->name += "_" + std::to_string(renamed_volumes_count + 1);
  4428. ++renamed_volumes_count;
  4429. }
  4430. }
  4431. return true;
  4432. }
  4433. */
  4434. void XMLCALL _BBS_3MF_Importer::_handle_start_model_xml_element(void* userData, const char* name, const char** attributes)
  4435. {
  4436. _BBS_3MF_Importer* importer = (_BBS_3MF_Importer*)userData;
  4437. if (importer != nullptr)
  4438. importer->_handle_start_model_xml_element(name, attributes);
  4439. }
  4440. void XMLCALL _BBS_3MF_Importer::_handle_end_model_xml_element(void* userData, const char* name)
  4441. {
  4442. _BBS_3MF_Importer* importer = (_BBS_3MF_Importer*)userData;
  4443. if (importer != nullptr)
  4444. importer->_handle_end_model_xml_element(name);
  4445. }
  4446. void XMLCALL _BBS_3MF_Importer::_handle_xml_characters(void* userData, const XML_Char* s, int len)
  4447. {
  4448. _BBS_3MF_Importer* importer = (_BBS_3MF_Importer*)userData;
  4449. if (importer != nullptr)
  4450. importer->_handle_xml_characters(s, len);
  4451. }
  4452. void XMLCALL _BBS_3MF_Importer::_handle_start_config_xml_element(void* userData, const char* name, const char** attributes)
  4453. {
  4454. _BBS_3MF_Importer* importer = (_BBS_3MF_Importer*)userData;
  4455. if (importer != nullptr)
  4456. importer->_handle_start_config_xml_element(name, attributes);
  4457. }
  4458. void XMLCALL _BBS_3MF_Importer::_handle_end_config_xml_element(void* userData, const char* name)
  4459. {
  4460. _BBS_3MF_Importer* importer = (_BBS_3MF_Importer*)userData;
  4461. if (importer != nullptr)
  4462. importer->_handle_end_config_xml_element(name);
  4463. }
  4464. /* functions of ObjectImporter */
  4465. bool _BBS_3MF_Importer::ObjectImporter::_handle_object_start_model(const char** attributes, unsigned int num_attributes)
  4466. {
  4467. object_unit_factor = bbs_get_unit_factor(bbs_get_attribute_value_string(attributes, num_attributes, UNIT_ATTR));
  4468. return true;
  4469. }
  4470. bool _BBS_3MF_Importer::ObjectImporter::_handle_object_end_model()
  4471. {
  4472. // do nothing
  4473. return true;
  4474. }
  4475. bool _BBS_3MF_Importer::ObjectImporter::_handle_object_start_resources(const char** attributes, unsigned int num_attributes)
  4476. {
  4477. // do nothing
  4478. return true;
  4479. }
  4480. bool _BBS_3MF_Importer::ObjectImporter::_handle_object_end_resources()
  4481. {
  4482. // do nothing
  4483. return true;
  4484. }
  4485. bool _BBS_3MF_Importer::ObjectImporter::_handle_object_start_object(const char** attributes, unsigned int num_attributes)
  4486. {
  4487. // reset current object data
  4488. if (current_object) {
  4489. delete current_object;
  4490. current_object = nullptr;
  4491. }
  4492. std::string object_type = bbs_get_attribute_value_string(attributes, num_attributes, TYPE_ATTR);
  4493. if (bbs_is_valid_object_type(object_type)) {
  4494. if (!current_object) {
  4495. current_object = new CurrentObject();
  4496. }
  4497. current_object->id = bbs_get_attribute_value_int(attributes, num_attributes, ID_ATTR);
  4498. current_object->name = bbs_get_attribute_value_string(attributes, num_attributes, NAME_ATTR);
  4499. current_object->uuid = bbs_get_attribute_value_string(attributes, num_attributes, PUUID_ATTR);
  4500. if (current_object->uuid.empty()) {
  4501. current_object->uuid = bbs_get_attribute_value_string(attributes, num_attributes, PUUID_LOWER_ATTR);
  4502. }
  4503. current_object->pid = bbs_get_attribute_value_int(attributes, num_attributes, PID_ATTR);
  4504. }
  4505. return true;
  4506. }
  4507. bool _BBS_3MF_Importer::ObjectImporter::_handle_object_end_object()
  4508. {
  4509. if (!current_object || (current_object->id == -1)) {
  4510. top_importer->add_error("Found invalid object for "+ object_path);
  4511. return false;
  4512. }
  4513. else {
  4514. if (is_bbl_3mf && boost::ends_with(current_object->uuid, OBJECT_UUID_SUFFIX) && top_importer->m_load_restore) {
  4515. std::istringstream iss(current_object->uuid);
  4516. int backup_id;
  4517. bool need_replace = false;
  4518. if (iss >> std::hex >> backup_id) {
  4519. need_replace = (current_object->id != backup_id);
  4520. current_object->id = backup_id;
  4521. }
  4522. //if (need_replace)
  4523. {
  4524. for (int index = 0; index < current_object->components.size(); index++)
  4525. {
  4526. int temp_id = (index + 1) << 16 | backup_id;
  4527. Component& component = current_object->components[index];
  4528. std::string new_path = component.object_id.first;
  4529. Id new_id = std::make_pair(new_path, temp_id);
  4530. IdToCurrentObjectMap::iterator object_it = object_list.find(component.object_id);
  4531. if (object_it != object_list.end()) {
  4532. CurrentObject new_object;
  4533. new_object.geometry = std::move(object_it->second.geometry);
  4534. new_object.id = temp_id;
  4535. new_object.model_object_idx = object_it->second.model_object_idx;
  4536. new_object.name = object_it->second.name;
  4537. new_object.uuid = object_it->second.uuid;
  4538. object_list.erase(object_it);
  4539. object_list.insert({ new_id, std::move(new_object) });
  4540. }
  4541. else {
  4542. top_importer->add_error("can not find object for component, id=" + std::to_string(component.object_id.second));
  4543. delete current_object;
  4544. current_object = nullptr;
  4545. return false;
  4546. }
  4547. component.object_id.second = temp_id;
  4548. }
  4549. }
  4550. }
  4551. Id id = std::make_pair(object_path, current_object->id);
  4552. if (object_list.find(id) == object_list.end()) {
  4553. object_list.insert({ id, std::move(*current_object) });
  4554. delete current_object;
  4555. current_object = nullptr;
  4556. }
  4557. else {
  4558. top_importer->add_error("Found object with duplicate id for "+object_path);
  4559. delete current_object;
  4560. current_object = nullptr;
  4561. return false;
  4562. }
  4563. }
  4564. return true;
  4565. }
  4566. bool _BBS_3MF_Importer::ObjectImporter::_handle_object_start_color_group(const char **attributes, unsigned int num_attributes)
  4567. {
  4568. object_current_color_group = bbs_get_attribute_value_int(attributes, num_attributes, ID_ATTR);
  4569. return true;
  4570. }
  4571. bool _BBS_3MF_Importer::ObjectImporter::_handle_object_end_color_group()
  4572. {
  4573. // do nothing
  4574. return true;
  4575. }
  4576. bool _BBS_3MF_Importer::ObjectImporter::_handle_object_start_color(const char **attributes, unsigned int num_attributes)
  4577. {
  4578. std::string color = bbs_get_attribute_value_string(attributes, num_attributes, COLOR_ATTR);
  4579. object_group_id_to_color[object_current_color_group] = color;
  4580. return true;
  4581. }
  4582. bool _BBS_3MF_Importer::ObjectImporter::_handle_object_end_color()
  4583. {
  4584. // do nothing
  4585. return true;
  4586. }
  4587. bool _BBS_3MF_Importer::ObjectImporter::_handle_object_start_mesh(const char** attributes, unsigned int num_attributes)
  4588. {
  4589. // reset current geometry
  4590. if (current_object)
  4591. current_object->geometry.reset();
  4592. return true;
  4593. }
  4594. bool _BBS_3MF_Importer::ObjectImporter::_handle_object_end_mesh()
  4595. {
  4596. // do nothing
  4597. return true;
  4598. }
  4599. bool _BBS_3MF_Importer::ObjectImporter::_handle_object_start_vertices(const char** attributes, unsigned int num_attributes)
  4600. {
  4601. // reset current vertices
  4602. if (current_object)
  4603. current_object->geometry.vertices.clear();
  4604. return true;
  4605. }
  4606. bool _BBS_3MF_Importer::ObjectImporter::_handle_object_end_vertices()
  4607. {
  4608. // do nothing
  4609. return true;
  4610. }
  4611. bool _BBS_3MF_Importer::ObjectImporter::_handle_object_start_vertex(const char** attributes, unsigned int num_attributes)
  4612. {
  4613. // appends the vertex coordinates
  4614. // missing values are set equal to ZERO
  4615. if (current_object)
  4616. current_object->geometry.vertices.emplace_back(
  4617. object_unit_factor * bbs_get_attribute_value_float(attributes, num_attributes, X_ATTR),
  4618. object_unit_factor * bbs_get_attribute_value_float(attributes, num_attributes, Y_ATTR),
  4619. object_unit_factor * bbs_get_attribute_value_float(attributes, num_attributes, Z_ATTR));
  4620. return true;
  4621. }
  4622. bool _BBS_3MF_Importer::ObjectImporter::_handle_object_end_vertex()
  4623. {
  4624. // do nothing
  4625. return true;
  4626. }
  4627. bool _BBS_3MF_Importer::ObjectImporter::_handle_object_start_triangles(const char** attributes, unsigned int num_attributes)
  4628. {
  4629. // reset current triangles
  4630. if (current_object)
  4631. current_object->geometry.triangles.clear();
  4632. return true;
  4633. }
  4634. bool _BBS_3MF_Importer::ObjectImporter::_handle_object_end_triangles()
  4635. {
  4636. // do nothing
  4637. return true;
  4638. }
  4639. bool _BBS_3MF_Importer::ObjectImporter::_handle_object_start_triangle(const char** attributes, unsigned int num_attributes)
  4640. {
  4641. // we are ignoring the following attributes:
  4642. // p1
  4643. // p2
  4644. // p3
  4645. // pid
  4646. // see specifications
  4647. // appends the triangle's vertices indices
  4648. // missing values are set equal to ZERO
  4649. if (current_object) {
  4650. current_object->geometry.triangles.emplace_back(
  4651. bbs_get_attribute_value_int(attributes, num_attributes, V1_ATTR),
  4652. bbs_get_attribute_value_int(attributes, num_attributes, V2_ATTR),
  4653. bbs_get_attribute_value_int(attributes, num_attributes, V3_ATTR));
  4654. current_object->geometry.custom_supports.push_back(bbs_get_attribute_value_string(attributes, num_attributes, CUSTOM_SUPPORTS_ATTR));
  4655. current_object->geometry.custom_seam.push_back(bbs_get_attribute_value_string(attributes, num_attributes, CUSTOM_SEAM_ATTR));
  4656. current_object->geometry.mmu_segmentation.push_back(bbs_get_attribute_value_string(attributes, num_attributes, MMU_SEGMENTATION_ATTR));
  4657. // BBS
  4658. current_object->geometry.face_properties.push_back(bbs_get_attribute_value_string(attributes, num_attributes, FACE_PROPERTY_ATTR));
  4659. }
  4660. return true;
  4661. }
  4662. bool _BBS_3MF_Importer::ObjectImporter::_handle_object_end_triangle()
  4663. {
  4664. // do nothing
  4665. return true;
  4666. }
  4667. bool _BBS_3MF_Importer::ObjectImporter::_handle_object_start_components(const char** attributes, unsigned int num_attributes)
  4668. {
  4669. // reset current components
  4670. if (current_object)
  4671. current_object->components.clear();
  4672. return true;
  4673. }
  4674. bool _BBS_3MF_Importer::ObjectImporter::_handle_object_end_components()
  4675. {
  4676. // do nothing
  4677. return true;
  4678. }
  4679. bool _BBS_3MF_Importer::ObjectImporter::_handle_object_start_component(const char** attributes, unsigned int num_attributes)
  4680. {
  4681. int object_id = bbs_get_attribute_value_int(attributes, num_attributes, OBJECTID_ATTR);
  4682. Transform3d transform = bbs_get_transform_from_3mf_specs_string(bbs_get_attribute_value_string(attributes, num_attributes, TRANSFORM_ATTR));
  4683. /*Id id = std::make_pair(m_sub_model_path, object_id);
  4684. IdToModelObjectMap::iterator object_item = m_objects.find(id);
  4685. if (object_item == m_objects.end()) {
  4686. IdToAliasesMap::iterator alias_item = m_objects_aliases.find(id);
  4687. if (alias_item == m_objects_aliases.end()) {
  4688. add_error("Found component with invalid object id");
  4689. return false;
  4690. }
  4691. }*/
  4692. if (current_object) {
  4693. Id id = std::make_pair(object_path, object_id);
  4694. current_object->components.emplace_back(id, transform);
  4695. }
  4696. return true;
  4697. }
  4698. bool _BBS_3MF_Importer::ObjectImporter::_handle_object_end_component()
  4699. {
  4700. // do nothing
  4701. return true;
  4702. }
  4703. bool _BBS_3MF_Importer::ObjectImporter::_handle_object_start_metadata(const char** attributes, unsigned int num_attributes)
  4704. {
  4705. obj_curr_metadata_name.clear();
  4706. std::string name = bbs_get_attribute_value_string(attributes, num_attributes, NAME_ATTR);
  4707. if (!name.empty()) {
  4708. obj_curr_metadata_name = name;
  4709. }
  4710. return true;
  4711. }
  4712. bool _BBS_3MF_Importer::ObjectImporter::_handle_object_end_metadata()
  4713. {
  4714. if ((obj_curr_metadata_name == BBS_3MF_VERSION)||(obj_curr_metadata_name == BBS_3MF_VERSION1)) {
  4715. is_bbl_3mf = true;
  4716. }
  4717. return true;
  4718. }
  4719. void _BBS_3MF_Importer::ObjectImporter::_handle_object_start_model_xml_element(const char* name, const char** attributes)
  4720. {
  4721. if (object_xml_parser == nullptr)
  4722. return;
  4723. bool res = true;
  4724. unsigned int num_attributes = (unsigned int)XML_GetSpecifiedAttributeCount(object_xml_parser);
  4725. if (::strcmp(MODEL_TAG, name) == 0)
  4726. res = _handle_object_start_model(attributes, num_attributes);
  4727. else if (::strcmp(RESOURCES_TAG, name) == 0)
  4728. res = _handle_object_start_resources(attributes, num_attributes);
  4729. else if (::strcmp(OBJECT_TAG, name) == 0)
  4730. res = _handle_object_start_object(attributes, num_attributes);
  4731. else if (::strcmp(COLOR_GROUP_TAG, name) == 0)
  4732. res = _handle_object_start_color_group(attributes, num_attributes);
  4733. else if (::strcmp(COLOR_TAG, name) == 0)
  4734. res = _handle_object_start_color(attributes, num_attributes);
  4735. else if (::strcmp(MESH_TAG, name) == 0)
  4736. res = _handle_object_start_mesh(attributes, num_attributes);
  4737. else if (::strcmp(VERTICES_TAG, name) == 0)
  4738. res = _handle_object_start_vertices(attributes, num_attributes);
  4739. else if (::strcmp(VERTEX_TAG, name) == 0)
  4740. res = _handle_object_start_vertex(attributes, num_attributes);
  4741. else if (::strcmp(TRIANGLES_TAG, name) == 0)
  4742. res = _handle_object_start_triangles(attributes, num_attributes);
  4743. else if (::strcmp(TRIANGLE_TAG, name) == 0)
  4744. res = _handle_object_start_triangle(attributes, num_attributes);
  4745. else if (::strcmp(COMPONENTS_TAG, name) == 0)
  4746. res = _handle_object_start_components(attributes, num_attributes);
  4747. else if (::strcmp(COMPONENT_TAG, name) == 0)
  4748. res = _handle_object_start_component(attributes, num_attributes);
  4749. else if (::strcmp(METADATA_TAG, name) == 0)
  4750. res = _handle_object_start_metadata(attributes, num_attributes);
  4751. if (!res)
  4752. _stop_object_xml_parser();
  4753. }
  4754. void _BBS_3MF_Importer::ObjectImporter::_handle_object_end_model_xml_element(const char* name)
  4755. {
  4756. if (object_xml_parser == nullptr)
  4757. return;
  4758. bool res = true;
  4759. if (::strcmp(MODEL_TAG, name) == 0)
  4760. res = _handle_object_end_model();
  4761. else if (::strcmp(RESOURCES_TAG, name) == 0)
  4762. res = _handle_object_end_resources();
  4763. else if (::strcmp(OBJECT_TAG, name) == 0)
  4764. res = _handle_object_end_object();
  4765. else if (::strcmp(COLOR_GROUP_TAG, name) == 0)
  4766. res = _handle_object_end_color_group();
  4767. else if (::strcmp(COLOR_TAG, name) == 0)
  4768. res = _handle_object_end_color();
  4769. else if (::strcmp(MESH_TAG, name) == 0)
  4770. res = _handle_object_end_mesh();
  4771. else if (::strcmp(VERTICES_TAG, name) == 0)
  4772. res = _handle_object_end_vertices();
  4773. else if (::strcmp(VERTEX_TAG, name) == 0)
  4774. res = _handle_object_end_vertex();
  4775. else if (::strcmp(TRIANGLES_TAG, name) == 0)
  4776. res = _handle_object_end_triangles();
  4777. else if (::strcmp(TRIANGLE_TAG, name) == 0)
  4778. res = _handle_object_end_triangle();
  4779. else if (::strcmp(COMPONENTS_TAG, name) == 0)
  4780. res = _handle_object_end_components();
  4781. else if (::strcmp(COMPONENT_TAG, name) == 0)
  4782. res = _handle_object_end_component();
  4783. else if (::strcmp(METADATA_TAG, name) == 0)
  4784. res = _handle_object_end_metadata();
  4785. if (!res)
  4786. _stop_object_xml_parser();
  4787. }
  4788. void _BBS_3MF_Importer::ObjectImporter::_handle_object_xml_characters(const XML_Char* s, int len)
  4789. {
  4790. obj_curr_characters.append(s, len);
  4791. }
  4792. void XMLCALL _BBS_3MF_Importer::ObjectImporter::_handle_object_start_model_xml_element(void* userData, const char* name, const char** attributes)
  4793. {
  4794. ObjectImporter* importer = (ObjectImporter*)userData;
  4795. if (importer != nullptr)
  4796. importer->_handle_object_start_model_xml_element(name, attributes);
  4797. }
  4798. void XMLCALL _BBS_3MF_Importer::ObjectImporter::_handle_object_end_model_xml_element(void* userData, const char* name)
  4799. {
  4800. ObjectImporter* importer = (ObjectImporter*)userData;
  4801. if (importer != nullptr)
  4802. importer->_handle_object_end_model_xml_element(name);
  4803. }
  4804. void XMLCALL _BBS_3MF_Importer::ObjectImporter::_handle_object_xml_characters(void* userData, const XML_Char* s, int len)
  4805. {
  4806. ObjectImporter* importer = (ObjectImporter*)userData;
  4807. if (importer != nullptr)
  4808. importer->_handle_object_xml_characters(s, len);
  4809. }
  4810. bool _BBS_3MF_Importer::ObjectImporter::_extract_object_from_archive(mz_zip_archive& archive, const mz_zip_archive_file_stat& stat)
  4811. {
  4812. if (stat.m_uncomp_size == 0) {
  4813. top_importer->add_error("Found invalid size for "+object_path);
  4814. return false;
  4815. }
  4816. object_xml_parser = XML_ParserCreate(nullptr);
  4817. if (object_xml_parser == nullptr) {
  4818. top_importer->add_error("Unable to create parser for "+object_path);
  4819. return false;
  4820. }
  4821. XML_SetUserData(object_xml_parser, (void*)this);
  4822. XML_SetElementHandler(object_xml_parser, _BBS_3MF_Importer::ObjectImporter::_handle_object_start_model_xml_element, _BBS_3MF_Importer::ObjectImporter::_handle_object_end_model_xml_element);
  4823. XML_SetCharacterDataHandler(object_xml_parser, _BBS_3MF_Importer::ObjectImporter::_handle_object_xml_characters);
  4824. struct CallbackData
  4825. {
  4826. XML_Parser& parser;
  4827. _BBS_3MF_Importer::ObjectImporter& importer;
  4828. const mz_zip_archive_file_stat& stat;
  4829. CallbackData(XML_Parser& parser, _BBS_3MF_Importer::ObjectImporter& importer, const mz_zip_archive_file_stat& stat) : parser(parser), importer(importer), stat(stat) {}
  4830. };
  4831. CallbackData data(object_xml_parser, *this, stat);
  4832. mz_bool res = 0;
  4833. try
  4834. {
  4835. mz_file_write_func callback = [](void* pOpaque, mz_uint64 file_ofs, const void* pBuf, size_t n)->size_t {
  4836. CallbackData* data = (CallbackData*)pOpaque;
  4837. if (!XML_Parse(data->parser, (const char*)pBuf, (int)n, (file_ofs + n == data->stat.m_uncomp_size) ? 1 : 0) || data->importer.object_parse_error()) {
  4838. char error_buf[1024];
  4839. ::snprintf(error_buf, 1024, "Error (%s) while parsing '%s' at line %d", data->importer.object_parse_error_message(), data->stat.m_filename, (int)XML_GetCurrentLineNumber(data->parser));
  4840. throw Slic3r::FileIOError(error_buf);
  4841. }
  4842. return n;
  4843. };
  4844. void* opaque = &data;
  4845. res = mz_zip_reader_extract_to_callback(&archive, stat.m_file_index, callback, opaque, 0);
  4846. }
  4847. catch (const version_error& e)
  4848. {
  4849. // rethrow the exception
  4850. std::string error_message = std::string(e.what()) + " for " + object_path;
  4851. throw Slic3r::FileIOError(error_message);
  4852. }
  4853. catch (std::exception& e)
  4854. {
  4855. std::string error_message = std::string(e.what()) + " for " + object_path;
  4856. top_importer->add_error(error_message);
  4857. return false;
  4858. }
  4859. if (res == 0) {
  4860. top_importer->add_error("Error while extracting model data from zip archive for "+object_path);
  4861. return false;
  4862. }
  4863. return true;
  4864. }
  4865. //////////////////////////////////////////////////// EXPORTER /////////////////////////////////////////////////////
  4866. //
  4867. // class _BBS_3MF_Exporter : public _BBS_3MF_Base
  4868. // {
  4869. // struct BuildItem
  4870. // {
  4871. // std::string path;
  4872. // unsigned int id;
  4873. // Transform3d transform;
  4874. // bool printable;
  4875. //
  4876. // BuildItem(std::string const & path, unsigned int id, const Transform3d& transform, const bool printable)
  4877. // : path(path)
  4878. // , id(id)
  4879. // , transform(transform)
  4880. // , printable(printable)
  4881. // {
  4882. // }
  4883. // };
  4884. //
  4885. // //BBS: change volume to seperate objects
  4886. // /*struct Offsets
  4887. // {
  4888. // unsigned int first_vertex_id;
  4889. // unsigned int first_triangle_id;
  4890. // unsigned int last_triangle_id;
  4891. //
  4892. // Offsets(unsigned int first_vertex_id)
  4893. // : first_vertex_id(first_vertex_id)
  4894. // , first_triangle_id(-1)
  4895. // , last_triangle_id(-1)
  4896. // {
  4897. // }
  4898. // };*/
  4899. //
  4900. // //typedef std::map<const ModelVolume*, Offsets> VolumeToOffsetsMap;
  4901. // typedef std::map<const ModelVolume*, int> VolumeToObjectIDMap;
  4902. //
  4903. // struct ObjectData
  4904. // {
  4905. // ModelObject const * object;
  4906. // int backup_id;
  4907. // int object_id = 0;
  4908. // std::string sub_path;
  4909. // bool share_mesh = false;
  4910. // VolumeToObjectIDMap volumes_objectID;
  4911. // };
  4912. //
  4913. // typedef std::vector<BuildItem> BuildItemsList;
  4914. // typedef std::map<ModelObject const *, ObjectData> ObjectToObjectDataMap;
  4915. //
  4916. // bool m_fullpath_sources{ true };
  4917. // bool m_zip64 { true };
  4918. // bool m_production_ext { false }; // save with Production Extention
  4919. // bool m_skip_static{ false }; // not save mesh and other big static contents
  4920. // bool m_from_backup_save{ false }; // the object save is from backup store
  4921. // bool m_split_model { false }; // save object per file with Production Extention
  4922. // bool m_save_gcode { false }; // whether to save gcode for normal save
  4923. // bool m_skip_model { false }; // skip model when exporting .gcode.3mf
  4924. // bool m_skip_auxiliary { false }; // skip normal axuiliary files
  4925. // bool m_use_loaded_id { false }; // whether to use loaded id for identify_id
  4926. // bool m_share_mesh { false }; // whether to share mesh between objects
  4927. // std::string m_thumbnail_middle = PRINTER_THUMBNAIL_MIDDLE_FILE;
  4928. // std::string m_thumbnail_small = PRINTER_THUMBNAIL_SMALL_FILE;
  4929. // std::map<void const *, std::pair<ObjectData*, ModelVolume const *>> m_shared_meshes;
  4930. // std::map<ModelVolume const *, std::pair<std::string, int>> m_volume_paths;
  4931. // public:
  4932. // //BBS: add plate data related logic
  4933. //
  4934. // // add backup logic
  4935. // //bool save_model_to_file(const std::string& filename, Model& model, PlateDataPtrs& plate_data_list, std::vector<Preset*>& project_presets, const DynamicPrintConfig* config, bool fullpath_sources, const std::vector<ThumbnailData*>& thumbnail_data, bool zip64, bool skip_static, Export3mfProgressFn proFn = nullptr, bool silence = false);
  4936. //
  4937. // bool save_model_to_file(StoreParams& store_params);
  4938. // // add backup logic
  4939. // bool save_object_mesh(const std::string& temp_path, ModelObject const & object, int obj_id);
  4940. // static void add_transformation(std::stringstream &stream, const Transform3d &tr);
  4941. //
  4942. // private:
  4943. // //BBS: add plate data related logic
  4944. // bool _save_model_to_file(const std::string& filename,
  4945. // Model& model, PlateDataPtrs& plate_data_list,
  4946. // std::vector<Preset*>& project_presets,
  4947. // const DynamicPrintConfig* config,
  4948. // const std::vector<ThumbnailData*>& thumbnail_data,
  4949. // const std::vector<ThumbnailData*>& top_thumbnail_data,
  4950. // const std::vector<ThumbnailData*>& pick_thumbnail_data,
  4951. // Export3mfProgressFn proFn,
  4952. // const std::vector<ThumbnailData*>& calibration_data,
  4953. // //const std::vector<PlateBBoxData*>& id_bboxes, //Susi_not_impl
  4954. // //BBLProject* project = nullptr, //Susi_not_impl
  4955. // int export_plate_idx = -1);
  4956. //
  4957. // bool _add_file_to_archive(mz_zip_archive& archive, const std::string & path_in_zip, const std::string & file_path);
  4958. //
  4959. // bool _add_content_types_file_to_archive(mz_zip_archive& archive);
  4960. //
  4961. // bool _add_thumbnail_file_to_archive(mz_zip_archive& archive, const ThumbnailData& thumbnail_data, const char* local_path, int index, bool generate_small_thumbnail = false);
  4962. // bool _add_calibration_file_to_archive(mz_zip_archive& archive, const ThumbnailData& thumbnail_data, int index);
  4963. // bool _add_bbox_file_to_archive(mz_zip_archive& archive, /*const PlateBBoxData& id_bboxes,*/ int index);
  4964. // bool _add_relationships_file_to_archive(mz_zip_archive & archive,
  4965. // std::string const & from = {},
  4966. // std::vector<std::string> const &targets = {},
  4967. // std::vector<std::string> const &types = {},
  4968. // PackingTemporaryData data = PackingTemporaryData(),
  4969. // int export_plate_idx = -1) const;
  4970. // bool _add_model_file_to_archive(const std::string& filename, mz_zip_archive& archive, const Model& model, ObjectToObjectDataMap& objects_data, Export3mfProgressFn proFn = nullptr, /*BBLProject* project = nullptr*/) const;
  4971. // bool _add_object_to_model_stream(mz_zip_writer_staged_context &context, ObjectData const &object_data) const;
  4972. // void _add_object_components_to_stream(std::stringstream &stream, ObjectData const &object_data) const;
  4973. // //BBS: change volume to seperate objects
  4974. // bool _add_mesh_to_object_stream(std::function<bool(std::string &, bool)> const &flush, ObjectData const &object_data) const;
  4975. // bool _add_build_to_model_stream(std::stringstream& stream, const BuildItemsList& build_items) const;
  4976. // bool _add_cut_information_file_to_archive(mz_zip_archive& archive, Model& model);
  4977. // bool _add_layer_height_profile_file_to_archive(mz_zip_archive& archive, Model& model);
  4978. // bool _add_layer_config_ranges_file_to_archive(mz_zip_archive& archive, Model& model);
  4979. // bool _add_sla_support_points_file_to_archive(mz_zip_archive& archive, Model& model);
  4980. // bool _add_sla_drain_holes_file_to_archive(mz_zip_archive& archive, Model& model);
  4981. // bool _add_print_config_file_to_archive(mz_zip_archive& archive, const DynamicPrintConfig &config);
  4982. // //BBS: add project config file logic for json format
  4983. // bool _add_project_config_file_to_archive(mz_zip_archive& archive, const DynamicPrintConfig &config, Model& model);
  4984. // //BBS: add project embedded preset files
  4985. // bool _add_project_embedded_presets_to_archive(mz_zip_archive& archive, Model& model, std::vector<Preset*> project_presets);
  4986. // bool _add_model_config_file_to_archive(mz_zip_archive& archive, const Model& model, PlateDataPtrs& plate_data_list, const ObjectToObjectDataMap &objects_data, int export_plate_idx = -1, bool save_gcode = true, bool use_loaded_id = false);
  4987. // bool _add_slice_info_config_file_to_archive(mz_zip_archive &archive, const Model &model, PlateDataPtrs &plate_data_list, const ObjectToObjectDataMap &objects_data, const DynamicPrintConfig& config);
  4988. // bool _add_gcode_file_to_archive(mz_zip_archive& archive, const Model& model, PlateDataPtrs& plate_data_list, Export3mfProgressFn proFn = nullptr);
  4989. // bool _add_custom_gcode_per_print_z_file_to_archive(mz_zip_archive& archive, Model& model, const DynamicPrintConfig* config);
  4990. // bool _add_auxiliary_dir_to_archive(mz_zip_archive &archive, const std::string &aux_dir, PackingTemporaryData &data);
  4991. //
  4992. // static int convert_instance_id_to_resource_id(const Model& model, int obj_id, int instance_id)
  4993. // {
  4994. // int resource_id = 1;
  4995. //
  4996. // for (int i = 0; i < obj_id; ++i)
  4997. // {
  4998. // resource_id += model.objects[i]->volumes.size() + 1;
  4999. // }
  5000. //
  5001. // resource_id += model.objects[obj_id]->volumes.size();
  5002. //
  5003. // return resource_id;
  5004. // }
  5005. // };
  5006. //
  5007. // bool _BBS_3MF_Exporter::save_model_to_file(StoreParams& store_params)
  5008. // {
  5009. // clear_errors();
  5010. // m_fullpath_sources = store_params.strategy & SaveStrategy::FullPathSources;
  5011. // m_zip64 = store_params.strategy & SaveStrategy::Zip64;
  5012. // m_production_ext = store_params.strategy & SaveStrategy::ProductionExt;
  5013. //
  5014. // m_skip_static = store_params.strategy & SaveStrategy::SkipStatic;
  5015. // m_split_model = store_params.strategy & SaveStrategy::SplitModel;
  5016. // m_save_gcode = store_params.strategy & SaveStrategy::WithGcode;
  5017. // m_skip_model = store_params.strategy & SaveStrategy::SkipModel;
  5018. // m_skip_auxiliary = store_params.strategy & SaveStrategy::SkipAuxiliary;
  5019. // m_share_mesh = store_params.strategy & SaveStrategy::ShareMesh;
  5020. // m_from_backup_save = store_params.strategy & SaveStrategy::Backup;
  5021. //
  5022. // m_use_loaded_id = store_params.strategy & SaveStrategy::UseLoadedId;
  5023. //
  5024. // //if (auto info = store_params.model->model_info) {
  5025. // // if (auto iter = info->metadata_items.find("Thumbnail_Small"); iter != info->metadata_items.end())
  5026. // // m_thumbnail_small = iter->second;
  5027. // // if (auto iter = info->metadata_items.find("Thumbnail_Middle"); iter != info->metadata_items.end())
  5028. // // m_thumbnail_middle = iter->second;
  5029. // //} //Susi_not_impl
  5030. // boost::system::error_code ec;
  5031. // std::string filename = std::string(store_params.path);
  5032. // boost::filesystem::remove(filename + ".tmp", ec);
  5033. //
  5034. // bool result = _save_model_to_file(filename + ".tmp", *store_params.model, store_params.plate_data_list, store_params.project_presets, store_params.config,
  5035. // store_params.thumbnail_data, store_params.top_thumbnail_data, store_params.pick_thumbnail_data, store_params.proFn,
  5036. // store_params.calibration_thumbnail_data, /*store_params.id_bboxes, store_params.project, */store_params.export_plate_idx);
  5037. // if (result) {
  5038. // boost::filesystem::rename(filename + ".tmp", filename, ec);
  5039. // if (ec) {
  5040. // add_error("Failed to rename file: " + ec.message());
  5041. // boost::filesystem::remove(filename + ".tmp", ec);
  5042. // return false;
  5043. // }
  5044. // if (!(store_params.strategy & SaveStrategy::Silence))
  5045. // //save_string_file(store_params.model->get_backup_path() + "/origin.txt", filename);
  5046. // save_string_file(get_temp_file(*store_params.model) / "origin.txt", filename);
  5047. // }
  5048. // return result;
  5049. // }
  5050. //
  5051. // // backup mesh-only
  5052. // bool _BBS_3MF_Exporter::save_object_mesh(const std::string& temp_path, ModelObject const & object, int obj_id)
  5053. // {
  5054. // m_production_ext = true;
  5055. // m_from_backup_save = true;
  5056. // Model const & model = *object.get_model();
  5057. //
  5058. // mz_zip_archive archive;
  5059. // mz_zip_zero_struct(&archive);
  5060. //
  5061. // auto filename = boost::format("3D/Objects/%s_%d.model") % object.name % obj_id;
  5062. // std::string filepath = temp_path + "/" + filename.str();
  5063. // std::string filepath_tmp = filepath + ".tmp";
  5064. // boost::system::error_code ec;
  5065. // boost::filesystem::remove(filepath_tmp, ec);
  5066. // if (!open_zip_writer(&archive, filepath_tmp)) {
  5067. // add_error("Unable to open the file"+filepath_tmp);
  5068. // BOOST_LOG_TRIVIAL(error) << __FUNCTION__ << ":" << __LINE__ << boost::format(", Unable to open the file\n");
  5069. // return false;
  5070. // }
  5071. //
  5072. // struct close_lock
  5073. // {
  5074. // mz_zip_archive & archive;
  5075. // std::string const * filename;
  5076. // void close() {
  5077. // close_zip_writer(&archive);
  5078. // filename = nullptr;
  5079. // }
  5080. // ~close_lock() {
  5081. // if (filename) {
  5082. // close_zip_writer(&archive);
  5083. // boost::system::error_code ec;
  5084. // boost::filesystem::remove(*filename, ec);
  5085. // }
  5086. // }
  5087. // } lock{archive, &filepath_tmp};
  5088. //
  5089. // ObjectToObjectDataMap objects_data;
  5090. // auto & volumes_objectID = objects_data.insert({&object, {&object, obj_id}}).first->second.volumes_objectID;
  5091. // unsigned int volume_count = 0;
  5092. // for (ModelVolume *volume : object.volumes) {
  5093. // if (volume == nullptr) continue;
  5094. // volumes_objectID.insert({volume, (++volume_count << 16 | obj_id)});
  5095. // }
  5096. //
  5097. // _add_model_file_to_archive(filename.str(), archive, model, objects_data);
  5098. //
  5099. // mz_zip_writer_finalize_archive(&archive);
  5100. // lock.close();
  5101. // boost::filesystem::rename(filepath_tmp, filepath, ec);
  5102. // return true;
  5103. // }
  5104. //
  5105. // //BBS: add plate data related logic
  5106. // bool _BBS_3MF_Exporter::_save_model_to_file(const std::string& filename,
  5107. // Model& model,
  5108. // PlateDataPtrs& plate_data_list,
  5109. // std::vector<Preset*>& project_presets,
  5110. // const DynamicPrintConfig* config,
  5111. // const std::vector<ThumbnailData*>& thumbnail_data,
  5112. // const std::vector<ThumbnailData*>& top_thumbnail_data,
  5113. // const std::vector<ThumbnailData*>& pick_thumbnail_data,
  5114. // Export3mfProgressFn proFn,
  5115. // const std::vector<ThumbnailData*>& calibration_data,
  5116. // //const std::vector<PlateBBoxData*>& id_bboxes, //Susi_not_impl
  5117. // //BBLProject* project, //Susi_not_impl
  5118. // int export_plate_idx)
  5119. // {
  5120. // PackingTemporaryData temp_data;
  5121. //
  5122. // mz_zip_archive archive;
  5123. // mz_zip_zero_struct(&archive);
  5124. //
  5125. // bool cb_cancel = false;
  5126. //
  5127. // //BBS progress point
  5128. // BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << ":" <<__LINE__ <<
  5129. // boost::format(",before open zip writer, m_skip_static %1%, m_save_gcode %2%, m_use_loaded_id %3%")%m_skip_static %m_save_gcode %m_use_loaded_id;
  5130. // if (proFn) {
  5131. // proFn(EXPORT_STAGE_OPEN_3MF, 0, 1, cb_cancel);
  5132. // if (cb_cancel)
  5133. // return false;
  5134. // }
  5135. //
  5136. // if (!open_zip_writer(&archive, filename)) {
  5137. // add_error("Unable to open the file"+filename);
  5138. // BOOST_LOG_TRIVIAL(error) << __FUNCTION__ << ":" << __LINE__ << boost::format(", Unable to open the file\n");
  5139. // return false;
  5140. // }
  5141. //
  5142. // struct close_lock
  5143. // {
  5144. // mz_zip_archive & archive;
  5145. // std::string const * filename;
  5146. // ~close_lock() {
  5147. // close_zip_writer(&archive);
  5148. // if (filename) {
  5149. // boost::system::error_code ec;
  5150. // boost::filesystem::remove(*filename, ec);
  5151. // }
  5152. // }
  5153. // } lock{ archive, &filename};
  5154. //
  5155. // //BBS progress point
  5156. // BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << ":" <<__LINE__ << boost::format(", before add _add_content_types_file_to_archive\n");
  5157. // if (proFn) {
  5158. // proFn(EXPORT_STAGE_CONTENT_TYPES, 0, 1, cb_cancel);
  5159. // if (cb_cancel)
  5160. // return false;
  5161. // }
  5162. //
  5163. // // Adds content types file ("[Content_Types].xml";).
  5164. // // The content of this file is the same for each OrcaSlicer 3mf.
  5165. // if (!_add_content_types_file_to_archive(archive)) {
  5166. // return false;
  5167. // }
  5168. //
  5169. // //BBS progress point
  5170. // BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << ":" << __LINE__ << boost::format(",before add thumbnails, count %1%") % thumbnail_data.size();
  5171. // BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << ":" <<__LINE__ << boost::format(",top&&pick thumbnails, count %1%")%top_thumbnail_data.size();
  5172. //
  5173. // //BBS: add thumbnail for each plate
  5174. // if (!m_skip_static) {
  5175. // std::vector<bool> thumbnail_status(plate_data_list.size(), false);
  5176. // std::vector<bool> top_thumbnail_status(plate_data_list.size(), false);
  5177. // std::vector<bool> pick_thumbnail_status(plate_data_list.size(), false);
  5178. //
  5179. // if ((thumbnail_data.size() > 0)&&(thumbnail_data.size() > plate_data_list.size())) {
  5180. // BOOST_LOG_TRIVIAL(error) << __FUNCTION__ << ":" << __LINE__ << boost::format(", thumbnail_data size %1% > plate count %2%")
  5181. // % thumbnail_data.size() %plate_data_list.size();
  5182. // return false;
  5183. // }
  5184. // if ((top_thumbnail_data.size() > 0)&&(top_thumbnail_data.size() > plate_data_list.size())) {
  5185. // BOOST_LOG_TRIVIAL(error) << __FUNCTION__ << ":" << __LINE__ << boost::format(", top_thumbnail_data size %1% > plate count %2%")
  5186. // % top_thumbnail_data.size() %plate_data_list.size();
  5187. // return false;
  5188. // }
  5189. // if ((pick_thumbnail_data.size() > 0)&&(pick_thumbnail_data.size() > plate_data_list.size())) {
  5190. // BOOST_LOG_TRIVIAL(error) << __FUNCTION__ << ":" << __LINE__ << boost::format(", pick_thumbnail_data size %1% > plate count %2%")
  5191. // % pick_thumbnail_data.size() %plate_data_list.size();
  5192. // return false;
  5193. // }
  5194. // if (top_thumbnail_data.size() != pick_thumbnail_data.size()) {
  5195. // BOOST_LOG_TRIVIAL(error) << __FUNCTION__ << ":" << __LINE__ << boost::format(", top_thumbnail_data size %1% != pick_thumbnail_data size %2%")
  5196. // % top_thumbnail_data.size() %pick_thumbnail_data.size();
  5197. // return false;
  5198. // }
  5199. //
  5200. // if (proFn) {
  5201. // proFn(EXPORT_STAGE_ADD_THUMBNAILS, 0, plate_data_list.size(), cb_cancel);
  5202. // if (cb_cancel)
  5203. // return false;
  5204. // }
  5205. //
  5206. // for (unsigned int index = 0; index < thumbnail_data.size(); index++)
  5207. // {
  5208. // if (thumbnail_data[index]->is_valid())
  5209. // {
  5210. // if (!_add_thumbnail_file_to_archive(archive, *thumbnail_data[index], "Metadata/plate", index, true)) {
  5211. // return false;
  5212. // }
  5213. //
  5214. // BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << ":" <<__LINE__ << boost::format(",add thumbnail %1%'s data into 3mf")%(index+1);
  5215. // thumbnail_status[index] = true;
  5216. // }
  5217. // }
  5218. //
  5219. // // Adds the file Metadata/top_i.png and Metadata/pick_i.png
  5220. // for (unsigned int index = 0; index < top_thumbnail_data.size(); index++)
  5221. // {
  5222. // if (top_thumbnail_data[index]->is_valid())
  5223. // {
  5224. // BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << ":" <<__LINE__ << boost::format(",add top thumbnail %1%'s data into 3mf")%(index+1);
  5225. // if (!_add_thumbnail_file_to_archive(archive, *top_thumbnail_data[index], "Metadata/top", index)) {
  5226. // return false;
  5227. // }
  5228. // top_thumbnail_status[index] = true;
  5229. // }
  5230. //
  5231. // if (pick_thumbnail_data[index]->is_valid())
  5232. // {
  5233. // BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << ":" <<__LINE__ << boost::format(",add pick thumbnail %1%'s data into 3mf")%(index+1);
  5234. // if (!_add_thumbnail_file_to_archive(archive, *pick_thumbnail_data[index], "Metadata/pick", index)) {
  5235. // return false;
  5236. // }
  5237. // pick_thumbnail_status[index] = true;
  5238. // }
  5239. // }
  5240. //
  5241. // for (int i = 0; i < plate_data_list.size(); i++) {
  5242. // PlateData *plate_data = plate_data_list[i];
  5243. //
  5244. // if (!thumbnail_status[i] && !plate_data->thumbnail_file.empty() && (boost::filesystem::exists(plate_data->thumbnail_file))){
  5245. // std::string dst_in_3mf = (boost::format("Metadata/plate_%1%.png") % (i + 1)).str();
  5246. // BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << ":" <<__LINE__ << boost::format(", add thumbnail %1% from file %2%") % (i+1) %plate_data->thumbnail_file;
  5247. //
  5248. // if (!_add_file_to_archive(archive, dst_in_3mf, plate_data->thumbnail_file)) {
  5249. // BOOST_LOG_TRIVIAL(error) << __FUNCTION__ << ":" << __LINE__ << boost::format(", add thumbnail %1% from file %2% failed\n") % (i+1) %plate_data->thumbnail_file;
  5250. // return false;
  5251. // }
  5252. // }
  5253. //
  5254. // if (!top_thumbnail_status[i] && !plate_data->top_file.empty() && (boost::filesystem::exists(plate_data->top_file))){
  5255. // std::string dst_in_3mf = (boost::format("Metadata/top_%1%.png") % (i + 1)).str();
  5256. //
  5257. // BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << ":" <<__LINE__ << boost::format(", add top thumbnail %1% from file %2%") % (i+1) %plate_data->top_file;
  5258. // if (!_add_file_to_archive(archive, dst_in_3mf, plate_data->top_file)) {
  5259. // BOOST_LOG_TRIVIAL(error) << __FUNCTION__ << ":" << __LINE__ << boost::format(", add top thumbnail %1% failed") % (i+1);
  5260. // return false;
  5261. // }
  5262. // top_thumbnail_status[i] = true;
  5263. // }
  5264. //
  5265. // if (!pick_thumbnail_status[i] && !plate_data->pick_file.empty() && (boost::filesystem::exists(plate_data->pick_file))){
  5266. // std::string dst_in_3mf = (boost::format("Metadata/pick_%1%.png") % (i + 1)).str();
  5267. //
  5268. // BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << ":" <<__LINE__ << boost::format(", add pick thumbnail %1% from file %2%") % (i+1) %plate_data->pick_file;
  5269. // if (!_add_file_to_archive(archive, dst_in_3mf, plate_data->pick_file)) {
  5270. // BOOST_LOG_TRIVIAL(error) << __FUNCTION__ << ":" << __LINE__ << boost::format(", add pick thumbnail %1% failed") % (i+1);
  5271. // return false;
  5272. // }
  5273. // pick_thumbnail_status[i] = true;
  5274. // }
  5275. // }
  5276. // if (proFn) {
  5277. // proFn(EXPORT_STAGE_ADD_THUMBNAILS, plate_data_list.size(), plate_data_list.size(), cb_cancel);
  5278. // if (cb_cancel)
  5279. // return false;
  5280. // }
  5281. // }
  5282. //
  5283. // BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << ":" <<__LINE__ << boost::format(",before add calibration thumbnails, count %1%\n")%calibration_data.size();
  5284. // //BBS add calibration thumbnail for each plate
  5285. // if (!m_skip_static && calibration_data.size() > 0) {
  5286. // // Adds the file Metadata/calibration_p[X].png.
  5287. // for (unsigned int index = 0; index < calibration_data.size(); index++)
  5288. // {
  5289. // if (proFn) {
  5290. // proFn(EXPORT_STAGE_ADD_THUMBNAILS, index, calibration_data.size(), cb_cancel);
  5291. // if (cb_cancel)
  5292. // return false;
  5293. // }
  5294. //
  5295. // if (calibration_data[index]->is_valid())
  5296. // {
  5297. // if (!_add_calibration_file_to_archive(archive, *calibration_data[index], index)) {
  5298. // close_zip_writer(&archive);
  5299. // return false;
  5300. // }
  5301. // }
  5302. // }
  5303. // }
  5304. //
  5305. // BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << ":" <<__LINE__ << boost::format(",before add calibration boundingbox, count %1%\n")%id_bboxes.size();
  5306. // //if (!m_skip_static && id_bboxes.size() > 0) {
  5307. // // // Adds the file Metadata/calibration_p[X].png.
  5308. // // for (unsigned int index = 0; index < id_bboxes.size(); index++)
  5309. // // {
  5310. // // // BBS: save bounding box to json
  5311. // // if (id_bboxes[index]->is_valid()) {
  5312. // // if (!_add_bbox_file_to_archive(archive, *id_bboxes[index], index)) {
  5313. // // close_zip_writer(&archive);
  5314. // // return false;
  5315. // // }
  5316. // // }
  5317. // // }
  5318. // //} //s
  5319. //
  5320. // //BBS progress point
  5321. // BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << ":" <<__LINE__ << boost::format(", before add models\n");
  5322. // if (proFn) {
  5323. // proFn(EXPORT_STAGE_ADD_MODELS, 0, 1, cb_cancel);
  5324. // if (cb_cancel)
  5325. // return false;
  5326. // }
  5327. //
  5328. // // Adds model file ("3D/3dmodel.model").
  5329. // // This is the one and only file that contains all the geometry (vertices and triangles) of all ModelVolumes.
  5330. // ObjectToObjectDataMap objects_data;
  5331. // //if (!m_skip_model)
  5332. // {
  5333. // if (!_add_model_file_to_archive(filename, archive, model, objects_data, proFn, project)) { return false; }
  5334. //
  5335. // // Adds layer height profile file ("Metadata/Slic3r_PE_layer_heights_profile.txt").
  5336. // // All layer height profiles of all ModelObjects are stored here, indexed by 1 based index of the ModelObject in Model.
  5337. // // The index differes from the index of an object ID of an object instance of a 3MF file!
  5338. // if (!_add_layer_height_profile_file_to_archive(archive, model)) {
  5339. // close_zip_writer(&archive);
  5340. // return false;
  5341. // }
  5342. //
  5343. // // BBS progress point
  5344. // /*BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << ":" <<__LINE__ << boost::format("export 3mf EXPORT_STAGE_ADD_LAYER_RANGE\n");
  5345. // if (proFn) {
  5346. // proFn(EXPORT_STAGE_ADD_LAYER_RANGE, 0, 1, cb_cancel);
  5347. // if (cb_cancel)
  5348. // return false;
  5349. // }*/
  5350. //
  5351. // // Adds layer config ranges file ("Metadata/Slic3r_PE_layer_config_ranges.txt").
  5352. // // All layer height profiles of all ModelObjects are stored here, indexed by 1 based index of the ModelObject in Model.
  5353. // // The index differes from the index of an object ID of an object instance of a 3MF file!
  5354. // if (!_add_layer_config_ranges_file_to_archive(archive, model)) {
  5355. // close_zip_writer(&archive);
  5356. // return false;
  5357. // }
  5358. //
  5359. // // BBS progress point
  5360. // /*BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << ":" <<__LINE__ << boost::format("export 3mf EXPORT_STAGE_ADD_SUPPORT\n");
  5361. // if (proFn) {
  5362. // proFn(EXPORT_STAGE_ADD_SUPPORT, 0, 1, cb_cancel);
  5363. // if (cb_cancel)
  5364. // return false;
  5365. // }
  5366. //
  5367. // // Adds sla support points file ("Metadata/Slic3r_PE_sla_support_points.txt").
  5368. // // All sla support points of all ModelObjects are stored here, indexed by 1 based index of the ModelObject in Model.
  5369. // // The index differes from the index of an object ID of an object instance of a 3MF file!
  5370. // if (!_add_sla_support_points_file_to_archive(archive, model)) {
  5371. // return false;
  5372. // }
  5373. //
  5374. // if (!_add_sla_drain_holes_file_to_archive(archive, model)) {
  5375. // return false;
  5376. // }*/
  5377. //
  5378. // // BBS progress point
  5379. // BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << ":" << __LINE__ << boost::format(", before add custom gcodes\n");
  5380. // if (proFn) {
  5381. // proFn(EXPORT_STAGE_ADD_CUSTOM_GCODE, 0, 1, cb_cancel);
  5382. // if (cb_cancel) return false;
  5383. // }
  5384. //
  5385. // // Adds custom gcode per height file ("Metadata/Prusa_Slicer_custom_gcode_per_print_z.xml").
  5386. // // All custom gcode per height of whole Model are stored here
  5387. // if (!_add_custom_gcode_per_print_z_file_to_archive(archive, model, config)) { return false; }
  5388. //
  5389. // // BBS progress point
  5390. // BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << ":" << __LINE__ << boost::format(", before add project_settings\n");
  5391. // if (proFn) {
  5392. // proFn(EXPORT_STAGE_ADD_PRINT_CONFIG, 0, 1, cb_cancel);
  5393. // if (cb_cancel) return false;
  5394. // }
  5395. //
  5396. // // Adds slic3r print config file ("Metadata/Slic3r_PE.config").
  5397. // // This file contains the content of FullPrintConfing / SLAFullPrintConfig.
  5398. // if (config != nullptr) {
  5399. // // BBS: change to json format
  5400. // // if (!_add_print_config_file_to_archive(archive, *config)) {
  5401. // if (!_add_project_config_file_to_archive(archive, *config, model)) { return false; }
  5402. // }
  5403. //
  5404. // // BBS progress point
  5405. // BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << ":" << __LINE__ << boost::format(", before add project embedded settings\n");
  5406. // if (proFn) {
  5407. // proFn(EXPORT_STAGE_ADD_CONFIG_FILE, 0, 1, cb_cancel);
  5408. // if (cb_cancel) return false;
  5409. // }
  5410. //
  5411. // // BBS: add project config
  5412. // if (project_presets.size() > 0) {
  5413. // // BBS: add project embedded preset files
  5414. // _add_project_embedded_presets_to_archive(archive, model, project_presets);
  5415. //
  5416. // BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << ":" << __LINE__ << boost::format(", finished add project embedded settings, size %1%\n")%project_presets.size();
  5417. // if (proFn) {
  5418. // proFn(EXPORT_STAGE_ADD_PROJECT_CONFIG, 0, 1, cb_cancel);
  5419. // if (cb_cancel) return false;
  5420. // }
  5421. // }
  5422. // }
  5423. //
  5424. // // add plate_N.gcode.md5 to file
  5425. // if (!m_skip_static && m_save_gcode) {
  5426. // for (int i = 0; i < plate_data_list.size(); i++) {
  5427. // PlateData *plate_data = plate_data_list[i];
  5428. // if (!plate_data->gcode_file.empty() && plate_data->is_sliced_valid && boost::filesystem::exists(plate_data->gcode_file)) {
  5429. // unsigned char digest[16];
  5430. // MD5_CTX ctx;
  5431. // MD5_Init(&ctx);
  5432. // auto src_gcode_file = plate_data->gcode_file;
  5433. // boost::filesystem::ifstream ifs(src_gcode_file, std::ios::binary);
  5434. // std::string buf(64 * 1024, 0);
  5435. // const std::size_t & size = boost::filesystem::file_size(src_gcode_file);
  5436. // std::size_t left_size = size;
  5437. // while (ifs) {
  5438. // ifs.read(buf.data(), buf.size());
  5439. // int read_bytes = ifs.gcount();
  5440. // MD5_Update(&ctx, (unsigned char *) buf.data(), read_bytes);
  5441. // }
  5442. // MD5_Final(digest, &ctx);
  5443. // char md5_str[33];
  5444. // for (int j = 0; j < 16; j++) { sprintf(&md5_str[j * 2], "%02X", (unsigned int) digest[j]); }
  5445. // plate_data->gcode_file_md5 = std::string(md5_str);
  5446. // std::string target_file = (boost::format("Metadata/plate_%1%.gcode.md5") % (plate_data->plate_index + 1)).str();
  5447. // if (!mz_zip_writer_add_mem(&archive, target_file.c_str(), (const void *) plate_data->gcode_file_md5.c_str(), plate_data->gcode_file_md5.length(),
  5448. // MZ_DEFAULT_COMPRESSION)) {
  5449. // BOOST_LOG_TRIVIAL(error) << __FUNCTION__ << ":" << __LINE__
  5450. // << boost::format(", store gcode md5 to 3mf's %1%, length %2%, failed\n") %target_file %plate_data->gcode_file_md5.length();
  5451. // return false;
  5452. // }
  5453. // }
  5454. // }
  5455. // }
  5456. //
  5457. // // Adds gcode files ("Metadata/plate_1.gcode, plate_2.gcode, ...)
  5458. // // Before _add_model_config_file_to_archive, because we modify plate_data
  5459. // //if (!m_skip_static && !_add_gcode_file_to_archive(archive, model, plate_data_list, proFn)) {
  5460. // if (!m_skip_static && m_save_gcode && !_add_gcode_file_to_archive(archive, model, plate_data_list, proFn)) {
  5461. // BOOST_LOG_TRIVIAL(error) << __FUNCTION__ << ":" << __LINE__ << boost::format(", _add_gcode_file_to_archive failed\n");
  5462. // return false;
  5463. // }
  5464. //
  5465. // // Adds slic3r model config file ("Metadata/Slic3r_PE_model.config").
  5466. // // This file contains all the attributes of all ModelObjects and their ModelVolumes (names, parameter overrides).
  5467. // // As there is just a single Indexed Triangle Set data stored per ModelObject, offsets of volumes into their respective Indexed Triangle Set data
  5468. // // is stored here as well.
  5469. // if (!_add_model_config_file_to_archive(archive, model, plate_data_list, objects_data, export_plate_idx, m_save_gcode, m_use_loaded_id)) {
  5470. // BOOST_LOG_TRIVIAL(error) << __FUNCTION__ << ":" << __LINE__ << boost::format(", _add_model_config_file_to_archive failed\n");
  5471. // return false;
  5472. // }
  5473. //
  5474. // if (!_add_cut_information_file_to_archive(archive, model)) {
  5475. // BOOST_LOG_TRIVIAL(error) << __FUNCTION__ << ":" << __LINE__ << boost::format(", _add_cut_information_file_to_archive failed\n");
  5476. // return false;
  5477. // }
  5478. //
  5479. // //BBS progress point
  5480. // BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << ":" <<__LINE__ << boost::format(", before add sliced info to 3mf\n");
  5481. // if (proFn) {
  5482. // proFn(EXPORT_STAGE_ADD_SLICE_INFO, 0, 1, cb_cancel);
  5483. // if (cb_cancel)
  5484. // return false;
  5485. // }
  5486. //
  5487. // // Adds sliced info of plate file ("Metadata/slice_info.config")
  5488. // // This file contains all sliced info of all plates
  5489. // if (!_add_slice_info_config_file_to_archive(archive, model, plate_data_list, objects_data, *config)) {
  5490. // BOOST_LOG_TRIVIAL(error) << __FUNCTION__ << ":" << __LINE__ << boost::format(", _add_slice_info_config_file_to_archive failed\n");
  5491. // return false;
  5492. // }
  5493. //
  5494. // //BBS progress point
  5495. // BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << ":" <<__LINE__ << boost::format(", before add auxiliary dir to 3mf\n");
  5496. // if (proFn) {
  5497. // proFn(EXPORT_STAGE_ADD_AUXILIARIES, 0, 1, cb_cancel);
  5498. // if (cb_cancel)
  5499. // return false;
  5500. // }
  5501. //
  5502. // if (!m_skip_static && !_add_auxiliary_dir_to_archive(archive, model.get_auxiliary_file_temp_path(), temp_data)) {
  5503. // BOOST_LOG_TRIVIAL(error) << __FUNCTION__ << ":" <<__LINE__ << boost::format(", _add_auxiliary_dir_to_archive failed\n");
  5504. // return false;
  5505. // }
  5506. //
  5507. // //BBS progress point
  5508. // BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << ":" <<__LINE__ << boost::format(", before add relation file to 3mf\n");
  5509. // if (proFn) {
  5510. // proFn(EXPORT_STAGE_ADD_RELATIONS, 0, 1, cb_cancel);
  5511. // if (cb_cancel)
  5512. // return false;
  5513. // }
  5514. //
  5515. // // Adds relationships file ("_rels/.rels").
  5516. // // The content of this file is the same for each OrcaSlicer 3mf.
  5517. // // The relationshis file contains a reference to the geometry file "3D/3dmodel.model", the name was chosen to be compatible with CURA.
  5518. // if (!_add_relationships_file_to_archive(archive, {}, {}, {}, temp_data, export_plate_idx)) {
  5519. // BOOST_LOG_TRIVIAL(error) << __FUNCTION__ << ":" <<__LINE__ << boost::format(", _add_relationships_file_to_archive failed\n");
  5520. // return false;
  5521. // }
  5522. //
  5523. // if (!mz_zip_writer_finalize_archive(&archive)) {
  5524. // add_error("Unable to finalize the archive");
  5525. // BOOST_LOG_TRIVIAL(error) << __FUNCTION__ << ":" << __LINE__ << boost::format(", Unable to finalize the archive\n");
  5526. // return false;
  5527. // }
  5528. //
  5529. // //BBS progress point
  5530. // BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << ":" <<__LINE__ << boost::format(", finished exporting 3mf\n");
  5531. // if (proFn) {
  5532. // proFn(EXPORT_STAGE_FINISH, 0, 1, cb_cancel);
  5533. // if (cb_cancel)
  5534. // return false;
  5535. // }
  5536. //
  5537. // lock.filename = nullptr;
  5538. //
  5539. // return true;
  5540. // }
  5541. //
  5542. // bool _BBS_3MF_Exporter::_add_file_to_archive(mz_zip_archive& archive, const std::string& path_in_zip, const std::string& src_file_path)
  5543. // {
  5544. // static std::string const nocomp_exts[] = {".png", ".jpg", ".mp4", ".jpeg", ".zip", ".3mf"};
  5545. // auto end = nocomp_exts + sizeof(nocomp_exts) / sizeof(nocomp_exts[0]);
  5546. // bool nocomp = std::find_if(nocomp_exts, end, [&path_in_zip](auto & ext) { return boost::algorithm::ends_with(path_in_zip, ext); }) != end;
  5547. //#if WRITE_ZIP_LANGUAGE_ENCODING
  5548. // bool result = mz_zip_writer_add_file(&archive, path_in_zip.c_str(), encode_path(src_file_path.c_str()).c_str(), NULL, 0, nocomp ? MZ_NO_COMPRESSION : MZ_DEFAULT_LEVEL);
  5549. //#else
  5550. // std::string native_path = encode_path(path_in_zip.c_str());
  5551. // std::string extra = ZipUnicodePathExtraField::encode(path_in_zip, native_path);
  5552. // bool result = mz_zip_writer_add_file_ex(&archive, native_path.c_str(), encode_path(src_file_path.c_str()).c_str(), NULL, 0, nocomp ? MZ_ZIP_FLAG_ASCII_FILENAME : MZ_DEFAULT_COMPRESSION,
  5553. // extra.c_str(), extra.length(), extra.c_str(), extra.length());
  5554. //#endif
  5555. // if (!result) {
  5556. // add_error("Unable to add file " + src_file_path + " to archive");
  5557. // BOOST_LOG_TRIVIAL(error) << __FUNCTION__ << ":" <<__LINE__ << boost::format(", Unable to add file %1% to archive %2%\n") % src_file_path % path_in_zip;
  5558. // } else {
  5559. // BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << ":" <<__LINE__ << boost::format(", add file %1% to archive %2%\n") % src_file_path % path_in_zip;
  5560. // }
  5561. // return result;
  5562. // }
  5563. //
  5564. // bool _BBS_3MF_Exporter::_add_content_types_file_to_archive(mz_zip_archive& archive)
  5565. // {
  5566. // std::stringstream stream;
  5567. // stream << "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n";
  5568. // stream << "<Types xmlns=\"http://schemas.openxmlformats.org/package/2006/content-types\">\n";
  5569. // stream << " <Default Extension=\"rels\" ContentType=\"application/vnd.openxmlformats-package.relationships+xml\"/>\n";
  5570. // stream << " <Default Extension=\"model\" ContentType=\"application/vnd.ms-package.3dmanufacturing-3dmodel+xml\"/>\n";
  5571. // stream << " <Default Extension=\"png\" ContentType=\"image/png\"/>\n";
  5572. // stream << " <Default Extension=\"gcode\" ContentType=\"text/x.gcode\"/>\n";
  5573. // stream << "</Types>";
  5574. //
  5575. // std::string out = stream.str();
  5576. //
  5577. // if (!mz_zip_writer_add_mem(&archive, CONTENT_TYPES_FILE.c_str(), (const void*)out.data(), out.length(), MZ_DEFAULT_COMPRESSION)) {
  5578. // add_error("Unable to add content types file to archive");
  5579. // BOOST_LOG_TRIVIAL(error) << __FUNCTION__ << ":" << __LINE__ << boost::format(", Unable to add content types file to archive\n");
  5580. // return false;
  5581. // }
  5582. //
  5583. // return true;
  5584. // }
  5585. //
  5586. // bool _BBS_3MF_Exporter::_add_thumbnail_file_to_archive(mz_zip_archive& archive, const ThumbnailData& thumbnail_data, const char* local_path, int index, bool generate_small_thumbnail)
  5587. // {
  5588. // bool res = false;
  5589. //
  5590. // size_t png_size = 0;
  5591. // void* png_data = tdefl_write_image_to_png_file_in_memory_ex((const void*)thumbnail_data.pixels.data(), thumbnail_data.width, thumbnail_data.height, 4, &png_size, MZ_DEFAULT_COMPRESSION, 1);
  5592. // if (png_data != nullptr) {
  5593. // std::string thumbnail_name = (boost::format("%1%_%2%.png")%local_path % (index + 1)).str();
  5594. // res = mz_zip_writer_add_mem(&archive, thumbnail_name.c_str(), (const void*)png_data, png_size, MZ_NO_COMPRESSION);
  5595. // mz_free(png_data);
  5596. // }
  5597. //
  5598. // if (!res) {
  5599. // add_error("Unable to add thumbnail file to archive");
  5600. // BOOST_LOG_TRIVIAL(error) << __FUNCTION__ << ":" << __LINE__ << boost::format(", Unable to add thumbnail file to archive\n");
  5601. // }
  5602. //
  5603. // if (generate_small_thumbnail && thumbnail_data.is_valid()) {
  5604. // //generate small size of thumbnail
  5605. // std::vector<unsigned char> small_pixels;
  5606. // small_pixels.resize(PLATE_THUMBNAIL_SMALL_WIDTH * PLATE_THUMBNAIL_SMALL_HEIGHT * 4);
  5607. // /* step width and step height */
  5608. // int sw = thumbnail_data.width / PLATE_THUMBNAIL_SMALL_WIDTH;
  5609. // int sh = thumbnail_data.height / PLATE_THUMBNAIL_SMALL_HEIGHT;
  5610. // int clampped_width = sw * PLATE_THUMBNAIL_SMALL_WIDTH;
  5611. // int clampped_height = sh * PLATE_THUMBNAIL_SMALL_HEIGHT;
  5612. //
  5613. // for (int i = 0; i < clampped_height; i += sh) {
  5614. // for (int j = 0; j < clampped_width; j += sw) {
  5615. // int r = 0, g = 0, b = 0, a = 0;
  5616. // for (int m = 0; m < sh; m++) {
  5617. // for (int n = 0; n < sw; n++) {
  5618. // r += (int)thumbnail_data.pixels[4 * ((i + m) * thumbnail_data.width + j + n) + 0];
  5619. // g += (int)thumbnail_data.pixels[4 * ((i + m) * thumbnail_data.width + j + n) + 1];
  5620. // b += (int)thumbnail_data.pixels[4 * ((i + m) * thumbnail_data.width + j + n) + 2];
  5621. // a += (int)thumbnail_data.pixels[4 * ((i + m) * thumbnail_data.width + j + n) + 3];
  5622. // }
  5623. // }
  5624. // r = std::clamp(0, r / sw / sh, 255);
  5625. // g = std::clamp(0, g / sw / sh, 255);
  5626. // b = std::clamp(0, b / sw / sh, 255);
  5627. // a = std::clamp(0, a / sw / sh, 255);
  5628. // small_pixels[4 * (i / sw * PLATE_THUMBNAIL_SMALL_WIDTH + j / sh) + 0] = (unsigned char)r;
  5629. // small_pixels[4 * (i / sw * PLATE_THUMBNAIL_SMALL_WIDTH + j / sh) + 1] = (unsigned char)g;
  5630. // small_pixels[4 * (i / sw * PLATE_THUMBNAIL_SMALL_WIDTH + j / sh) + 2] = (unsigned char)b;
  5631. // small_pixels[4 * (i / sw * PLATE_THUMBNAIL_SMALL_WIDTH + j / sh) + 3] = (unsigned char)a;
  5632. // //memcpy((void*)&small_pixels[4*(i / sw * PLATE_THUMBNAIL_SMALL_WIDTH + j / sh)], thumbnail_data.pixels.data() + 4*(i * thumbnail_data.width + j), 4);
  5633. // }
  5634. // }
  5635. // size_t small_png_size = 0;
  5636. // void* small_png_data = tdefl_write_image_to_png_file_in_memory_ex((const void*)small_pixels.data(), PLATE_THUMBNAIL_SMALL_WIDTH, PLATE_THUMBNAIL_SMALL_HEIGHT, 4, &small_png_size, MZ_DEFAULT_COMPRESSION, 1);
  5637. // if (png_data != nullptr) {
  5638. // std::string thumbnail_name = (boost::format("%1%_%2%_small.png") % local_path % (index + 1)).str();
  5639. // res = mz_zip_writer_add_mem(&archive, thumbnail_name.c_str(), (const void*)small_png_data, small_png_size, MZ_NO_COMPRESSION);
  5640. // mz_free(small_png_data);
  5641. // }
  5642. //
  5643. // if (!res) {
  5644. // add_error("Unable to add small thumbnail file to archive");
  5645. // BOOST_LOG_TRIVIAL(error) << __FUNCTION__ << ":" << __LINE__ << boost::format(", Unable to add small thumbnail file to archive\n");
  5646. // }
  5647. // }
  5648. //
  5649. // return res;
  5650. // }
  5651. //
  5652. // bool _BBS_3MF_Exporter::_add_calibration_file_to_archive(mz_zip_archive& archive, const ThumbnailData& thumbnail_data, int index)
  5653. // {
  5654. // bool res = false;
  5655. //
  5656. // /*size_t png_size = 0;
  5657. // void* png_data = tdefl_write_image_to_png_file_in_memory_ex((const void*)thumbnail_data.pixels.data(), thumbnail_data.width, thumbnail_data.height, 4, &png_size, MZ_DEFAULT_COMPRESSION, 1);
  5658. // if (png_data != nullptr) {
  5659. // std::string thumbnail_name = (boost::format(PATTERN_FILE_FORMAT) % (index + 1)).str();
  5660. // res = mz_zip_writer_add_mem(&archive, thumbnail_name.c_str(), (const void*)png_data, png_size, MZ_NO_COMPRESSION);
  5661. // mz_free(png_data);
  5662. // }
  5663. //
  5664. // if (!res) {
  5665. // add_error("Unable to add thumbnail file to archive");
  5666. // BOOST_LOG_TRIVIAL(error) << __FUNCTION__ << ":" << __LINE__ << boost::format(", Unable to add thumbnail file to archive\n");
  5667. // }*/
  5668. //
  5669. // return res;
  5670. // }
  5671. //
  5672. // bool _BBS_3MF_Exporter::_add_bbox_file_to_archive(mz_zip_archive& archive, /*const PlateBBoxData& id_bboxes, */int index)
  5673. // {
  5674. // bool res = false;
  5675. // nlohmann::json j;
  5676. // //id_bboxes.to_json(j);
  5677. // std::string out = j.dump();
  5678. //
  5679. // std::string json_file_name = (boost::format(PATTERN_CONFIG_FILE_FORMAT) % (index + 1)).str();
  5680. // if (!mz_zip_writer_add_mem(&archive, json_file_name.c_str(), (const void*)out.data(), out.length(), MZ_DEFAULT_COMPRESSION)) {
  5681. // add_error("Unable to add json file to archive");
  5682. // BOOST_LOG_TRIVIAL(error) << __FUNCTION__ << ":" << __LINE__ << boost::format(", Unable to add json file to archive\n");
  5683. // return false;
  5684. // }
  5685. //
  5686. // return true;
  5687. // }
  5688. //
  5689. // bool _BBS_3MF_Exporter::_add_relationships_file_to_archive(
  5690. // mz_zip_archive &archive, std::string const &from, std::vector<std::string> const &targets, std::vector<std::string> const &types, PackingTemporaryData data, int export_plate_idx) const
  5691. // {
  5692. // std::stringstream stream;
  5693. // stream << "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n";
  5694. // stream << "<Relationships xmlns=\"http://schemas.openxmlformats.org/package/2006/relationships\">\n";
  5695. // if (from.empty()) {
  5696. // stream << " <Relationship Target=\"/" << MODEL_FILE << "\" Id=\"rel-1\" Type=\"http://schemas.microsoft.com/3dmanufacturing/2013/01/3dmodel\"/>\n";
  5697. //
  5698. // if (export_plate_idx < 0) {
  5699. // //use cover image if have
  5700. // if (data._3mf_thumbnail.empty()) {
  5701. // stream << " <Relationship Target=\"/Metadata/plate_1.png"
  5702. // << "\" Id=\"rel-2\" Type=\"http://schemas.openxmlformats.org/package/2006/relationships/metadata/thumbnail\"/>\n";
  5703. // } else {
  5704. // stream << " <Relationship Target=\"/" << xml_escape(data._3mf_thumbnail)
  5705. // << "\" Id=\"rel-2\" Type=\"http://schemas.openxmlformats.org/package/2006/relationships/metadata/thumbnail\"/>\n";
  5706. // }
  5707. //
  5708. // if (data._3mf_printer_thumbnail_middle.empty()) {
  5709. // stream << " <Relationship Target=\"/Metadata/plate_1.png"
  5710. // << "\" Id=\"rel-4\" Type=\"http://schemas.bambulab.com/package/2021/cover-thumbnail-middle\"/>\n";
  5711. // } else {
  5712. // stream << " <Relationship Target=\"/" << xml_escape(data._3mf_printer_thumbnail_middle)
  5713. // << "\" Id=\"rel-4\" Type=\"http://schemas.bambulab.com/package/2021/cover-thumbnail-middle\"/>\n";
  5714. // }
  5715. //
  5716. // if (data._3mf_printer_thumbnail_small.empty()) {
  5717. // stream << "<Relationship Target=\"/Metadata/plate_1_small.png"
  5718. // << "\" Id=\"rel-5\" Type=\"http://schemas.bambulab.com/package/2021/cover-thumbnail-small\"/>\n";
  5719. // } else {
  5720. // stream << " <Relationship Target=\"/" << xml_escape(data._3mf_printer_thumbnail_small)
  5721. // << "\" Id=\"rel-5\" Type=\"http://schemas.bambulab.com/package/2021/cover-thumbnail-small\"/>\n";
  5722. // }
  5723. // }
  5724. // else {
  5725. // //always use plate thumbnails
  5726. // std::string thumbnail_file_str = (boost::format("Metadata/plate_%1%.png") % (export_plate_idx + 1)).str();
  5727. // stream << " <Relationship Target=\"/" << xml_escape(thumbnail_file_str)
  5728. // << "\" Id=\"rel-2\" Type=\"http://schemas.openxmlformats.org/package/2006/relationships/metadata/thumbnail\"/>\n";
  5729. //
  5730. // thumbnail_file_str = (boost::format("Metadata/plate_%1%.png") % (export_plate_idx + 1)).str();
  5731. // stream << " <Relationship Target=\"/" << xml_escape(thumbnail_file_str)
  5732. // << "\" Id=\"rel-4\" Type=\"http://schemas.bambulab.com/package/2021/cover-thumbnail-middle\"/>\n";
  5733. //
  5734. // thumbnail_file_str = (boost::format("Metadata/plate_%1%_small.png") % (export_plate_idx + 1)).str();
  5735. // stream << " <Relationship Target=\"/" << xml_escape(thumbnail_file_str)
  5736. // << "\" Id=\"rel-5\" Type=\"http://schemas.bambulab.com/package/2021/cover-thumbnail-small\"/>\n";
  5737. // }
  5738. // }
  5739. // else if (targets.empty()) {
  5740. // return false;
  5741. // }
  5742. // else {
  5743. // int i = 0;
  5744. // for (auto & path : targets) {
  5745. // for (auto & type : types)
  5746. // stream << " <Relationship Target=\"/" << xml_escape(path) << "\" Id=\"rel-" << std::to_string(++i) << "\" Type=\"" << type << "\"/>\n";
  5747. // }
  5748. // }
  5749. // stream << "</Relationships>";
  5750. //
  5751. // std::string out = stream.str();
  5752. //
  5753. // if (!mz_zip_writer_add_mem(&archive, from.empty() ? RELATIONSHIPS_FILE.c_str() : from.c_str(), (const void*)out.data(), out.length(), MZ_DEFAULT_COMPRESSION)) {
  5754. // add_error("Unable to add relationships file to archive");
  5755. // BOOST_LOG_TRIVIAL(error) << __FUNCTION__ << ":" << __LINE__ << boost::format(", Unable to add relationships file to archive\n");
  5756. // return false;
  5757. // }
  5758. //
  5759. // return true;
  5760. // }
  5761. //
  5762. //
  5763. // static void reset_stream(std::stringstream &stream)
  5764. // {
  5765. // stream.str("");
  5766. // stream.clear();
  5767. // // https://en.cppreference.com/w/cpp/types/numeric_limits/max_digits10
  5768. // // Conversion of a floating-point value to text and back is exact as long as at least max_digits10 were used (9 for float, 17 for double).
  5769. // // It is guaranteed to produce the same floating-point value, even though the intermediate text representation is not exact.
  5770. // // The default value of std::stream precision is 6 digits only!
  5771. // stream << std::setprecision(std::numeric_limits<float>::max_digits10);
  5772. // }
  5773. //
  5774. // /*
  5775. // * BBS: Production Extension (SplitModel)
  5776. // * save sub model if objects_data is not empty
  5777. // * not collect build items in sub model
  5778. // */
  5779. // bool _BBS_3MF_Exporter::_add_model_file_to_archive(const std::string& filename, mz_zip_archive& archive, const Model& model, ObjectToObjectDataMap& objects_data, Export3mfProgressFn proFn, /*BBLProject* project*/) const
  5780. // {
  5781. // bool sub_model = !objects_data.empty();
  5782. // bool write_object = sub_model || !m_split_model;
  5783. //
  5784. // BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << ":" << __LINE__ << boost::format(", filename %1%, m_split_model %2%, sub_model %3%")%filename % m_split_model % sub_model;
  5785. //
  5786. //#if WRITE_ZIP_LANGUAGE_ENCODING
  5787. // auto & zip_filename = filename;
  5788. //#else
  5789. // std::string zip_filename = encode_path(filename.c_str());
  5790. // std::string extra = sub_model ? ZipUnicodePathExtraField::encode(filename, zip_filename) : "";
  5791. //#endif
  5792. // mz_zip_writer_staged_context context;
  5793. // if (!mz_zip_writer_add_staged_open(&archive, &context, sub_model ? zip_filename.c_str() : MODEL_FILE.c_str(),
  5794. // m_zip64 ?
  5795. // // Maximum expected and allowed 3MF file size is 16GiB.
  5796. // // This switches the ZIP file to a 64bit mode, which adds a tiny bit of overhead to file records.
  5797. // (uint64_t(1) << 30) * 16 :
  5798. // // Maximum expected 3MF file size is 4GB-1. This is a workaround for interoperability with Windows 10 3D model fixing API, see
  5799. // // GH issue #6193.
  5800. // (uint64_t(1) << 32) - 1,
  5801. //#if WRITE_ZIP_LANGUAGE_ENCODING
  5802. // nullptr, nullptr, 0, MZ_DEFAULT_LEVEL, nullptr, 0, nullptr, 0)) {
  5803. //#else
  5804. // nullptr, nullptr, 0, MZ_DEFAULT_COMPRESSION, extra.c_str(), extra.length(), extra.c_str(), extra.length())) {
  5805. //#endif
  5806. // add_error("Unable to add model file to archive");
  5807. // BOOST_LOG_TRIVIAL(error) << __FUNCTION__ << ":" << __LINE__ << boost::format(", Unable to add model file to archive\n");
  5808. // return false;
  5809. // }
  5810. //
  5811. //
  5812. // {
  5813. // std::stringstream stream;
  5814. // reset_stream(stream);
  5815. // stream << "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n";
  5816. // stream << "<" << MODEL_TAG << " unit=\"millimeter\" xml:lang=\"en-US\" xmlns=\"http://schemas.microsoft.com/3dmanufacturing/core/2015/02\" xmlns:slic3rpe=\"http://schemas.slic3r.org/3mf/2017/06\"";
  5817. // if (m_production_ext)
  5818. // stream << " xmlns:p=\"http://schemas.microsoft.com/3dmanufacturing/production/2015/06\" requiredextensions=\"p\"";
  5819. // stream << ">\n";
  5820. //
  5821. // std::string origin;
  5822. // std::string name;
  5823. // std::string user_name;
  5824. // std::string user_id;
  5825. // std::string design_cover;
  5826. // std::string license;
  5827. // std::string description;
  5828. // std::string copyright;
  5829. // std::string rating;
  5830. // std::string model_id;
  5831. // std::string region_code;
  5832. // //if (model.design_info) {
  5833. // // user_name = model.design_info->Designer;
  5834. // // user_id = model.design_info->DesignerUserId;
  5835. // // BOOST_LOG_TRIVIAL(trace) << "design_info, save_3mf found designer = " << user_name;
  5836. // // BOOST_LOG_TRIVIAL(trace) << "design_info, save_3mf found designer_user_id = " << user_id;
  5837. // //} //Susi_not_impl
  5838. //
  5839. // //if (project) {
  5840. // // model_id = project->project_model_id;
  5841. // // region_code = project->project_country_code;
  5842. // //} //Susi_not_impl
  5843. //
  5844. // //if (model.model_info) {
  5845. // // design_cover = model.model_info->cover_file;
  5846. // // license = model.model_info->license;
  5847. // // description = model.model_info->description;
  5848. // // copyright = model.model_info->copyright;
  5849. // // name = model.model_info->model_name;
  5850. // // origin = model.model_info->origin;
  5851. // // BOOST_LOG_TRIVIAL(trace) << "design_info, save_3mf found designer_cover = " << design_cover;
  5852. // //} //Susi_not_impl
  5853. // // remember to use metadata_item_map to store metadata info
  5854. // std::map<std::string, std::string> metadata_item_map;
  5855. // if (!sub_model) {
  5856. // // update metadat_items
  5857. // //if (model.model_info && model.model_info.get()) {
  5858. // // metadata_item_map = model.model_info.get()->metadata_items;
  5859. // //} //Susi_not_impl
  5860. //
  5861. // metadata_item_map[BBL_MODEL_NAME_TAG] = xml_escape(name);
  5862. // metadata_item_map[BBL_ORIGIN_TAG] = xml_escape(origin);
  5863. // metadata_item_map[BBL_DESIGNER_TAG] = xml_escape(user_name);
  5864. // metadata_item_map[BBL_DESIGNER_USER_ID_TAG] = user_id;
  5865. // metadata_item_map[BBL_DESIGNER_COVER_FILE_TAG] = xml_escape(design_cover);
  5866. // metadata_item_map[BBL_DESCRIPTION_TAG] = xml_escape(description);
  5867. // metadata_item_map[BBL_COPYRIGHT_NORMATIVE_TAG] = xml_escape(copyright);
  5868. // metadata_item_map[BBL_LICENSE_TAG] = xml_escape(license);
  5869. //
  5870. // /* save model info */
  5871. // if (!model_id.empty()) {
  5872. // metadata_item_map[BBL_MODEL_ID_TAG] = model_id;
  5873. // metadata_item_map[BBL_REGION_TAG] = region_code;
  5874. // }
  5875. //
  5876. // std::string date = Slic3r::Utils::utc_timestamp(Slic3r::Utils::get_current_time_utc());
  5877. // // keep only the date part of the string
  5878. // date = date.substr(0, 10);
  5879. // metadata_item_map[BBL_CREATION_DATE_TAG] = date;
  5880. // metadata_item_map[BBL_MODIFICATION_TAG] = date;
  5881. // //SoftFever: write BambuStudio tag to keep it compatible
  5882. // metadata_item_map[BBL_APPLICATION_TAG] = (boost::format("%1%-%2%") % "BambuStudio" % SoftFever_VERSION).str();
  5883. // }
  5884. // metadata_item_map[BBS_3MF_VERSION] = std::to_string(VERSION_BBS_3MF);
  5885. //
  5886. // // store metadata info
  5887. // for (auto item : metadata_item_map) {
  5888. // BOOST_LOG_TRIVIAL(info) << "bbs_3mf: save key= " << item.first << ", value = " << item.second;
  5889. // stream << " <" << METADATA_TAG << " name=\"" << item.first << "\">"
  5890. // << xml_escape(item.second) << "</" << METADATA_TAG << ">\n";
  5891. // }
  5892. //
  5893. // stream << " <" << RESOURCES_TAG << ">\n";
  5894. // std::string buf = stream.str();
  5895. // if (! buf.empty() && ! mz_zip_writer_add_staged_data(&context, buf.data(), buf.size())) {
  5896. // add_error("Unable to add model file to archive");
  5897. // BOOST_LOG_TRIVIAL(error) << __FUNCTION__ << ":" << __LINE__ << boost::format(", Unable to add model file to archive\n");
  5898. // return false;
  5899. // }
  5900. // }
  5901. //
  5902. // // Instance transformations, indexed by the 3MF object ID (which is a linear serialization of all instances of all ModelObjects).
  5903. // BuildItemsList build_items;
  5904. //
  5905. // // The object_id here is a one based identifier of the first instance of a ModelObject in the 3MF file, where
  5906. // // all the object instances of all ModelObjects are stored and indexed in a 1 based linear fashion.
  5907. // // Therefore the list of object_ids here may not be continuous.
  5908. // unsigned int object_id = 1;
  5909. // unsigned int object_index = 0;
  5910. //
  5911. // bool cb_cancel = false;
  5912. // std::vector<std::string> object_paths;
  5913. // // if (!m_skip_model) {
  5914. // for (ModelObject* obj : model.objects) {
  5915. // if (sub_model && obj != objects_data.begin()->second.object) continue;
  5916. //
  5917. // if (proFn) {
  5918. // proFn(EXPORT_STAGE_ADD_MODELS, object_index++, model.objects.size(), cb_cancel);
  5919. // if (cb_cancel)
  5920. // return false;
  5921. // }
  5922. //
  5923. // if (obj == nullptr)
  5924. // continue;
  5925. //
  5926. // // Index of an object in the 3MF file corresponding to the 1st instance of a ModelObject.
  5927. // ObjectToObjectDataMap::iterator object_it = objects_data.begin();
  5928. //
  5929. // if (!sub_model) {
  5930. // // For backup, use backup id as object id
  5931. // int backup_id = const_cast<Model&>(model).get_object_backup_id(*obj);
  5932. // if (m_from_backup_save) object_id = backup_id;
  5933. // object_it = objects_data.insert({obj, {obj, backup_id} }).first;
  5934. // auto & object_data = object_it->second;
  5935. //
  5936. // if (m_split_model) {
  5937. // auto filename = boost::format("3D/Objects/%s_%d.model") % obj->name % backup_id;
  5938. // object_data.sub_path = "/" + filename.str();
  5939. // object_paths.push_back(filename.str());
  5940. // }
  5941. //
  5942. // auto &volumes_objectID = object_data.volumes_objectID;
  5943. // unsigned int volume_id = object_id, volume_count = 0;
  5944. // for (ModelVolume *volume : obj->volumes) {
  5945. // if (volume == nullptr)
  5946. // continue;
  5947. // volume_count++;
  5948. // if (m_share_mesh) {
  5949. // auto iter = m_shared_meshes.find(volume->mesh_ptr().get());
  5950. // if (iter != m_shared_meshes.end())
  5951. // {
  5952. // const ModelVolume* shared_volume = iter->second.second;
  5953. // if ((shared_volume->supported_facets.equals(volume->supported_facets))
  5954. // && (shared_volume->seam_facets.equals(volume->seam_facets))
  5955. // && (shared_volume->mmu_segmentation_facets.equals(volume->mmu_segmentation_facets)))
  5956. // {
  5957. // auto data = iter->second.first;
  5958. // const_cast<_BBS_3MF_Exporter *>(this)->m_volume_paths.insert({volume, {data->sub_path, data->volumes_objectID.find(iter->second.second)->second}});
  5959. // volumes_objectID.insert({volume, 0});
  5960. // object_data.share_mesh = true;
  5961. // continue;
  5962. // }
  5963. // }
  5964. // const_cast<_BBS_3MF_Exporter *>(this)->m_shared_meshes.insert({volume->mesh_ptr().get(), {&object_data, volume}});
  5965. // }
  5966. // if (m_from_backup_save)
  5967. // volume_id = (volume_count << 16 | backup_id);
  5968. // volumes_objectID.insert({volume, volume_id});
  5969. // volume_id++;
  5970. // }
  5971. //
  5972. // if (!m_from_backup_save) object_id = volume_id;
  5973. // object_data.object_id = object_id;
  5974. // }
  5975. //
  5976. // if (m_skip_model) continue;
  5977. //
  5978. // if (write_object) {
  5979. // // Store geometry of all ModelVolumes contained in a single ModelObject into a single 3MF indexed triangle set object.
  5980. // // object_it->second.volumes_objectID will contain the offsets of the ModelVolumes in that single indexed triangle set.
  5981. // // object_id will be increased to point to the 1st instance of the next ModelObject.
  5982. // if (!_add_object_to_model_stream(context, object_it->second)) {
  5983. // add_error("Unable to add object to archive");
  5984. // BOOST_LOG_TRIVIAL(error) << __FUNCTION__ << ":" << __LINE__ << boost::format(", Unable to add object to archive\n");
  5985. // return false;
  5986. // }
  5987. // }
  5988. //
  5989. // if (sub_model) break;
  5990. //
  5991. // unsigned int count = 0;
  5992. // for (const ModelInstance* instance : obj->instances) {
  5993. // Transform3d t = instance->get_matrix();
  5994. // // instance_id is just a 1 indexed index in build_items.
  5995. // //assert(m_skip_static || curr_id == build_items.size() + 1);
  5996. // build_items.emplace_back("", object_it->second.object_id, t, instance->printable);
  5997. // count++;
  5998. // }
  5999. //
  6000. // if (!m_from_backup_save) object_id++;
  6001. // }
  6002. // // }
  6003. //
  6004. // {
  6005. // std::stringstream stream;
  6006. // reset_stream(stream);
  6007. //
  6008. // if (!m_skip_model && !sub_model) {
  6009. // for (auto object : model.objects) {
  6010. // auto &data = objects_data[object];
  6011. // _add_object_components_to_stream(stream, data);
  6012. // }
  6013. // }
  6014. //
  6015. // stream << " </" << RESOURCES_TAG << ">\n";
  6016. //
  6017. // // Store the transformations of all the ModelInstances of all ModelObjects, indexed in a linear fashion.
  6018. // if (!_add_build_to_model_stream(stream, build_items)) {
  6019. // add_error("Unable to add build to archive");
  6020. // BOOST_LOG_TRIVIAL(error) << __FUNCTION__ << ":" << __LINE__ << boost::format(", Unable to add build to archive\n");
  6021. // return false;
  6022. // }
  6023. //
  6024. // stream << "</" << MODEL_TAG << ">\n";
  6025. //
  6026. // std::string buf = stream.str();
  6027. //
  6028. // if ((! buf.empty() && ! mz_zip_writer_add_staged_data(&context, buf.data(), buf.size())) ||
  6029. // ! mz_zip_writer_add_staged_finish(&context)) {
  6030. // add_error("Unable to add model file to archive");
  6031. // BOOST_LOG_TRIVIAL(error) << __FUNCTION__ << ":" << __LINE__ << boost::format(", Unable to add model file to archive\n");
  6032. // return false;
  6033. // }
  6034. // }
  6035. //
  6036. // if (m_skip_model || write_object) return true;
  6037. //
  6038. // // write model rels
  6039. // _add_relationships_file_to_archive(archive, MODEL_RELS_FILE, object_paths, {"http://schemas.microsoft.com/3dmanufacturing/2013/01/3dmodel"});
  6040. //
  6041. // if (!m_from_backup_save) {
  6042. // boost::mutex mutex;
  6043. // tbb::parallel_for(tbb::blocked_range<size_t>(0, objects_data.size(), 1), [this, &mutex, &model, objects = model.objects, &objects_data, &object_paths, main = &archive, project](const tbb::blocked_range<size_t>& range) {
  6044. // for (size_t i = range.begin(); i < range.end(); ++i) {
  6045. // auto iter = objects_data.find(objects[i]);
  6046. // ObjectToObjectDataMap objects_data2;
  6047. // objects_data2.insert(*iter);
  6048. // auto & object = *iter->second.object;
  6049. // mz_zip_archive archive;
  6050. // mz_zip_zero_struct(&archive);
  6051. // mz_zip_writer_init_heap(&archive, 0, 1024 * 1024);
  6052. // CNumericLocalesSetter locales_setter;
  6053. // _add_model_file_to_archive(object_paths[i], archive, model, objects_data2, nullptr, project);
  6054. // iter->second = objects_data2.begin()->second;
  6055. // void *ppBuf; size_t pSize;
  6056. // mz_zip_writer_finalize_heap_archive(&archive, &ppBuf, &pSize);
  6057. // mz_zip_writer_end(&archive);
  6058. // mz_zip_zero_struct(&archive);
  6059. // mz_zip_reader_init_mem(&archive, ppBuf, pSize, 0);
  6060. // {
  6061. // boost::unique_lock l(mutex);
  6062. // mz_zip_writer_add_from_zip_reader(main, &archive, 0);
  6063. // }
  6064. // mz_zip_reader_end(&archive);
  6065. // }
  6066. // });
  6067. // }
  6068. //
  6069. // return true;
  6070. // }
  6071. //
  6072. // bool _BBS_3MF_Exporter::_add_object_to_model_stream(mz_zip_writer_staged_context &context, ObjectData const &object_data) const
  6073. // {
  6074. // // backup: make _add_mesh_to_object_stream() reusable
  6075. // auto flush = [this, &context](std::string & buf, bool force = false) {
  6076. // if ((force && !buf.empty()) || buf.size() >= 65536 * 16) {
  6077. // if (!mz_zip_writer_add_staged_data(&context, buf.data(), buf.size())) {
  6078. // add_error("Error during writing or compression");
  6079. // BOOST_LOG_TRIVIAL(error) << __FUNCTION__ << ":" << __LINE__ << boost::format(", Error during writing or compression\n");
  6080. // return false;
  6081. // }
  6082. // buf.clear();
  6083. // }
  6084. // return true;
  6085. // };
  6086. // if (!_add_mesh_to_object_stream(flush, object_data)) {
  6087. // add_error("Unable to add mesh to archive");
  6088. // BOOST_LOG_TRIVIAL(error) << __FUNCTION__ << ":" << __LINE__ << boost::format(", Unable to add mesh to archive\n");
  6089. // return false;
  6090. // }
  6091. //
  6092. // // Move all components to main model
  6093. // //_add_object_components_to_stream(stream, object_data);
  6094. //
  6095. // return true;
  6096. // }
  6097. //
  6098. // void _BBS_3MF_Exporter::_add_object_components_to_stream(std::stringstream &stream, ObjectData const &object_data) const
  6099. // {
  6100. // auto & object = *object_data.object;
  6101. //
  6102. // stream << " <" << OBJECT_TAG << " id=\"" << object_data.object_id;
  6103. // if (m_production_ext)
  6104. // stream << "\" " << PUUID_ATTR << "=\"" << hex_wrap<boost::uint32_t>{(boost::uint32_t)object_data.backup_id}
  6105. // << (object_data.share_mesh ? OBJECT_UUID_SUFFIX2 : OBJECT_UUID_SUFFIX);
  6106. // stream << "\" type=\"model\">\n";
  6107. //
  6108. // stream << " <" << COMPONENTS_TAG << ">\n";
  6109. //
  6110. // for (unsigned int index = 0; index < object.volumes.size(); index ++) {
  6111. // ModelVolume *volume = object.volumes[index];
  6112. // unsigned int volume_id = object_data.volumes_objectID.find(volume)->second;
  6113. // auto * ppath = &object_data.sub_path;
  6114. // auto iter = m_volume_paths.find(volume);
  6115. // if (iter != m_volume_paths.end()) {
  6116. // ppath = &iter->second.first;
  6117. // volume_id = iter->second.second;
  6118. // }
  6119. // //add the transform of the volume
  6120. // if (ppath->empty())
  6121. // stream << " <" << COMPONENT_TAG << " objectid=\"" << volume_id;
  6122. // else
  6123. // stream << " <" << COMPONENT_TAG << " p:path=\"" << xml_escape(*ppath) << "\" objectid=\"" << volume_id; // << "\"/>\n";
  6124. // if (m_production_ext)
  6125. // stream << "\" " << PUUID_ATTR << "=\"" << hex_wrap<boost::uint32_t>{(boost::uint32_t) (index + (object_data.backup_id << 16))} << COMPONENT_UUID_SUFFIX;
  6126. // const Transform3d &transf = volume->get_matrix();
  6127. // stream << "\" " << TRANSFORM_ATTR << "=\"";
  6128. // for (unsigned c = 0; c < 4; ++c) {
  6129. // for (unsigned r = 0; r < 3; ++r) {
  6130. // stream << transf(r, c);
  6131. // if (r != 2 || c != 3)
  6132. // stream << " ";
  6133. // }
  6134. // }
  6135. // stream << "\"/>\n";
  6136. // }
  6137. //
  6138. // stream << " </" << COMPONENTS_TAG << ">\n";
  6139. //
  6140. // stream << " </" << OBJECT_TAG << ">\n";
  6141. // }
  6142. //
  6143. //#if EXPORT_3MF_USE_SPIRIT_KARMA_FP
  6144. // template <typename Num>
  6145. // struct coordinate_policy_fixed : boost::spirit::karma::real_policies<Num>
  6146. // {
  6147. // static int floatfield(Num n) { return fmtflags::fixed; }
  6148. // // Number of decimal digits to maintain float accuracy when storing into a text file and parsing back.
  6149. // static unsigned precision(Num /* n */) { return std::numeric_limits<Num>::max_digits10 + 1; }
  6150. // // No trailing zeros, thus for fmtflags::fixed usually much less than max_digits10 decimal numbers will be produced.
  6151. // static bool trailing_zeros(Num /* n */) { return false; }
  6152. // };
  6153. // template <typename Num>
  6154. // struct coordinate_policy_scientific : coordinate_policy_fixed<Num>
  6155. // {
  6156. // static int floatfield(Num n) { return fmtflags::scientific; }
  6157. // };
  6158. // // Define a new generator type based on the new coordinate policy.
  6159. // using coordinate_type_fixed = boost::spirit::karma::real_generator<float, coordinate_policy_fixed<float>>;
  6160. // using coordinate_type_scientific = boost::spirit::karma::real_generator<float, coordinate_policy_scientific<float>>;
  6161. //#endif // EXPORT_3MF_USE_SPIRIT_KARMA_FP
  6162. //
  6163. // //BBS: change volume to seperate objects
  6164. // bool _BBS_3MF_Exporter::_add_mesh_to_object_stream(std::function<bool(std::string &, bool)> const &flush, ObjectData const &object_data) const
  6165. // {
  6166. // std::string output_buffer;
  6167. //
  6168. //#if 0
  6169. // auto flush = [this, &output_buffer, &context](bool force = false) {
  6170. // if ((force && ! output_buffer.empty()) || output_buffer.size() >= 65536 * 16) {
  6171. // if (! mz_zip_writer_add_staged_data(&context, output_buffer.data(), output_buffer.size())) {
  6172. // add_error("Error during writing or compression");
  6173. // BOOST_LOG_TRIVIAL(error) << __FUNCTION__ << ":" << __LINE__ << boost::format(", Error during writing or compression\n");
  6174. // return false;
  6175. // }
  6176. // output_buffer.clear();
  6177. // }
  6178. // return true;
  6179. // };
  6180. //#endif
  6181. //
  6182. // /*output_buffer += " <";
  6183. // output_buffer += MESH_TAG;
  6184. // output_buffer += ">\n <";
  6185. // output_buffer += VERTICES_TAG;
  6186. // output_buffer += ">\n";*/
  6187. //
  6188. // auto format_coordinate = [](float f, char *buf) -> char* {
  6189. // assert(is_decimal_separator_point());
  6190. //#if EXPORT_3MF_USE_SPIRIT_KARMA_FP
  6191. // // Slightly faster than sprintf("%.9g"), but there is an issue with the karma floating point formatter,
  6192. // // https://github.com/boostorg/spirit/pull/586
  6193. // // where the exported string is one digit shorter than it should be to guarantee lossless round trip.
  6194. // // The code is left here for the ocasion boost guys improve.
  6195. // coordinate_type_fixed const coordinate_fixed = coordinate_type_fixed();
  6196. // coordinate_type_scientific const coordinate_scientific = coordinate_type_scientific();
  6197. // // Format "f" in a fixed format.
  6198. // char *ptr = buf;
  6199. // boost::spirit::karma::generate(ptr, coordinate_fixed, f);
  6200. // // Format "f" in a scientific format.
  6201. // char *ptr2 = ptr;
  6202. // boost::spirit::karma::generate(ptr2, coordinate_scientific, f);
  6203. // // Return end of the shorter string.
  6204. // auto len2 = ptr2 - ptr;
  6205. // if (ptr - buf > len2) {
  6206. // // Move the shorter scientific form to the front.
  6207. // memcpy(buf, ptr, len2);
  6208. // ptr = buf + len2;
  6209. // }
  6210. // // Return pointer to the end.
  6211. // return ptr;
  6212. //#else
  6213. // // Round-trippable float, shortest possible.
  6214. // return buf + sprintf(buf, "%.9g", f);
  6215. //#endif
  6216. // };
  6217. //
  6218. // auto const & object = *object_data.object;
  6219. //
  6220. // char buf[256];
  6221. // unsigned int vertices_count = 0;
  6222. // //unsigned int triangles_count = 0;
  6223. // for (unsigned int index = 0; index < object.volumes.size(); index++) {
  6224. // ModelVolume *volume = object.volumes[index];
  6225. // if (volume == nullptr)
  6226. // continue;
  6227. //
  6228. // int volume_id = object_data.volumes_objectID.find(volume)->second;
  6229. // if (m_share_mesh && volume_id == 0)
  6230. // continue;
  6231. //
  6232. // //if (!volume->mesh().stats().repaired())
  6233. // // throw Slic3r::FileIOError("store_3mf() requires repair()");
  6234. //
  6235. // const indexed_triangle_set &its = volume->mesh().its;
  6236. // if (its.vertices.empty()) {
  6237. // add_error("Found invalid mesh");
  6238. // BOOST_LOG_TRIVIAL(error) << __FUNCTION__ << ":" << __LINE__ << boost::format(", Found invalid mesh\n");
  6239. // return false;
  6240. // }
  6241. //
  6242. // std::string type = (volume->type() == ModelVolumeType::MODEL_PART)?"model":"other";
  6243. //
  6244. // output_buffer += " <";
  6245. // output_buffer += OBJECT_TAG;
  6246. // output_buffer += " id=\"";
  6247. // output_buffer += std::to_string(volume_id);
  6248. // if (m_production_ext) {
  6249. // std::stringstream stream;
  6250. // reset_stream(stream);
  6251. // stream << "\" " << PUUID_ATTR << "=\"" << hex_wrap<boost::uint32_t>{(boost::uint32_t) (index + (object_data.backup_id << 16))} << SUB_OBJECT_UUID_SUFFIX;
  6252. // //output_buffer += "\" ";
  6253. // //output_buffer += PUUID_ATTR;
  6254. // //output_buffer += "=\"";
  6255. // //output_buffer += std::to_string(hex_wrap<boost::uint32_t>{(boost::uint32_t)backup_id});
  6256. // //output_buffer += OBJECT_UUID_SUFFIX;
  6257. // output_buffer += stream.str();
  6258. // }
  6259. // output_buffer += "\" type=\"";
  6260. // output_buffer += type;
  6261. // output_buffer += "\">\n";
  6262. // output_buffer += " <";
  6263. // output_buffer += MESH_TAG;
  6264. // output_buffer += ">\n <";
  6265. // output_buffer += VERTICES_TAG;
  6266. // output_buffer += ">\n";
  6267. //
  6268. // vertices_count += (int)its.vertices.size();
  6269. //
  6270. // for (size_t i = 0; i < its.vertices.size(); ++i) {
  6271. // //don't save the volume's matrix into vertex data
  6272. // //add the shared mesh logic
  6273. // //Vec3f v = (matrix * its.vertices[i].cast<double>()).cast<float>();
  6274. // Vec3f v = its.vertices[i];
  6275. // char* ptr = buf;
  6276. // boost::spirit::karma::generate(ptr, boost::spirit::lit(" <") << VERTEX_TAG << " x=\"");
  6277. // ptr = format_coordinate(v.x(), ptr);
  6278. // boost::spirit::karma::generate(ptr, "\" y=\"");
  6279. // ptr = format_coordinate(v.y(), ptr);
  6280. // boost::spirit::karma::generate(ptr, "\" z=\"");
  6281. // ptr = format_coordinate(v.z(), ptr);
  6282. // boost::spirit::karma::generate(ptr, "\"/>\n");
  6283. // *ptr = '\0';
  6284. // output_buffer += buf;
  6285. // if (!flush(output_buffer, false))
  6286. // return false;
  6287. // }
  6288. // //}
  6289. //
  6290. // output_buffer += " </";
  6291. // output_buffer += VERTICES_TAG;
  6292. // output_buffer += ">\n <";
  6293. // output_buffer += TRIANGLES_TAG;
  6294. // output_buffer += ">\n";
  6295. //
  6296. // //for (ModelVolume* volume : object.volumes) {
  6297. // // if (volume == nullptr)
  6298. // // continue;
  6299. //
  6300. // //BBS: as we stored matrix seperately, not multiplied into vertex
  6301. // //we don't need to consider this left hand case specially
  6302. // //bool is_left_handed = volume->is_left_handed();
  6303. // bool is_left_handed = false;
  6304. // //VolumeToOffsetsMap::iterator volume_it = volumes_objectID.find(volume);
  6305. // //assert(volume_it != volumes_objectID.end());
  6306. //
  6307. // //const indexed_triangle_set &its = volume->mesh().its;
  6308. //
  6309. // // updates triangle offsets
  6310. // //unsigned int first_triangle_id = triangles_count;
  6311. // //triangles_count += (int)its.indices.size();
  6312. // //unsigned int last_triangle_id = triangles_count - 1;
  6313. //
  6314. // for (int i = 0; i < int(its.indices.size()); ++ i) {
  6315. // {
  6316. // const Vec3i &idx = its.indices[i];
  6317. // char *ptr = buf;
  6318. // boost::spirit::karma::generate(ptr, boost::spirit::lit(" <") << TRIANGLE_TAG <<
  6319. // " v1=\"" << boost::spirit::int_ <<
  6320. // "\" v2=\"" << boost::spirit::int_ <<
  6321. // "\" v3=\"" << boost::spirit::int_ << "\"",
  6322. // idx[is_left_handed ? 2 : 0],
  6323. // idx[1],
  6324. // idx[is_left_handed ? 0 : 2]);
  6325. // *ptr = '\0';
  6326. // output_buffer += buf;
  6327. // }
  6328. //
  6329. // std::string custom_supports_data_string = volume->supported_facets.get_triangle_as_string(i);
  6330. // if (! custom_supports_data_string.empty()) {
  6331. // output_buffer += " ";
  6332. // output_buffer += CUSTOM_SUPPORTS_ATTR;
  6333. // output_buffer += "=\"";
  6334. // output_buffer += custom_supports_data_string;
  6335. // output_buffer += "\"";
  6336. // }
  6337. //
  6338. // std::string custom_seam_data_string = volume->seam_facets.get_triangle_as_string(i);
  6339. // if (! custom_seam_data_string.empty()) {
  6340. // output_buffer += " ";
  6341. // output_buffer += CUSTOM_SEAM_ATTR;
  6342. // output_buffer += "=\"";
  6343. // output_buffer += custom_seam_data_string;
  6344. // output_buffer += "\"";
  6345. // }
  6346. //
  6347. // std::string mmu_painting_data_string = volume->mmu_segmentation_facets.get_triangle_as_string(i);
  6348. // if (! mmu_painting_data_string.empty()) {
  6349. // output_buffer += " ";
  6350. // output_buffer += MMU_SEGMENTATION_ATTR;
  6351. // output_buffer += "=\"";
  6352. // output_buffer += mmu_painting_data_string;
  6353. // output_buffer += "\"";
  6354. // }
  6355. //
  6356. // // BBS
  6357. // if (i < its.properties.size()) {
  6358. // std::string prop_str = its.properties[i].to_string();
  6359. // if (!prop_str.empty()) {
  6360. // output_buffer += " ";
  6361. // output_buffer += FACE_PROPERTY_ATTR;
  6362. // output_buffer += "=\"";
  6363. // output_buffer += prop_str;
  6364. // output_buffer += "\"";
  6365. // }
  6366. // }
  6367. //
  6368. // output_buffer += "/>\n";
  6369. //
  6370. // if (! flush(output_buffer, false))
  6371. // return false;
  6372. // }
  6373. // output_buffer += " </";
  6374. // output_buffer += TRIANGLES_TAG;
  6375. // output_buffer += ">\n </";
  6376. // output_buffer += MESH_TAG;
  6377. // output_buffer += ">\n";
  6378. // output_buffer += " </";
  6379. // output_buffer += OBJECT_TAG;
  6380. // output_buffer += ">\n";
  6381. // }
  6382. //
  6383. // // Force flush.
  6384. // return flush(output_buffer, true);
  6385. // }
  6386. //
  6387. // void _BBS_3MF_Exporter::add_transformation(std::stringstream &stream, const Transform3d &tr)
  6388. // {
  6389. // for (unsigned c = 0; c < 4; ++c) {
  6390. // for (unsigned r = 0; r < 3; ++r) {
  6391. // stream << tr(r, c);
  6392. // if (r != 2 || c != 3) stream << " ";
  6393. // }
  6394. // }
  6395. // }
  6396. //
  6397. // bool _BBS_3MF_Exporter::_add_build_to_model_stream(std::stringstream& stream, const BuildItemsList& build_items) const
  6398. // {
  6399. // // This happens for empty projects
  6400. // if (build_items.size() == 0) {
  6401. // stream << " <" << BUILD_TAG << "/>\n";
  6402. // return true;
  6403. // }
  6404. //
  6405. // stream << " <" << BUILD_TAG;
  6406. // if (m_production_ext)
  6407. // stream << " " << PUUID_ATTR << "=\"" << BUILD_UUID << "\"";
  6408. // stream << ">\n";
  6409. //
  6410. // for (const BuildItem& item : build_items) {
  6411. // stream << " <" << ITEM_TAG << " " << OBJECTID_ATTR << "=\"" << item.id;
  6412. // if (m_production_ext)
  6413. // stream << "\" " << PUUID_ATTR << "=\"" << hex_wrap<boost::uint32_t>{item.id} << BUILD_UUID_SUFFIX;
  6414. // if (!item.path.empty())
  6415. // stream << "\" " << PPATH_ATTR << "=\"" << xml_escape(item.path);
  6416. // stream << "\" " << TRANSFORM_ATTR << "=\"";
  6417. // add_transformation(stream, item.transform);
  6418. // stream << "\" " << PRINTABLE_ATTR << "=\"" << item.printable << "\"/>\n";
  6419. // }
  6420. //
  6421. // stream << " </" << BUILD_TAG << ">\n";
  6422. //
  6423. // return true;
  6424. // }
  6425. //
  6426. // bool _BBS_3MF_Exporter::_add_cut_information_file_to_archive(mz_zip_archive &archive, Model &model)
  6427. // {
  6428. // std::string out = "";
  6429. // pt::ptree tree;
  6430. //
  6431. // unsigned int object_cnt = 0;
  6432. // for (const ModelObject* object : model.objects) {
  6433. // object_cnt++;
  6434. // if (!object->is_cut())
  6435. // continue;
  6436. // pt::ptree& obj_tree = tree.add("objects.object", "");
  6437. //
  6438. // obj_tree.put("<xmlattr>.id", object_cnt);
  6439. //
  6440. // // Store info for cut_id
  6441. // pt::ptree& cut_id_tree = obj_tree.add("cut_id", "");
  6442. //
  6443. // // store cut_id atributes
  6444. // cut_id_tree.put("<xmlattr>.id", object->cut_id.id().id);
  6445. // cut_id_tree.put("<xmlattr>.check_sum", object->cut_id.check_sum());
  6446. // cut_id_tree.put("<xmlattr>.connectors_cnt", object->cut_id.connectors_cnt());
  6447. //
  6448. // int volume_idx = -1;
  6449. // for (const ModelVolume* volume : object->volumes) {
  6450. // ++volume_idx;
  6451. // if (volume->is_cut_connector()) {
  6452. // pt::ptree& connectors_tree = obj_tree.add("connectors.connector", "");
  6453. // connectors_tree.put("<xmlattr>.volume_id", volume_idx);
  6454. // connectors_tree.put("<xmlattr>.type", int(volume->cut_info.connector_type));
  6455. // connectors_tree.put("<xmlattr>.r_tolerance", volume->cut_info.radius_tolerance);
  6456. // connectors_tree.put("<xmlattr>.h_tolerance", volume->cut_info.height_tolerance);
  6457. // }
  6458. // }
  6459. // }
  6460. //
  6461. // if (!tree.empty()) {
  6462. // std::ostringstream oss;
  6463. // pt::write_xml(oss, tree);
  6464. // out = oss.str();
  6465. //
  6466. // // Post processing("beautification") of the output string for a better preview
  6467. // boost::replace_all(out, "><object", ">\n <object");
  6468. // boost::replace_all(out, "><cut_id", ">\n <cut_id");
  6469. // boost::replace_all(out, "></cut_id>", ">\n </cut_id>");
  6470. // boost::replace_all(out, "><connectors", ">\n <connectors");
  6471. // boost::replace_all(out, "></connectors>", ">\n </connectors>");
  6472. // boost::replace_all(out, "><connector", ">\n <connector");
  6473. // boost::replace_all(out, "></connector>", ">\n </connector>");
  6474. // boost::replace_all(out, "></object>", ">\n </object>");
  6475. // // OR just
  6476. // boost::replace_all(out, "><", ">\n<");
  6477. // }
  6478. //
  6479. // if (!out.empty()) {
  6480. // if (!mz_zip_writer_add_mem(&archive, CUT_INFORMATION_FILE.c_str(), (const void*)out.data(), out.length(), MZ_DEFAULT_COMPRESSION)) {
  6481. // add_error("Unable to add cut information file to archive");
  6482. // return false;
  6483. // }
  6484. // }
  6485. //
  6486. // return true;
  6487. // }
  6488. //
  6489. // bool _BBS_3MF_Exporter::_add_layer_height_profile_file_to_archive(mz_zip_archive& archive, Model& model)
  6490. // {
  6491. // assert(is_decimal_separator_point());
  6492. // std::string out = "";
  6493. // char buffer[1024];
  6494. //
  6495. // unsigned int count = 0;
  6496. // for (const ModelObject* object : model.objects) {
  6497. // ++count;
  6498. // const std::vector<double>& layer_height_profile = object->layer_height_profile.get();
  6499. // if (layer_height_profile.size() >= 4 && layer_height_profile.size() % 2 == 0) {
  6500. // snprintf(buffer, 1024, "object_id=%d|", count);
  6501. // out += buffer;
  6502. //
  6503. // // Store the layer height profile as a single semicolon separated list.
  6504. // for (size_t i = 0; i < layer_height_profile.size(); ++i) {
  6505. // snprintf(buffer, 1024, (i == 0) ? "%f" : ";%f", layer_height_profile[i]);
  6506. // out += buffer;
  6507. // }
  6508. //
  6509. // out += "\n";
  6510. // }
  6511. // }
  6512. //
  6513. // if (!out.empty()) {
  6514. // if (!mz_zip_writer_add_mem(&archive, BBS_LAYER_HEIGHTS_PROFILE_FILE.c_str(), (const void*)out.data(), out.length(), MZ_DEFAULT_COMPRESSION)) {
  6515. // add_error("Unable to add layer heights profile file to archive");
  6516. // BOOST_LOG_TRIVIAL(error) << __FUNCTION__ << ":" << __LINE__ << boost::format("Unable to add layer heights profile file to archive\n");
  6517. // return false;
  6518. // }
  6519. // }
  6520. //
  6521. // return true;
  6522. // }
  6523. //
  6524. // bool _BBS_3MF_Exporter::_add_layer_config_ranges_file_to_archive(mz_zip_archive& archive, Model& model)
  6525. // {
  6526. // std::string out = "";
  6527. // pt::ptree tree;
  6528. //
  6529. // unsigned int object_cnt = 0;
  6530. // for (const ModelObject* object : model.objects) {
  6531. // object_cnt++;
  6532. // const t_layer_config_ranges& ranges = object->layer_config_ranges;
  6533. // if (!ranges.empty())
  6534. // {
  6535. // pt::ptree& obj_tree = tree.add("objects.object","");
  6536. //
  6537. // obj_tree.put("<xmlattr>.id", object_cnt);
  6538. //
  6539. // // Store the layer config ranges.
  6540. // for (const auto& range : ranges) {
  6541. // pt::ptree& range_tree = obj_tree.add("range", "");
  6542. //
  6543. // // store minX and maxZ
  6544. // range_tree.put("<xmlattr>.min_z", range.first.first);
  6545. // range_tree.put("<xmlattr>.max_z", range.first.second);
  6546. //
  6547. // // store range configuration
  6548. // const ModelConfig& config = range.second;
  6549. // for (const std::string& opt_key : config.keys()) {
  6550. // pt::ptree& opt_tree = range_tree.add("option", config.opt_serialize(opt_key));
  6551. // opt_tree.put("<xmlattr>.opt_key", opt_key);
  6552. // }
  6553. // }
  6554. // }
  6555. // }
  6556. //
  6557. // if (!tree.empty()) {
  6558. // std::ostringstream oss;
  6559. // pt::write_xml(oss, tree);
  6560. // out = oss.str();
  6561. //
  6562. // // Post processing("beautification") of the output string for a better preview
  6563. // boost::replace_all(out, "><object", ">\n <object");
  6564. // boost::replace_all(out, "><range", ">\n <range");
  6565. // boost::replace_all(out, "><option", ">\n <option");
  6566. // boost::replace_all(out, "></range>", ">\n </range>");
  6567. // boost::replace_all(out, "></object>", ">\n </object>");
  6568. // // OR just
  6569. // boost::replace_all(out, "><", ">\n<");
  6570. // }
  6571. //
  6572. // if (!out.empty()) {
  6573. // if (!mz_zip_writer_add_mem(&archive, LAYER_CONFIG_RANGES_FILE.c_str(), (const void*)out.data(), out.length(), MZ_DEFAULT_COMPRESSION)) {
  6574. // add_error("Unable to add layer heights profile file to archive");
  6575. // BOOST_LOG_TRIVIAL(error) << __FUNCTION__ << ":" << __LINE__ << boost::format("Unable to add layer heights profile file to archive\n");
  6576. // return false;
  6577. // }
  6578. // }
  6579. //
  6580. // return true;
  6581. // }
  6582. //
  6583. // /*
  6584. // bool _BBS_3MF_Exporter::_add_sla_support_points_file_to_archive(mz_zip_archive& archive, Model& model)
  6585. // {
  6586. // assert(is_decimal_separator_point());
  6587. // std::string out = "";
  6588. // char buffer[1024];
  6589. //
  6590. // unsigned int count = 0;
  6591. // for (const ModelObject* object : model.objects) {
  6592. // ++count;
  6593. // const std::vector<sla::SupportPoint>& sla_support_points = object->sla_support_points;
  6594. // if (!sla_support_points.empty()) {
  6595. // sprintf(buffer, "object_id=%d|", count);
  6596. // out += buffer;
  6597. //
  6598. // // Store the layer height profile as a single space separated list.
  6599. // for (size_t i = 0; i < sla_support_points.size(); ++i) {
  6600. // sprintf(buffer, (i==0 ? "%f %f %f %f %f" : " %f %f %f %f %f"), sla_support_points[i].pos(0), sla_support_points[i].pos(1), sla_support_points[i].pos(2), sla_support_points[i].head_front_radius, (float)sla_support_points[i].is_new_island);
  6601. // out += buffer;
  6602. // }
  6603. // out += "\n";
  6604. // }
  6605. // }
  6606. //
  6607. // if (!out.empty()) {
  6608. // // Adds version header at the beginning:
  6609. // //out = std::string("support_points_format_version=") + std::to_string(support_points_format_version) + std::string("\n") + out;
  6610. //
  6611. // if (!mz_zip_writer_add_mem(&archive, SLA_SUPPORT_POINTS_FILE.c_str(), (const void*)out.data(), out.length(), MZ_DEFAULT_COMPRESSION)) {
  6612. // add_error("Unable to add sla support points file to archive");
  6613. // BOOST_LOG_TRIVIAL(error) << __FUNCTION__ << ":" << __LINE__ << boost::format("Unable to add sla support points file to archive\n");
  6614. // return false;
  6615. // }
  6616. // }
  6617. // return true;
  6618. // }
  6619. //
  6620. // bool _BBS_3MF_Exporter::_add_sla_drain_holes_file_to_archive(mz_zip_archive& archive, Model& model)
  6621. // {
  6622. // assert(is_decimal_separator_point());
  6623. // const char *const fmt = "object_id=%d|";
  6624. // std::string out;
  6625. //
  6626. // unsigned int count = 0;
  6627. // for (const ModelObject* object : model.objects) {
  6628. // ++count;
  6629. // sla::DrainHoles drain_holes = object->sla_drain_holes;
  6630. //
  6631. // // The holes were placed 1mm above the mesh in the first implementation.
  6632. // // This was a bad idea and the reference point was changed in 2.3 so
  6633. // // to be on the mesh exactly. The elevated position is still saved
  6634. // // in 3MFs for compatibility reasons.
  6635. // for (sla::DrainHole& hole : drain_holes) {
  6636. // hole.pos -= hole.normal.normalized();
  6637. // hole.height += 1.f;
  6638. // }
  6639. //
  6640. // if (!drain_holes.empty()) {
  6641. // out += string_printf(fmt, count);
  6642. //
  6643. // // Store the layer height profile as a single space separated list.
  6644. // for (size_t i = 0; i < drain_holes.size(); ++i)
  6645. // out += string_printf((i == 0 ? "%f %f %f %f %f %f %f %f" : " %f %f %f %f %f %f %f %f"),
  6646. // drain_holes[i].pos(0),
  6647. // drain_holes[i].pos(1),
  6648. // drain_holes[i].pos(2),
  6649. // drain_holes[i].normal(0),
  6650. // drain_holes[i].normal(1),
  6651. // drain_holes[i].normal(2),
  6652. // drain_holes[i].radius,
  6653. // drain_holes[i].height);
  6654. //
  6655. // out += "\n";
  6656. // }
  6657. // }
  6658. //
  6659. // if (!out.empty()) {
  6660. // // Adds version header at the beginning:
  6661. // //out = std::string("drain_holes_format_version=") + std::to_string(drain_holes_format_version) + std::string("\n") + out;
  6662. //
  6663. // if (!mz_zip_writer_add_mem(&archive, SLA_DRAIN_HOLES_FILE.c_str(), static_cast<const void*>(out.data()), out.length(), mz_uint(MZ_DEFAULT_COMPRESSION))) {
  6664. // add_error("Unable to add sla support points file to archive");
  6665. // BOOST_LOG_TRIVIAL(error) << __FUNCTION__ << ":" << __LINE__ << boost::format("Unable to add sla support points file to archive\n");
  6666. // return false;
  6667. // }
  6668. // }
  6669. // return true;
  6670. // }*/
  6671. //
  6672. // bool _BBS_3MF_Exporter::_add_print_config_file_to_archive(mz_zip_archive& archive, const DynamicPrintConfig &config)
  6673. // {
  6674. // assert(is_decimal_separator_point());
  6675. // char buffer[1024];
  6676. // snprintf(buffer, 1024, "; %s\n\n", header_slic3r_generated().c_str());
  6677. // std::string out = buffer;
  6678. //
  6679. // for (const std::string &key : config.keys())
  6680. // if (key != "compatible_printers")
  6681. // out += "; " + key + " = " + config.opt_serialize(key) + "\n";
  6682. //
  6683. // if (!out.empty()) {
  6684. // if (!mz_zip_writer_add_mem(&archive, BBS_PRINT_CONFIG_FILE.c_str(), (const void*)out.data(), out.length(), MZ_DEFAULT_COMPRESSION)) {
  6685. // add_error("Unable to add print config file to archive");
  6686. // BOOST_LOG_TRIVIAL(error) << __FUNCTION__ << ":" << __LINE__ << boost::format("Unable to add print config file to archive\n");
  6687. // return false;
  6688. // }
  6689. // }
  6690. //
  6691. // return true;
  6692. // }
  6693. //
  6694. // //BBS: add project config file logic for new json format
  6695. // bool _BBS_3MF_Exporter::_add_project_config_file_to_archive(mz_zip_archive& archive, const DynamicPrintConfig &config, Model& model)
  6696. // {
  6697. // const std::string& temp_path = model.get_backup_path();
  6698. // std::string temp_file = temp_path + std::string("/") + "_temp_1.config";
  6699. // config.save_to_json(temp_file, std::string("project_settings"), std::string("project"), std::string(SoftFever_VERSION));
  6700. // return _add_file_to_archive(archive, BBS_PROJECT_CONFIG_FILE, temp_file);
  6701. // }
  6702. //
  6703. // //BBS: add project embedded preset files
  6704. // bool _BBS_3MF_Exporter::_add_project_embedded_presets_to_archive(mz_zip_archive& archive, Model& model, std::vector<Preset*> project_presets)
  6705. // {
  6706. // char buffer[1024];
  6707. // snprintf(buffer, 1024, "; %s\n\n", header_slic3r_generated().c_str());
  6708. // std::string out = buffer;
  6709. // int print_count = 0, filament_count = 0, printer_count = 0;
  6710. // const std::string& temp_path = model.get_backup_path();
  6711. //
  6712. // for (int i = 0; i < project_presets.size(); i++)
  6713. // {
  6714. // Preset* preset = project_presets[i];
  6715. //
  6716. // if (preset) {
  6717. // preset->file = temp_path + std::string("/") + "_temp_1.config";
  6718. // DynamicPrintConfig& config = preset->config;
  6719. // //config.save(preset->file);
  6720. // config.save_to_json(preset->file, preset->name, std::string("project"), preset->version.to_string());
  6721. //
  6722. // std::string dest_file;
  6723. // if (preset->type == Preset::TYPE_PRINT) {
  6724. // dest_file = (boost::format(EMBEDDED_PRINT_FILE_FORMAT) % (print_count + 1)).str();
  6725. // print_count++;
  6726. // }
  6727. // else if (preset->type == Preset::TYPE_FILAMENT) {
  6728. // dest_file = (boost::format(EMBEDDED_FILAMENT_FILE_FORMAT) % (filament_count + 1)).str();
  6729. // filament_count++;
  6730. // }
  6731. // else if (preset->type == Preset::TYPE_PRINTER) {
  6732. // dest_file = (boost::format(EMBEDDED_PRINTER_FILE_FORMAT) % (printer_count + 1)).str();
  6733. // printer_count++;
  6734. // }
  6735. // else
  6736. // continue;
  6737. //
  6738. // _add_file_to_archive(archive, dest_file, preset->file);
  6739. // }
  6740. // }
  6741. //
  6742. // return true;
  6743. // }
  6744. //
  6745. // bool _BBS_3MF_Exporter::_add_model_config_file_to_archive(mz_zip_archive& archive, const Model& model, PlateDataPtrs& plate_data_list, const ObjectToObjectDataMap &objects_data, int export_plate_idx, bool save_gcode, bool use_loaded_id)
  6746. // {
  6747. // std::stringstream stream;
  6748. // // Store mesh transformation in full precision, as the volumes are stored transformed and they need to be transformed back
  6749. // // when loaded as accurately as possible.
  6750. // stream << std::setprecision(std::numeric_limits<double>::max_digits10);
  6751. // stream << "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n";
  6752. // stream << "<" << CONFIG_TAG << ">\n";
  6753. //
  6754. // if (!m_skip_model)
  6755. // for (const ObjectToObjectDataMap::value_type& obj_metadata : objects_data) {
  6756. // auto object_data = obj_metadata.second;
  6757. // const ModelObject *obj = object_data.object;
  6758. // if (obj != nullptr) {
  6759. // // Output of instances count added because of github #3435, currently not used by PrusaSlicer
  6760. // //stream << " <" << OBJECT_TAG << " " << ID_ATTR << "=\"" << obj_metadata.first << "\" " << INSTANCESCOUNT_ATTR << "=\"" << obj->instances.size() << "\">\n";
  6761. // stream << " <" << OBJECT_TAG << " " << ID_ATTR << "=\"" << object_data.object_id << "\">\n";
  6762. //
  6763. // // stores object's name
  6764. // if (!obj->name.empty())
  6765. // stream << " <" << METADATA_TAG << " " << KEY_ATTR << "=\"name\" " << VALUE_ATTR << "=\"" << xml_escape(obj->name) << "\"/>\n";
  6766. //
  6767. // //BBS: store object's module name
  6768. // if (!obj->module_name.empty())
  6769. // stream << " <" << METADATA_TAG << " " << KEY_ATTR << "=\"module\" " << VALUE_ATTR << "=\"" << xml_escape(obj->module_name) << "\"/>\n";
  6770. //
  6771. // // stores object's config data
  6772. // for (const std::string& key : obj->config.keys()) {
  6773. // stream << " <" << METADATA_TAG << " " << KEY_ATTR << "=\"" << key << "\" " << VALUE_ATTR << "=\"" << obj->config.opt_serialize(key) << "\"/>\n";
  6774. // }
  6775. //
  6776. // for (const ModelVolume* volume : obj_metadata.second.object->volumes) {
  6777. // if (volume != nullptr) {
  6778. // const VolumeToObjectIDMap& objectIDs = obj_metadata.second.volumes_objectID;
  6779. // VolumeToObjectIDMap::const_iterator it = objectIDs.find(volume);
  6780. // if (it != objectIDs.end()) {
  6781. // // stores volume's offsets
  6782. // stream << " <" << PART_TAG << " ";
  6783. // //stream << FIRST_TRIANGLE_ID_ATTR << "=\"" << it->second.first_triangle_id << "\" ";
  6784. // //stream << LAST_TRIANGLE_ID_ATTR << "=\"" << it->second.last_triangle_id << "\" ";
  6785. // int volume_id = it->second;
  6786. // if (m_share_mesh && volume_id == 0)
  6787. // volume_id = m_volume_paths.find(volume)->second.second;
  6788. // stream << ID_ATTR << "=\"" << volume_id << "\" ";
  6789. //
  6790. // stream << SUBTYPE_ATTR << "=\"" << ModelVolume::type_to_string(volume->type()) << "\">\n";
  6791. // //stream << " <" << PART_TAG << " " << ID_ATTR << "=\"" << it->second << "\" " << SUBTYPE_ATTR << "=\"" << ModelVolume::type_to_string(volume->type()) << "\">\n";
  6792. //
  6793. // // stores volume's name
  6794. // if (!volume->name.empty())
  6795. // stream << " <" << METADATA_TAG << " " << KEY_ATTR << "=\"" << NAME_KEY << "\" " << VALUE_ATTR << "=\"" << xml_escape(volume->name) << "\"/>\n";
  6796. // //stream << " <" << METADATA_TAG << " " << KEY_ATTR << "=\"" << NAME_KEY << "\" " << VALUE_ATTR << "=\"" << xml_escape(volume->name) << "\"/>\n";
  6797. //
  6798. // // stores volume's modifier field (legacy, to support old slicers)
  6799. // /*if (volume->is_modifier())
  6800. // stream << " <" << METADATA_TAG << " " << TYPE_ATTR << "=\"" << PART_TYPE << "\" " << KEY_ATTR << "=\"" << MODIFIER_KEY << "\" " << VALUE_ATTR << "=\"1\"/>\n";
  6801. // // stores volume's type (overrides the modifier field above)
  6802. // stream << " <" << METADATA_TAG << " " << TYPE_ATTR << "=\"" << PART_TYPE << "\" " << KEY_ATTR << "=\"" << PART_TYPE_KEY << "\" " <<
  6803. // VALUE_ATTR << "=\"" << ModelVolume::type_to_string(volume->type()) << "\"/>\n";*/
  6804. //
  6805. // // stores volume's local matrix
  6806. // stream << " <" << METADATA_TAG << " " << KEY_ATTR << "=\"" << MATRIX_KEY << "\" " << VALUE_ATTR << "=\"";
  6807. // Transform3d matrix = volume->get_matrix() * volume->source.transform.get_matrix();
  6808. // for (int r = 0; r < 4; ++r) {
  6809. // for (int c = 0; c < 4; ++c) {
  6810. // stream << matrix(r, c);
  6811. // if (r != 3 || c != 3)
  6812. // stream << " ";
  6813. // }
  6814. // }
  6815. // stream << "\"/>\n";
  6816. //
  6817. // // stores volume's source data
  6818. // {
  6819. // std::string input_file = xml_escape(m_fullpath_sources ? volume->source.input_file : boost::filesystem::path(volume->source.input_file).filename().string());
  6820. // //std::string prefix = std::string(" <") + METADATA_TAG + " " + KEY_ATTR + "=\"";
  6821. // std::string prefix = std::string(" <") + METADATA_TAG + " " + KEY_ATTR + "=\"";
  6822. // if (! volume->source.input_file.empty()) {
  6823. // stream << prefix << SOURCE_FILE_KEY << "\" " << VALUE_ATTR << "=\"" << input_file << "\"/>\n";
  6824. // stream << prefix << SOURCE_OBJECT_ID_KEY << "\" " << VALUE_ATTR << "=\"" << volume->source.object_idx << "\"/>\n";
  6825. // stream << prefix << SOURCE_VOLUME_ID_KEY << "\" " << VALUE_ATTR << "=\"" << volume->source.volume_idx << "\"/>\n";
  6826. // stream << prefix << SOURCE_OFFSET_X_KEY << "\" " << VALUE_ATTR << "=\"" << volume->source.mesh_offset(0) << "\"/>\n";
  6827. // stream << prefix << SOURCE_OFFSET_Y_KEY << "\" " << VALUE_ATTR << "=\"" << volume->source.mesh_offset(1) << "\"/>\n";
  6828. // stream << prefix << SOURCE_OFFSET_Z_KEY << "\" " << VALUE_ATTR << "=\"" << volume->source.mesh_offset(2) << "\"/>\n";
  6829. // }
  6830. // assert(! volume->source.is_converted_from_inches || ! volume->source.is_converted_from_meters);
  6831. // if (volume->source.is_converted_from_inches)
  6832. // stream << prefix << SOURCE_IN_INCHES << "\" " << VALUE_ATTR << "=\"1\"/>\n";
  6833. // else if (volume->source.is_converted_from_meters)
  6834. // stream << prefix << SOURCE_IN_METERS << "\" " << VALUE_ATTR << "=\"1\"/>\n";
  6835. // }
  6836. //
  6837. // // stores volume's config data
  6838. // for (const std::string& key : volume->config.keys()) {
  6839. // stream << " <" << METADATA_TAG << " "<< KEY_ATTR << "=\"" << key << "\" " << VALUE_ATTR << "=\"" << volume->config.opt_serialize(key) << "\"/>\n";
  6840. // }
  6841. //
  6842. // if (const std::optional<EmbossShape> &es = volume->emboss_shape;
  6843. // es.has_value())
  6844. // to_xml(stream, *es, *volume, archive);
  6845. //
  6846. // if (const std::optional<TextConfiguration> &tc = volume->text_configuration;
  6847. // tc.has_value())
  6848. // TextConfigurationSerialization::to_xml(stream, *tc);
  6849. //
  6850. // // stores mesh's statistics
  6851. // const RepairedMeshErrors& stats = volume->mesh().stats().repaired_errors;
  6852. // stream << " <" << MESH_STAT_TAG << " ";
  6853. // stream << MESH_STAT_EDGES_FIXED << "=\"" << stats.edges_fixed << "\" ";
  6854. // stream << MESH_STAT_DEGENERATED_FACETS << "=\"" << stats.degenerate_facets << "\" ";
  6855. // stream << MESH_STAT_FACETS_REMOVED << "=\"" << stats.facets_removed << "\" ";
  6856. // stream << MESH_STAT_FACETS_RESERVED << "=\"" << stats.facets_reversed << "\" ";
  6857. // stream << MESH_STAT_BACKWARDS_EDGES << "=\"" << stats.backwards_edges << "\"/>\n";
  6858. //
  6859. // stream << " </" << PART_TAG << ">\n";
  6860. // }
  6861. // }
  6862. // }
  6863. //
  6864. // stream << " </" << OBJECT_TAG << ">\n";
  6865. // }
  6866. // }
  6867. //
  6868. // //BBS: store plate related logic
  6869. // std::vector<std::string> gcode_paths;
  6870. // for (unsigned int i = 0; i < (unsigned int)plate_data_list.size(); ++i)
  6871. // {
  6872. // PlateData* plate_data = plate_data_list[i];
  6873. // int instance_size = plate_data->objects_and_instances.size();
  6874. //
  6875. // if (plate_data != nullptr) {
  6876. // stream << " <" << PLATE_TAG << ">\n";
  6877. // //plate index
  6878. // stream << " <" << METADATA_TAG << " " << KEY_ATTR << "=\"" << PLATERID_ATTR << "\" " << VALUE_ATTR << "=\"" << plate_data->plate_index + 1 << "\"/>\n";
  6879. // stream << " <" << METADATA_TAG << " " << KEY_ATTR << "=\"" << PLATER_NAME_ATTR << "\" " << VALUE_ATTR << "=\"" << xml_escape(plate_data->plate_name.c_str()) << "\"/>\n";
  6880. // stream << " <" << METADATA_TAG << " " << KEY_ATTR << "=\"" << LOCK_ATTR << "\" " << VALUE_ATTR << "=\"" << std::boolalpha<< plate_data->locked<< "\"/>\n";
  6881. // ConfigOption* bed_type_opt = plate_data->config.option("curr_bed_type");
  6882. // t_config_enum_names bed_type_names = ConfigOptionEnum<BedType>::get_enum_names();
  6883. // if (bed_type_opt != nullptr && bed_type_names.size() > bed_type_opt->getInt())
  6884. // stream << " <" << METADATA_TAG << " " << KEY_ATTR << "=\"" << BED_TYPE_ATTR << "\" " << VALUE_ATTR << "=\"" << bed_type_names[bed_type_opt->getInt()] << "\"/>\n";
  6885. //
  6886. // ConfigOption* print_sequence_opt = plate_data->config.option("print_sequence");
  6887. // t_config_enum_names print_sequence_names = ConfigOptionEnum<PrintSequence>::get_enum_names();
  6888. // if (print_sequence_opt != nullptr && print_sequence_names.size() > print_sequence_opt->getInt())
  6889. // stream << " <" << METADATA_TAG << " " << KEY_ATTR << "=\"" << PRINT_SEQUENCE_ATTR << "\" " << VALUE_ATTR << "=\"" << print_sequence_names[print_sequence_opt->getInt()] << "\"/>\n";
  6890. //
  6891. // ConfigOptionInts *first_layer_print_sequence_opt = plate_data->config.option<ConfigOptionInts>("first_layer_print_sequence");
  6892. // if (first_layer_print_sequence_opt != nullptr) {
  6893. // stream << " <" << METADATA_TAG << " " << KEY_ATTR << "=\"" << FIRST_LAYER_PRINT_SEQUENCE_ATTR << "\" " << VALUE_ATTR << "=\"";
  6894. // const std::vector<int>& values = first_layer_print_sequence_opt->values;
  6895. // for (int i = 0; i < values.size(); ++i) {
  6896. // stream << values[i];
  6897. // if (i != (values.size() - 1))
  6898. // stream << " ";
  6899. // }
  6900. // stream << "\"/>\n";
  6901. // }
  6902. //
  6903. // ConfigOption* spiral_mode_opt = plate_data->config.option("spiral_mode");
  6904. // if (spiral_mode_opt)
  6905. // stream << " <" << METADATA_TAG << " " << KEY_ATTR << "=\"" << SPIRAL_VASE_MODE << "\" " << VALUE_ATTR << "=\"" << spiral_mode_opt->getBool() << "\"/>\n";
  6906. //
  6907. // if (save_gcode)
  6908. // stream << " <" << METADATA_TAG << " " << KEY_ATTR << "=\"" << GCODE_FILE_ATTR << "\" " << VALUE_ATTR << "=\"" << std::boolalpha << xml_escape(plate_data->gcode_file) << "\"/>\n";
  6909. // if (!plate_data->gcode_file.empty()) {
  6910. // gcode_paths.push_back(plate_data->gcode_file);
  6911. // }
  6912. // if (plate_data->plate_thumbnail.is_valid()) {
  6913. // std::string thumbnail_file_in_3mf = (boost::format(THUMBNAIL_FILE_FORMAT) % (plate_data->plate_index + 1)).str();
  6914. // stream << " <" << METADATA_TAG << " " << KEY_ATTR << "=\"" << THUMBNAIL_FILE_ATTR << "\" " << VALUE_ATTR << "=\"" << std::boolalpha << thumbnail_file_in_3mf << "\"/>\n";
  6915. // }
  6916. // else if (!plate_data->thumbnail_file.empty() && (boost::filesystem::exists(plate_data->thumbnail_file))){
  6917. // std::string thumbnail_file_in_3mf = (boost::format(THUMBNAIL_FILE_FORMAT) % (plate_data->plate_index + 1)).str();
  6918. // stream << " <" << METADATA_TAG << " " << KEY_ATTR << "=\"" << THUMBNAIL_FILE_ATTR << "\" " << VALUE_ATTR << "=\"" << std::boolalpha << thumbnail_file_in_3mf << "\"/>\n";
  6919. // }
  6920. //
  6921. // if (!plate_data->top_file.empty()) {
  6922. // std::string top_file_in_3mf = (boost::format(TOP_FILE_FORMAT) % (plate_data->plate_index + 1)).str();
  6923. // stream << " <" << METADATA_TAG << " " << KEY_ATTR << "=\"" << TOP_FILE_ATTR << "\" " << VALUE_ATTR << "=\"" << std::boolalpha << top_file_in_3mf << "\"/>\n";
  6924. // }
  6925. //
  6926. // if (!plate_data->pick_file.empty()) {
  6927. // std::string pick_file_in_3mf = (boost::format(PICK_FILE_FORMAT) % (plate_data->plate_index + 1)).str();
  6928. // stream << " <" << METADATA_TAG << " " << KEY_ATTR << "=\"" << PICK_FILE_ATTR << "\" " << VALUE_ATTR << "=\"" << std::boolalpha << pick_file_in_3mf << "\"/>\n";
  6929. // }
  6930. //
  6931. // /*if (!plate_data->pattern_file.empty()) {
  6932. // std::string pattern_file_in_3mf = (boost::format(PATTERN_FILE_FORMAT) % (plate_data->plate_index + 1)).str();
  6933. // stream << " <" << METADATA_TAG << " " << KEY_ATTR << "=\"" << PATTERN_FILE_ATTR << "\" " << VALUE_ATTR << "=\"" << std::boolalpha << pattern_file_in_3mf << "\"/>\n";
  6934. // }*/
  6935. // if (!plate_data->pattern_bbox_file.empty()) {
  6936. // std::string pattern_bbox_file_in_3mf = (boost::format(PATTERN_CONFIG_FILE_FORMAT) % (plate_data->plate_index + 1)).str();
  6937. // stream << " <" << METADATA_TAG << " " << KEY_ATTR << "=\"" << PATTERN_BBOX_FILE_ATTR << "\" " << VALUE_ATTR << "=\"" << std::boolalpha << pattern_bbox_file_in_3mf << "\"/>\n";
  6938. // }
  6939. //
  6940. // if (!m_skip_model && instance_size > 0)
  6941. // {
  6942. // for (unsigned int j = 0; j < instance_size; ++j)
  6943. // {
  6944. // stream << " <" << INSTANCE_TAG << ">\n";
  6945. // int obj_id = plate_data->objects_and_instances[j].first;
  6946. // int inst_id = plate_data->objects_and_instances[j].second;
  6947. // int identify_id = 0;
  6948. // ModelObject* obj = NULL;
  6949. // ModelInstance* inst = NULL;
  6950. // if (obj_id >= model.objects.size()) {
  6951. // BOOST_LOG_TRIVIAL(warning) << __FUNCTION__ << ":" << __LINE__ << boost::format("invalid object id %1%\n")%obj_id;
  6952. // }
  6953. // else
  6954. // obj = model.objects[obj_id];
  6955. //
  6956. // if (obj && (inst_id >= obj->instances.size())) {
  6957. // BOOST_LOG_TRIVIAL(warning) << __FUNCTION__ << ":" << __LINE__ << boost::format("invalid instance id %1%\n")%inst_id;
  6958. // }
  6959. // else if (obj){
  6960. // inst = obj->instances[inst_id];
  6961. // if (use_loaded_id && (inst->loaded_id > 0))
  6962. // identify_id = inst->loaded_id;
  6963. // else
  6964. // identify_id = inst->id().id;
  6965. // }
  6966. // obj_id = objects_data.find(obj)->second.object_id;
  6967. //
  6968. // stream << " <" << METADATA_TAG << " " << KEY_ATTR << "=\"" << OBJECT_ID_ATTR << "\" " << VALUE_ATTR << "=\"" << obj_id << "\"/>\n";
  6969. // stream << " <" << METADATA_TAG << " " << KEY_ATTR << "=\"" << INSTANCEID_ATTR << "\" " << VALUE_ATTR << "=\"" << inst_id << "\"/>\n";
  6970. // stream << " <" << METADATA_TAG << " " << KEY_ATTR << "=\"" << IDENTIFYID_ATTR << "\" " << VALUE_ATTR << "=\"" << identify_id << "\"/>\n";
  6971. // stream << " </" << INSTANCE_TAG << ">\n";
  6972. // }
  6973. // }
  6974. // stream << " </" << PLATE_TAG << ">\n";
  6975. // }
  6976. // }
  6977. //
  6978. // // write model rels
  6979. // if (save_gcode)
  6980. // _add_relationships_file_to_archive(archive, BBS_MODEL_CONFIG_RELS_FILE, gcode_paths, {"http://schemas.bambulab.com/package/2021/gcode"}, Slic3r::PackingTemporaryData(), export_plate_idx);
  6981. //
  6982. // if (!m_skip_model) {
  6983. // //BBS: store assemble related info
  6984. // stream << " <" << ASSEMBLE_TAG << ">\n";
  6985. // for (const ObjectToObjectDataMap::value_type& obj_metadata : objects_data) {
  6986. // auto object_data = obj_metadata.second;
  6987. // const ModelObject* obj = object_data.object;
  6988. // if (obj != nullptr) {
  6989. // for (int instance_idx = 0; instance_idx < obj->instances.size(); ++instance_idx) {
  6990. // if (obj->instances[instance_idx]->is_assemble_initialized()) {
  6991. // stream << " <" << ASSEMBLE_ITEM_TAG << " " << OBJECT_ID_ATTR << "=\"" << object_data.object_id << "\" ";
  6992. // stream << INSTANCEID_ATTR << "=\"" << instance_idx << "\" " << TRANSFORM_ATTR << "=\"";
  6993. // for (unsigned c = 0; c < 4; ++c) {
  6994. // for (unsigned r = 0; r < 3; ++r) {
  6995. // const Transform3d assemble_trans = obj->instances[instance_idx]->get_assemble_transformation().get_matrix();
  6996. // stream << assemble_trans(r, c);
  6997. // if (r != 2 || c != 3)
  6998. // stream << " ";
  6999. // }
  7000. // }
  7001. //
  7002. // stream << "\" ";
  7003. //
  7004. // stream << OFFSET_ATTR << "=\"";
  7005. // const Vec3d ofs2ass = obj->instances[instance_idx]->get_offset_to_assembly();
  7006. // stream << ofs2ass(0) << " " << ofs2ass(1) << " " << ofs2ass(2);
  7007. // stream << "\" />\n";
  7008. // }
  7009. // }
  7010. // }
  7011. // }
  7012. // stream << " </" << ASSEMBLE_TAG << ">\n";
  7013. // }
  7014. //
  7015. // stream << "</" << CONFIG_TAG << ">\n";
  7016. //
  7017. // std::string out = stream.str();
  7018. // if (!mz_zip_writer_add_mem(&archive, BBS_MODEL_CONFIG_FILE.c_str(), (const void*)out.data(), out.length(), MZ_DEFAULT_COMPRESSION)) {
  7019. // BOOST_LOG_TRIVIAL(error) << __FUNCTION__ << ":" << __LINE__ << boost::format("Unable to add model config file to archive\n");
  7020. // add_error("Unable to add model config file to archive");
  7021. // return false;
  7022. // }
  7023. //
  7024. // return true;
  7025. // }
  7026. //
  7027. // bool _BBS_3MF_Exporter::_add_slice_info_config_file_to_archive(mz_zip_archive& archive, const Model& model, PlateDataPtrs& plate_data_list, const ObjectToObjectDataMap &objects_data, const DynamicPrintConfig& config)
  7028. // {
  7029. // std::stringstream stream;
  7030. // // Store mesh transformation in full precision, as the volumes are stored transformed and they need to be transformed back
  7031. // // when loaded as accurately as possible.
  7032. // stream << std::setprecision(std::numeric_limits<double>::max_digits10);
  7033. // stream << std::setiosflags(std::ios::fixed) << std::setprecision(2);
  7034. // stream << "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n";
  7035. // stream << "<" << CONFIG_TAG << ">\n";
  7036. //
  7037. // // save slice header for debug
  7038. // stream << " <" << SLICE_HEADER_TAG << ">\n";
  7039. // stream << " <" << SLICE_HEADER_ITEM_TAG << " " << KEY_ATTR << "=\"" << "X-BBL-Client-Type" << "\" " << VALUE_ATTR << "=\"" << "slicer" << "\"/>\n";
  7040. // stream << " <" << SLICE_HEADER_ITEM_TAG << " " << KEY_ATTR << "=\"" << "X-BBL-Client-Version" << "\" " << VALUE_ATTR << "=\"" << convert_to_full_version(SoftFever_VERSION) << "\"/>\n";
  7041. // stream << " </" << SLICE_HEADER_TAG << ">\n";
  7042. //
  7043. // for (unsigned int i = 0; i < (unsigned int)plate_data_list.size(); ++i)
  7044. // {
  7045. // PlateData* plate_data = plate_data_list[i];
  7046. // //int instance_size = plate_data->objects_and_instances.size();
  7047. //
  7048. // if (plate_data != nullptr && plate_data->is_sliced_valid) {
  7049. // stream << " <" << PLATE_TAG << ">\n";
  7050. // //plate index
  7051. // stream << " <" << METADATA_TAG << " " << KEY_ATTR << "=\"" << PLATE_IDX_ATTR << "\" " << VALUE_ATTR << "=\"" << plate_data->plate_index + 1 << "\"/>\n";
  7052. //
  7053. // int timelapse_type = int(config.opt_enum<TimelapseType>("timelapse_type"));
  7054. // for (auto it = plate_data->warnings.begin(); it != plate_data->warnings.end(); it++) {
  7055. // if (it->msg == NOT_GENERATE_TIMELAPSE) {
  7056. // timelapse_type = -1;
  7057. // break;
  7058. // }
  7059. // }
  7060. // stream << " <" << METADATA_TAG << " " << KEY_ATTR << "=\"" << PRINTER_MODEL_ID_ATTR << "\" " << VALUE_ATTR << "=\"" << plate_data->printer_model_id << "\"/>\n";
  7061. // stream << " <" << METADATA_TAG << " " << KEY_ATTR << "=\"" << NOZZLE_DIAMETERS_ATTR << "\" " << VALUE_ATTR << "=\"" << plate_data->nozzle_diameters << "\"/>\n";
  7062. // stream << " <" << METADATA_TAG << " " << KEY_ATTR << "=\"" << TIMELAPSE_TYPE_ATTR << "\" " << VALUE_ATTR << "=\"" << timelapse_type << "\"/>\n";
  7063. // //stream << " <" << METADATA_TAG << " " << KEY_ATTR << "=\"" << TIMELAPSE_ERROR_CODE_ATTR << "\" " << VALUE_ATTR << "=\"" << plate_data->timelapse_warning_code << "\"/>\n";
  7064. // stream << " <" << METADATA_TAG << " " << KEY_ATTR << "=\"" << SLICE_PREDICTION_ATTR << "\" " << VALUE_ATTR << "=\"" << plate_data->get_gcode_prediction_str() << "\"/>\n";
  7065. // stream << " <" << METADATA_TAG << " " << KEY_ATTR << "=\"" << SLICE_WEIGHT_ATTR << "\" " << VALUE_ATTR << "=\"" << plate_data->get_gcode_weight_str() << "\"/>\n";
  7066. // stream << " <" << METADATA_TAG << " " << KEY_ATTR << "=\"" << OUTSIDE_ATTR << "\" " << VALUE_ATTR << "=\"" << std::boolalpha<< plate_data->toolpath_outside << "\"/>\n";
  7067. // stream << " <" << METADATA_TAG << " " << KEY_ATTR << "=\"" << SUPPORT_USED_ATTR << "\" " << VALUE_ATTR << "=\"" << std::boolalpha<< plate_data->is_support_used << "\"/>\n";
  7068. // stream << " <" << METADATA_TAG << " " << KEY_ATTR << "=\"" << LABEL_OBJECT_ENABLED_ATTR << "\" " << VALUE_ATTR << "=\"" << std::boolalpha<< plate_data->is_label_object_enabled << "\"/>\n";
  7069. //
  7070. // for (auto it = plate_data->objects_and_instances.begin(); it != plate_data->objects_and_instances.end(); it++)
  7071. // {
  7072. // int obj_id = it->first;
  7073. // int inst_id = it->second;
  7074. // int identify_id = 0;
  7075. // ModelObject* obj = NULL;
  7076. // ModelInstance* inst = NULL;
  7077. // if (obj_id >= model.objects.size()) {
  7078. // BOOST_LOG_TRIVIAL(warning) << __FUNCTION__ << ":" << __LINE__ << boost::format("invalid object id %1%\n")%obj_id;
  7079. // continue;
  7080. // }
  7081. // obj = model.objects[obj_id];
  7082. //
  7083. // if (obj && (inst_id >= obj->instances.size())) {
  7084. // BOOST_LOG_TRIVIAL(warning) << __FUNCTION__ << ":" << __LINE__ << boost::format("invalid instance id %1%\n")%inst_id;
  7085. // continue;
  7086. // }
  7087. // inst = obj->instances[inst_id];
  7088. // if (m_use_loaded_id && (inst->loaded_id > 0))
  7089. // identify_id = inst->loaded_id;
  7090. // else
  7091. // identify_id = inst->id().id;
  7092. // bool skipped = std::find(plate_data->skipped_objects.begin(), plate_data->skipped_objects.end(), identify_id) !=
  7093. // plate_data->skipped_objects.end();
  7094. // stream << " <" << OBJECT_TAG << " " << IDENTIFYID_ATTR << "=\"" << std::to_string(identify_id) << "\" " << NAME_ATTR << "=\"" << xml_escape(obj->name)
  7095. // << "\" " << SKIPPED_ATTR << "=\"" << (skipped ? "true" : "false")
  7096. // << "\" />\n";
  7097. // }
  7098. //
  7099. // for (auto it = plate_data->slice_filaments_info.begin(); it != plate_data->slice_filaments_info.end(); it++)
  7100. // {
  7101. // stream << " <" << FILAMENT_TAG << " " << FILAMENT_ID_TAG << "=\"" << std::to_string(it->id + 1) << "\" "
  7102. // << FILAMENT_TYPE_TAG << "=\"" << it->type << "\" "
  7103. // << FILAMENT_COLOR_TAG << "=\"" << it->color << "\" "
  7104. // << FILAMENT_USED_M_TAG << "=\"" << it->used_m << "\" "
  7105. // << FILAMENT_USED_G_TAG << "=\"" << it->used_g << "\" />\n";
  7106. // }
  7107. //
  7108. // for (auto it = plate_data->warnings.begin(); it != plate_data->warnings.end(); it++) {
  7109. // stream << " <" << SLICE_WARNING_TAG << " msg=\"" << it->msg << "\" level=\"" << std::to_string(it->level) << "\" error_code =\"" << it->error_code << "\" />\n";
  7110. // }
  7111. // stream << " </" << PLATE_TAG << ">\n";
  7112. // }
  7113. // }
  7114. // stream << "</" << CONFIG_TAG << ">\n";
  7115. //
  7116. // std::string out = stream.str();
  7117. //
  7118. // if (!mz_zip_writer_add_mem(&archive, SLICE_INFO_CONFIG_FILE.c_str(), (const void*)out.data(), out.length(), MZ_DEFAULT_COMPRESSION)) {
  7119. // add_error("Unable to add model config file to archive");
  7120. // BOOST_LOG_TRIVIAL(error) << __FUNCTION__ << ":" << __LINE__ << boost::format(", store slice-info to 3mf, length %1%, failed\n") % out.length();
  7121. // return false;
  7122. // }
  7123. //
  7124. // return true;
  7125. // }
  7126. //bool _BBS_3MF_Exporter::_add_gcode_file_to_archive(mz_zip_archive& archive, const Model& model, PlateDataPtrs& plate_data_list, Export3mfProgressFn proFn)
  7127. //{
  7128. // bool result = true;
  7129. // bool cb_cancel = false;
  7130. //
  7131. // PlateDataPtrs plate_data_list2;
  7132. // for (unsigned int i = 0; i < (unsigned int)plate_data_list.size(); ++i)
  7133. // {
  7134. // if (proFn) {
  7135. // proFn(EXPORT_STAGE_ADD_GCODE, i, plate_data_list.size(), cb_cancel);
  7136. // if (cb_cancel)
  7137. // return false;
  7138. // }
  7139. //
  7140. // PlateData* plate_data = plate_data_list[i];
  7141. // if (!plate_data->gcode_file.empty() && plate_data->is_sliced_valid && boost::filesystem::exists(plate_data->gcode_file)) {
  7142. // plate_data_list2.push_back(plate_data);
  7143. // }
  7144. // }
  7145. //
  7146. // boost::mutex mutex;
  7147. // tbb::parallel_for(tbb::blocked_range<size_t>(0, plate_data_list2.size(), 1), [this, &plate_data_list2, &root_archive = archive, &mutex, &result](const tbb::blocked_range<size_t>& range) {
  7148. // for (int i = range.begin(); i < range.end(); ++i) {
  7149. // PlateData* plate_data = plate_data_list2[i];
  7150. // auto src_gcode_file = plate_data->gcode_file;
  7151. // std::string gcode_in_3mf = (boost::format(GCODE_FILE_FORMAT) % (plate_data->plate_index + 1)).str();
  7152. //
  7153. // plate_data->gcode_file = gcode_in_3mf;
  7154. // mz_zip_archive archive;
  7155. // mz_zip_writer_staged_context context;
  7156. // mz_zip_zero_struct(&archive);
  7157. // mz_zip_writer_init_heap(&archive, 0, 1024 * 1024);
  7158. // {
  7159. // mz_zip_writer_add_staged_open(&archive, &context, gcode_in_3mf.c_str(), m_zip64 ? (uint64_t(1) << 30) * 16 : (uint64_t(1) << 32) - 1, nullptr, nullptr, 0,
  7160. // MZ_DEFAULT_COMPRESSION, nullptr, 0, nullptr, 0);
  7161. // boost::filesystem::path src_gcode_path(src_gcode_file);
  7162. // if (!boost::filesystem::exists(src_gcode_path)) {
  7163. // BOOST_LOG_TRIVIAL(error) << "Gcode is missing, filename = " << src_gcode_file;
  7164. // result = false;
  7165. // }
  7166. // boost::filesystem::ifstream ifs(src_gcode_file, std::ios::binary);
  7167. // std::string buf(64 * 1024, 0);
  7168. // while (ifs) {
  7169. // ifs.read(buf.data(), buf.size());
  7170. // mz_zip_writer_add_staged_data(&context, buf.data(), ifs.gcount());
  7171. // }
  7172. // mz_zip_writer_add_staged_finish(&context);
  7173. // }
  7174. // void *ppBuf; size_t pSize;
  7175. // mz_zip_writer_finalize_heap_archive(&archive, &ppBuf, &pSize);
  7176. // mz_zip_writer_end(&archive);
  7177. // mz_zip_zero_struct(&archive);
  7178. // mz_zip_reader_init_mem(&archive, ppBuf, pSize, 0);
  7179. // {
  7180. // boost::unique_lock l(mutex);
  7181. // mz_zip_writer_add_from_zip_reader(&root_archive, &archive, 0);
  7182. // }
  7183. // mz_zip_reader_end(&archive);
  7184. // BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << ":" <<__LINE__ << boost::format(", store %1% to 3mf %2%\n") % src_gcode_file % gcode_in_3mf;
  7185. // }
  7186. // });
  7187. // return result;
  7188. //}
  7189. //
  7190. //bool _BBS_3MF_Exporter::_add_custom_gcode_per_print_z_file_to_archive(mz_zip_archive& archive, Model& model, const DynamicPrintConfig* config)
  7191. //{
  7192. // //BBS: add plate tree related logic
  7193. // std::string out = "";
  7194. // bool has_custom_gcode = false;
  7195. // pt::ptree tree;
  7196. // pt::ptree& main_tree = tree.add("custom_gcodes_per_layer", "");
  7197. // for (auto custom_gcodes : model.plates_custom_gcodes) {
  7198. // has_custom_gcode = true;
  7199. // pt::ptree& plate_tree = main_tree.add("plate", "");
  7200. // pt::ptree& plate_idx_tree = plate_tree.add("plate_info", "");
  7201. // plate_idx_tree.put("<xmlattr>.id", custom_gcodes.first + 1);
  7202. //
  7203. // // store data of custom_gcode_per_print_z
  7204. // for (const CustomGCode::Item& code : custom_gcodes.second.gcodes) {
  7205. // pt::ptree& code_tree = plate_tree.add("layer", "");
  7206. // code_tree.put("<xmlattr>.top_z", code.print_z);
  7207. // code_tree.put("<xmlattr>.type", static_cast<int>(code.type));
  7208. // code_tree.put("<xmlattr>.extruder", code.extruder);
  7209. // code_tree.put("<xmlattr>.color", code.color);
  7210. // code_tree.put("<xmlattr>.extra", code.extra);
  7211. //
  7212. // //BBS
  7213. // std::string gcode = //code.type == CustomGCode::ColorChange ? config->opt_string("color_change_gcode") :
  7214. // code.type == CustomGCode::PausePrint ? config->opt_string("machine_pause_gcode") :
  7215. // code.type == CustomGCode::Template ? config->opt_string("template_custom_gcode") :
  7216. // code.type == CustomGCode::ToolChange ? "tool_change" : code.extra;
  7217. // code_tree.put("<xmlattr>.gcode", gcode);
  7218. // }
  7219. //
  7220. // pt::ptree& mode_tree = plate_tree.add("mode", "");
  7221. // // store mode of a custom_gcode_per_print_z
  7222. // mode_tree.put("<xmlattr>.value", custom_gcodes.second.mode == CustomGCode::Mode::SingleExtruder ? CustomGCode::SingleExtruderMode :
  7223. // custom_gcodes.second.mode == CustomGCode::Mode::MultiAsSingle ? CustomGCode::MultiAsSingleMode :
  7224. // CustomGCode::MultiExtruderMode);
  7225. //
  7226. // }
  7227. // if (has_custom_gcode) {
  7228. // std::ostringstream oss;
  7229. // boost::property_tree::write_xml(oss, tree);
  7230. // out = oss.str();
  7231. //
  7232. // // Post processing("beautification") of the output string
  7233. // boost::replace_all(out, "><", ">\n<");
  7234. // }
  7235. //
  7236. // if (!out.empty()) {
  7237. // if (!mz_zip_writer_add_mem(&archive, CUSTOM_GCODE_PER_PRINT_Z_FILE.c_str(), (const void*)out.data(), out.length(), MZ_DEFAULT_COMPRESSION)) {
  7238. // add_error("Unable to add custom Gcodes per print_z file to archive");
  7239. // BOOST_LOG_TRIVIAL(error) << __FUNCTION__ << ":" << __LINE__ << boost::format(", Unable to add custom Gcodes per print_z file to archive\n");
  7240. // return false;
  7241. // }
  7242. // }
  7243. //
  7244. // return true;
  7245. //}
  7246. //
  7247. //bool _BBS_3MF_Exporter::_add_auxiliary_dir_to_archive(mz_zip_archive &archive, const std::string &aux_dir, PackingTemporaryData &data)
  7248. //{
  7249. // bool result = true;
  7250. //
  7251. // if (aux_dir.empty()) {
  7252. // //no accessory directories
  7253. // return result;
  7254. // }
  7255. //
  7256. // boost::filesystem::path dir = boost::filesystem::path(aux_dir);
  7257. // if (!boost::filesystem::exists(dir))
  7258. // {
  7259. // //no accessory directories
  7260. // return result;
  7261. // }
  7262. //
  7263. // static std::string const nocomp_exts[] = {".png", ".jpg", ".mp4", ".jpeg"};
  7264. // std::deque<boost::filesystem::path> directories({dir});
  7265. // int root_dir_len = dir.string().length() + 1;
  7266. // //boost file access
  7267. // while (!directories.empty()) {
  7268. // boost::filesystem::directory_iterator iterator(directories.front());
  7269. // directories.pop_front();
  7270. // for (auto &dir_entry : iterator)
  7271. // {
  7272. // std::string src_file;
  7273. // std::string dst_in_3mf;
  7274. // if (boost::filesystem::is_directory(dir_entry.path()))
  7275. // {
  7276. // directories.push_back(dir_entry.path());
  7277. // continue;
  7278. // }
  7279. // if (boost::filesystem::is_regular_file(dir_entry.path()) && !m_skip_auxiliary)
  7280. // {
  7281. // src_file = dir_entry.path().string();
  7282. // dst_in_3mf = dir_entry.path().string();
  7283. // dst_in_3mf.replace(0, root_dir_len, AUXILIARY_DIR);
  7284. // std::replace(dst_in_3mf.begin(), dst_in_3mf.end(), '\\', '/');
  7285. // if (_3MF_COVER_FILE.compare(1, _3MF_COVER_FILE.length() - 1, dst_in_3mf) == 0) {
  7286. // data._3mf_thumbnail = dst_in_3mf;
  7287. // } else if (m_thumbnail_small.compare(1, m_thumbnail_small.length() - 1, dst_in_3mf) == 0) {
  7288. // data._3mf_printer_thumbnail_small = dst_in_3mf;
  7289. // if (m_thumbnail_middle == m_thumbnail_small) data._3mf_printer_thumbnail_middle = dst_in_3mf;
  7290. // } else if (m_thumbnail_middle.compare(1, m_thumbnail_middle.length() - 1, dst_in_3mf) == 0) {
  7291. // data._3mf_printer_thumbnail_middle = dst_in_3mf;
  7292. // }
  7293. // result &= _add_file_to_archive(archive, dst_in_3mf, src_file);
  7294. // }
  7295. // }
  7296. // }
  7297. //
  7298. // return result;
  7299. //}
  7300. //
  7301. //// Perform conversions based on the config values available.
  7302. ////FIXME provide a version of PrusaSlicer that stored the project file (3MF).
  7303. //static void handle_legacy_project_loaded(unsigned int version_project_file, DynamicPrintConfig& config)
  7304. //{
  7305. // if (! config.has("brim_object_gap")) {
  7306. // if (auto *opt_elephant_foot = config.option<ConfigOptionFloat>("elefant_foot_compensation", false); opt_elephant_foot) {
  7307. // // Conversion from older PrusaSlicer which applied brim separation equal to elephant foot compensation.
  7308. // auto *opt_brim_separation = config.option<ConfigOptionFloat>("brim_object_gap", true);
  7309. // opt_brim_separation->value = opt_elephant_foot->value;
  7310. // }
  7311. // }
  7312. //}
  7313. //
  7314. //// backup backgroud thread to dispatch tasks and coperate with ui thread
  7315. //class _BBS_Backup_Manager
  7316. //{
  7317. //public:
  7318. // static _BBS_Backup_Manager& get() {
  7319. // static _BBS_Backup_Manager m;
  7320. // return m;
  7321. // }
  7322. //
  7323. // void set_post_callback(std::function<void(int)> c) {
  7324. // boost::lock_guard lock(m_mutex);
  7325. // m_post_callback = c;
  7326. // }
  7327. //
  7328. // void run_ui_tasks() {
  7329. // std::deque<Task> tasks;
  7330. // {
  7331. // boost::lock_guard lock(m_mutex);
  7332. // std::swap(tasks, m_ui_tasks);
  7333. // }
  7334. // for (auto& t : tasks)
  7335. // {
  7336. // process_ui_task(t);
  7337. // }
  7338. // }
  7339. //
  7340. // void push_object_gaurd(ModelObject& object) {
  7341. // m_gaurd_objects.push_back(std::make_pair(&object, 0));
  7342. // }
  7343. //
  7344. // void pop_object_gaurd() {
  7345. // auto object = m_gaurd_objects.back();
  7346. // m_gaurd_objects.pop_back();
  7347. // if (object.second)
  7348. // add_object_mesh(*object.first);
  7349. // }
  7350. //
  7351. // void add_object_mesh(ModelObject& object) {
  7352. // for (auto& g : m_gaurd_objects) {
  7353. // if (g.first == &object) {
  7354. // ++g.second;
  7355. // return;
  7356. // }
  7357. // }
  7358. // // clone object
  7359. // auto model = object.get_model();
  7360. // auto o = m_temp_model.add_object(object);
  7361. // int backup_id = model->get_object_backup_id(object);
  7362. // push_task({ AddObject, (size_t) backup_id, object.get_model()->get_backup_path(), o, 1 });
  7363. // }
  7364. //
  7365. // void remove_object_mesh(ModelObject& object) {
  7366. // push_task({ RemoveObject, object.id().id, object.get_model()->get_backup_path() });
  7367. // }
  7368. //
  7369. // void backup_soon() {
  7370. // boost::lock_guard lock(m_mutex);
  7371. // m_other_changes_backup = true;
  7372. // m_tasks.push_back({ Backup, 0, std::string(), nullptr, ++m_task_seq });
  7373. // m_cond.notify_all();
  7374. // }
  7375. //
  7376. // void remove_backup(Model& model, bool removeAll) {
  7377. // BOOST_LOG_TRIVIAL(info)
  7378. // << "remove_backup " << model.get_backup_path() << ", " << removeAll;
  7379. // std::deque<Task> canceled_tasks;
  7380. // boost::unique_lock lock(m_mutex);
  7381. // if (removeAll && model.is_need_backup()) {
  7382. // // running task may not be canceled
  7383. // for (auto & t : m_ui_tasks)
  7384. // canceled_tasks.push_back(t);
  7385. // for (auto & t : m_tasks)
  7386. // canceled_tasks.push_back(t);
  7387. // m_ui_tasks.clear();
  7388. // m_tasks.clear();
  7389. // }
  7390. // m_tasks.push_back({ RemoveBackup, model.id().id, model.get_backup_path(), nullptr, removeAll });
  7391. // ++m_task_seq;
  7392. // if (model.is_need_backup()) {
  7393. // m_other_changes = false;
  7394. // m_other_changes_backup = false;
  7395. // }
  7396. // m_cond.notify_all();
  7397. // lock.unlock();
  7398. // for (auto& t : canceled_tasks) {
  7399. // process_ui_task(t, true);
  7400. // }
  7401. // }
  7402. //
  7403. // void set_interval(long n) {
  7404. // boost::lock_guard lock(m_mutex);
  7405. // BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << " entry, and last interval is: " << m_interval;
  7406. // m_next_backup -= boost::posix_time::seconds(m_interval);
  7407. // m_interval = n;
  7408. // m_next_backup += boost::posix_time::seconds(m_interval);
  7409. // m_cond.notify_all();
  7410. // BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << " exit, and new interval is: " << m_interval;
  7411. // }
  7412. //
  7413. // void put_other_changes()
  7414. // {
  7415. // BOOST_LOG_TRIVIAL(info) << "put_other_changes";
  7416. // m_other_changes = true;
  7417. // m_other_changes_backup = true;
  7418. // }
  7419. //
  7420. // void clear_other_changes(bool backup)
  7421. // {
  7422. // if (backup)
  7423. // m_other_changes_backup = false;
  7424. // else
  7425. // m_other_changes = false;
  7426. // }
  7427. //
  7428. // bool has_other_changes(bool backup)
  7429. // {
  7430. // return backup ? m_other_changes_backup : m_other_changes;
  7431. // }
  7432. //
  7433. //private:
  7434. // enum TaskType {
  7435. // None,
  7436. // Backup, // this task is working as response in ui thread
  7437. // AddObject,
  7438. // RemoveObject,
  7439. // RemoveBackup,
  7440. // Exit
  7441. // };
  7442. // struct Task {
  7443. // TaskType type;
  7444. // size_t id = 0;
  7445. // std::string path;
  7446. // ModelObject* object = nullptr;
  7447. // union {
  7448. // size_t delay = 0; // delay sequence, only last task is delayed
  7449. // size_t sequence;
  7450. // bool removeAll;
  7451. // };
  7452. // friend bool operator==(Task const& l, Task const& r) {
  7453. // return l.type == r.type && l.id == r.id;
  7454. // }
  7455. // std::string to_string() const {
  7456. // constexpr char const *type_names[] = {"None",
  7457. // "Backup",
  7458. // "AddObject",
  7459. // "RemoveObject",
  7460. // "RemoveBackup",
  7461. // "Exit"};
  7462. // std::ostringstream os;
  7463. // os << "{ type:" << type_names[type] << ", id:" << id
  7464. // << ", path:" << path
  7465. // << ", object:" << (object ? object->id().id : 0) << ", extra:" << delay << "}";
  7466. // return os.str();
  7467. // }
  7468. // };
  7469. //
  7470. // struct timer {
  7471. // timer(char const * msg) : msg(msg), start(boost::posix_time::microsec_clock::universal_time()) { }
  7472. // ~timer() {
  7473. //#ifdef __WIN32__
  7474. // auto end = boost::posix_time::microsec_clock::universal_time();
  7475. // int duration = (int)(end - start).total_milliseconds();
  7476. // char buf[20];
  7477. // OutputDebugStringA(msg);
  7478. // OutputDebugStringA(": ");
  7479. // OutputDebugStringA(itoa(duration, buf, 10));
  7480. // OutputDebugStringA("\n");
  7481. //#endif
  7482. // }
  7483. // char const* msg;
  7484. // boost::posix_time::ptime start;
  7485. // };
  7486. //private:
  7487. // _BBS_Backup_Manager() {
  7488. // BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << " inital and interval = " << m_interval;
  7489. // m_next_backup = boost::get_system_time() + boost::posix_time::seconds(m_interval);
  7490. // boost::unique_lock lock(m_mutex);
  7491. // m_thread = std::move(boost::thread(boost::ref(*this)));
  7492. // }
  7493. //
  7494. // ~_BBS_Backup_Manager() {
  7495. // push_task({Exit});
  7496. // m_thread.join();
  7497. // }
  7498. //
  7499. // void push_task(Task const & t) {
  7500. // boost::unique_lock lock(m_mutex);
  7501. // if (t.delay && !m_tasks.empty() && m_tasks.back() == t) {
  7502. // auto t2 = m_tasks.back();
  7503. // m_tasks.back() = t;
  7504. // m_tasks.back().delay = t2.delay + 1;
  7505. // m_cond.notify_all();
  7506. // lock.unlock();
  7507. // process_ui_task(t2);
  7508. // }
  7509. // else {
  7510. // m_tasks.push_back(t);
  7511. // ++m_task_seq;
  7512. // m_cond.notify_all();
  7513. // }
  7514. // }
  7515. //
  7516. // void process_ui_task(Task& t, bool canceled = false) {
  7517. // BOOST_LOG_TRIVIAL(info) << "process_ui_task" << t.to_string() << " and interval = " << m_interval;
  7518. // switch (t.type) {
  7519. // case Backup: {
  7520. // if (canceled)
  7521. // break;
  7522. // std::function<void(int)> callback;
  7523. // boost::unique_lock lock(m_mutex);
  7524. // if (m_task_seq != t.sequence) {
  7525. // if (find(m_tasks.begin(), m_tasks.end(), Task{ Backup }) == m_tasks.end()) {
  7526. // t.sequence = ++m_task_seq; // may has pending tasks, retry later
  7527. // m_tasks.push_back(t);
  7528. // m_cond.notify_all();
  7529. // }
  7530. // break;
  7531. // }
  7532. // callback = m_post_callback;
  7533. // lock.unlock();
  7534. // {
  7535. // timer t("backup cost");
  7536. // try {
  7537. // if (callback) callback(1);
  7538. // } catch (...) {}
  7539. // }
  7540. // m_other_changes_backup = false;
  7541. // break;
  7542. // }
  7543. // case AddObject:
  7544. // m_temp_model.delete_object(t.object);
  7545. // break;
  7546. // case RemoveBackup:
  7547. // if (t.removeAll) {
  7548. // try {
  7549. // boost::filesystem::remove(t.path + "/lock.txt");
  7550. // boost::filesystem::remove_all(t.path);
  7551. // BOOST_LOG_TRIVIAL(info) << "process_ui_task: remove all of backup path " << t.path;
  7552. // } catch (std::exception &ex) {
  7553. // BOOST_LOG_TRIVIAL(error) << "process_ui_task: failed to remove backup path" << t.path << ": " << ex.what();
  7554. // }
  7555. // }
  7556. // break;
  7557. // }
  7558. // }
  7559. //
  7560. // void process_task(Task& t) {
  7561. // BOOST_LOG_TRIVIAL(info) << "process_task" << t.to_string() << " and interval = " << m_interval;
  7562. // switch (t.type) {
  7563. // case Backup:
  7564. // // do it in response
  7565. // break;
  7566. // case AddObject: {
  7567. // {
  7568. // CNumericLocalesSetter locales_setter;
  7569. // _BBS_3MF_Exporter e;
  7570. // e.save_object_mesh(t.path, *t.object, (int) t.id);
  7571. // // response to delete cloned object
  7572. // }
  7573. // break;
  7574. // }
  7575. // case RemoveObject: {
  7576. // boost::system::error_code ec;
  7577. // boost::filesystem::remove(t.path + "/mesh_" + boost::lexical_cast<std::string>(t.id) + ".xml", ec);
  7578. // t.type = None;
  7579. // break;
  7580. // }
  7581. // case RemoveBackup: {
  7582. // try {
  7583. // boost::system::error_code ec;
  7584. // boost::filesystem::remove(t.path + "/.3mf", ec);
  7585. // // We Saved with SplitModel now, so we can safe delete these sub models.
  7586. // boost::filesystem::remove_all(t.path + "/3D/Objects");
  7587. // boost::filesystem::create_directory(t.path + "/3D/Objects");
  7588. // }
  7589. // catch (...) {}
  7590. // }
  7591. // }
  7592. // }
  7593. //
  7594. //public:
  7595. // void operator()() {
  7596. // boost::unique_lock lock(m_mutex);
  7597. // while (true)
  7598. // {
  7599. // while (m_tasks.empty()) {
  7600. // if (m_interval > 0)
  7601. // m_cond.timed_wait(lock, m_next_backup);
  7602. // else
  7603. // m_cond.wait(lock);
  7604. // if (m_interval > 0 && boost::get_system_time() > m_next_backup) {
  7605. // m_tasks.push_back({ Backup, 0, std::string(), nullptr, ++m_task_seq });
  7606. // m_next_backup += boost::posix_time::seconds(m_interval);
  7607. // // Maybe wakeup from power sleep
  7608. // if (m_next_backup < boost::get_system_time())
  7609. // m_next_backup = boost::get_system_time() + boost::posix_time::seconds(m_interval);
  7610. // }
  7611. // }
  7612. // Task t = m_tasks.front();
  7613. // if (t.type == Exit) break;
  7614. // if (t.object && t.delay) {
  7615. // if (!delay_task(t, lock))
  7616. // continue;
  7617. // }
  7618. // m_tasks.pop_front();
  7619. // auto callback = m_post_callback;
  7620. // lock.unlock();
  7621. // process_task(t);
  7622. // lock.lock();
  7623. // if (t.type > None) {
  7624. // m_ui_tasks.push_back(t);
  7625. // if (m_ui_tasks.size() == 1 && callback)
  7626. // callback(0);
  7627. // }
  7628. // }
  7629. // }
  7630. //
  7631. // bool delay_task(Task& t, boost::unique_lock<boost::mutex> & lock) {
  7632. // // delay last task for 3 seconds after last modify
  7633. // auto now = boost::get_system_time();
  7634. // auto delay_expire = now + boost::posix_time::seconds(10); // must not delay over this time
  7635. // auto wait = now + boost::posix_time::seconds(3);
  7636. // while (true) {
  7637. // m_cond.timed_wait(lock, wait);
  7638. // // Only delay when it's the only-one task
  7639. // if (m_tasks.size() != 1 || m_tasks.front().delay == t.delay)
  7640. // break;
  7641. // t.delay = m_tasks.front().delay;
  7642. // now = boost::get_system_time();
  7643. // if (now >= delay_expire)
  7644. // break;
  7645. // wait = now + boost::posix_time::seconds(3);
  7646. // if (wait > delay_expire)
  7647. // wait = delay_expire;
  7648. // };
  7649. // // task maybe canceled
  7650. // if (m_tasks.empty())
  7651. // return false;
  7652. // t = m_tasks.front();
  7653. // return true;
  7654. // }
  7655. //
  7656. //private:
  7657. // boost::mutex m_mutex;
  7658. // boost::condition_variable m_cond;
  7659. // std::deque<Task> m_tasks;
  7660. // std::deque<Task> m_ui_tasks;
  7661. // size_t m_task_seq = 0;
  7662. // // param 0: should call run_ui_tasks
  7663. // // param 1: should backup current project
  7664. // std::function<void(int)> m_post_callback;
  7665. // long m_interval = 1 * 60;
  7666. // boost::system_time m_next_backup;
  7667. // Model m_temp_model; // visit only in main thread
  7668. // bool m_other_changes = false; // visit only in main thread
  7669. // bool m_other_changes_backup = false; // visit only in main thread
  7670. // std::vector<std::pair<ModelObject*, size_t>> m_gaurd_objects;
  7671. // boost::thread m_thread;
  7672. //};
  7673. //
  7674. //
  7675. ////BBS: add plate data list related logic
  7676. bool load_bbs_3mf(const char* path, DynamicPrintConfig* config, ConfigSubstitutionContext* config_substitutions, Model* model, PlateDataPtrs* plate_data_list, std::vector<Preset*>* project_presets,
  7677. bool* is_bbl_3mf, Semver* file_version, Import3mfProgressFn proFn, LoadStrategy strategy, /*BBLProject *project,*/ int plate_id)
  7678. {
  7679. if (path == nullptr || config == nullptr || model == nullptr)
  7680. return false;
  7681. // All import should use "C" locales for number formatting.
  7682. CNumericLocalesSetter locales_setter;
  7683. _BBS_3MF_Importer importer;
  7684. bool res = importer.load_model_from_file(path, *model, *plate_data_list, *project_presets, *config, *config_substitutions, strategy, *is_bbl_3mf, *file_version, proFn, /*project,*/ plate_id);
  7685. importer.log_errors();
  7686. //BBS: remove legacy project logic currently
  7687. //handle_legacy_project_loaded(importer.version(), *config);
  7688. return res;
  7689. }
  7690. //
  7691. //std::string bbs_3mf_get_thumbnail(const char *path)
  7692. //{
  7693. // _BBS_3MF_Importer importer;
  7694. // std::string data;
  7695. // bool res = importer.get_thumbnail(path, data);
  7696. // if (!res) importer.log_errors();
  7697. // return data;
  7698. //}
  7699. //
  7700. //bool load_gcode_3mf_from_stream(std::istream &data, DynamicPrintConfig *config, Model *model, PlateDataPtrs *plate_data_list, Semver *file_version)
  7701. //{
  7702. // CNumericLocalesSetter locales_setter;
  7703. // _BBS_3MF_Importer importer;
  7704. // bool res = importer.load_gcode_3mf_from_stream(data, *model, *plate_data_list, *config, *file_version);
  7705. // importer.log_errors();
  7706. // return res;
  7707. //}
  7708. //
  7709. //bool store_bbs_3mf(StoreParams& store_params)
  7710. //{
  7711. // // All export should use "C" locales for number formatting.
  7712. // CNumericLocalesSetter locales_setter;
  7713. //
  7714. // if (store_params.path == nullptr || store_params.model == nullptr)
  7715. // return false;
  7716. //
  7717. // _BBS_3MF_Exporter exporter;
  7718. // bool res = exporter.save_model_to_file(store_params);
  7719. // if (!res)
  7720. // exporter.log_errors();
  7721. //
  7722. // return res;
  7723. //}
  7724. //
  7725. ////BBS: release plate data list
  7726. //void release_PlateData_list(PlateDataPtrs& plate_data_list)
  7727. //{
  7728. // //clear
  7729. // for (unsigned int i = 0; i < plate_data_list.size(); i++)
  7730. // {
  7731. // delete plate_data_list[i];
  7732. // }
  7733. // plate_data_list.clear();
  7734. //
  7735. // return;
  7736. //}
  7737. //
  7738. //// backup interface
  7739. //
  7740. //void save_object_mesh(ModelObject& object)
  7741. //{
  7742. // if (!object.get_model() || !object.get_model()->is_need_backup())
  7743. // return;
  7744. // if (object.volumes.empty() || object.instances.empty())
  7745. // return;
  7746. // _BBS_Backup_Manager::get().add_object_mesh(object);
  7747. //}
  7748. //
  7749. //void delete_object_mesh(ModelObject& object)
  7750. //{
  7751. // // not really remove
  7752. // // _BBS_Backup_Manager::get().remove_object_mesh(object);
  7753. //}
  7754. //
  7755. //void backup_soon()
  7756. //{
  7757. // _BBS_Backup_Manager::get().backup_soon();
  7758. //}
  7759. //
  7760. //void remove_backup(Model& model, bool removeAll)
  7761. //{
  7762. // _BBS_Backup_Manager::get().remove_backup(model, removeAll);
  7763. //}
  7764. //
  7765. //void set_backup_interval(long interval)
  7766. //{
  7767. // _BBS_Backup_Manager::get().set_interval(interval);
  7768. //}
  7769. //
  7770. //void set_backup_callback(std::function<void(int)> callback)
  7771. //{
  7772. // _BBS_Backup_Manager::get().set_post_callback(callback);
  7773. //}
  7774. //
  7775. //void run_backup_ui_tasks()
  7776. //{
  7777. // _BBS_Backup_Manager::get().run_ui_tasks();
  7778. //}
  7779. //
  7780. //bool has_restore_data(std::string & path, std::string& origin)
  7781. //{
  7782. // if (path.empty()) {
  7783. // origin = "<lock>";
  7784. // return false;
  7785. // }
  7786. // if (boost::filesystem::exists(path + "/lock.txt")) {
  7787. // std::string pid;
  7788. // load_string_file(path + "/lock.txt", pid);
  7789. // try {
  7790. // if (get_process_name(boost::lexical_cast<int>(pid)) ==
  7791. // get_process_name(0)) {
  7792. // origin = "<lock>";
  7793. // return false;
  7794. // }
  7795. // }
  7796. // catch (...) {
  7797. // return false;
  7798. // }
  7799. // }
  7800. // std::string file3mf = path + "/.3mf";
  7801. // if (!boost::filesystem::exists(file3mf))
  7802. // return false;
  7803. // try {
  7804. // if (boost::filesystem::exists(path + "/origin.txt"))
  7805. // load_string_file(path + "/origin.txt", origin);
  7806. // }
  7807. // catch (...) {
  7808. // }
  7809. // path = file3mf;
  7810. // return true;
  7811. //}
  7812. //
  7813. //void put_other_changes()
  7814. //{
  7815. // _BBS_Backup_Manager::get().put_other_changes();
  7816. //}
  7817. //
  7818. //void clear_other_changes(bool backup)
  7819. //{
  7820. // _BBS_Backup_Manager::get().clear_other_changes(backup);
  7821. //}
  7822. //
  7823. //bool has_other_changes(bool backup)
  7824. //{
  7825. // return _BBS_Backup_Manager::get().has_other_changes(backup);
  7826. //}
  7827. //
  7828. //SaveObjectGaurd::SaveObjectGaurd(ModelObject& object)
  7829. //{
  7830. // _BBS_Backup_Manager::get().push_object_gaurd(object);
  7831. //}
  7832. //
  7833. //SaveObjectGaurd::~SaveObjectGaurd()
  7834. //{
  7835. // _BBS_Backup_Manager::get().pop_object_gaurd();
  7836. //}
  7837. //
  7838. //namespace{
  7839. //
  7840. //// Conversion with bidirectional map
  7841. //// F .. first, S .. second
  7842. //template<typename F, typename S>
  7843. //F bimap_cvt(const boost::bimap<F, S> &bmap, S s, const F & def_value) {
  7844. // const auto &map = bmap.right;
  7845. // auto found_item = map.find(s);
  7846. //
  7847. // // only for back and forward compatibility
  7848. // assert(found_item != map.end());
  7849. // if (found_item == map.end())
  7850. // return def_value;
  7851. //
  7852. // return found_item->second;
  7853. //}
  7854. //
  7855. //template<typename F, typename S>
  7856. //S bimap_cvt(const boost::bimap<F, S> &bmap, F f, const S &def_value)
  7857. //{
  7858. // const auto &map = bmap.left;
  7859. // auto found_item = map.find(f);
  7860. //
  7861. // // only for back and forward compatibility
  7862. // assert(found_item != map.end());
  7863. // if (found_item == map.end())
  7864. // return def_value;
  7865. //
  7866. // return found_item->second;
  7867. //}
  7868. //
  7869. //} // namespace
  7870. /// <summary>
  7871. /// TextConfiguration serialization //Susi_not_impl
  7872. /// </summary>
  7873. //const TextConfigurationSerialization::TypeToName TextConfigurationSerialization::type_to_name =
  7874. // boost::assign::list_of<TypeToName::relation>
  7875. // (EmbossStyle::Type::file_path, "file_name")
  7876. // (EmbossStyle::Type::wx_win_font_descr, "wxFontDescriptor_Windows")
  7877. // (EmbossStyle::Type::wx_lin_font_descr, "wxFontDescriptor_Linux")
  7878. // (EmbossStyle::Type::wx_mac_font_descr, "wxFontDescriptor_MacOsX");
  7879. //
  7880. //const TextConfigurationSerialization::HorizontalAlignToName TextConfigurationSerialization::horizontal_align_to_name =
  7881. // boost::assign::list_of<HorizontalAlignToName::relation>
  7882. // (FontProp::HorizontalAlign::left, "left")
  7883. // (FontProp::HorizontalAlign::center, "center")
  7884. // (FontProp::HorizontalAlign::right, "right");
  7885. //
  7886. //const TextConfigurationSerialization::VerticalAlignToName TextConfigurationSerialization::vertical_align_to_name =
  7887. // boost::assign::list_of<VerticalAlignToName::relation>
  7888. // (FontProp::VerticalAlign::top, "top")
  7889. // (FontProp::VerticalAlign::center, "middle")
  7890. // (FontProp::VerticalAlign::bottom, "bottom");
  7891. //
  7892. //void TextConfigurationSerialization::to_xml(std::stringstream &stream, const TextConfiguration &tc)
  7893. //{
  7894. // stream << " <" << TEXT_TAG << " ";
  7895. //
  7896. // stream << TEXT_DATA_ATTR << "=\"" << xml_escape_double_quotes_attribute_value(tc.text) << "\" ";
  7897. // // font item
  7898. // const EmbossStyle &style = tc.style;
  7899. // stream << STYLE_NAME_ATTR << "=\"" << xml_escape_double_quotes_attribute_value(style.name) << "\" ";
  7900. // stream << FONT_DESCRIPTOR_ATTR << "=\"" << xml_escape_double_quotes_attribute_value(style.path) << "\" ";
  7901. // constexpr std::string_view dafault_type{"undefined"};
  7902. // std::string_view style_type = bimap_cvt(type_to_name, style.type, dafault_type);
  7903. // stream << FONT_DESCRIPTOR_TYPE_ATTR << "=\"" << style_type << "\" ";
  7904. //
  7905. // // font property
  7906. // const FontProp &fp = tc.style.prop;
  7907. // if (fp.char_gap.has_value())
  7908. // stream << CHAR_GAP_ATTR << "=\"" << *fp.char_gap << "\" ";
  7909. // if (fp.line_gap.has_value())
  7910. // stream << LINE_GAP_ATTR << "=\"" << *fp.line_gap << "\" ";
  7911. //
  7912. // stream << LINE_HEIGHT_ATTR << "=\"" << fp.size_in_mm << "\" ";
  7913. // if (fp.boldness.has_value())
  7914. // stream << BOLDNESS_ATTR << "=\"" << *fp.boldness << "\" ";
  7915. // if (fp.skew.has_value())
  7916. // stream << SKEW_ATTR << "=\"" << *fp.skew << "\" ";
  7917. // if (fp.per_glyph)
  7918. // stream << PER_GLYPH_ATTR << "=\"" << 1 << "\" ";
  7919. // stream << HORIZONTAL_ALIGN_ATTR << "=\"" << bimap_cvt(horizontal_align_to_name, fp.align.first, dafault_type) << "\" ";
  7920. // stream << VERTICAL_ALIGN_ATTR << "=\"" << bimap_cvt(vertical_align_to_name, fp.align.second, dafault_type) << "\" ";
  7921. // if (fp.collection_number.has_value())
  7922. // stream << COLLECTION_NUMBER_ATTR << "=\"" << *fp.collection_number << "\" ";
  7923. // // font descriptor
  7924. // if (fp.family.has_value())
  7925. // stream << FONT_FAMILY_ATTR << "=\"" << *fp.family << "\" ";
  7926. // if (fp.face_name.has_value())
  7927. // stream << FONT_FACE_NAME_ATTR << "=\"" << *fp.face_name << "\" ";
  7928. // if (fp.style.has_value())
  7929. // stream << FONT_STYLE_ATTR << "=\"" << *fp.style << "\" ";
  7930. // if (fp.weight.has_value())
  7931. // stream << FONT_WEIGHT_ATTR << "=\"" << *fp.weight << "\" ";
  7932. //
  7933. // stream << "/>\n"; // end TEXT_TAG
  7934. //}
  7935. //namespace {
  7936. //
  7937. //FontProp::HorizontalAlign read_horizontal_align(const char **attributes, unsigned int num_attributes, const TextConfigurationSerialization::HorizontalAlignToName& horizontal_align_to_name){
  7938. // std::string horizontal_align_str = bbs_get_attribute_value_string(attributes, num_attributes, HORIZONTAL_ALIGN_ATTR);
  7939. //
  7940. // // Back compatibility
  7941. // // PS 2.6.0 do not have align
  7942. // if (horizontal_align_str.empty())
  7943. // return FontProp::HorizontalAlign::center;
  7944. //
  7945. // // Back compatibility
  7946. // // PS 2.6.1 store indices(0|1|2) instead of text for align
  7947. // if (horizontal_align_str.length() == 1) {
  7948. // int horizontal_align_int = 0;
  7949. // if(boost::spirit::qi::parse(horizontal_align_str.c_str(), horizontal_align_str.c_str() + 1, boost::spirit::qi::int_, horizontal_align_int))
  7950. // return static_cast<FontProp::HorizontalAlign>(horizontal_align_int);
  7951. // }
  7952. //
  7953. // return bimap_cvt(horizontal_align_to_name, std::string_view(horizontal_align_str), FontProp::HorizontalAlign::center);
  7954. //}
  7955. //
  7956. //
  7957. //FontProp::VerticalAlign read_vertical_align(const char **attributes, unsigned int num_attributes, const TextConfigurationSerialization::VerticalAlignToName& vertical_align_to_name){
  7958. // std::string vertical_align_str = bbs_get_attribute_value_string(attributes, num_attributes, VERTICAL_ALIGN_ATTR);
  7959. //
  7960. // // Back compatibility
  7961. // // PS 2.6.0 do not have align
  7962. // if (vertical_align_str.empty())
  7963. // return FontProp::VerticalAlign::center;
  7964. //
  7965. // // Back compatibility
  7966. // // PS 2.6.1 store indices(0|1|2) instead of text for align
  7967. // if (vertical_align_str.length() == 1) {
  7968. // int vertical_align_int = 0;
  7969. // if(boost::spirit::qi::parse(vertical_align_str.c_str(), vertical_align_str.c_str() + 1, boost::spirit::qi::int_, vertical_align_int))
  7970. // return static_cast<FontProp::VerticalAlign>(vertical_align_int);
  7971. // }
  7972. //
  7973. // return bimap_cvt(vertical_align_to_name, std::string_view(vertical_align_str), FontProp::VerticalAlign::center);
  7974. //}
  7975. //
  7976. //} // namespace
  7977. //
  7978. //std::optional<TextConfiguration> TextConfigurationSerialization::read(const char **attributes, unsigned int num_attributes)
  7979. //{
  7980. // FontProp fp;
  7981. // int char_gap = bbs_get_attribute_value_int(attributes, num_attributes, CHAR_GAP_ATTR);
  7982. // if (char_gap != 0) fp.char_gap = char_gap;
  7983. // int line_gap = bbs_get_attribute_value_int(attributes, num_attributes, LINE_GAP_ATTR);
  7984. // if (line_gap != 0) fp.line_gap = line_gap;
  7985. // float boldness = bbs_get_attribute_value_float(attributes, num_attributes, BOLDNESS_ATTR);
  7986. // if (std::fabs(boldness) > std::numeric_limits<float>::epsilon())
  7987. // fp.boldness = boldness;
  7988. // float skew = bbs_get_attribute_value_float(attributes, num_attributes, SKEW_ATTR);
  7989. // if (std::fabs(skew) > std::numeric_limits<float>::epsilon())
  7990. // fp.skew = skew;
  7991. // int per_glyph = bbs_get_attribute_value_int(attributes, num_attributes, PER_GLYPH_ATTR);
  7992. // if (per_glyph == 1) fp.per_glyph = true;
  7993. //
  7994. // fp.align = FontProp::Align(
  7995. // read_horizontal_align(attributes, num_attributes, horizontal_align_to_name),
  7996. // read_vertical_align(attributes, num_attributes, vertical_align_to_name));
  7997. //
  7998. // int collection_number = bbs_get_attribute_value_int(attributes, num_attributes, COLLECTION_NUMBER_ATTR);
  7999. // if (collection_number > 0) fp.collection_number = static_cast<unsigned int>(collection_number);
  8000. //
  8001. // fp.size_in_mm = bbs_get_attribute_value_float(attributes, num_attributes, LINE_HEIGHT_ATTR);
  8002. //
  8003. // std::string family = bbs_get_attribute_value_string(attributes, num_attributes, FONT_FAMILY_ATTR);
  8004. // if (!family.empty()) fp.family = family;
  8005. // std::string face_name = bbs_get_attribute_value_string(attributes, num_attributes, FONT_FACE_NAME_ATTR);
  8006. // if (!face_name.empty()) fp.face_name = face_name;
  8007. // std::string style = bbs_get_attribute_value_string(attributes, num_attributes, FONT_STYLE_ATTR);
  8008. // if (!style.empty()) fp.style = style;
  8009. // std::string weight = bbs_get_attribute_value_string(attributes, num_attributes, FONT_WEIGHT_ATTR);
  8010. // if (!weight.empty()) fp.weight = weight;
  8011. //
  8012. // std::string style_name = bbs_get_attribute_value_string(attributes, num_attributes, STYLE_NAME_ATTR);
  8013. // std::string font_descriptor = bbs_get_attribute_value_string(attributes, num_attributes, FONT_DESCRIPTOR_ATTR);
  8014. // std::string type_str = bbs_get_attribute_value_string(attributes, num_attributes, FONT_DESCRIPTOR_TYPE_ATTR);
  8015. // EmbossStyle::Type type = bimap_cvt(type_to_name, std::string_view{type_str}, EmbossStyle::Type::undefined);
  8016. //
  8017. // std::string text = bbs_get_attribute_value_string(attributes, num_attributes, TEXT_DATA_ATTR);
  8018. // EmbossStyle es{style_name, std::move(font_descriptor), type, std::move(fp)};
  8019. // return TextConfiguration{std::move(es), std::move(text)};
  8020. //}
  8021. //
  8022. //EmbossShape TextConfigurationSerialization::read_old(const char **attributes, unsigned int num_attributes)
  8023. //{
  8024. // EmbossShape es;
  8025. // std::string fix_tr_mat_str = bbs_get_attribute_value_string(attributes, num_attributes, TRANSFORM_ATTR);
  8026. // if (!fix_tr_mat_str.empty())
  8027. // es.fix_3mf_tr = bbs_get_transform_from_3mf_specs_string(fix_tr_mat_str);
  8028. //
  8029. //
  8030. // if (bbs_get_attribute_value_int(attributes, num_attributes, USE_SURFACE_ATTR) == 1)
  8031. // es.projection.use_surface = true;
  8032. //
  8033. // es.projection.depth = bbs_get_attribute_value_float(attributes, num_attributes, DEPTH_ATTR);
  8034. //
  8035. // int use_surface = bbs_get_attribute_value_int(attributes, num_attributes, USE_SURFACE_ATTR);
  8036. // if (use_surface == 1)
  8037. // es.projection.use_surface = true;
  8038. //
  8039. // return es;
  8040. //}
  8041. //
  8042. //namespace {
  8043. //Transform3d create_fix(const std::optional<Transform3d> &prev, const ModelVolume &volume)
  8044. //{
  8045. // // IMPROVE: check if volume was modified (translated, rotated OR scaled)
  8046. // // when no change do not calculate transformation only store original fix matrix
  8047. //
  8048. // // Create transformation used after load actual stored volume
  8049. // // Orca: do not bake volume transformation into meshes
  8050. // // const Transform3d &actual_trmat = volume.get_matrix();
  8051. // const Transform3d& actual_trmat = Transform3d::Identity();
  8052. //
  8053. // const auto &vertices = volume.mesh().its.vertices;
  8054. // Vec3d min = actual_trmat * vertices.front().cast<double>();
  8055. // Vec3d max = min;
  8056. // for (const Vec3f &v : vertices) {
  8057. // Vec3d vd = actual_trmat * v.cast<double>();
  8058. // for (size_t i = 0; i < 3; ++i) {
  8059. // if (min[i] > vd[i])
  8060. // min[i] = vd[i];
  8061. // if (max[i] < vd[i])
  8062. // max[i] = vd[i];
  8063. // }
  8064. // }
  8065. // Vec3d center = (max + min) / 2;
  8066. // Transform3d post_trmat = Transform3d::Identity();
  8067. // post_trmat.translate(center);
  8068. //
  8069. // Transform3d fix_trmat = actual_trmat.inverse() * post_trmat;
  8070. // if (!prev.has_value())
  8071. // return fix_trmat;
  8072. //
  8073. // // check whether fix somehow differ previous
  8074. // if (fix_trmat.isApprox(Transform3d::Identity(), 1e-5))
  8075. // return *prev;
  8076. //
  8077. // return *prev * fix_trmat;
  8078. //}
  8079. //
  8080. //bool to_xml(std::stringstream &stream, /*const EmbossShape::SvgFile &svg, */const ModelVolume &volume, mz_zip_archive &archive){
  8081. // if (svg.path_in_3mf.empty())
  8082. // return true; // EmbossedText OR unwanted store .svg file into .3mf (protection of copyRight)
  8083. //
  8084. // if (!svg.path.empty())
  8085. // stream << SVG_FILE_PATH_ATTR << "=\"" << xml_escape_double_quotes_attribute_value(svg.path) << "\" ";
  8086. // stream << SVG_FILE_PATH_IN_3MF_ATTR << "=\"" << xml_escape_double_quotes_attribute_value(svg.path_in_3mf) << "\" ";
  8087. //
  8088. // std::shared_ptr<std::string> file_data = svg.file_data;
  8089. // assert(file_data != nullptr);
  8090. // if (file_data == nullptr && !svg.path.empty())
  8091. // file_data = read_from_disk(svg.path);
  8092. // if (file_data == nullptr) {
  8093. // BOOST_LOG_TRIVIAL(warning) << "Can't write svg file no filedata";
  8094. // return false;
  8095. // }
  8096. // const std::string &file_data_str = *file_data;
  8097. //
  8098. // return mz_zip_writer_add_mem(&archive, svg.path_in_3mf.c_str(),
  8099. // (const void *) file_data_str.c_str(), file_data_str.size(), MZ_DEFAULT_COMPRESSION);
  8100. //}
  8101. //
  8102. //} // namespace
  8103. //
  8104. //void to_xml(std::stringstream &stream, const EmbossShape &es, const ModelVolume &volume, mz_zip_archive &archive)
  8105. //{
  8106. // stream << " <" << SHAPE_TAG << " ";
  8107. // if (es.svg_file.has_value())
  8108. // if(!to_xml(stream, *es.svg_file, volume, archive))
  8109. // BOOST_LOG_TRIVIAL(warning) << "Can't write svg file defiden embossed shape into 3mf";
  8110. //
  8111. // stream << SHAPE_SCALE_ATTR << "=\"" << es.scale << "\" ";
  8112. //
  8113. // if (!es.final_shape.is_healed)
  8114. // stream << UNHEALED_ATTR << "=\"" << 1 << "\" ";
  8115. //
  8116. // // projection
  8117. // const EmbossProjection &p = es.projection;
  8118. // stream << DEPTH_ATTR << "=\"" << p.depth << "\" ";
  8119. // if (p.use_surface)
  8120. // stream << USE_SURFACE_ATTR << "=\"" << 1 << "\" ";
  8121. //
  8122. // // FIX of baked transformation
  8123. // Transform3d fix = create_fix(es.fix_3mf_tr, volume);
  8124. // stream << TRANSFORM_ATTR << "=\"";
  8125. // _BBS_3MF_Exporter::add_transformation(stream, fix);
  8126. // stream << "\" ";
  8127. //
  8128. // stream << "/>\n"; // end SHAPE_TAG
  8129. //}
  8130. //
  8131. //std::optional<EmbossShape> read_emboss_shape(const char **attributes, unsigned int num_attributes) {
  8132. // double scale = bbs_get_attribute_value_float(attributes, num_attributes, SHAPE_SCALE_ATTR);
  8133. // int unhealed = bbs_get_attribute_value_int(attributes, num_attributes, UNHEALED_ATTR);
  8134. // bool is_healed = unhealed != 1;
  8135. //
  8136. // EmbossProjection projection;
  8137. // projection.depth = bbs_get_attribute_value_float(attributes, num_attributes, DEPTH_ATTR);
  8138. // if (is_approx(projection.depth, 0.))
  8139. // projection.depth = 10.;
  8140. //
  8141. // int use_surface = bbs_get_attribute_value_int(attributes, num_attributes, USE_SURFACE_ATTR);
  8142. // if (use_surface == 1)
  8143. // projection.use_surface = true;
  8144. //
  8145. // std::optional<Transform3d> fix_tr_mat;
  8146. // std::string fix_tr_mat_str = bbs_get_attribute_value_string(attributes, num_attributes, TRANSFORM_ATTR);
  8147. // if (!fix_tr_mat_str.empty()) {
  8148. // fix_tr_mat = bbs_get_transform_from_3mf_specs_string(fix_tr_mat_str);
  8149. // }
  8150. //
  8151. // std::string file_path = bbs_get_attribute_value_string(attributes, num_attributes, SVG_FILE_PATH_ATTR);
  8152. // std::string file_path_3mf = bbs_get_attribute_value_string(attributes, num_attributes, SVG_FILE_PATH_IN_3MF_ATTR);
  8153. //
  8154. // // MayBe: store also shapes to not store svg
  8155. // // But be carefull curve will be lost -> scale will not change sampling
  8156. // // shapes could be loaded from SVG
  8157. // ExPolygonsWithIds shapes;
  8158. // // final shape could be calculated from shapes
  8159. // HealedExPolygons final_shape;
  8160. // final_shape.is_healed = is_healed;
  8161. //
  8162. // EmbossShape::SvgFile svg{file_path, file_path_3mf};
  8163. // return EmbossShape{std::move(shapes), std::move(final_shape), scale, std::move(projection), std::move(fix_tr_mat), std::move(svg)};
  8164. //}
  8165. } // namespace Slic3r